From nobody Thu Apr 9 20:26:05 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5C4E0373BFC for ; Sat, 4 Apr 2026 05:40:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775281251; cv=none; b=cypFyaKfZVy67OZKCBCy4ygJzPSYn61ctZGW0Bx0d18+x/XmuF5456Ii/ErOjGoIWJqG2Mv3N+PiNJ7Y1vyys/rrnBMjOZ+1CaymabRr2/cFx2pzqsYp4TMC6FM0w9d423aHJcOG9AQPIFERakBYT4FBXdbq+eJRcUqdOW4cjz0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775281251; c=relaxed/simple; bh=j5yieE8hSYlV53kAvca5F51yaD1CYPkyvtF3GaMnv3Q=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=JQRaNrPsnzaWpcYf83pJ0hPEful1TuNG2UF0FTZxcmNMJ+mZMfUbNWPyJ3OZ4/ZTRjegblHQkQZAEJRUs3LPXvSupjwE7ZONjHWyCi0lOEa/3EwQzUPVTKKTHPVkDm/kh5vyfSkY4+xcxdYmZLDxSfee4B6HHwSl52QvnibYr0o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=r4++vhQ4; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="r4++vhQ4" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2c0ba59a830so3243584eec.0 for ; Fri, 03 Apr 2026 22:40:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1775281248; x=1775886048; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=g924aB07abUjt2qM0inH+SjTzydsVlP57tK6YtzR+iU=; b=r4++vhQ4ToLa2YKeM0SZZzYxf9xK34PC4/9GtCJO6xLl5bT48dfKE01GO0X304KRgT YEQJMXBA0PN7xxg+YDshVeYF4SWH7ds3nsHCNnLJffeol118L7jRyYNKZ5m6ZCD6qXUs Ey/wsMlHoerbintbY7Kt9bsUi/IqMRTd7lzYnJMQ4mxoLuo0nRwkBABcOa6Hs5r7rX3n BnkgUr0Q+ACEFJejahCprJ2fijpbyGKkOXqVHeSd9d7FzMgegcacbOa1Tq/osnVQjDVI Qn5/8LUsRbpfUOOoKCTLJUfWNG6gkjFM0T4aR3iYXUzbWNIu5E2GpQSD74r0Omi0kj7g LONQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775281248; x=1775886048; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=g924aB07abUjt2qM0inH+SjTzydsVlP57tK6YtzR+iU=; b=dYrSFmMYSzrsbU/byq+8p9YMNHtnlRdM2/awlG0BJCw3o6qNct+Zp8c4zPFddya1Kz 32ohuYQ3W9VxFxlW5y/V7KmneAGnh/I4vsiR2DD/7WT+rOKBFKbu5wSaczDT9s9LWJ7B YleAENue1vBui8H6bE2hOkmgTvOf2hlYH7i6uojNFloIVh5qYNunyOTFeyprkczKa3G7 4zMqxiXgrSiAMjUUeYqLJ5vvh3ToBseu3+GBYKYROsCBLaCfGId0fXGw2Hnx71kKcvgF tMqKYdhTGXL6Nk3x1yoPfDRe4pxH/mj9KAjaL+cYwS6WHm0EkMjnB24RcqKMFaDCnb9w koXQ== X-Forwarded-Encrypted: i=1; AJvYcCUN0NBQOvnGankxLvPSAjmM/PE5yCp2FGt6O2btauxQug/S8mI9csDTqHoWnS1jx5eG6B2vMucNOinc3YE=@vger.kernel.org X-Gm-Message-State: AOJu0Ywvovom39eot3B5f6sxSCopIJugaV0facF0fwUj5RlpicPELR1/ fACOpqvMolMGQY8ykarG515NMHVPSGcUEK/xsnfSue8M7oNUrvOg7GZnWapDshQCQhnyZdT/D4I hK6SrZ48sSw== X-Received: from dyff26.prod.google.com ([2002:a05:693c:805a:b0:2c3:cfb2:1036]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:a287:b0:2be:d6e:6ba3 with SMTP id 5a478bee46e88-2cbfc764f36mr2574888eec.24.1775281248135; Fri, 03 Apr 2026 22:40:48 -0700 (PDT) Date: Fri, 3 Apr 2026 22:40:25 -0700 In-Reply-To: <20260404054032.1538095-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260305221927.3237145-1-irogers@google.com> <20260404054032.1538095-1-irogers@google.com> X-Mailer: git-send-email 2.53.0.1213.gd9a14994de-goog Message-ID: <20260404054032.1538095-2-irogers@google.com> Subject: [PATCH v3 1/8] perf unwind: Refactor get_entries to allow dynamic libdw/libunwind selection From: Ian Rogers To: acme@kernel.org Cc: irogers@google.com, 9erthalion6@gmail.com, adrian.hunter@intel.com, alex@ghiti.fr, alexander.shishkin@linux.intel.com, andrew.jones@oss.qualcomm.com, aou@eecs.berkeley.edu, atrajeev@linux.ibm.com, blakejones@google.com, ctshao@google.com, dapeng1.mi@linux.intel.com, howardchu95@gmail.com, james.clark@linaro.org, john.g.garry@oracle.com, jolsa@kernel.org, leo.yan@linux.dev, libunwind-devel@nongnu.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-riscv@lists.infradead.org, mingo@redhat.com, namhyung@kernel.org, palmer@dabbelt.com, peterz@infradead.org, pjw@kernel.org, shimin.guo@skydio.com, tglozar@redhat.com, tmricht@linux.ibm.com, will@kernel.org, yuzhuo@google.com Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Currently, both libdw and libunwind define 'unwind__get_entries'. This causes a duplicate symbol build failure when both are compiled into perf. This commit refactors the DWARF unwind post-processing to be configurable at runtime via the .perfconfig file option 'unwind.style', or using the argument '--unwind-style' in the commands 'perf report', 'perf script' and 'perf inject', in a similar manner to the addr2line or the disassembler style. The file 'tools/perf/util/unwind.c' adds the top-level dispatch function 'unwind__get_entries'. The backend implementations are renamed to 'libdw__get_entries' and 'libunwind__get_entries'. Both are attempted as fallbacks if not configured, or if the primary backend fails. Fixes: 2e9191573a69 ("perf build: Remove NO_LIBDW_DWARF_UNWIND option") Signed-off-by: Ian Rogers --- tools/perf/builtin-inject.c | 4 ++ tools/perf/builtin-report.c | 4 ++ tools/perf/builtin-script.c | 4 ++ tools/perf/util/Build | 1 + tools/perf/util/symbol_conf.h | 10 +++ tools/perf/util/unwind-libdw.c | 2 +- tools/perf/util/unwind-libunwind.c | 2 +- tools/perf/util/unwind.c | 98 ++++++++++++++++++++++++++++++ tools/perf/util/unwind.h | 63 ++++++++++++------- 9 files changed, 163 insertions(+), 25 deletions(-) create mode 100644 tools/perf/util/unwind.c diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 5b29f4296861..9ad681b3c0dc 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -26,6 +26,7 @@ #include "util/synthetic-events.h" #include "util/thread.h" #include "util/namespaces.h" +#include "util/unwind.h" #include "util/util.h" #include "util/tsc.h" =20 @@ -2539,6 +2540,9 @@ int cmd_inject(int argc, const char **argv) OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory", "guest mount directory under which every guest os" " instance has a subdir"), + OPT_CALLBACK(0, "unwind-style", NULL, "unwind style", + "unwind styles (libdw,libunwind)", + unwind__option), OPT_BOOLEAN(0, "convert-callchain", &inject.convert_callchain, "Generate callchains using DWARF and drop register/stack data"), OPT_END() diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 95c0bdba6b11..0b0966d94128 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -48,6 +48,7 @@ #include "util/time-utils.h" #include "util/auxtrace.h" #include "util/units.h" +#include "util/unwind.h" #include "util/util.h" // perf_tip() #include "ui/ui.h" #include "ui/progress.h" @@ -1449,6 +1450,9 @@ int cmd_report(int argc, const char **argv) OPT_CALLBACK(0, "addr2line-style", NULL, "addr2line style", "addr2line styles (libdw,llvm,libbfd,addr2line)", report_parse_addr2line_config), + OPT_CALLBACK(0, "unwind-style", NULL, "unwind style", + "unwind styles (libdw,libunwind)", + unwind__option), OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle, "Symbol demangling. Enabled by default, use --no-demangle to disable= ."), OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 622130d3aed4..ee6131315f96 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -63,6 +63,7 @@ #include #include "util/dlfilter.h" #include "util/record.h" +#include "util/unwind.h" #include "util/util.h" #include "util/cgroup.h" #include "util/annotate.h" @@ -4155,6 +4156,9 @@ int cmd_script(int argc, const char **argv) "Enable symbol demangling"), OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, "Enable kernel symbol demangling"), + OPT_CALLBACK(0, "unwind-style", NULL, "unwind style", + "unwind styles (libdw,libunwind)", + unwind__option), OPT_STRING(0, "addr2line", &symbol_conf.addr2line_path, "path", "addr2line binary to use for line numbers"), OPT_STRING(0, "time", &script.time_str, "str", diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 70cc91d00804..01edfccebb88 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -216,6 +216,7 @@ ifndef CONFIG_SETNS perf-util-y +=3D setns.o endif =20 +perf-util-y +=3D unwind.o perf-util-$(CONFIG_LIBDW) +=3D probe-finder.o perf-util-$(CONFIG_LIBDW) +=3D dwarf-aux.o perf-util-$(CONFIG_LIBDW) +=3D dwarf-regs.o diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h index ac1b444a8fd8..004c472cdfb1 100644 --- a/tools/perf/util/symbol_conf.h +++ b/tools/perf/util/symbol_conf.h @@ -9,6 +9,15 @@ struct strlist; struct intlist; =20 +enum unwind_style { + UNWIND_STYLE_UNKNOWN =3D 0, + UNWIND_STYLE_LIBDW, + UNWIND_STYLE_LIBUNWIND, +}; + +#define MAX_UNWIND_STYLE (UNWIND_STYLE_LIBUNWIND + 1) + + enum a2l_style { A2L_STYLE_UNKNOWN =3D 0, A2L_STYLE_LIBDW, @@ -80,6 +89,7 @@ struct symbol_conf { *bt_stop_list_str; const char *addr2line_path; enum a2l_style addr2line_style[MAX_A2L_STYLE]; + enum unwind_style unwind_style[MAX_UNWIND_STYLE]; unsigned long time_quantum; struct strlist *dso_list, *comm_list, diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index 05e8e68bd49c..d8a5b7d54192 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c @@ -339,7 +339,7 @@ frame_callback(Dwfl_Frame *state, void *arg) DWARF_CB_ABORT : DWARF_CB_OK; } =20 -int unwind__get_entries(unwind_entry_cb_t cb, void *arg, +int libdw__get_entries(unwind_entry_cb_t cb, void *arg, struct thread *thread, struct perf_sample *data, int max_stack, diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-li= bunwind.c index cb8be6acfb6f..a0016b897dae 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -79,7 +79,7 @@ void unwind__finish_access(struct maps *maps) ops->finish_access(maps); } =20 -int unwind__get_entries(unwind_entry_cb_t cb, void *arg, +int libunwind__get_entries(unwind_entry_cb_t cb, void *arg, struct thread *thread, struct perf_sample *data, int max_stack, bool best_effort) diff --git a/tools/perf/util/unwind.c b/tools/perf/util/unwind.c new file mode 100644 index 000000000000..86c2d1692d08 --- /dev/null +++ b/tools/perf/util/unwind.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "debug.h" +#include "symbol_conf.h" +#include "unwind.h" +#include +#include +#include + +int unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, void *arg __m= aybe_unused, + struct thread *thread __maybe_unused, + struct perf_sample *data __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused) +{ + int ret =3D 0; + +#if defined(HAVE_LIBDW_SUPPORT) || defined(HAVE_LIBUNWIND_SUPPORT) + if (symbol_conf.unwind_style[0] =3D=3D UNWIND_STYLE_UNKNOWN) { + int i =3D 0; +#ifdef HAVE_LIBDW_SUPPORT + symbol_conf.unwind_style[i++] =3D UNWIND_STYLE_LIBDW; +#endif +#ifdef HAVE_LIBUNWIND_SUPPORT + symbol_conf.unwind_style[i++] =3D UNWIND_STYLE_LIBUNWIND; +#endif + } +#endif //defined(HAVE_LIBDW_SUPPORT) || defined(HAVE_LIBUNWIND_SUPPORT) + + for (size_t i =3D 0; i < ARRAY_SIZE(symbol_conf.unwind_style); i++) { + switch (symbol_conf.unwind_style[i]) { + case UNWIND_STYLE_LIBDW: + ret =3D libdw__get_entries(cb, arg, thread, data, max_stack, best_effor= t); + break; + case UNWIND_STYLE_LIBUNWIND: + ret =3D libunwind__get_entries(cb, arg, thread, data, max_stack, best_e= ffort); + break; + case UNWIND_STYLE_UNKNOWN: + default: +#if !defined(HAVE_LIBDW_SUPPORT) && !defined(HAVE_LIBUNWIND_SUPPORT) + pr_warning_once( + "Error: dwarf unwinding not supported, build perf with libdw or libunw= ind.\n"); +#endif + ret =3D -1; + break; + } + if (ret =3D=3D 0) + break; + } + return ret; +} + +int unwind__configure(const char *var, const char *value, void *cb __maybe= _unused) +{ + static const char * const unwind_style_names[] =3D { + [UNWIND_STYLE_LIBDW] =3D "libdw", + [UNWIND_STYLE_LIBUNWIND] =3D "libunwind", + NULL + }; + char *s, *p, *saveptr; + size_t i =3D 0; + + if (strcmp(var, "unwind.style")) + return 0; + + if (!value) + return -1; + + s =3D strdup(value); + if (!s) + return -1; + + p =3D strtok_r(s, ",", &saveptr); + while (p && i < ARRAY_SIZE(symbol_conf.unwind_style)) { + bool found =3D false; + char *q =3D strim(p); + + for (size_t j =3D UNWIND_STYLE_LIBDW; j < MAX_UNWIND_STYLE; j++) { + if (!strcasecmp(q, unwind_style_names[j])) { + symbol_conf.unwind_style[i++] =3D j; + found =3D true; + break; + } + } + if (!found) + pr_warning("Unknown unwind style: %s\n", q); + p =3D strtok_r(NULL, ",", &saveptr); + } + + free(s); + return 0; +} + +int unwind__option(const struct option *opt __maybe_unused, + const char *arg, + int unset __maybe_unused) +{ + return unwind__configure("unwind.style", arg, NULL); +} diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index 9f7164c6d9aa..ac0776e39f84 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h @@ -4,9 +4,10 @@ =20 #include #include -#include "util/map_symbol.h" +#include "map_symbol.h" =20 struct maps; +struct option; struct perf_sample; struct thread; =20 @@ -26,7 +27,9 @@ struct unwind_libunwind_ops { struct perf_sample *data, int max_stack, bool best_effort); }; =20 -#ifdef HAVE_DWARF_UNWIND_SUPPORT +int unwind__configure(const char *var, const char *value, void *cb); +int unwind__option(const struct option *opt, const char *arg, int unset); + /* * When best_effort is set, don't report errors and fail silently. This co= uld * be expanded in the future to be more permissive about things other than @@ -36,8 +39,31 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, struct thread *thread, struct perf_sample *data, int max_stack, bool best_effort); -/* libunwind specific */ + +#ifdef HAVE_LIBDW_SUPPORT +int libdw__get_entries(unwind_entry_cb_t cb, void *arg, + struct thread *thread, + struct perf_sample *data, int max_stack, + bool best_effort); +#else +#include "debug.h" +static inline int libdw__get_entries(unwind_entry_cb_t cb __maybe_unused, = void *arg __maybe_unused, + struct thread *thread __maybe_unused, + struct perf_sample *data __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused) +{ + pr_err("Error: libdw dwarf unwinding not built into perf\n"); + return -1; +} +#endif + #ifdef HAVE_LIBUNWIND_SUPPORT +/* libunwind specific */ +int libunwind__get_entries(unwind_entry_cb_t cb, void *arg, + struct thread *thread, + struct perf_sample *data, int max_stack, + bool best_effort); #ifndef LIBUNWIND__ARCH_REG_ID #define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arch_reg_id(regnum) #endif @@ -47,26 +73,16 @@ int unwind__prepare_access(struct maps *maps, struct ma= p *map, bool *initialized void unwind__flush_access(struct maps *maps); void unwind__finish_access(struct maps *maps); #else -static inline int unwind__prepare_access(struct maps *maps __maybe_unused, - struct map *map __maybe_unused, - bool *initialized __maybe_unused) -{ - return 0; -} - -static inline void unwind__flush_access(struct maps *maps __maybe_unused) = {} -static inline void unwind__finish_access(struct maps *maps __maybe_unused)= {} -#endif -#else -static inline int -unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, - void *arg __maybe_unused, - struct thread *thread __maybe_unused, - struct perf_sample *data __maybe_unused, - int max_stack __maybe_unused, - bool best_effort __maybe_unused) +#include "debug.h" +static inline int libunwind__get_entries(unwind_entry_cb_t cb __maybe_unus= ed, + void *arg __maybe_unused, + struct thread *thread __maybe_unused, + struct perf_sample *data __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused) { - return 0; + pr_err("Error: libunwind dwarf unwinding not built into perf\n"); + return -1; } =20 static inline int unwind__prepare_access(struct maps *maps __maybe_unused, @@ -78,5 +94,6 @@ static inline int unwind__prepare_access(struct maps *map= s __maybe_unused, =20 static inline void unwind__flush_access(struct maps *maps __maybe_unused) = {} static inline void unwind__finish_access(struct maps *maps __maybe_unused)= {} -#endif /* HAVE_DWARF_UNWIND_SUPPORT */ +#endif + #endif /* __UNWIND_H */ --=20 2.53.0.1213.gd9a14994de-goog From nobody Thu Apr 9 20:26:05 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5A78F37755A for ; Sat, 4 Apr 2026 05:40:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775281252; cv=none; b=UEnabu45VRrZV6mkkT8SvAXSV0IImIAASuw1IZM+RkbPY5QhsXvcmR855zN0iZ9NIwGidJxS80UMsUPGvFVRyiyVU0miNIAnn2fYRJQyIi9QPVPClETChjv36Ti2gc2GXRkrlVrN6+lCdY5zMOVSs2fvJ7AEB53QwDSGcFvQ9UA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775281252; c=relaxed/simple; bh=JeSLyEyVbGrzHWl/OHpa8voq5Wa/zBgvBxLzEbB0v1I=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=ZyJJO1a3mGyIzS7e1qhnwndarP1FWgwZvnHPQDrNuhDsi+dYtGHyC7+yeAgv/fx+27gMFh55BCWGVenVQiuTGUFue6dBKGKoKAgvFjv79XlrP9HKL8z2ZZ8lDee7OFElgdQZwA15NfEBzhSIE+PKvNY6VqP9++6YLLUe/8xXXo0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=JjAHrycn; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="JjAHrycn" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2bdf6fe90a9so3377921eec.1 for ; Fri, 03 Apr 2026 22:40:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1775281250; x=1775886050; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=4gq5oV2Uwm1Ltd6b9FECKA8ZBvmmiMQwyALIUU23nRA=; b=JjAHrycn4fc6ja+N0DsbhrcZ/aXewZeClhEaFXRD0+YHy0uXngW4fEbRppTZ3XE0EO 2wPKp15PF1UyzyBQ/MEbcRoF4m3UGhWZD4b1xz1AEqR4v0O5XVTnxZs0Vosiwn5m4Jsq gRj1S36jInBwSsQEOanAr2jt4s6s1KNyn+IbAhuOpABzYiC3MuZWTqvRfR6yFoXYsx2H yTjYmYXxuSC1Sj4uE1hjwJnxA9Vd4f8dBcR3updsMwj+f4Y3v26WlybKlagGFaI9Jor7 R/+rwhA7WKGkZDk3PdXwnRiqJJNU95KIEdjCy/GIGi9Kqd2LzGcaZz8RSo0H7c4pSBH8 Ehdg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775281250; x=1775886050; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=4gq5oV2Uwm1Ltd6b9FECKA8ZBvmmiMQwyALIUU23nRA=; b=A6ZZmF0O2x73UNWZC8m6vSnIfnMK3XGr8z24xB9cF75PaMHUTzVEG4UhdC1YYBh4Qv 4RbkN9vFLPrmxBTFY1/wgiH8tr9znHClYlUZL0W9pcUKSSOydcrKc9kKrzEZ1yTy/g/w hdIkToN1EaBi5R4KSg6UjUg3fjmcXx1N5hqzLHwrP593wrqd3HMsiIKo+SaJit5txDWb B28utgFEiNQGbFmvWPVuy3Gk4pxWpUeSwHCNn9Y3h/55G3Ez+OjFhiFK5fCE5tclrZNJ QzlToYKvGmxl5I/b4jdKsU8t7s5bi5NuYr+pYoCzyRrfV/AUdHM5/hoYGasYxbJLQaE9 HMNg== X-Forwarded-Encrypted: i=1; AJvYcCUiYAcdoAaW1yatDhk918rvKG6RMydbPcwxUYhRgvEVUkHdZvrol4n2jYY6v//PSazlTFc2meq/UnF8nfs=@vger.kernel.org X-Gm-Message-State: AOJu0Yw3nIH6MCQpZJP7FsFGaFHIpcW7dq0Lg9UsYomdgAada7vCmdIa bSe3LJ2fhw29nY3tKJFER4cGPdF07XAAKBAeHVpd7J2Ui3+m5VH54xNPD6CgYi2z61BfZOFqqJS Oh6AKgGUlLQ== X-Received: from dlbuy23.prod.google.com ([2002:a05:7022:1e17:b0:12c:5db:d6d1]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:fe08:b0:128:d577:dc21 with SMTP id a92af1059eb24-12bfb700f3dmr2718425c88.13.1775281250318; Fri, 03 Apr 2026 22:40:50 -0700 (PDT) Date: Fri, 3 Apr 2026 22:40:26 -0700 In-Reply-To: <20260404054032.1538095-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260305221927.3237145-1-irogers@google.com> <20260404054032.1538095-1-irogers@google.com> X-Mailer: git-send-email 2.53.0.1213.gd9a14994de-goog Message-ID: <20260404054032.1538095-3-irogers@google.com> Subject: [PATCH v3 2/8] perf build loongarch: Remove reference to missing file From: Ian Rogers To: acme@kernel.org Cc: irogers@google.com, 9erthalion6@gmail.com, adrian.hunter@intel.com, alex@ghiti.fr, alexander.shishkin@linux.intel.com, andrew.jones@oss.qualcomm.com, aou@eecs.berkeley.edu, atrajeev@linux.ibm.com, blakejones@google.com, ctshao@google.com, dapeng1.mi@linux.intel.com, howardchu95@gmail.com, james.clark@linaro.org, john.g.garry@oracle.com, jolsa@kernel.org, leo.yan@linux.dev, libunwind-devel@nongnu.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-riscv@lists.infradead.org, mingo@redhat.com, namhyung@kernel.org, palmer@dabbelt.com, peterz@infradead.org, pjw@kernel.org, shimin.guo@skydio.com, tglozar@redhat.com, tmricht@linux.ibm.com, will@kernel.org, yuzhuo@google.com Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The file was removed in commit e62fae9d9e85 ("perf unwind-libdw: Fix a cross-arch unwinding bug") but the Build file not updated. Fixes: e62fae9d9e85 ("perf unwind-libdw: Fix a cross-arch unwinding bug") Signed-off-by: Ian Rogers --- tools/perf/arch/loongarch/util/Build | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/perf/arch/loongarch/util/Build b/tools/perf/arch/loongar= ch/util/Build index 3ad73d0289f3..8d91e78d31c9 100644 --- a/tools/perf/arch/loongarch/util/Build +++ b/tools/perf/arch/loongarch/util/Build @@ -1,4 +1,3 @@ perf-util-y +=3D header.o =20 perf-util-$(CONFIG_LOCAL_LIBUNWIND) +=3D unwind-libunwind.o -perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) +=3D unwind-libdw.o --=20 2.53.0.1213.gd9a14994de-goog From nobody Thu Apr 9 20:26:05 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6331337267D for ; Sat, 4 Apr 2026 05:40:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775281254; cv=none; b=ZNccS7fHGhjNR7/ocw1ttgcMEeSxCdD7BdsR4Qfu/szSGFfcRFfEv/Fw1swc4YfAvCo+Q21FXlaB3+mcjAzH/kZxhD7ObkVvMLB5G7/GjNyFfqO63G4UsI94hZfF6NZ4Uo968Kjg2Dfe7BIYXYMbXJY6I7p1ZmklvLyPqNsUOpM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775281254; c=relaxed/simple; bh=lOpEGFFC2KgK1joOptPcmvB7OTBEM3ldVX7W72IFDtg=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=W1AuxrAJ4K5QqqjN+ggHR1Bz4giHd4hEf5I1zKBUx4TzLBMwLsWSnFrhNoZ/onveUeatY86SosJNXwv+ohyxIhkXlAp2kJnVDQSyVPDJviYazit8UAf0QmBS52Anqcz7J7f+NWZdLs+u64oOisr+6D7iKhWEMN3gfHa0BrcT0wE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=kSlhkqVm; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="kSlhkqVm" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2c3b8880ca9so3171716eec.1 for ; Fri, 03 Apr 2026 22:40:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1775281252; x=1775886052; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=bxZ0oIiJdlv9VJ3sh0ouWp3Sf/nz5L7jc5er/WUpbQI=; b=kSlhkqVmPDc6ucWfgTrq3nj98prPq9P1huqoznMKlzMMQT4O013VWEgQzSW/R6sa6w iTjF0pto+iA2GroXyJG5w/Wu5L12RwK+VRa08ePTyMKeZfAdqxVUnOQFDZF2KsJQE/s+ A+kn0z3vYa4grBJSb2zGZN+NTc5estQtrgfRogV1GUPMBHjJKJIY/s+3HZSFzr3pZt1o q0eeWjWByJ2NuyHMNNb6Fy42reHxQab81lBU5jEu51AVqqQ/pqmyOjqRIEfUSxvQserN AfcGdyUwE/oZl0f0DjZRSR02/zNcD+W7VD6awZ3XgTn6VCXXFGODZK/lmwowqJ7IDpC3 50sQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775281252; x=1775886052; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=bxZ0oIiJdlv9VJ3sh0ouWp3Sf/nz5L7jc5er/WUpbQI=; b=GfrtbOrhe6SSuFlYBGLaN52uU/BH4OP1TPUwexckD1hfMkzV3FGClm2mzcbDJKwK3X Ira8m+6DO9g6lB/fwbrxX8LDFe6/05HA4GaD1+ITdSPfgXhXkKXOom19fzHVtJI52y6x BhVT/Ky0zTkDuf8Zohv43y0tkTVDsKwI370NP8L9ghgWbAFfpfNUfYwpN/B+DeyxmgpL 4p0a87YGwyLnfYsPvdgE9peABc0kbts2SqMrnb37KPP88NPcIrg2qWNj4ix/i3zwZot9 09AXYQz3g652x3q7kHHnb2Pby7A6j+NJbKs9GUamTTQEDdAN0tzrQC9eyPBKXaleX1p8 cBAw== X-Forwarded-Encrypted: i=1; AJvYcCVtAg6vyJOWVUQplvUK81x2lYbu7x5eYocotF6PNHDTyH5bTMUzXL6BhnTnsfxZcOtY5Fv2d47Jjg9Q1Dw=@vger.kernel.org X-Gm-Message-State: AOJu0Yw9EYkmNGy9kJpLfFQN1lqGo+toNRzb1pw6Sji8eddhouRiKW4W biah0qwQLm6orNpjL5jdGN48IGwyR3zjRIED6lKIYwVJdhygtZTVOZ7gDIZpXiPihVUUKhtxG// DrcJBjN3DfA== X-Received: from dlbrl19.prod.google.com ([2002:a05:7022:f513:b0:12a:c3aa:1a1e]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:4585:b0:128:d5bd:357f with SMTP id a92af1059eb24-12bfb7653e6mr2415112c88.30.1775281252333; Fri, 03 Apr 2026 22:40:52 -0700 (PDT) Date: Fri, 3 Apr 2026 22:40:27 -0700 In-Reply-To: <20260404054032.1538095-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260305221927.3237145-1-irogers@google.com> <20260404054032.1538095-1-irogers@google.com> X-Mailer: git-send-email 2.53.0.1213.gd9a14994de-goog Message-ID: <20260404054032.1538095-4-irogers@google.com> Subject: [PATCH v3 3/8] tools build: Deduplicate test-libunwind for different architectures From: Ian Rogers To: acme@kernel.org Cc: irogers@google.com, 9erthalion6@gmail.com, adrian.hunter@intel.com, alex@ghiti.fr, alexander.shishkin@linux.intel.com, andrew.jones@oss.qualcomm.com, aou@eecs.berkeley.edu, atrajeev@linux.ibm.com, blakejones@google.com, ctshao@google.com, dapeng1.mi@linux.intel.com, howardchu95@gmail.com, james.clark@linaro.org, john.g.garry@oracle.com, jolsa@kernel.org, leo.yan@linux.dev, libunwind-devel@nongnu.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-riscv@lists.infradead.org, mingo@redhat.com, namhyung@kernel.org, palmer@dabbelt.com, peterz@infradead.org, pjw@kernel.org, shimin.guo@skydio.com, tglozar@redhat.com, tmricht@linux.ibm.com, will@kernel.org, yuzhuo@google.com Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The separate test files only exist to pass a different #include, instead have a single source file and pass -include to $(CC) to include the relevant header file for the architecture being tested. Generate the rules using a foreach loop. Include tests for all current libunwind architectures. Signed-off-by: Ian Rogers --- tools/build/feature/Makefile | 38 +++++++++---------- tools/build/feature/test-libunwind-aarch64.c | 27 ------------- tools/build/feature/test-libunwind-arm.c | 28 -------------- .../test-libunwind-debug-frame-aarch64.c | 17 --------- .../feature/test-libunwind-debug-frame-arm.c | 17 --------- .../feature/test-libunwind-debug-frame.c | 1 - tools/build/feature/test-libunwind-x86.c | 28 -------------- tools/build/feature/test-libunwind-x86_64.c | 28 -------------- tools/build/feature/test-libunwind.c | 1 - 9 files changed, 19 insertions(+), 166 deletions(-) delete mode 100644 tools/build/feature/test-libunwind-aarch64.c delete mode 100644 tools/build/feature/test-libunwind-arm.c delete mode 100644 tools/build/feature/test-libunwind-debug-frame-aarch64.c delete mode 100644 tools/build/feature/test-libunwind-debug-frame-arm.c delete mode 100644 tools/build/feature/test-libunwind-x86.c delete mode 100644 tools/build/feature/test-libunwind-x86_64.c diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index f163a245837a..bda8ef868e44 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -210,27 +210,27 @@ $(OUTPUT)test-numa_num_possible_cpus.bin: $(BUILD) -lnuma =20 $(OUTPUT)test-libunwind.bin: - $(BUILD) -lelf -llzma + $(BUILD) -include libunwind.h -lelf -llzma -lunwind =20 $(OUTPUT)test-libunwind-debug-frame.bin: - $(BUILD) -lelf -llzma -$(OUTPUT)test-libunwind-x86.bin: - $(BUILD) -lelf -llzma -lunwind-x86 - -$(OUTPUT)test-libunwind-x86_64.bin: - $(BUILD) -lelf -llzma -lunwind-x86_64 - -$(OUTPUT)test-libunwind-arm.bin: - $(BUILD) -lelf -llzma -lunwind-arm - -$(OUTPUT)test-libunwind-aarch64.bin: - $(BUILD) -lelf -llzma -lunwind-aarch64 - -$(OUTPUT)test-libunwind-debug-frame-arm.bin: - $(BUILD) -lelf -llzma -lunwind-arm - -$(OUTPUT)test-libunwind-debug-frame-aarch64.bin: - $(BUILD) -lelf -llzma -lunwind-aarch64 + $(BUILD) -include libunwind.h -lelf -llzma -lunwind + +LIBUNWIND_ARCHS:=3Daarch64 arm loongarch64 mips ppc32 ppc64 riscv s390x x8= 6 x86_64 +define LIBUNWIND_RULE +$$(OUTPUT)test-libunwind-$(1).bin: + $$(CC) $$(CFLAGS) -MD -Wall -Werror -include libunwind-$(1).h -o $$@ \ + test-libunwind.c $$(LDFLAGS) -lelf -llzma -lunwind-$(1) \ + > $$(@:.bin=3D.make.output) 2>&1 + +$$(OUTPUT)test-libunwind-debug-frame-$(1).bin: + $$(CC) $$(CFLAGS) -MD -Wall -Werror -include libunwind-$(1).h -o $$@ \ + test-libunwind-debug-frame.c $$(LDFLAGS) -lelf -llzma -lunwind-$(1) \ + > $$(@:.bin=3D.make.output) 2>&1 + +endef +$(foreach arch,$(LIBUNWIND_ARCHS), \ + $(eval $(call LIBUNWIND_RULE,$(arch))) \ +) =20 $(OUTPUT)test-libslang.bin: $(BUILD) -lslang diff --git a/tools/build/feature/test-libunwind-aarch64.c b/tools/build/fea= ture/test-libunwind-aarch64.c deleted file mode 100644 index 323803f49212..000000000000 --- a/tools/build/feature/test-libunwind-aarch64.c +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, - unw_word_t ip, - unw_dyn_info_t *di, - unw_proc_info_t *pi, - int need_unwind_info, void *arg); - -#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) - -static unw_accessors_t accessors; - -int main(void) -{ - unw_addr_space_t addr_space; - - addr_space =3D unw_create_addr_space(&accessors, 0); - if (addr_space) - return 0; - - unw_init_remote(NULL, addr_space, NULL); - dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL); - - return 0; -} diff --git a/tools/build/feature/test-libunwind-arm.c b/tools/build/feature= /test-libunwind-arm.c deleted file mode 100644 index cb378b7d6866..000000000000 --- a/tools/build/feature/test-libunwind-arm.c +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, - unw_word_t ip, - unw_dyn_info_t *di, - unw_proc_info_t *pi, - int need_unwind_info, void *arg); - - -#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) - -static unw_accessors_t accessors; - -int main(void) -{ - unw_addr_space_t addr_space; - - addr_space =3D unw_create_addr_space(&accessors, 0); - if (addr_space) - return 0; - - unw_init_remote(NULL, addr_space, NULL); - dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL); - - return 0; -} diff --git a/tools/build/feature/test-libunwind-debug-frame-aarch64.c b/too= ls/build/feature/test-libunwind-debug-frame-aarch64.c deleted file mode 100644 index 36d6646c185e..000000000000 --- a/tools/build/feature/test-libunwind-debug-frame-aarch64.c +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -extern int -UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, - unw_word_t ip, unw_word_t segbase, - const char *obj_name, unw_word_t start, - unw_word_t end); - -#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) - -int main(void) -{ - dwarf_find_debug_frame(0, NULL, 0, 0, NULL, 0, 0); - return 0; -} diff --git a/tools/build/feature/test-libunwind-debug-frame-arm.c b/tools/b= uild/feature/test-libunwind-debug-frame-arm.c deleted file mode 100644 index 8696e48e1268..000000000000 --- a/tools/build/feature/test-libunwind-debug-frame-arm.c +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -extern int -UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, - unw_word_t ip, unw_word_t segbase, - const char *obj_name, unw_word_t start, - unw_word_t end); - -#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) - -int main(void) -{ - dwarf_find_debug_frame(0, NULL, 0, 0, NULL, 0, 0); - return 0; -} diff --git a/tools/build/feature/test-libunwind-debug-frame.c b/tools/build= /feature/test-libunwind-debug-frame.c index efb55cdd8d01..4c57e37004b3 100644 --- a/tools/build/feature/test-libunwind-debug-frame.c +++ b/tools/build/feature/test-libunwind-debug-frame.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0 -#include #include =20 extern int diff --git a/tools/build/feature/test-libunwind-x86.c b/tools/build/feature= /test-libunwind-x86.c deleted file mode 100644 index e5e0f6c89637..000000000000 --- a/tools/build/feature/test-libunwind-x86.c +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, - unw_word_t ip, - unw_dyn_info_t *di, - unw_proc_info_t *pi, - int need_unwind_info, void *arg); - - -#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) - -static unw_accessors_t accessors; - -int main(void) -{ - unw_addr_space_t addr_space; - - addr_space =3D unw_create_addr_space(&accessors, 0); - if (addr_space) - return 0; - - unw_init_remote(NULL, addr_space, NULL); - dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL); - - return 0; -} diff --git a/tools/build/feature/test-libunwind-x86_64.c b/tools/build/feat= ure/test-libunwind-x86_64.c deleted file mode 100644 index 62ae4db597dc..000000000000 --- a/tools/build/feature/test-libunwind-x86_64.c +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, - unw_word_t ip, - unw_dyn_info_t *di, - unw_proc_info_t *pi, - int need_unwind_info, void *arg); - - -#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) - -static unw_accessors_t accessors; - -int main(void) -{ - unw_addr_space_t addr_space; - - addr_space =3D unw_create_addr_space(&accessors, 0); - if (addr_space) - return 0; - - unw_init_remote(NULL, addr_space, NULL); - dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL); - - return 0; -} diff --git a/tools/build/feature/test-libunwind.c b/tools/build/feature/tes= t-libunwind.c index 53fd26614ff0..5af5dc3a73d4 100644 --- a/tools/build/feature/test-libunwind.c +++ b/tools/build/feature/test-libunwind.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0 -#include #include =20 extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, --=20 2.53.0.1213.gd9a14994de-goog From nobody Thu Apr 9 20:26:05 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5709A34A3A5 for ; Sat, 4 Apr 2026 05:40:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775281256; cv=none; b=uPsTgkhvbRFa2+48YKh97uAnLi+yjYMAcIKOiz8nbQV1fRqil2Cut2spwThOrJAZk3yJO0EN9PxeVizjjMWa9fGDfKZnyNbUTNhD0Cn21DW9TNTn0vIbHmKD1rTyGvgtdfwR3HSFZpZ75BZVZZxvNDQ2Gdx+vaqqLwhqQDRcmHo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775281256; c=relaxed/simple; bh=hI44vGWtCdCn04CCcup5QmPadpgQO/d62FvKmq6JGdQ=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=mB6HAFCShgm0NEEgk9ynZBBD5UGZIm4fA9jcPGPxmQRKehtr5GTibpaX303Fji5Vvr/QUknEvuKabSYNPVwT1YuRmEakOgFieNHUDBSGnhRQOt7cuX3VLGzk/iCiEkhPluYDEuntYXImSUS4kuBJxuaMmL/e0yLwid/atuVKpp8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=hKV01bCr; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="hKV01bCr" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2c16233ee11so3577061eec.1 for ; Fri, 03 Apr 2026 22:40:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1775281254; x=1775886054; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=NjM/Qu3OIgvMlw6Ybfuzi2HOP0QWKIrv6KqEfslPyd8=; b=hKV01bCr68FC3Z+5xGtt1W4v9Zzn6taEYDhhPW4EF6LbjCKLdBG9kNGnvhDCOlO7Lh u+kyAVLsuqsjHgvXxjhicyZuIbQC4tU3XNesJt8rXGrv/piteHO6GtLgtE3sl10qfNjE ugu+Z+UipthhCXzdg/PpXWqfnsOUbhsCVt/tHTFzglS3yFY1UuKXgulIrxLoaAumf1+g Bu+FO8iqUlXkWZp5TKEOavJDZqsgOzTHfNUyQ/uMG6Fvzqjp54ld6dcrs+FFlfS4lMUW U97qX+38u0f/tS8x9Sz8pKIUIUuC/Sr6WARfw1h75hy8DNXbMMNo9S2n7YPHwxtXMxvL C0uQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775281254; x=1775886054; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=NjM/Qu3OIgvMlw6Ybfuzi2HOP0QWKIrv6KqEfslPyd8=; b=irurlsFnkOEx9w0FYTVwrqrQhYUgz4mp5aOl27vJfKmH9s126Zf6EpiywWhKm9dd5i 7fmEG8s+LTSyrPibB2UKpW2z7mgC7a1bD2hONJE8e3S1IeDmnH3EKhwdBxOf4UXODOpg Xp91uBz/dhTZ/3je1JigtygTedLVYRmm7/Nzhhmt/p7gFqLhhcxKkFmJL9e/e+kSYNrv 015yu4ABhcDX0pDIYdLdjpU0u7KOE5EBtMHmnz4M179hSmoiGvR6rKn4KltN9tLnmaf9 PcRI9sRbMU1MuFVXQAzXEa+Y8PEOknRR1s0dfrCMDQA4lt97etBJvLo/aexPgWZtNigs L/4g== X-Forwarded-Encrypted: i=1; AJvYcCXKv2CaEhHN72dXAOuvrGwaqcp7g1kdwZxEt45Ld07QSf8k8s7XjQQ0OokwDAlwFmRJ5LClgRqWPvHKv6Q=@vger.kernel.org X-Gm-Message-State: AOJu0Yy3PwqwDyrhulkwJY8lOfjePU26LYvwrz69KwQSQDH9S96ivfVA QXy60RepOrVtosWs/gntNYL4iVsxgGAVaBdSc3awD59H20LOYM+scveFIFaT0GffC2wd+VNxLAB 4d1F3uD+pIw== X-Received: from dyu4.prod.google.com ([2002:a05:693c:8104:b0:2cb:5486:3ae3]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:fd15:b0:2c6:7f49:a86d with SMTP id 5a478bee46e88-2cbfaaab106mr2503737eec.15.1775281254305; Fri, 03 Apr 2026 22:40:54 -0700 (PDT) Date: Fri, 3 Apr 2026 22:40:28 -0700 In-Reply-To: <20260404054032.1538095-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260305221927.3237145-1-irogers@google.com> <20260404054032.1538095-1-irogers@google.com> X-Mailer: git-send-email 2.53.0.1213.gd9a14994de-goog Message-ID: <20260404054032.1538095-5-irogers@google.com> Subject: [PATCH v3 4/8] perf build: Be more programmatic when setting up libunwind variables From: Ian Rogers To: acme@kernel.org Cc: irogers@google.com, 9erthalion6@gmail.com, adrian.hunter@intel.com, alex@ghiti.fr, alexander.shishkin@linux.intel.com, andrew.jones@oss.qualcomm.com, aou@eecs.berkeley.edu, atrajeev@linux.ibm.com, blakejones@google.com, ctshao@google.com, dapeng1.mi@linux.intel.com, howardchu95@gmail.com, james.clark@linaro.org, john.g.garry@oracle.com, jolsa@kernel.org, leo.yan@linux.dev, libunwind-devel@nongnu.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-riscv@lists.infradead.org, mingo@redhat.com, namhyung@kernel.org, palmer@dabbelt.com, peterz@infradead.org, pjw@kernel.org, shimin.guo@skydio.com, tglozar@redhat.com, tmricht@linux.ibm.com, will@kernel.org, yuzhuo@google.com Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Iterate LIBUNWIND_ARCHS when setting up CONFIG_ and HAVE_ definitions rather than treating each architecture individually. This sets up the libunwind build variables and C definitions beyond x86 and arm/aarch64. The existing naming convention is followed for compatibility. Signed-off-by: Ian Rogers --- tools/perf/Makefile.config | 215 +++++++++++++++---------------------- 1 file changed, 89 insertions(+), 126 deletions(-) diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 333ddd0e4bd8..9d31d0f6f52a 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -65,95 +65,83 @@ $(call detected_var,SRCARCH) =20 CFLAGS +=3D -I$(OUTPUT)arch/$(SRCARCH)/include/generated =20 -# Additional ARCH settings for ppc -ifeq ($(SRCARCH),powerpc) - ifndef NO_LIBUNWIND - LIBUNWIND_LIBS :=3D -lunwind -lunwind-ppc64 - endif -endif - # Additional ARCH settings for x86 ifeq ($(SRCARCH),x86) $(call detected,CONFIG_X86) ifeq (${IS_64_BIT}, 1) CFLAGS +=3D -DHAVE_ARCH_X86_64_SUPPORT ARCH_INCLUDE =3D ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/mem= set_64.S - ifndef NO_LIBUNWIND - LIBUNWIND_LIBS =3D -lunwind-x86_64 -lunwind -llzma - endif $(call detected,CONFIG_X86_64) - else - ifndef NO_LIBUNWIND - LIBUNWIND_LIBS =3D -lunwind-x86 -llzma -lunwind - endif endif endif =20 -ifeq ($(SRCARCH),arm) - ifndef NO_LIBUNWIND - LIBUNWIND_LIBS =3D -lunwind -lunwind-arm - endif +ifeq ($(ARCH),s390) + CFLAGS +=3D -fPIC endif =20 -ifeq ($(SRCARCH),arm64) - ifndef NO_LIBUNWIND - LIBUNWIND_LIBS =3D -lunwind -lunwind-aarch64 - endif +ifneq ($(LIBUNWIND),1) + NO_LIBUNWIND :=3D 1 endif =20 -ifeq ($(SRCARCH),loongarch) - ifndef NO_LIBUNWIND +ifndef NO_LIBUNWIND + ifeq ($(SRCARCH),arm) + LIBUNWIND_LIBS =3D -lunwind -lunwind-arm + endif + ifeq ($(SRCARCH),arm64) + LIBUNWIND_LIBS =3D -lunwind -lunwind-aarch64 + endif + ifeq ($(SRCARCH),loongarch) LIBUNWIND_LIBS =3D -lunwind -lunwind-loongarch64 endif -endif - -ifeq ($(ARCH),s390) - CFLAGS +=3D -fPIC -endif - -ifeq ($(ARCH),mips) - ifndef NO_LIBUNWIND + ifeq ($(ARCH),mips) LIBUNWIND_LIBS =3D -lunwind -lunwind-mips endif + ifeq ($(SRCARCH),powerpc) + LIBUNWIND_LIBS :=3D -lunwind -lunwind-ppc64 + endif + ifeq ($(SRCARCH),riscv) + LIBUNWIND_LIBS :=3D -lunwind -lunwind-riscv + endif + ifeq ($(SRCARCH),s390) + LIBUNWIND_LIBS :=3D -lunwind -lunwind-s390x + endif + ifeq ($(SRCARCH),x86) + ifeq (${IS_64_BIT}, 1) + LIBUNWIND_LIBS =3D -lunwind-x86_64 -lunwind -llzma + else + LIBUNWIND_LIBS =3D -lunwind-x86 -lunwind -llzma + endif + endif + ifeq ($(LIBUNWIND_LIBS),) + NO_LIBUNWIND :=3D 1 + endif endif =20 -ifneq ($(LIBUNWIND),1) - NO_LIBUNWIND :=3D 1 -endif - -ifeq ($(LIBUNWIND_LIBS),) - NO_LIBUNWIND :=3D 1 -endif # # For linking with debug library, run like: # # make DEBUG=3D1 LIBUNWIND_DIR=3D/opt/libunwind/ # - -libunwind_arch_set_flags =3D $(eval $(libunwind_arch_set_flags_code)) -define libunwind_arch_set_flags_code - FEATURE_CHECK_CFLAGS-libunwind-$(1) =3D -I$(LIBUNWIND_DIR)/include - FEATURE_CHECK_LDFLAGS-libunwind-$(1) =3D -L$(LIBUNWIND_DIR)/lib -endef - -ifdef LIBUNWIND_DIR - LIBUNWIND_CFLAGS =3D -I$(LIBUNWIND_DIR)/include - LIBUNWIND_LDFLAGS =3D -L$(LIBUNWIND_DIR)/lib - LIBUNWIND_ARCHS =3D x86 x86_64 arm aarch64 debug-frame-arm debug-frame-a= arch64 loongarch - $(foreach libunwind_arch,$(LIBUNWIND_ARCHS),$(call libunwind_arch_set_fl= ags,$(libunwind_arch))) -endif +LIBUNWIND_ARCHS:=3Daarch64 arm loongarch64 mips ppc32 ppc64 riscv s390x x8= 6 x86_64 =20 ifndef NO_LIBUNWIND - # Set per-feature check compilation flags FEATURE_CHECK_CFLAGS-libunwind =3D $(LIBUNWIND_CFLAGS) FEATURE_CHECK_LDFLAGS-libunwind =3D $(LIBUNWIND_LDFLAGS) $(LIBUNWIND_LIB= S) FEATURE_CHECK_CFLAGS-libunwind-debug-frame =3D $(LIBUNWIND_CFLAGS) FEATURE_CHECK_LDFLAGS-libunwind-debug-frame =3D $(LIBUNWIND_LDFLAGS) $(L= IBUNWIND_LIBS) - =20 - FEATURE_CHECK_LDFLAGS-libunwind-arm +=3D -lunwind -lunwind-arm - FEATURE_CHECK_LDFLAGS-libunwind-aarch64 +=3D -lunwind -lunwind-aarch64 - FEATURE_CHECK_LDFLAGS-libunwind-x86 +=3D -lunwind -llzma -lunwind-x86 - FEATURE_CHECK_LDFLAGS-libunwind-x86_64 +=3D -lunwind -llzma -lunwind-x86= _64 + + ifdef LIBUNWIND_DIR + LIBUNWIND_CFLAGS =3D -I$(LIBUNWIND_DIR)/include + LIBUNWIND_LDFLAGS =3D -L$(LIBUNWIND_DIR)/lib + + define libunwind_arch_set_flags + FEATURE_CHECK_CFLAGS-libunwind-$(1) =3D -I$(LIBUNWIND_DIR)/include + FEATURE_CHECK_LDFLAGS-libunwind-$(1) =3D -L$(LIBUNWIND_DIR)/lib + endef + $(foreach arch,$(LIBUNWIND_ARCHS), \ + $(eval $(call libunwind_arch_set_flags,$(arch))) \ + ) + endif endif =20 ifdef CSINCLUDES @@ -638,49 +626,6 @@ ifeq ($(SRCARCH),powerpc) endif endif =20 -ifndef NO_LIBUNWIND - have_libunwind :=3D - - $(call feature_check,libunwind) - - $(call feature_check,libunwind-x86) - ifeq ($(feature-libunwind-x86), 1) - $(call detected,CONFIG_LIBUNWIND_X86) - CFLAGS +=3D -DHAVE_LIBUNWIND_X86_SUPPORT - LDFLAGS +=3D -lunwind-x86 - EXTLIBS_LIBUNWIND +=3D -lunwind-x86 - have_libunwind =3D 1 - endif - - $(call feature_check,libunwind-aarch64) - ifeq ($(feature-libunwind-aarch64), 1) - $(call detected,CONFIG_LIBUNWIND_AARCH64) - CFLAGS +=3D -DHAVE_LIBUNWIND_AARCH64_SUPPORT - LDFLAGS +=3D -lunwind-aarch64 - EXTLIBS_LIBUNWIND +=3D -lunwind-aarch64 - have_libunwind =3D 1 - $(call feature_check,libunwind-debug-frame-aarch64) - ifneq ($(feature-libunwind-debug-frame-aarch64), 1) - $(warning No debug_frame support found in libunwind-aarch64) - CFLAGS +=3D -DNO_LIBUNWIND_DEBUG_FRAME_AARCH64 - endif - endif - - ifneq ($(feature-libunwind), 1) - $(warning No libunwind found. Please install libunwind-dev[el] >=3D 1.= 1 and/or set LIBUNWIND_DIR and set LIBUNWIND=3D1 in the make command line a= s it is opt-in now) - NO_LOCAL_LIBUNWIND :=3D 1 - else - have_libunwind :=3D 1 - $(call detected,CONFIG_LOCAL_LIBUNWIND) - endif - - ifneq ($(have_libunwind), 1) - NO_LIBUNWIND :=3D 1 - endif -else - NO_LOCAL_LIBUNWIND :=3D 1 -endif - ifndef NO_LIBBPF ifneq ($(feature-bpf), 1) $(warning BPF API too old. Please install recent kernel headers. BPF s= upport in 'perf record' is disabled.) @@ -739,6 +684,49 @@ ifndef GEN_VMLINUX_H VMLINUX_H=3D$(src-perf)/util/bpf_skel/vmlinux/vmlinux.h endif =20 +ifndef NO_LIBUNWIND + have_libunwind :=3D + + $(call feature_check,libunwind) + ifneq ($(feature-libunwind), 1) + $(warning No libunwind found. Please install libunwind-dev[el] >=3D 1.= 1 and/or set LIBUNWIND_DIR and set LIBUNWIND=3D1 in the make command line a= s it is opt-in now) + NO_LOCAL_LIBUNWIND :=3D 1 + else + have_libunwind :=3D 1 + $(call detected,CONFIG_LOCAL_LIBUNWIND) + CFLAGS +=3D -DHAVE_LIBUNWIND_SUPPORT + CFLAGS +=3D $(LIBUNWIND_CFLAGS) + LDFLAGS +=3D $(LIBUNWIND_LDFLAGS) + EXTLIBS +=3D $(EXTLIBS_LIBUNWIND) + $(call feature_check,libunwind-debug-frame) + ifneq ($(feature-libunwind-debug-frame), 1) + CFLAGS +=3D -DNO_LIBUNWIND_DEBUG_FRAME + endif + endif + + define PROCESS_REMOTE_LIBUNWIND_ARCH + $(call feature_check,libunwind-$(1)) + + ifeq ($$(feature-libunwind-$(1)), 1) + upper_arch :=3D $$(shell echo $(1) | tr '[:lower:]' '[:upper:]') + $$(call detected,CONFIG_LIBUNWIND_$$(upper_arch)) + + CFLAGS +=3D -DHAVE_LIBUNWIND_$$(upper_arch)_SUPPORT + LDFLAGS +=3D -lunwind-$(1) + EXTLIBS_LIBUNWIND +=3D -lunwind-$(1) + have_libunwind :=3D 1 + + $$(call feature_check,libunwind-debug-frame-$(1)) + ifneq ($$(feature-libunwind-debug-frame-$(1)), 1) + CFLAGS +=3D -DNO_LIBUNWIND_DEBUG_FRAME_$$(upper_arch) + endif + endif + endef + $(foreach arch,$(LIBUNWIND_ARCHS), \ + $(eval $(call PROCESS_REMOTE_LIBUNWIND_ARCH,$(arch))) \ + ) +endif + dwarf-post-unwind :=3D 1 dwarf-post-unwind-text :=3D BUG =20 @@ -761,31 +749,6 @@ ifeq ($(dwarf-post-unwind),1) $(call detected,CONFIG_DWARF_UNWIND) endif =20 -ifndef NO_LIBUNWIND - ifndef NO_LOCAL_LIBUNWIND - ifeq ($(SRCARCH),$(filter $(SRCARCH),arm arm64)) - $(call feature_check,libunwind-debug-frame) - ifneq ($(feature-libunwind-debug-frame), 1) - $(warning No debug_frame support found in libunwind) - CFLAGS +=3D -DNO_LIBUNWIND_DEBUG_FRAME - endif - else - # non-ARM has no dwarf_find_debug_frame() function: - CFLAGS +=3D -DNO_LIBUNWIND_DEBUG_FRAME - endif - EXTLIBS +=3D $(LIBUNWIND_LIBS) - LDFLAGS +=3D $(LIBUNWIND_LIBS) - endif - ifeq ($(findstring -static,${LDFLAGS}),-static) - # gcc -static links libgcc_eh which contans piece of libunwind - LIBUNWIND_LDFLAGS +=3D -Wl,--allow-multiple-definition - endif - CFLAGS +=3D -DHAVE_LIBUNWIND_SUPPORT - CFLAGS +=3D $(LIBUNWIND_CFLAGS) - LDFLAGS +=3D $(LIBUNWIND_LDFLAGS) - EXTLIBS +=3D $(EXTLIBS_LIBUNWIND) -endif - ifneq ($(NO_LIBTRACEEVENT),1) $(call detected,CONFIG_TRACE) endif --=20 2.53.0.1213.gd9a14994de-goog From nobody Thu Apr 9 20:26:05 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 73DE5378805 for ; Sat, 4 Apr 2026 05:40:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775281262; cv=none; b=t4xdUluG4PzuGHke+vts3heNshdPE620YK7ODqrwgOHc+o0uK8IHY9d2VeluUhYFMDkSBscW59dUB8VAJ5QuJKcYGdlUCDPYAZJmCYt7gac9yPXT+0WCrg4bvSiWfwCS3VSrAfiCeEWhOBOttUanrs3Y/OuFXO42/0ukyIb2j9M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775281262; c=relaxed/simple; bh=e7LhtZE7qp3S+FMHAh9j1qEz5Ab+tayPVBk+5A3JXy8=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=WG2zkGvaT32/IbjRiu9LxgHKit4hULF1nyfifccMvDtsT2C91cOi7Quot8OtpSapkAZYGu4Uml7eXRtks9EZFjt0dKRWe2Tg+k2PHJPgZaJJKlHyLmoAlygOE4qLgsXR0BF0jxf6vk7wfjUKXrqr3BaMecKzxB0LuUQHGFLEsHs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=K1oknm23; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="K1oknm23" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2c0ba59a830so3243635eec.0 for ; Fri, 03 Apr 2026 22:40:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1775281257; x=1775886057; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=L8Op3VU4RiYxjEcgQHCVuqUjLSMaKMi96whlyZxJiZk=; b=K1oknm23JEgUydxqORUQd7KV3olMCfpzJVWczVc8+qNG+qkwfODInshZwdWSswxX4a JqO4JxhQpZ4+3cNq48RfobrSRWuZJWXuWTvheNhCq9KvxTF0YsoljYmOxSYMEvuQ5rdf U9l0aNHHjr+z/R0O81ifZNt2aUGSPpr1JKBW9EDh+GuT4UJT6AqzeCltyD9oO/hIv6jc YLi+iINNsSJIQFShTeM96rijLlOgHdgfb7dgHnU2jLLgRzgCCupj3reqzX2h016tv+gM lkF+oo6LnhtAZrAk68RbwYcxt3+9gPq4tKBGoJxJfH1Nf/vVnu/RoM0SN3qWIqPlQvjx lXlg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775281257; x=1775886057; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=L8Op3VU4RiYxjEcgQHCVuqUjLSMaKMi96whlyZxJiZk=; b=YIzV4gqAeqqvqs1fN3teDOcb4ftQ4ltCh+jtnGszD8nrehcdY1bL25aMExcDH2bZQd bUn9eE/hVujtPZT4LvuWButBXpBfkrHS10gtsuVkVew+zJoiNwEQdlp6z/AN1IUqmwE8 +EJJwY3btUjwVjb/bA29sYXJXNnnSJvXBlRQhn6ComBMhUN20zOzLbc5hs/htPvGuIPY HEFGNdG3hyHXH3vhg4C9wAa2+AgDuKEyIPQmZ9ut9lrnP7SAq04/E7N/+DwP93ffTncZ XQi1HapCdAifKCf6asLLDrz4GIqNo9rWpacZbtcNcVMR6RW3VhCxicr7Qn8cWZ/UOFwp U4+g== X-Forwarded-Encrypted: i=1; AJvYcCVobmkI+8YmzqckVvWv2frUo4VHTU7BgJ9HNStiWd5fZUKzALlHn+h+SwMrKbDLf2Qg4ztMoIuxxp0wxbQ=@vger.kernel.org X-Gm-Message-State: AOJu0YyyB1RQgsZ/uK3woMu566m2tH4yhDIFiHCQ4WJALbO64PDxAyTF h6ZQ1L5N8wDT4uw1e0g6IkURQ92uSR9l03tP+sKvsb91dtW+BmnyA6YH/pBk8actbWljPmjq5zE ZsVDNhsRBCw== X-Received: from dleb6-n2.prod.google.com ([2002:a05:701b:4246:20b0:12b:eb91:8863]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:1005:b0:128:cf5c:535a with SMTP id a92af1059eb24-12bfb6f4e06mr2748096c88.11.1775281256296; Fri, 03 Apr 2026 22:40:56 -0700 (PDT) Date: Fri, 3 Apr 2026 22:40:29 -0700 In-Reply-To: <20260404054032.1538095-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260305221927.3237145-1-irogers@google.com> <20260404054032.1538095-1-irogers@google.com> X-Mailer: git-send-email 2.53.0.1213.gd9a14994de-goog Message-ID: <20260404054032.1538095-6-irogers@google.com> Subject: [PATCH v3 5/8] perf unwind-libunwind: Make libunwind register reading cross platform From: Ian Rogers To: acme@kernel.org Cc: irogers@google.com, 9erthalion6@gmail.com, adrian.hunter@intel.com, alex@ghiti.fr, alexander.shishkin@linux.intel.com, andrew.jones@oss.qualcomm.com, aou@eecs.berkeley.edu, atrajeev@linux.ibm.com, blakejones@google.com, ctshao@google.com, dapeng1.mi@linux.intel.com, howardchu95@gmail.com, james.clark@linaro.org, john.g.garry@oracle.com, jolsa@kernel.org, leo.yan@linux.dev, libunwind-devel@nongnu.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-riscv@lists.infradead.org, mingo@redhat.com, namhyung@kernel.org, palmer@dabbelt.com, peterz@infradead.org, pjw@kernel.org, shimin.guo@skydio.com, tglozar@redhat.com, tmricht@linux.ibm.com, will@kernel.org, yuzhuo@google.com Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Move the libunwind register to perf register mapping functions in arch/../util/unwind-libunwind.c into a new libunwind-arch directory. Rename the functions to __get_perf_regnum_for_unw_regnum_. Add untested ppc32 and s390 functions. Add a get_perf_regnum_for_unw_regnum function that takes an ELF machine as well as a register number and chooses the appropriate architecture implementation. Split the x86 and powerpc 32 and 64-bit implementations apart so that a single libunwind-.h header is included. Move the e_machine into the unwind_info struct to make it easier to pass. Signed-off-by: Ian Rogers --- tools/perf/arch/arm/util/Build | 2 - tools/perf/arch/arm/util/unwind-libunwind.c | 50 -------- tools/perf/arch/arm64/util/Build | 1 - tools/perf/arch/arm64/util/unwind-libunwind.c | 17 --- tools/perf/arch/loongarch/util/Build | 2 - .../arch/loongarch/util/unwind-libunwind.c | 82 ------------- tools/perf/arch/mips/Build | 1 - tools/perf/arch/mips/util/Build | 1 - tools/perf/arch/mips/util/unwind-libunwind.c | 22 ---- tools/perf/arch/powerpc/util/Build | 1 - .../perf/arch/powerpc/util/unwind-libunwind.c | 92 -------------- tools/perf/arch/x86/util/Build | 3 - tools/perf/arch/x86/util/unwind-libunwind.c | 115 ------------------ tools/perf/util/Build | 1 + tools/perf/util/libunwind-arch/Build | 10 ++ .../perf/util/libunwind-arch/libunwind-arch.c | 32 +++++ .../perf/util/libunwind-arch/libunwind-arch.h | 16 +++ .../perf/util/libunwind-arch/libunwind-arm.c | 15 +++ .../util/libunwind-arch/libunwind-arm64.c | 14 +++ .../perf/util/libunwind-arch/libunwind-i386.c | 43 +++++++ .../util/libunwind-arch/libunwind-loongarch.c | 27 ++++ .../perf/util/libunwind-arch/libunwind-mips.c | 29 +++++ .../util/libunwind-arch/libunwind-ppc32.c | 31 +++++ .../util/libunwind-arch/libunwind-ppc64.c | 33 +++++ .../perf/util/libunwind-arch/libunwind-s390.c | 29 +++++ .../util/libunwind-arch/libunwind-x86_64.c | 52 ++++++++ tools/perf/util/libunwind/arm64.c | 5 - tools/perf/util/libunwind/x86_32.c | 12 -- tools/perf/util/unwind-libunwind-local.c | 12 +- tools/perf/util/unwind.h | 5 - 30 files changed, 338 insertions(+), 417 deletions(-) delete mode 100644 tools/perf/arch/arm/util/unwind-libunwind.c delete mode 100644 tools/perf/arch/arm64/util/unwind-libunwind.c delete mode 100644 tools/perf/arch/loongarch/util/unwind-libunwind.c delete mode 100644 tools/perf/arch/mips/Build delete mode 100644 tools/perf/arch/mips/util/Build delete mode 100644 tools/perf/arch/mips/util/unwind-libunwind.c delete mode 100644 tools/perf/arch/powerpc/util/unwind-libunwind.c delete mode 100644 tools/perf/arch/x86/util/unwind-libunwind.c create mode 100644 tools/perf/util/libunwind-arch/Build create mode 100644 tools/perf/util/libunwind-arch/libunwind-arch.c create mode 100644 tools/perf/util/libunwind-arch/libunwind-arch.h create mode 100644 tools/perf/util/libunwind-arch/libunwind-arm.c create mode 100644 tools/perf/util/libunwind-arch/libunwind-arm64.c create mode 100644 tools/perf/util/libunwind-arch/libunwind-i386.c create mode 100644 tools/perf/util/libunwind-arch/libunwind-loongarch.c create mode 100644 tools/perf/util/libunwind-arch/libunwind-mips.c create mode 100644 tools/perf/util/libunwind-arch/libunwind-ppc32.c create mode 100644 tools/perf/util/libunwind-arch/libunwind-ppc64.c create mode 100644 tools/perf/util/libunwind-arch/libunwind-s390.c create mode 100644 tools/perf/util/libunwind-arch/libunwind-x86_64.c diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build index b94bf3c5279a..768ae5d16553 100644 --- a/tools/perf/arch/arm/util/Build +++ b/tools/perf/arch/arm/util/Build @@ -1,3 +1 @@ -perf-util-$(CONFIG_LOCAL_LIBUNWIND) +=3D unwind-libunwind.o - perf-util-y +=3D pmu.o auxtrace.o cs-etm.o diff --git a/tools/perf/arch/arm/util/unwind-libunwind.c b/tools/perf/arch/= arm/util/unwind-libunwind.c deleted file mode 100644 index 438906bf0014..000000000000 --- a/tools/perf/arch/arm/util/unwind-libunwind.c +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include -#include "perf_regs.h" -#include "../../../util/unwind.h" -#include "../../../util/debug.h" - -int libunwind__arch_reg_id(int regnum) -{ - switch (regnum) { - case UNW_ARM_R0: - return PERF_REG_ARM_R0; - case UNW_ARM_R1: - return PERF_REG_ARM_R1; - case UNW_ARM_R2: - return PERF_REG_ARM_R2; - case UNW_ARM_R3: - return PERF_REG_ARM_R3; - case UNW_ARM_R4: - return PERF_REG_ARM_R4; - case UNW_ARM_R5: - return PERF_REG_ARM_R5; - case UNW_ARM_R6: - return PERF_REG_ARM_R6; - case UNW_ARM_R7: - return PERF_REG_ARM_R7; - case UNW_ARM_R8: - return PERF_REG_ARM_R8; - case UNW_ARM_R9: - return PERF_REG_ARM_R9; - case UNW_ARM_R10: - return PERF_REG_ARM_R10; - case UNW_ARM_R11: - return PERF_REG_ARM_FP; - case UNW_ARM_R12: - return PERF_REG_ARM_IP; - case UNW_ARM_R13: - return PERF_REG_ARM_SP; - case UNW_ARM_R14: - return PERF_REG_ARM_LR; - case UNW_ARM_R15: - return PERF_REG_ARM_PC; - default: - pr_err("unwind: invalid reg id %d\n", regnum); - return -EINVAL; - } - - return -EINVAL; -} diff --git a/tools/perf/arch/arm64/util/Build b/tools/perf/arch/arm64/util/= Build index 4e06a08d281a..4b70c4788c80 100644 --- a/tools/perf/arch/arm64/util/Build +++ b/tools/perf/arch/arm64/util/Build @@ -1,4 +1,3 @@ -perf-util-$(CONFIG_LOCAL_LIBUNWIND) +=3D unwind-libunwind.o perf-util-y +=3D ../../arm/util/auxtrace.o perf-util-y +=3D ../../arm/util/cs-etm.o perf-util-y +=3D ../../arm/util/pmu.o diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arc= h/arm64/util/unwind-libunwind.c deleted file mode 100644 index 871af5992298..000000000000 --- a/tools/perf/arch/arm64/util/unwind-libunwind.c +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include - -#ifndef REMOTE_UNWIND_LIBUNWIND -#include -#include "perf_regs.h" -#include "../../../util/unwind.h" -#endif -#include "../../../util/debug.h" - -int LIBUNWIND__ARCH_REG_ID(int regnum) -{ - if (regnum < 0 || regnum >=3D PERF_REG_ARM64_EXTENDED_MAX) - return -EINVAL; - - return regnum; -} diff --git a/tools/perf/arch/loongarch/util/Build b/tools/perf/arch/loongar= ch/util/Build index 8d91e78d31c9..2328fb9a30a3 100644 --- a/tools/perf/arch/loongarch/util/Build +++ b/tools/perf/arch/loongarch/util/Build @@ -1,3 +1 @@ perf-util-y +=3D header.o - -perf-util-$(CONFIG_LOCAL_LIBUNWIND) +=3D unwind-libunwind.o diff --git a/tools/perf/arch/loongarch/util/unwind-libunwind.c b/tools/perf= /arch/loongarch/util/unwind-libunwind.c deleted file mode 100644 index f693167b86ef..000000000000 --- a/tools/perf/arch/loongarch/util/unwind-libunwind.c +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include -#include "perf_regs.h" -#include "../../util/unwind.h" -#include "util/debug.h" - -int libunwind__arch_reg_id(int regnum) -{ - switch (regnum) { - case UNW_LOONGARCH64_R1: - return PERF_REG_LOONGARCH_R1; - case UNW_LOONGARCH64_R2: - return PERF_REG_LOONGARCH_R2; - case UNW_LOONGARCH64_R3: - return PERF_REG_LOONGARCH_R3; - case UNW_LOONGARCH64_R4: - return PERF_REG_LOONGARCH_R4; - case UNW_LOONGARCH64_R5: - return PERF_REG_LOONGARCH_R5; - case UNW_LOONGARCH64_R6: - return PERF_REG_LOONGARCH_R6; - case UNW_LOONGARCH64_R7: - return PERF_REG_LOONGARCH_R7; - case UNW_LOONGARCH64_R8: - return PERF_REG_LOONGARCH_R8; - case UNW_LOONGARCH64_R9: - return PERF_REG_LOONGARCH_R9; - case UNW_LOONGARCH64_R10: - return PERF_REG_LOONGARCH_R10; - case UNW_LOONGARCH64_R11: - return PERF_REG_LOONGARCH_R11; - case UNW_LOONGARCH64_R12: - return PERF_REG_LOONGARCH_R12; - case UNW_LOONGARCH64_R13: - return PERF_REG_LOONGARCH_R13; - case UNW_LOONGARCH64_R14: - return PERF_REG_LOONGARCH_R14; - case UNW_LOONGARCH64_R15: - return PERF_REG_LOONGARCH_R15; - case UNW_LOONGARCH64_R16: - return PERF_REG_LOONGARCH_R16; - case UNW_LOONGARCH64_R17: - return PERF_REG_LOONGARCH_R17; - case UNW_LOONGARCH64_R18: - return PERF_REG_LOONGARCH_R18; - case UNW_LOONGARCH64_R19: - return PERF_REG_LOONGARCH_R19; - case UNW_LOONGARCH64_R20: - return PERF_REG_LOONGARCH_R20; - case UNW_LOONGARCH64_R21: - return PERF_REG_LOONGARCH_R21; - case UNW_LOONGARCH64_R22: - return PERF_REG_LOONGARCH_R22; - case UNW_LOONGARCH64_R23: - return PERF_REG_LOONGARCH_R23; - case UNW_LOONGARCH64_R24: - return PERF_REG_LOONGARCH_R24; - case UNW_LOONGARCH64_R25: - return PERF_REG_LOONGARCH_R25; - case UNW_LOONGARCH64_R26: - return PERF_REG_LOONGARCH_R26; - case UNW_LOONGARCH64_R27: - return PERF_REG_LOONGARCH_R27; - case UNW_LOONGARCH64_R28: - return PERF_REG_LOONGARCH_R28; - case UNW_LOONGARCH64_R29: - return PERF_REG_LOONGARCH_R29; - case UNW_LOONGARCH64_R30: - return PERF_REG_LOONGARCH_R30; - case UNW_LOONGARCH64_R31: - return PERF_REG_LOONGARCH_R31; - case UNW_LOONGARCH64_PC: - return PERF_REG_LOONGARCH_PC; - default: - pr_err("unwind: invalid reg id %d\n", regnum); - return -EINVAL; - } - - return -EINVAL; -} diff --git a/tools/perf/arch/mips/Build b/tools/perf/arch/mips/Build deleted file mode 100644 index e63eabc2c8f4..000000000000 --- a/tools/perf/arch/mips/Build +++ /dev/null @@ -1 +0,0 @@ -perf-util-y +=3D util/ diff --git a/tools/perf/arch/mips/util/Build b/tools/perf/arch/mips/util/Bu= ild deleted file mode 100644 index 818b808a8247..000000000000 --- a/tools/perf/arch/mips/util/Build +++ /dev/null @@ -1 +0,0 @@ -perf-util-$(CONFIG_LOCAL_LIBUNWIND) +=3D unwind-libunwind.o diff --git a/tools/perf/arch/mips/util/unwind-libunwind.c b/tools/perf/arch= /mips/util/unwind-libunwind.c deleted file mode 100644 index 0d8c99c29da6..000000000000 --- a/tools/perf/arch/mips/util/unwind-libunwind.c +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include -#include "perf_regs.h" -#include "../../util/unwind.h" -#include "util/debug.h" - -int libunwind__arch_reg_id(int regnum) -{ - switch (regnum) { - case UNW_MIPS_R1 ... UNW_MIPS_R25: - return regnum - UNW_MIPS_R1 + PERF_REG_MIPS_R1; - case UNW_MIPS_R28 ... UNW_MIPS_R31: - return regnum - UNW_MIPS_R28 + PERF_REG_MIPS_R28; - case UNW_MIPS_PC: - return PERF_REG_MIPS_PC; - default: - pr_err("unwind: invalid reg id %d\n", regnum); - return -EINVAL; - } -} diff --git a/tools/perf/arch/powerpc/util/Build b/tools/perf/arch/powerpc/u= til/Build index d66574cbb9a9..ae928050e07a 100644 --- a/tools/perf/arch/powerpc/util/Build +++ b/tools/perf/arch/powerpc/util/Build @@ -6,5 +6,4 @@ perf-util-y +=3D evsel.o =20 perf-util-$(CONFIG_LIBDW) +=3D skip-callchain-idx.o =20 -perf-util-$(CONFIG_LIBUNWIND) +=3D unwind-libunwind.o perf-util-y +=3D auxtrace.o diff --git a/tools/perf/arch/powerpc/util/unwind-libunwind.c b/tools/perf/a= rch/powerpc/util/unwind-libunwind.c deleted file mode 100644 index 90a6beda20de..000000000000 --- a/tools/perf/arch/powerpc/util/unwind-libunwind.c +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright 2016 Chandan Kumar, IBM Corporation. - */ - -#include -#include -#include -#include "../../util/unwind.h" -#include "../../util/debug.h" - -int libunwind__arch_reg_id(int regnum) -{ - switch (regnum) { - case UNW_PPC64_R0: - return PERF_REG_POWERPC_R0; - case UNW_PPC64_R1: - return PERF_REG_POWERPC_R1; - case UNW_PPC64_R2: - return PERF_REG_POWERPC_R2; - case UNW_PPC64_R3: - return PERF_REG_POWERPC_R3; - case UNW_PPC64_R4: - return PERF_REG_POWERPC_R4; - case UNW_PPC64_R5: - return PERF_REG_POWERPC_R5; - case UNW_PPC64_R6: - return PERF_REG_POWERPC_R6; - case UNW_PPC64_R7: - return PERF_REG_POWERPC_R7; - case UNW_PPC64_R8: - return PERF_REG_POWERPC_R8; - case UNW_PPC64_R9: - return PERF_REG_POWERPC_R9; - case UNW_PPC64_R10: - return PERF_REG_POWERPC_R10; - case UNW_PPC64_R11: - return PERF_REG_POWERPC_R11; - case UNW_PPC64_R12: - return PERF_REG_POWERPC_R12; - case UNW_PPC64_R13: - return PERF_REG_POWERPC_R13; - case UNW_PPC64_R14: - return PERF_REG_POWERPC_R14; - case UNW_PPC64_R15: - return PERF_REG_POWERPC_R15; - case UNW_PPC64_R16: - return PERF_REG_POWERPC_R16; - case UNW_PPC64_R17: - return PERF_REG_POWERPC_R17; - case UNW_PPC64_R18: - return PERF_REG_POWERPC_R18; - case UNW_PPC64_R19: - return PERF_REG_POWERPC_R19; - case UNW_PPC64_R20: - return PERF_REG_POWERPC_R20; - case UNW_PPC64_R21: - return PERF_REG_POWERPC_R21; - case UNW_PPC64_R22: - return PERF_REG_POWERPC_R22; - case UNW_PPC64_R23: - return PERF_REG_POWERPC_R23; - case UNW_PPC64_R24: - return PERF_REG_POWERPC_R24; - case UNW_PPC64_R25: - return PERF_REG_POWERPC_R25; - case UNW_PPC64_R26: - return PERF_REG_POWERPC_R26; - case UNW_PPC64_R27: - return PERF_REG_POWERPC_R27; - case UNW_PPC64_R28: - return PERF_REG_POWERPC_R28; - case UNW_PPC64_R29: - return PERF_REG_POWERPC_R29; - case UNW_PPC64_R30: - return PERF_REG_POWERPC_R30; - case UNW_PPC64_R31: - return PERF_REG_POWERPC_R31; - case UNW_PPC64_LR: - return PERF_REG_POWERPC_LINK; - case UNW_PPC64_CTR: - return PERF_REG_POWERPC_CTR; - case UNW_PPC64_XER: - return PERF_REG_POWERPC_XER; - case UNW_PPC64_NIP: - return PERF_REG_POWERPC_NIP; - default: - pr_err("unwind: invalid reg id %d\n", regnum); - return -EINVAL; - } - return -EINVAL; -} diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build index b94c91984c66..7f89fffe4615 100644 --- a/tools/perf/arch/x86/util/Build +++ b/tools/perf/arch/x86/util/Build @@ -8,9 +8,6 @@ perf-util-y +=3D evlist.o perf-util-y +=3D mem-events.o perf-util-y +=3D evsel.o perf-util-y +=3D iostat.o - -perf-util-$(CONFIG_LOCAL_LIBUNWIND) +=3D unwind-libunwind.o - perf-util-y +=3D auxtrace.o perf-util-y +=3D intel-pt.o perf-util-y +=3D intel-bts.o diff --git a/tools/perf/arch/x86/util/unwind-libunwind.c b/tools/perf/arch/= x86/util/unwind-libunwind.c deleted file mode 100644 index 47357973b55b..000000000000 --- a/tools/perf/arch/x86/util/unwind-libunwind.c +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include "../../util/debug.h" -#ifndef REMOTE_UNWIND_LIBUNWIND -#include -#include "perf_regs.h" -#include "../../util/unwind.h" -#endif - -#ifdef HAVE_ARCH_X86_64_SUPPORT -int LIBUNWIND__ARCH_REG_ID(int regnum) -{ - int id; - - switch (regnum) { - case UNW_X86_64_RAX: - id =3D PERF_REG_X86_AX; - break; - case UNW_X86_64_RDX: - id =3D PERF_REG_X86_DX; - break; - case UNW_X86_64_RCX: - id =3D PERF_REG_X86_CX; - break; - case UNW_X86_64_RBX: - id =3D PERF_REG_X86_BX; - break; - case UNW_X86_64_RSI: - id =3D PERF_REG_X86_SI; - break; - case UNW_X86_64_RDI: - id =3D PERF_REG_X86_DI; - break; - case UNW_X86_64_RBP: - id =3D PERF_REG_X86_BP; - break; - case UNW_X86_64_RSP: - id =3D PERF_REG_X86_SP; - break; - case UNW_X86_64_R8: - id =3D PERF_REG_X86_R8; - break; - case UNW_X86_64_R9: - id =3D PERF_REG_X86_R9; - break; - case UNW_X86_64_R10: - id =3D PERF_REG_X86_R10; - break; - case UNW_X86_64_R11: - id =3D PERF_REG_X86_R11; - break; - case UNW_X86_64_R12: - id =3D PERF_REG_X86_R12; - break; - case UNW_X86_64_R13: - id =3D PERF_REG_X86_R13; - break; - case UNW_X86_64_R14: - id =3D PERF_REG_X86_R14; - break; - case UNW_X86_64_R15: - id =3D PERF_REG_X86_R15; - break; - case UNW_X86_64_RIP: - id =3D PERF_REG_X86_IP; - break; - default: - pr_err("unwind: invalid reg id %d\n", regnum); - return -EINVAL; - } - - return id; -} -#else -int LIBUNWIND__ARCH_REG_ID(int regnum) -{ - int id; - - switch (regnum) { - case UNW_X86_EAX: - id =3D PERF_REG_X86_AX; - break; - case UNW_X86_EDX: - id =3D PERF_REG_X86_DX; - break; - case UNW_X86_ECX: - id =3D PERF_REG_X86_CX; - break; - case UNW_X86_EBX: - id =3D PERF_REG_X86_BX; - break; - case UNW_X86_ESI: - id =3D PERF_REG_X86_SI; - break; - case UNW_X86_EDI: - id =3D PERF_REG_X86_DI; - break; - case UNW_X86_EBP: - id =3D PERF_REG_X86_BP; - break; - case UNW_X86_ESP: - id =3D PERF_REG_X86_SP; - break; - case UNW_X86_EIP: - id =3D PERF_REG_X86_IP; - break; - default: - pr_err("unwind: invalid reg id %d\n", regnum); - return -EINVAL; - } - - return id; -} -#endif /* HAVE_ARCH_X86_64_SUPPORT */ diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 01edfccebb88..bf4204135ccb 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -228,6 +228,7 @@ perf-util-$(CONFIG_LIBDW) +=3D unwind-libdw.o =20 perf-util-$(CONFIG_LOCAL_LIBUNWIND) +=3D unwind-libunwind-local.o perf-util-$(CONFIG_LIBUNWIND) +=3D unwind-libunwind.o +perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-arch/ perf-util-$(CONFIG_LIBUNWIND_X86) +=3D libunwind/x86_32.o perf-util-$(CONFIG_LIBUNWIND_AARCH64) +=3D libunwind/arm64.o =20 diff --git a/tools/perf/util/libunwind-arch/Build b/tools/perf/util/libunwi= nd-arch/Build new file mode 100644 index 000000000000..87fd657a3248 --- /dev/null +++ b/tools/perf/util/libunwind-arch/Build @@ -0,0 +1,10 @@ +perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-arch.o +perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-arm64.o +perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-arm.o +perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-loongarch.o +perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-mips.o +perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-ppc32.o +perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-ppc64.o +perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-s390.o +perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-i386.o +perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-x86_64.o diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.c b/tools/perf/u= til/libunwind-arch/libunwind-arch.c new file mode 100644 index 000000000000..5439bf90d161 --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-arch.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libunwind-arch.h" +#include "../debug.h" +#include +#include + +int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum) +{ + switch (e_machine) { + case EM_ARM: + return __get_perf_regnum_for_unw_regnum_arm(unw_regnum); + case EM_AARCH64: + return __get_perf_regnum_for_unw_regnum_arm64(unw_regnum); + case EM_LOONGARCH: + return __get_perf_regnum_for_unw_regnum_loongarch(unw_regnum); + case EM_MIPS: + return __get_perf_regnum_for_unw_regnum_mips(unw_regnum); + case EM_PPC: + return __get_perf_regnum_for_unw_regnum_ppc32(unw_regnum); + case EM_PPC64: + return __get_perf_regnum_for_unw_regnum_ppc64(unw_regnum); + case EM_S390: + return __get_perf_regnum_for_unw_regnum_s390(unw_regnum); + case EM_386: + return __get_perf_regnum_for_unw_regnum_i386(unw_regnum); + case EM_X86_64: + return __get_perf_regnum_for_unw_regnum_x86_64(unw_regnum); + default: + pr_err("ELF MACHINE %x is not supported.\n", e_machine); + return -EINVAL; + } +} diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.h b/tools/perf/u= til/libunwind-arch/libunwind-arch.h new file mode 100644 index 000000000000..e1009c6cb965 --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-arch.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBUNWIND_ARCH_H +#define __LIBUNWIND_ARCH_H + +int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum); +int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum); +int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum); +int __get_perf_regnum_for_unw_regnum_mips(int unw_regnum); +int __get_perf_regnum_for_unw_regnum_ppc32(int unw_regnum); +int __get_perf_regnum_for_unw_regnum_ppc64(int unw_regnum); +int __get_perf_regnum_for_unw_regnum_s390(int unw_regnum); +int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum); +int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum); +int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum); + +#endif /* __LIBUNWIND_ARCH_H */ diff --git a/tools/perf/util/libunwind-arch/libunwind-arm.c b/tools/perf/ut= il/libunwind-arch/libunwind-arm.c new file mode 100644 index 000000000000..6740ee55b043 --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-arm.c @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libunwind-arch.h" +#include "../debug.h" +#include "../../../arch/arm/include/uapi/asm/perf_regs.h" +#include +#include + +int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum) +{ + if (unw_regnum < 0 || unw_regnum >=3D PERF_REG_ARM_MAX) { + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } + return unw_regnum; +} diff --git a/tools/perf/util/libunwind-arch/libunwind-arm64.c b/tools/perf/= util/libunwind-arch/libunwind-arm64.c new file mode 100644 index 000000000000..53b1877dfa04 --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-arm64.c @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libunwind-arch.h" +#include "../debug.h" +#include "../../../arch/arm64/include/uapi/asm/perf_regs.h" +#include + +int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum) +{ + if (unw_regnum < 0 || unw_regnum >=3D PERF_REG_ARM64_EXTENDED_MAX) { + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } + return unw_regnum; +} diff --git a/tools/perf/util/libunwind-arch/libunwind-i386.c b/tools/perf/u= til/libunwind-arch/libunwind-i386.c new file mode 100644 index 000000000000..9eaf4ebff0c2 --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-i386.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libunwind-arch.h" +#include "../debug.h" +#include "../../../arch/x86/include/uapi/asm/perf_regs.h" +#include +#include +#include + +#ifdef HAVE_LIBUNWIND_X86_SUPPORT +#include +#endif + +int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum __maybe_unused) +{ +#ifndef HAVE_LIBUNWIND_X86_SUPPORT + return -EINVAL; +#else + static const int perf_i386_regnums[] =3D { +#define REGNUM(reg) [UNW_X86_E ## reg] =3D PERF_REG_X86_ ## reg + REGNUM(AX), + REGNUM(DX), + REGNUM(CX), + REGNUM(BX), + REGNUM(SI), + REGNUM(DI), + REGNUM(BP), + REGNUM(SP), + REGNUM(IP), +#undef REGNUM + }; + + if (unw_regnum =3D=3D UNW_X86_EAX) + return PERF_REG_X86_AX; + + if (unw_regnum < 0 || unw_regnum > (int)ARRAY_SIZE(perf_i386_regnums) || + perf_i386_regnums[unw_regnum] =3D=3D 0) { + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } + + return perf_i386_regnums[unw_regnum]; +#endif // HAVE_LIBUNWIND_X86_SUPPORT +} diff --git a/tools/perf/util/libunwind-arch/libunwind-loongarch.c b/tools/p= erf/util/libunwind-arch/libunwind-loongarch.c new file mode 100644 index 000000000000..7009410989bc --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-loongarch.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libunwind-arch.h" +#include "../debug.h" +#include "../../../arch/loongarch/include/uapi/asm/perf_regs.h" +#include +#include + +#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT +#include +#endif + +int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum __maybe_unus= ed) +{ +#ifndef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT + return -EINVAL; +#else + switch (unw_regnum) { + case UNW_LOONGARCH64_R1 ... UNW_LOONGARCH64_31: + return unw_regnum - UNW_LOONGARCH64_R1 + PERF_REG_LOONGARCH_R1; + case UNW_LOONGARCH64_PC: + return PERF_REG_LOONGARCH_PC; + default: + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } +#endif // HAVE_LIBUNWIND_LOONGARCH64_SUPPORT +} diff --git a/tools/perf/util/libunwind-arch/libunwind-mips.c b/tools/perf/u= til/libunwind-arch/libunwind-mips.c new file mode 100644 index 000000000000..01a506c8079c --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-mips.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libunwind-arch.h" +#include "../debug.h" +#include "../../../arch/mips/include/uapi/asm/perf_regs.h" +#include +#include + +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT +#include +#endif + +int __get_perf_regnum_for_unw_regnum_mips(int unw_regnum __maybe_unused) +{ +#ifndef HAVE_LIBUNWIND_MIPS_SUPPORT + return -EINVAL; +#else + switch (unw_regnum) { + case UNW_MIPS_R1 ... UNW_MIPS_R25: + return unw_regnum - UNW_MIPS_R1 + PERF_REG_MIPS_R1; + case UNW_MIPS_R28 ... UNW_MIPS_R31: + return unw_regnum - UNW_MIPS_R28 + PERF_REG_MIPS_R28; + case UNW_MIPS_PC: + return PERF_REG_MIPS_PC; + default: + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } +#endif // HAVE_LIBUNWIND_MIPS_SUPPORT +} diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc32.c b/tools/perf/= util/libunwind-arch/libunwind-ppc32.c new file mode 100644 index 000000000000..edcb0ec95dd7 --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-ppc32.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include "libunwind-arch.h" +#include "../debug.h" +#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h" +#include +#include + +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT +#include +#endif + +int __get_perf_regnum_for_unw_regnum_ppc32(int unw_regnum __maybe_unused) +{ +#ifndef HAVE_LIBUNWIND_PPC32_SUPPORT + return -EINVAL; +#else + switch (unw_regnum) { + case UNW_PPC32_R0 ... UNW_PPC32_31: + return unw_regnum - UNW_PPC32_R0 + PERF_REG_POWERPC_R0; + case UNW_PPC32_LR: + return PERF_REG_POWERPC_LINK; + case UNW_PPC32_CTR: + return PERF_REG_POWERPC_CTR; + case UNW_PPC32_XER: + return PERF_REG_POWERPC_XER; + default: + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } +#endif // HAVE_LIBUNWIND_PPC32_SUPPORT +} diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc64.c b/tools/perf/= util/libunwind-arch/libunwind-ppc64.c new file mode 100644 index 000000000000..9f57a049600b --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-ppc64.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include "libunwind-arch.h" +#include "../debug.h" +#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h" +#include +#include + +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT +#include +#endif + +int __get_perf_regnum_for_unw_regnum_ppc64(int unw_regnum __maybe_unused) +{ +#ifndef HAVE_LIBUNWIND_PPC64_SUPPORT + return -EINVAL; +#else + switch (unw_regnum) { + case UNW_PPC64_R0 ... UNW_PPC64_31: + return unw_regnum - UNW_PPC64_R0 + PERF_REG_POWERPC_R0; + case UNW_PPC64_LR: + return PERF_REG_POWERPC_LINK; + case UNW_PPC64_CTR: + return PERF_REG_POWERPC_CTR; + case UNW_PPC64_XER: + return PERF_REG_POWERPC_XER; + case UNW_PPC64_NIP: + return PERF_REG_POWERPC_NIP; + default: + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } +#endif // HAVE_LIBUNWIND_PPC64_SUPPORT +} diff --git a/tools/perf/util/libunwind-arch/libunwind-s390.c b/tools/perf/u= til/libunwind-arch/libunwind-s390.c new file mode 100644 index 000000000000..9fcc7885ca55 --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-s390.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libunwind-arch.h" +#include "../debug.h" +#include "../../../arch/s390/include/uapi/asm/perf_regs.h" +#include +#include + +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT +#include +#endif + +int __get_perf_regnum_for_unw_regnum_s390(int unw_regnum __maybe_unused) +{ +#ifndef HAVE_LIBUNWIND_S390X_SUPPORT + return -EINVAL; +#else + switch (unw_regnum) { + case UNW_S390X_R0 ... UNW_S390_R15: + return unw_regnum - UNW_S390_R0 + PERF_REG_S390_R0; + case UNW_S390X_F0 ... UNW_S390_F15: + return unw_regnum - UNW_S390_F0 + PERF_REG_S390_FP0; + case UNW_S390X_IP: + return PERF_REG_S390_PC; + default: + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } +#endif // HAVE_LIBUNWIND_S390X_SUPPORT +} diff --git a/tools/perf/util/libunwind-arch/libunwind-x86_64.c b/tools/perf= /util/libunwind-arch/libunwind-x86_64.c new file mode 100644 index 000000000000..6072e3597e61 --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-x86_64.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libunwind-arch.h" +#include "../debug.h" +#include "../../../arch/x86/include/uapi/asm/perf_regs.h" +#include +#include +#include + +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT +#include +#endif + +int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum __maybe_unused) +{ +#ifndef HAVE_LIBUNWIND_X86_64_SUPPORT + return -EINVAL; +#else + static const int perf_x86_64_regnums[] =3D { +#define REGNUM(reg) [UNW_X86_64_R ## reg] =3D PERF_REG_X86_ ## reg + REGNUM(AX), + REGNUM(DX), + REGNUM(CX), + REGNUM(BX), + REGNUM(SI), + REGNUM(DI), + REGNUM(BP), + REGNUM(SP), + REGNUM(IP), +#undef REGNUM +#define REGNUM(reg) [UNW_X86_64_ ## reg] =3D PERF_REG_X86_ ## reg + REGNUM(R8), + REGNUM(R9), + REGNUM(R10), + REGNUM(R11), + REGNUM(R12), + REGNUM(R13), + REGNUM(R14), + REGNUM(R15), +#undef REGNUM + }; + + if (unw_regnum =3D=3D UNW_X86_64_RAX) + return PERF_REG_X86_AX; + + if (unw_regnum < 0 || unw_regnum > (int)ARRAY_SIZE(perf_x86_64_regnums) = || + perf_x86_64_regnums[unw_regnum] =3D=3D 0) { + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } + return perf_x86_64_regnums[unw_regnum]; +#endif // HAVE_LIBUNWIND_X86_64_SUPPORT +} diff --git a/tools/perf/util/libunwind/arm64.c b/tools/perf/util/libunwind/= arm64.c index 37ecef0c53b9..15670a964495 100644 --- a/tools/perf/util/libunwind/arm64.c +++ b/tools/perf/util/libunwind/arm64.c @@ -14,11 +14,6 @@ =20 #define REMOTE_UNWIND_LIBUNWIND =20 -/* Define arch specific functions & regs for libunwind, should be - * defined before including "unwind.h" - */ -#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arm64_reg_id(regnum) - #include "unwind.h" #include "libunwind-aarch64.h" #define perf_event_arm_regs perf_event_arm64_regs diff --git a/tools/perf/util/libunwind/x86_32.c b/tools/perf/util/libunwind= /x86_32.c index 1697dece1b74..1e9fb8bfec44 100644 --- a/tools/perf/util/libunwind/x86_32.c +++ b/tools/perf/util/libunwind/x86_32.c @@ -14,20 +14,8 @@ =20 #define REMOTE_UNWIND_LIBUNWIND =20 -/* Define arch specific functions & regs for libunwind, should be - * defined before including "unwind.h" - */ -#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__x86_reg_id(regnum) - #include "unwind.h" #include "libunwind-x86.h" -#include <../../../../arch/x86/include/uapi/asm/perf_regs.h> - -/* HAVE_ARCH_X86_64_SUPPORT is used in'arch/x86/util/unwind-libunwind.c' - * for x86_32, we undef it to compile code for x86_32 only. - */ -#undef HAVE_ARCH_X86_64_SUPPORT -#include "../../arch/x86/util/unwind-libunwind.c" =20 /* Explicitly define NO_LIBUNWIND_DEBUG_FRAME, because non-ARM has no * dwarf_find_debug_frame() function. diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unw= ind-libunwind-local.c index 5b39ce21e333..3ecdb468b859 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c @@ -40,6 +40,7 @@ #include "debug.h" #include "asm/bug.h" #include "dso.h" +#include "libunwind-arch/libunwind-arch.h" =20 extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, @@ -96,6 +97,7 @@ struct unwind_info { struct perf_sample *sample; struct machine *machine; struct thread *thread; + uint16_t e_machine; bool best_effort; }; =20 @@ -584,9 +586,7 @@ static int access_mem(unw_addr_space_t __maybe_unused a= s, } =20 ret =3D perf_reg_value(&start, perf_sample__user_regs(ui->sample), - perf_arch_reg_sp(thread__e_machine(ui->thread, - ui->machine, - /*e_flags=3D*/NULL))); + perf_arch_reg_sp(ui->e_machine)); if (ret) return ret; =20 @@ -634,7 +634,7 @@ static int access_reg(unw_addr_space_t __maybe_unused a= s, return 0; } =20 - id =3D LIBUNWIND__ARCH_REG_ID(regnum); + id =3D get_perf_regnum_for_unw_regnum(ui->e_machine, regnum); if (id < 0) return -EINVAL; =20 @@ -735,7 +735,6 @@ static void _unwind__finish_access(struct maps *maps) static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, void *arg, int max_stack) { - uint16_t e_machine =3D thread__e_machine(ui->thread, ui->machine, /*e_fla= gs=3D*/NULL); u64 val; unw_word_t ips[max_stack]; unw_addr_space_t addr_space; @@ -743,7 +742,7 @@ static int get_entries(struct unwind_info *ui, unwind_e= ntry_cb_t cb, int ret, i =3D 0; =20 ret =3D perf_reg_value(&val, perf_sample__user_regs(ui->sample), - perf_arch_reg_ip(e_machine)); + perf_arch_reg_ip(ui->e_machine)); if (ret) return ret; =20 @@ -806,6 +805,7 @@ static int _unwind__get_entries(unwind_entry_cb_t cb, v= oid *arg, .sample =3D data, .thread =3D thread, .machine =3D maps__machine(thread__maps(thread)), + .e_machine =3D thread__e_machine(thread, /*machine=3D*/NULL, /*e_flag= s=3D*/NULL), .best_effort =3D best_effort }; =20 diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index ac0776e39f84..f8a8788ac986 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h @@ -64,11 +64,6 @@ int libunwind__get_entries(unwind_entry_cb_t cb, void *a= rg, struct thread *thread, struct perf_sample *data, int max_stack, bool best_effort); -#ifndef LIBUNWIND__ARCH_REG_ID -#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arch_reg_id(regnum) -#endif - -int LIBUNWIND__ARCH_REG_ID(int regnum); int unwind__prepare_access(struct maps *maps, struct map *map, bool *initi= alized); void unwind__flush_access(struct maps *maps); void unwind__finish_access(struct maps *maps); --=20 2.53.0.1213.gd9a14994de-goog From nobody Thu Apr 9 20:26:05 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A65F1376BE0 for ; Sat, 4 Apr 2026 05:40:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775281261; cv=none; b=XOxKj/eJygKaFi57bMEG+dLPNhxBM4OaxCDKkjYMMrJRpiC1o4Xwait09VivbfGlCidd0yyfQ39I/VFiKqVTy5qxBqvsBrqoEhGxTZc33csmUye0fq2w1JLapdzBTKkl/T1PH/qmJZRaSDBoqOxpDeSM0/CGNx/6pBX5jPgnwD0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775281261; c=relaxed/simple; bh=+VrpbxsBwgI98uuJwu6wVI8Gs7yAHtKCtUUW+f3IOU0=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=cyjvbLgOpnUVAy+iainBnZDveOSU9qFr2FY1jjRuN/BAhB/ZDmD3+BSZws5emsfku3GUrvi8MsR1T1Edj1d1dVt4Szr0mENnoLiGh2CqrzGTLRHlghNh8N9pCE7Y6303TR67ThtRbi5uZxMXXYd5a6F0u3HBJnda3+9PY/8KVPY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=P03oH/r8; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="P03oH/r8" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2bda35eab74so2839341eec.0 for ; Fri, 03 Apr 2026 22:40:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1775281259; x=1775886059; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=FfR8puUJPzn7FEEYKJ1A3cryLQ1Z0C1vhPD7LiUzP0M=; b=P03oH/r8FZfqyVztIbpRSsFvK4nAjoYEXqH+HdGg24DDQ+nCDWxT2lAcqSthU77+a+ YBZS8mN0vEMyKGGDbow0whE7j8DV7NSRURK3HV1GJSWlPa/jBL3GC6YM09WpQCCxM6OW stHU/uyoNKs5UE78xtJwWcWlvaQtUE/OQfluhk3VuZCkU5lfV67GhQeGYAHdEWYCxm71 MINN2GDWzG7GLthKGcCoIpwbtW0u+EsJRdbadOLjywzVtWp10nAZiMAbp3uM1pPg1LeO g0v7Ai7mfnA1zE4ufNCdL9qzdY/Ya/8sBtfo/Kvg9ZTwSbIwDv6bj9aV0RiRoSnz4ek3 GIGg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775281259; x=1775886059; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=FfR8puUJPzn7FEEYKJ1A3cryLQ1Z0C1vhPD7LiUzP0M=; b=ASN/Ss1cSlrKATaaqWB88v6MVvlMLmWnum8U6b5SoNkAfyYdmEvr4dliuWe+P9/Pya MDzzDzZwEJUCoO+W2zaH/nevWnx+fE13J54uStfiVHn8XdSSWrn9BFSreqQjLqoGNWrL yM6PonyP/3WH7BknaV9XNfcb/fZYP08FtIoscwgL5x6N+7N09qgH/dBIiqTFNlRtthOB EGJ0+4UwPdO2mOxZ0qoOH7va2WkCul4EPyoQ5n6yrZeIQCGHS8FYIBbIxesTA4tvOy3a Ol9zoFrjF1KMVto5e/ty+uGRPqAAaFyjBQOR1cnYniNG9N7SpcyCBNSQ0Lqa3L0RxtPf q1cg== X-Forwarded-Encrypted: i=1; AJvYcCUoTZwHnTLf6VfIksSNKb4MUfS0dwF2LLkQNlzHAi7MjJBkm41s7t7MBCAMGMzJ31h+kvrbgx8oZ8VSpPU=@vger.kernel.org X-Gm-Message-State: AOJu0YztUHGnFUEYnh3bNsGuz8ShB5LmUQvhrdjxM4RkyrK7Z71mUFJX OrOmwCcXMU3i+SDTUReJFxBpzwGceLZ2PjI4DcQKItFQmq0FHDEZWVTypFlzy9n6xzKes8o5zkW GRag7M2S3ig== X-Received: from dleb18-n1.prod.google.com ([2002:a05:701b:4252:10b0:12a:c10f:98fe]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:221b:b0:12c:427:dacf with SMTP id a92af1059eb24-12c0427de30mr294548c88.10.1775281258489; Fri, 03 Apr 2026 22:40:58 -0700 (PDT) Date: Fri, 3 Apr 2026 22:40:30 -0700 In-Reply-To: <20260404054032.1538095-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260305221927.3237145-1-irogers@google.com> <20260404054032.1538095-1-irogers@google.com> X-Mailer: git-send-email 2.53.0.1213.gd9a14994de-goog Message-ID: <20260404054032.1538095-7-irogers@google.com> Subject: [PATCH v3 6/8] perf unwind-libunwind: Move flush/finish access out of local From: Ian Rogers To: acme@kernel.org Cc: irogers@google.com, 9erthalion6@gmail.com, adrian.hunter@intel.com, alex@ghiti.fr, alexander.shishkin@linux.intel.com, andrew.jones@oss.qualcomm.com, aou@eecs.berkeley.edu, atrajeev@linux.ibm.com, blakejones@google.com, ctshao@google.com, dapeng1.mi@linux.intel.com, howardchu95@gmail.com, james.clark@linaro.org, john.g.garry@oracle.com, jolsa@kernel.org, leo.yan@linux.dev, libunwind-devel@nongnu.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-riscv@lists.infradead.org, mingo@redhat.com, namhyung@kernel.org, palmer@dabbelt.com, peterz@infradead.org, pjw@kernel.org, shimin.guo@skydio.com, tglozar@redhat.com, tmricht@linux.ibm.com, will@kernel.org, yuzhuo@google.com Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Flush and finish access are relatively simple calls into libunwind, move them out struct unwind_libunwind_ops. So that the correct version can be called, add an e_machine variable to maps. This size regression will go away when the unwind_libunwind_ops no longer need stashing in the maps. To set the e_machine up pass it into unwind__prepare_access, which no longer needs to determine the unwind operations based on a map dso because of this. This also means the maps copying code can call unwind__prepare_access once for the e_machine rather than once per map. Signed-off-by: Ian Rogers --- .../perf/util/libunwind-arch/libunwind-arch.c | 82 +++++++++++++++++++ .../perf/util/libunwind-arch/libunwind-arch.h | 24 ++++++ .../perf/util/libunwind-arch/libunwind-arm.c | 19 +++++ .../util/libunwind-arch/libunwind-arm64.c | 20 +++++ .../perf/util/libunwind-arch/libunwind-i386.c | 15 ++++ .../util/libunwind-arch/libunwind-loongarch.c | 15 ++++ .../perf/util/libunwind-arch/libunwind-mips.c | 15 ++++ .../util/libunwind-arch/libunwind-ppc32.c | 15 ++++ .../util/libunwind-arch/libunwind-ppc64.c | 15 ++++ .../perf/util/libunwind-arch/libunwind-s390.c | 15 ++++ .../util/libunwind-arch/libunwind-x86_64.c | 15 ++++ tools/perf/util/maps.c | 31 ++++--- tools/perf/util/maps.h | 2 + tools/perf/util/thread.c | 29 +------ tools/perf/util/unwind-libunwind-local.c | 12 --- tools/perf/util/unwind-libunwind.c | 61 +++++--------- tools/perf/util/unwind.h | 8 +- 17 files changed, 299 insertions(+), 94 deletions(-) diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.c b/tools/perf/u= til/libunwind-arch/libunwind-arch.c index 5439bf90d161..9692e6c81492 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arch.c +++ b/tools/perf/util/libunwind-arch/libunwind-arch.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include #include =20 @@ -30,3 +31,84 @@ int get_perf_regnum_for_unw_regnum(unsigned int e_machin= e, int unw_regnum) return -EINVAL; } } + + +void libunwind_arch__flush_access(struct maps *maps) +{ + unsigned int e_machine =3D maps__e_machine(maps); + + switch (e_machine) { + case EM_NONE: + break; // No libunwind info on the maps. + case EM_ARM: + __libunwind_arch__flush_access_arm(maps); + break; + case EM_AARCH64: + __libunwind_arch__flush_access_arm64(maps); + break; + case EM_LOONGARCH: + __libunwind_arch__flush_access_loongarch(maps); + break; + case EM_MIPS: + __libunwind_arch__flush_access_mips(maps); + break; + case EM_PPC: + __libunwind_arch__flush_access_ppc32(maps); + break; + case EM_PPC64: + __libunwind_arch__flush_access_ppc64(maps); + break; + case EM_S390: + __libunwind_arch__flush_access_s390(maps); + break; + case EM_386: + __libunwind_arch__flush_access_i386(maps); + break; + case EM_X86_64: + __libunwind_arch__flush_access_x86_64(maps); + break; + default: + pr_err("ELF MACHINE %x is not supported.\n", e_machine); + break; + } +} + +void libunwind_arch__finish_access(struct maps *maps) +{ + unsigned int e_machine =3D maps__e_machine(maps); + + switch (e_machine) { + case EM_NONE: + break; // No libunwind info on the maps. + case EM_ARM: + __libunwind_arch__finish_access_arm(maps); + break; + case EM_AARCH64: + __libunwind_arch__finish_access_arm64(maps); + break; + case EM_LOONGARCH: + __libunwind_arch__finish_access_loongarch(maps); + break; + case EM_MIPS: + __libunwind_arch__finish_access_mips(maps); + break; + case EM_PPC: + __libunwind_arch__finish_access_ppc32(maps); + break; + case EM_PPC64: + __libunwind_arch__finish_access_ppc64(maps); + break; + case EM_S390: + __libunwind_arch__finish_access_s390(maps); + break; + case EM_386: + __libunwind_arch__finish_access_i386(maps); + break; + case EM_X86_64: + __libunwind_arch__finish_access_x86_64(maps); + break; + default: + pr_err("ELF MACHINE %x is not supported.\n", e_machine); + break; + } +} diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.h b/tools/perf/u= til/libunwind-arch/libunwind-arch.h index e1009c6cb965..c00277a5e914 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arch.h +++ b/tools/perf/util/libunwind-arch/libunwind-arch.h @@ -2,6 +2,8 @@ #ifndef __LIBUNWIND_ARCH_H #define __LIBUNWIND_ARCH_H =20 +struct maps; + int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum); int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum); int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum); @@ -13,4 +15,26 @@ int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum= ); int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum); int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum); =20 +void __libunwind_arch__flush_access_arm(struct maps *maps); +void __libunwind_arch__flush_access_arm64(struct maps *maps); +void __libunwind_arch__flush_access_loongarch(struct maps *maps); +void __libunwind_arch__flush_access_mips(struct maps *maps); +void __libunwind_arch__flush_access_ppc32(struct maps *maps); +void __libunwind_arch__flush_access_ppc64(struct maps *maps); +void __libunwind_arch__flush_access_s390(struct maps *maps); +void __libunwind_arch__flush_access_i386(struct maps *maps); +void __libunwind_arch__flush_access_x86_64(struct maps *maps); +void libunwind_arch__flush_access(struct maps *maps); + +void __libunwind_arch__finish_access_arm(struct maps *maps); +void __libunwind_arch__finish_access_arm64(struct maps *maps); +void __libunwind_arch__finish_access_loongarch(struct maps *maps); +void __libunwind_arch__finish_access_mips(struct maps *maps); +void __libunwind_arch__finish_access_ppc32(struct maps *maps); +void __libunwind_arch__finish_access_ppc64(struct maps *maps); +void __libunwind_arch__finish_access_s390(struct maps *maps); +void __libunwind_arch__finish_access_i386(struct maps *maps); +void __libunwind_arch__finish_access_x86_64(struct maps *maps); +void libunwind_arch__finish_access(struct maps *maps); + #endif /* __LIBUNWIND_ARCH_H */ diff --git a/tools/perf/util/libunwind-arch/libunwind-arm.c b/tools/perf/ut= il/libunwind-arch/libunwind-arm.c index 6740ee55b043..bbaf01406c52 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arm.c +++ b/tools/perf/util/libunwind-arch/libunwind-arm.c @@ -1,10 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include "../../../arch/arm/include/uapi/asm/perf_regs.h" #include #include =20 +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT +#include +#endif + int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum) { if (unw_regnum < 0 || unw_regnum >=3D PERF_REG_ARM_MAX) { @@ -13,3 +18,17 @@ int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum) } return unw_regnum; } + +void __libunwind_arch__flush_access_arm(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_arm(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-arm64.c b/tools/perf/= util/libunwind-arch/libunwind-arm64.c index 53b1877dfa04..8ba510089736 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arm64.c +++ b/tools/perf/util/libunwind-arch/libunwind-arm64.c @@ -1,9 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include "../../../arch/arm64/include/uapi/asm/perf_regs.h" +#include #include =20 +#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT +#include +#endif + int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum) { if (unw_regnum < 0 || unw_regnum >=3D PERF_REG_ARM64_EXTENDED_MAX) { @@ -12,3 +18,17 @@ int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnu= m) } return unw_regnum; } + +void __libunwind_arch__flush_access_arm64(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_arm64(struct maps *maps __maybe_unuse= d) +{ +#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-i386.c b/tools/perf/u= til/libunwind-arch/libunwind-i386.c index 9eaf4ebff0c2..45ff30c95c1b 100644 --- a/tools/perf/util/libunwind-arch/libunwind-i386.c +++ b/tools/perf/util/libunwind-arch/libunwind-i386.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include "../../../arch/x86/include/uapi/asm/perf_regs.h" #include #include @@ -41,3 +42,17 @@ int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum= __maybe_unused) return perf_i386_regnums[unw_regnum]; #endif // HAVE_LIBUNWIND_X86_SUPPORT } + +void __libunwind_arch__flush_access_i386(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_i386(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-loongarch.c b/tools/p= erf/util/libunwind-arch/libunwind-loongarch.c index 7009410989bc..837aa11e2b9f 100644 --- a/tools/perf/util/libunwind-arch/libunwind-loongarch.c +++ b/tools/perf/util/libunwind-arch/libunwind-loongarch.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include "../../../arch/loongarch/include/uapi/asm/perf_regs.h" #include #include @@ -25,3 +26,17 @@ int __get_perf_regnum_for_unw_regnum_loongarch(int unw_r= egnum __maybe_unused) } #endif // HAVE_LIBUNWIND_LOONGARCH64_SUPPORT } + +void __libunwind_arch__flush_access_loongarch(struct maps *maps __maybe_un= used) +{ +#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_loongarch(struct maps *maps __maybe_u= nused) +{ +#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-mips.c b/tools/perf/u= til/libunwind-arch/libunwind-mips.c index 01a506c8079c..1fa81742ff4a 100644 --- a/tools/perf/util/libunwind-arch/libunwind-mips.c +++ b/tools/perf/util/libunwind-arch/libunwind-mips.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include "../../../arch/mips/include/uapi/asm/perf_regs.h" #include #include @@ -27,3 +28,17 @@ int __get_perf_regnum_for_unw_regnum_mips(int unw_regnum= __maybe_unused) } #endif // HAVE_LIBUNWIND_MIPS_SUPPORT } + +void __libunwind_arch__flush_access_mips(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_mips(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc32.c b/tools/perf/= util/libunwind-arch/libunwind-ppc32.c index edcb0ec95dd7..fa8471c74bf3 100644 --- a/tools/perf/util/libunwind-arch/libunwind-ppc32.c +++ b/tools/perf/util/libunwind-arch/libunwind-ppc32.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include "../../../arch/powerpc/include/uapi/asm/perf_regs.h" #include #include @@ -29,3 +30,17 @@ int __get_perf_regnum_for_unw_regnum_ppc32(int unw_regnu= m __maybe_unused) } #endif // HAVE_LIBUNWIND_PPC32_SUPPORT } + +void __libunwind_arch__flush_access_ppc32(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_ppc32(struct maps *maps __maybe_unuse= d) +{ +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc64.c b/tools/perf/= util/libunwind-arch/libunwind-ppc64.c index 9f57a049600b..2f746e347336 100644 --- a/tools/perf/util/libunwind-arch/libunwind-ppc64.c +++ b/tools/perf/util/libunwind-arch/libunwind-ppc64.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include "../../../arch/powerpc/include/uapi/asm/perf_regs.h" #include #include @@ -31,3 +32,17 @@ int __get_perf_regnum_for_unw_regnum_ppc64(int unw_regnu= m __maybe_unused) } #endif // HAVE_LIBUNWIND_PPC64_SUPPORT } + +void __libunwind_arch__flush_access_ppc64(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_ppc64(struct maps *maps __maybe_unuse= d) +{ +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-s390.c b/tools/perf/u= til/libunwind-arch/libunwind-s390.c index 9fcc7885ca55..9f68d15438b2 100644 --- a/tools/perf/util/libunwind-arch/libunwind-s390.c +++ b/tools/perf/util/libunwind-arch/libunwind-s390.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include "../../../arch/s390/include/uapi/asm/perf_regs.h" #include #include @@ -27,3 +28,17 @@ int __get_perf_regnum_for_unw_regnum_s390(int unw_regnum= __maybe_unused) } #endif // HAVE_LIBUNWIND_S390X_SUPPORT } + +void __libunwind_arch__flush_access_s390(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_s390(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-x86_64.c b/tools/perf= /util/libunwind-arch/libunwind-x86_64.c index 6072e3597e61..25e326bd3e15 100644 --- a/tools/perf/util/libunwind-arch/libunwind-x86_64.c +++ b/tools/perf/util/libunwind-arch/libunwind-x86_64.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include "../../../arch/x86/include/uapi/asm/perf_regs.h" #include #include @@ -50,3 +51,17 @@ int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regn= um __maybe_unused) return perf_x86_64_regnums[unw_regnum]; #endif // HAVE_LIBUNWIND_X86_64_SUPPORT } + +void __libunwind_arch__flush_access_x86_64(struct maps *maps __maybe_unuse= d) +{ +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_x86_64(struct maps *maps __maybe_unus= ed) +{ +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c index 4092211cff62..8c7b2a1e7642 100644 --- a/tools/perf/util/maps.c +++ b/tools/perf/util/maps.c @@ -40,6 +40,7 @@ DECLARE_RC_STRUCT(maps) { #ifdef HAVE_LIBUNWIND_SUPPORT void *addr_space; const struct unwind_libunwind_ops *unwind_libunwind_ops; + uint16_t e_machine; #endif #ifdef HAVE_LIBDW_SUPPORT void *libdw_addr_space_dwfl; @@ -206,6 +207,16 @@ void maps__set_unwind_libunwind_ops(struct maps *maps,= const struct unwind_libun { RC_CHK_ACCESS(maps)->unwind_libunwind_ops =3D ops; } + +uint16_t maps__e_machine(const struct maps *maps) +{ + return RC_CHK_ACCESS(maps)->e_machine; +} + +void maps__set_e_machine(struct maps *maps, uint16_t e_machine) +{ + RC_CHK_ACCESS(maps)->e_machine =3D e_machine; +} #endif #ifdef HAVE_LIBDW_SUPPORT void *maps__libdw_addr_space_dwfl(const struct maps *maps) @@ -1038,6 +1049,9 @@ int maps__copy_from(struct maps *dest, struct maps *p= arent) down_write(maps__lock(dest)); down_read(maps__lock(parent)); =20 +#ifdef HAVE_LIBUNWIND_SUPPORT + unwind__prepare_access(dest, maps__e_machine(parent)); +#endif parent_maps_by_address =3D maps__maps_by_address(parent); n =3D maps__nr_maps(parent); if (maps__nr_maps(dest) =3D=3D 0) { @@ -1067,14 +1081,11 @@ int maps__copy_from(struct maps *dest, struct maps = *parent) if (!new) err =3D -ENOMEM; else { - err =3D unwind__prepare_access(dest, new, NULL); - if (!err) { - dest_maps_by_address[i] =3D new; - map__set_kmap_maps(new, dest); - if (dest_maps_by_name) - dest_maps_by_name[i] =3D map__get(new); - RC_CHK_ACCESS(dest)->nr_maps =3D i + 1; - } + dest_maps_by_address[i] =3D new; + map__set_kmap_maps(new, dest); + if (dest_maps_by_name) + dest_maps_by_name[i] =3D map__get(new); + RC_CHK_ACCESS(dest)->nr_maps =3D i + 1; } if (err) map__put(new); @@ -1099,9 +1110,7 @@ int maps__copy_from(struct maps *dest, struct maps *p= arent) if (!new) err =3D -ENOMEM; else { - err =3D unwind__prepare_access(dest, new, NULL); - if (!err) - err =3D __maps__insert(dest, new); + err =3D __maps__insert(dest, new); } map__put(new); } diff --git a/tools/perf/util/maps.h b/tools/perf/util/maps.h index 20c52084ba9e..6469f62c41a8 100644 --- a/tools/perf/util/maps.h +++ b/tools/perf/util/maps.h @@ -51,6 +51,8 @@ void *maps__addr_space(const struct maps *maps); void maps__set_addr_space(struct maps *maps, void *addr_space); const struct unwind_libunwind_ops *maps__unwind_libunwind_ops(const struct= maps *maps); void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind= _libunwind_ops *ops); +uint16_t maps__e_machine(const struct maps *maps); +void maps__set_e_machine(struct maps *maps, uint16_t e_machine); #endif #ifdef HAVE_LIBDW_SUPPORT void *maps__libdw_addr_space_dwfl(const struct maps *maps); diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 22be77225bb0..c5df11ad329c 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -358,41 +358,20 @@ size_t thread__fprintf(struct thread *thread, FILE *f= p) int thread__insert_map(struct thread *thread, struct map *map) { int ret; + uint16_t e_machine =3D thread__e_machine(thread, /*machine=3D*/NULL, /*e_= flags=3D*/NULL); =20 - ret =3D unwind__prepare_access(thread__maps(thread), map, NULL); + ret =3D unwind__prepare_access(thread__maps(thread), e_machine); if (ret) return ret; =20 return maps__fixup_overlap_and_insert(thread__maps(thread), map); } =20 -struct thread__prepare_access_maps_cb_args { - int err; - struct maps *maps; -}; - -static int thread__prepare_access_maps_cb(struct map *map, void *data) -{ - bool initialized =3D false; - struct thread__prepare_access_maps_cb_args *args =3D data; - - args->err =3D unwind__prepare_access(args->maps, map, &initialized); - - return (args->err || initialized) ? 1 : 0; -} - static int thread__prepare_access(struct thread *thread) { - struct thread__prepare_access_maps_cb_args args =3D { - .err =3D 0, - }; - - if (dwarf_callchain_users) { - args.maps =3D thread__maps(thread); - maps__for_each_map(thread__maps(thread), thread__prepare_access_maps_cb,= &args); - } + uint16_t e_machine =3D thread__e_machine(thread, /*machine=3D*/NULL, /*e_= flags=3D*/NULL); =20 - return args.err; + return unwind__prepare_access(thread__maps(thread), e_machine); } =20 static int thread__clone_maps(struct thread *thread, struct thread *parent= , bool do_maps_clone) diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unw= ind-libunwind-local.c index 3ecdb468b859..8f0128ba05a7 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c @@ -722,16 +722,6 @@ static int _unwind__prepare_access(struct maps *maps) return 0; } =20 -static void _unwind__flush_access(struct maps *maps) -{ - unw_flush_cache(maps__addr_space(maps), 0, 0); -} - -static void _unwind__finish_access(struct maps *maps) -{ - unw_destroy_addr_space(maps__addr_space(maps)); -} - static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, void *arg, int max_stack) { @@ -821,8 +811,6 @@ static int _unwind__get_entries(unwind_entry_cb_t cb, v= oid *arg, static struct unwind_libunwind_ops _unwind_libunwind_ops =3D { .prepare_access =3D _unwind__prepare_access, - .flush_access =3D _unwind__flush_access, - .finish_access =3D _unwind__finish_access, .get_entries =3D _unwind__get_entries, }; =20 diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-li= bunwind.c index a0016b897dae..eaee7b78d87c 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -7,76 +7,55 @@ #include "debug.h" #include "env.h" #include "callchain.h" +#include "libunwind-arch/libunwind-arch.h" +#include +#include =20 struct unwind_libunwind_ops __weak *local_unwind_libunwind_ops; struct unwind_libunwind_ops __weak *x86_32_unwind_libunwind_ops; struct unwind_libunwind_ops __weak *arm64_unwind_libunwind_ops; =20 -int unwind__prepare_access(struct maps *maps, struct map *map, bool *initi= alized) +int unwind__prepare_access(struct maps *maps, uint16_t e_machine) { - const char *arch; - enum dso_type dso_type; struct unwind_libunwind_ops *ops =3D local_unwind_libunwind_ops; - struct dso *dso =3D map__dso(map); - struct machine *machine; - int err; =20 if (!dwarf_callchain_users) return 0; =20 if (maps__addr_space(maps)) { - pr_debug("unwind: thread map already set, dso=3D%s\n", dso__name(dso)); - if (initialized) - *initialized =3D true; + pr_debug3("unwind: thread map already set\n"); return 0; } =20 - machine =3D maps__machine(maps); - /* env->arch is NULL for live-mode (i.e. perf top) */ - if (!machine->env || !machine->env->arch) - goto out_register; - - dso_type =3D dso__type(dso, machine); - if (dso_type =3D=3D DSO__TYPE_UNKNOWN) - return 0; - - arch =3D perf_env__arch(machine->env); - - if (!strcmp(arch, "x86")) { - if (dso_type !=3D DSO__TYPE_64BIT) + if (e_machine !=3D EM_HOST) { + /* If not live/local mode. */ + switch (e_machine) { + case EM_386: ops =3D x86_32_unwind_libunwind_ops; - } else if (!strcmp(arch, "arm64") || !strcmp(arch, "arm")) { - if (dso_type =3D=3D DSO__TYPE_64BIT) + break; + case EM_AARCH64: ops =3D arm64_unwind_libunwind_ops; - } - - if (!ops) { - pr_warning_once("unwind: target platform=3D%s is not supported\n", arch); + break; + default: + pr_warning_once("unwind: ELF machine type %d is not supported\n", + e_machine); return 0; + } } -out_register: maps__set_unwind_libunwind_ops(maps, ops); + maps__set_e_machine(maps, e_machine); =20 - err =3D maps__unwind_libunwind_ops(maps)->prepare_access(maps); - if (initialized) - *initialized =3D err ? false : true; - return err; + return maps__unwind_libunwind_ops(maps)->prepare_access(maps); } =20 void unwind__flush_access(struct maps *maps) { - const struct unwind_libunwind_ops *ops =3D maps__unwind_libunwind_ops(map= s); - - if (ops) - ops->flush_access(maps); + libunwind_arch__flush_access(maps); } =20 void unwind__finish_access(struct maps *maps) { - const struct unwind_libunwind_ops *ops =3D maps__unwind_libunwind_ops(map= s); - - if (ops) - ops->finish_access(maps); + libunwind_arch__finish_access(maps); } =20 int libunwind__get_entries(unwind_entry_cb_t cb, void *arg, diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index f8a8788ac986..ad610c5a241c 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h @@ -2,6 +2,7 @@ #ifndef __UNWIND_H #define __UNWIND_H =20 +#include #include #include #include "map_symbol.h" @@ -20,8 +21,6 @@ typedef int (*unwind_entry_cb_t)(struct unwind_entry *ent= ry, void *arg); =20 struct unwind_libunwind_ops { int (*prepare_access)(struct maps *maps); - void (*flush_access)(struct maps *maps); - void (*finish_access)(struct maps *maps); int (*get_entries)(unwind_entry_cb_t cb, void *arg, struct thread *thread, struct perf_sample *data, int max_stack, bool best_effort); @@ -64,7 +63,7 @@ int libunwind__get_entries(unwind_entry_cb_t cb, void *ar= g, struct thread *thread, struct perf_sample *data, int max_stack, bool best_effort); -int unwind__prepare_access(struct maps *maps, struct map *map, bool *initi= alized); +int unwind__prepare_access(struct maps *maps, uint16_t e_machine); void unwind__flush_access(struct maps *maps); void unwind__finish_access(struct maps *maps); #else @@ -81,8 +80,7 @@ static inline int libunwind__get_entries(unwind_entry_cb_= t cb __maybe_unused, } =20 static inline int unwind__prepare_access(struct maps *maps __maybe_unused, - struct map *map __maybe_unused, - bool *initialized __maybe_unused) + uint16_t e_machine __maybe_unused) { return 0; } --=20 2.53.0.1213.gd9a14994de-goog From nobody Thu Apr 9 20:26:05 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3A248373BFC for ; Sat, 4 Apr 2026 05:41:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775281269; cv=none; b=irLJ6S5z6fRcrEaP4BXLIR5rnDXg0K03md51bocFIwWzPbxnK1U3IVEWwz7MX4RgRqz4jJn0xZwErR3kX4e1MVXGeIGn0VGECMd8ArclNxfVqxf9HbwvQmTV56j9Q3KJNb2bEysgzDKPnDfTumdHbGe5zgfY1e2WTSbnlxPi16k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775281269; c=relaxed/simple; bh=COBuGpY5ERGwxSP6FoueTOCdx4bzGWh2TQkzXkPBqfM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=OBz9g6c5PjUosVHSmXRUuwPiRgWXT5SbImL4Yw4xgHenxycg1uGfA7dGTGMRDr+ebbCindVVsziOhjRKT5/lAK0igJkra9ieNRhP55ZRYkAIl9KEdQJBj4jnHF7P/goRjxtoXWmUdIuEiiqZs/qqFeg+nXLUmd0PGGVnxFOa56o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=vlbm/Czw; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="vlbm/Czw" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2bdf6fe90a9so3377983eec.1 for ; Fri, 03 Apr 2026 22:41:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1775281261; x=1775886061; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=Oi5oXu9pk6iDvpcxQBgljxkyamiyBaudRtXU+2mmKBk=; b=vlbm/CzwPXxDCkZb4Ex6cVPttI/U0GY9pFoxulIRM6aqp0PhNzHNLYonyL52HzT+lG nu3vUiUUbD7DmWS8TSI7txeXgczHkD7dc7sv72ss8vWQ+7BlIJZsCkZex9bdeEeH13GC 6te5Gu57fkHvzkqSz6QqQdaCBM1m4ITBne8ElZEFM6ZdNQwV5Iwbg9lXQCWbMjm9R4de FeGlE99zL6uhpTOWex0Pran2z5ewaJ+wArV4R0gv+Hvqp38VVHjupOBuWIZGxdT4ctz/ MrCm7iSzoxChUWkiddbIfaFgUUQ/G4V3blwVLDZPK0hVpXwQiR8daGx+xUhItN6GBkS+ 44xQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775281261; x=1775886061; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=Oi5oXu9pk6iDvpcxQBgljxkyamiyBaudRtXU+2mmKBk=; b=ADovgvti+KpSsWiniFe+4oMhVKTFDR9ECo55Av24Vmqr2nCwfb2ybkBxlyIrC0dP9Y vLfq0a8Xk1k+lDHPKkfpxWD0JSP4b7TbSsIWvqRpjiXx/s0ogaS3XaUR5ctCnhE6mFoo 8n0QGu6T8YrBeo4UHPlNmGWiWuDXX0q3MgEtcBtzMN0pMig8cWq43l7vuLYAu9BdWVTa QddMEGLFtl6BmaNX6E1BCzlIHTTzYz5xvo0ZSTtomj13y0EKVPcz7NWx8S3DUjlFmy5H Bl+ci1G4qOIUGZUTBvpn6JhLUQb/ATRi3UfrJxRACjqjNEL8qyzB5r/Apa7GbQBql6id 7lKw== X-Forwarded-Encrypted: i=1; AJvYcCU8B2sfOoBawLi0tOAndwls0H8pSas+S1Oumi+VA1pThZcPzfwhq0tfqFEET9cHqVSqVvSpgfFn5gkGBsg=@vger.kernel.org X-Gm-Message-State: AOJu0YzmLQzuNSWowTa84kOQ1z3UwW5yLnUeRpj6ImEpG6Q8C7C2A4i4 wf5LdPOJcq7oaWTZ7Q9R86W0DQAoFv1SC7Ah74+NJY6pcogz7+zHgZAtLUszwMpuINi3vyXUCc2 Y20MxI9WB+g== X-Received: from dyblf33.prod.google.com ([2002:a05:7301:a21:b0:2c5:2b09:16ef]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:a506:b0:2c1:27c:75cd with SMTP id 5a478bee46e88-2cbfa2d0e93mr2713178eec.13.1775281260610; Fri, 03 Apr 2026 22:41:00 -0700 (PDT) Date: Fri, 3 Apr 2026 22:40:31 -0700 In-Reply-To: <20260404054032.1538095-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260305221927.3237145-1-irogers@google.com> <20260404054032.1538095-1-irogers@google.com> X-Mailer: git-send-email 2.53.0.1213.gd9a14994de-goog Message-ID: <20260404054032.1538095-8-irogers@google.com> Subject: [PATCH v3 7/8] perf unwind-libunwind: Remove libunwind-local From: Ian Rogers To: acme@kernel.org Cc: irogers@google.com, 9erthalion6@gmail.com, adrian.hunter@intel.com, alex@ghiti.fr, alexander.shishkin@linux.intel.com, andrew.jones@oss.qualcomm.com, aou@eecs.berkeley.edu, atrajeev@linux.ibm.com, blakejones@google.com, ctshao@google.com, dapeng1.mi@linux.intel.com, howardchu95@gmail.com, james.clark@linaro.org, john.g.garry@oracle.com, jolsa@kernel.org, leo.yan@linux.dev, libunwind-devel@nongnu.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-riscv@lists.infradead.org, mingo@redhat.com, namhyung@kernel.org, palmer@dabbelt.com, peterz@infradead.org, pjw@kernel.org, shimin.guo@skydio.com, tglozar@redhat.com, tmricht@linux.ibm.com, will@kernel.org, yuzhuo@google.com Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Local unwinding only works on the machine libunwind is built for, rather than cross platform, the APIs for remote and local unwinding are similar but types like unw_word_t depend on the included header. Place the architecture specific code into the appropriate libunwind-.c file. Put generic code in unwind-libunwind.c and use libunwind-arch to choose the correct implementation based on the thread's e_machine. Structuring the code this way avoids including the unwind-libunwind-local.c for each architecture of remote unwinding. Data is moved into the struct unwind_info to simplify the architecture and generic code, trying to keep as much code as possible generic. Signed-off-by: Ian Rogers --- tools/perf/util/Build | 3 - .../perf/util/libunwind-arch/libunwind-arch.c | 184 ++++ .../perf/util/libunwind-arch/libunwind-arch.h | 234 +++++ .../perf/util/libunwind-arch/libunwind-arm.c | 256 ++++++ .../util/libunwind-arch/libunwind-arm64.c | 255 ++++++ .../perf/util/libunwind-arch/libunwind-i386.c | 254 ++++++ .../util/libunwind-arch/libunwind-loongarch.c | 255 ++++++ .../perf/util/libunwind-arch/libunwind-mips.c | 255 ++++++ .../util/libunwind-arch/libunwind-ppc32.c | 255 ++++++ .../util/libunwind-arch/libunwind-ppc64.c | 255 ++++++ .../perf/util/libunwind-arch/libunwind-s390.c | 255 ++++++ .../util/libunwind-arch/libunwind-x86_64.c | 253 ++++++ tools/perf/util/libunwind/arm64.c | 35 - tools/perf/util/libunwind/x86_32.c | 29 - tools/perf/util/maps.c | 10 - tools/perf/util/maps.h | 2 - tools/perf/util/unwind-libunwind-local.c | 820 ------------------ tools/perf/util/unwind-libunwind.c | 654 +++++++++++++- tools/perf/util/unwind.h | 7 - 19 files changed, 3334 insertions(+), 937 deletions(-) delete mode 100644 tools/perf/util/libunwind/arm64.c delete mode 100644 tools/perf/util/libunwind/x86_32.c delete mode 100644 tools/perf/util/unwind-libunwind-local.c diff --git a/tools/perf/util/Build b/tools/perf/util/Build index bf4204135ccb..2bb60f50f62d 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -226,11 +226,8 @@ perf-util-$(CONFIG_LIBDW) +=3D annotate-data.o perf-util-$(CONFIG_LIBDW) +=3D libdw.o perf-util-$(CONFIG_LIBDW) +=3D unwind-libdw.o =20 -perf-util-$(CONFIG_LOCAL_LIBUNWIND) +=3D unwind-libunwind-local.o perf-util-$(CONFIG_LIBUNWIND) +=3D unwind-libunwind.o perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-arch/ -perf-util-$(CONFIG_LIBUNWIND_X86) +=3D libunwind/x86_32.o -perf-util-$(CONFIG_LIBUNWIND_AARCH64) +=3D libunwind/arm64.o =20 ifeq ($(CONFIG_LIBTRACEEVENT),y) perf-util-$(CONFIG_LIBBABELTRACE) +=3D data-convert-bt.o diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.c b/tools/perf/u= til/libunwind-arch/libunwind-arch.c index 9692e6c81492..8539b4233df4 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arch.c +++ b/tools/perf/util/libunwind-arch/libunwind-arch.c @@ -112,3 +112,187 @@ void libunwind_arch__finish_access(struct maps *maps) break; } } + +void *libunwind_arch__create_addr_space(unsigned int e_machine) +{ + switch (e_machine) { + case EM_ARM: + return __libunwind_arch__create_addr_space_arm(); + case EM_AARCH64: + return __libunwind_arch__create_addr_space_arm64(); + case EM_LOONGARCH: + return __libunwind_arch__create_addr_space_loongarch(); + case EM_MIPS: + return __libunwind_arch__create_addr_space_mips(); + case EM_PPC: + return __libunwind_arch__create_addr_space_ppc32(); + case EM_PPC64: + return __libunwind_arch__create_addr_space_ppc64(); + case EM_S390: + return __libunwind_arch__create_addr_space_s390(); + case EM_386: + return __libunwind_arch__create_addr_space_i386(); + case EM_X86_64: + return __libunwind_arch__create_addr_space_x86_64(); + default: + pr_err("ELF MACHINE %x is not supported.\n", e_machine); + return NULL; + } +} + +int libunwind_arch__dwarf_search_unwind_table(unsigned int e_machine, + void *as, + uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg) +{ + switch (e_machine) { + case EM_ARM: + return __libunwind_arch__dwarf_search_unwind_table_arm(as, ip, di, pi, + need_unwind_info, arg); + case EM_AARCH64: + return __libunwind_arch__dwarf_search_unwind_table_arm64(as, ip, di, pi, + need_unwind_info, arg); + case EM_LOONGARCH: + return __libunwind_arch__dwarf_search_unwind_table_loongarch(as, ip, di,= pi, + need_unwind_info, arg); + case EM_MIPS: + return __libunwind_arch__dwarf_search_unwind_table_mips(as, ip, di, pi, + need_unwind_info, arg); + case EM_PPC: + return __libunwind_arch__dwarf_search_unwind_table_ppc32(as, ip, di, pi, + need_unwind_info, arg); + case EM_PPC64: + return __libunwind_arch__dwarf_search_unwind_table_ppc64(as, ip, di, pi, + need_unwind_info, arg); + case EM_S390: + return __libunwind_arch__dwarf_search_unwind_table_s390(as, ip, di, pi, + need_unwind_info, arg); + case EM_386: + return __libunwind_arch__dwarf_search_unwind_table_i386(as, ip, di, pi, + need_unwind_info, arg); + case EM_X86_64: + return __libunwind_arch__dwarf_search_unwind_table_x86_64(as, ip, di, pi, + need_unwind_info, arg); + default: + pr_err("ELF MACHINE %x is not supported.\n", e_machine); + return -EINVAL; + } +} + +int libunwind_arch__dwarf_find_debug_frame(unsigned int e_machine, + int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end) +{ + switch (e_machine) { + case EM_ARM: + return __libunwind_arch__dwarf_find_debug_frame_arm(found, di_debug, ip,= segbase, + obj_name, start, end); + case EM_AARCH64: + return __libunwind_arch__dwarf_find_debug_frame_arm64(found, di_debug, i= p, segbase, + obj_name, start, end); + case EM_LOONGARCH: + return __libunwind_arch__dwarf_find_debug_frame_loongarch(found, di_debu= g, ip, + segbase, obj_name, + start, end); + case EM_MIPS: + return __libunwind_arch__dwarf_find_debug_frame_mips(found, di_debug, ip= , segbase, + obj_name, start, end); + case EM_PPC: + return __libunwind_arch__dwarf_find_debug_frame_ppc32(found, di_debug, i= p, segbase, + obj_name, start, end); + case EM_PPC64: + return __libunwind_arch__dwarf_find_debug_frame_ppc64(found, di_debug, i= p, segbase, + obj_name, start, end); + case EM_S390: + return __libunwind_arch__dwarf_find_debug_frame_s390(found, di_debug, ip= , segbase, + obj_name, start, end); + case EM_386: + return __libunwind_arch__dwarf_find_debug_frame_i386(found, di_debug, ip= , segbase, + obj_name, start, end); + case EM_X86_64: + return __libunwind_arch__dwarf_find_debug_frame_x86_64(found, di_debug, = ip, segbase, + obj_name, start, end); + default: + pr_err("ELF MACHINE %x is not supported.\n", e_machine); + return -EINVAL; + } +} + +struct unwind_info *libunwind_arch_unwind_info__new(struct thread *thread, + struct perf_sample *sample, int max_stack, + bool best_effort, uint16_t e_machine, + uint64_t first_ip) +{ + switch (e_machine) { + case EM_ARM: + return __libunwind_arch_unwind_info__new_arm(thread, sample, max_stack, + best_effort, first_ip); + case EM_AARCH64: + return __libunwind_arch_unwind_info__new_arm64(thread, sample, max_stack, + best_effort, first_ip); + case EM_LOONGARCH: + return __libunwind_arch_unwind_info__new_loongarch(thread, sample, max_s= tack, + best_effort, first_ip); + case EM_MIPS: + return __libunwind_arch_unwind_info__new_mips(thread, sample, max_stack, + best_effort, first_ip); + case EM_PPC: + return __libunwind_arch_unwind_info__new_ppc32(thread, sample, max_stack, + best_effort, first_ip); + case EM_PPC64: + return __libunwind_arch_unwind_info__new_ppc64(thread, sample, max_stack, + best_effort, first_ip); + case EM_S390: + return __libunwind_arch_unwind_info__new_s390(thread, sample, max_stack, + best_effort, first_ip); + case EM_386: + return __libunwind_arch_unwind_info__new_i386(thread, sample, max_stack, + best_effort, first_ip); + case EM_X86_64: + return __libunwind_arch_unwind_info__new_x86_64(thread, sample, max_stac= k, + best_effort, first_ip); + default: + pr_err("ELF MACHINE %x is not supported.\n", e_machine); + return NULL; + } +} + +void libunwind_arch_unwind_info__delete(struct unwind_info *ui) +{ + free(ui); +} + +int libunwind_arch__unwind_step(struct unwind_info *ui) +{ + switch (ui->e_machine) { + case EM_ARM: + return __libunwind_arch__unwind_step_arm(ui); + case EM_AARCH64: + return __libunwind_arch__unwind_step_arm64(ui); + case EM_LOONGARCH: + return __libunwind_arch__unwind_step_loongarch(ui); + case EM_MIPS: + return __libunwind_arch__unwind_step_mips(ui); + case EM_PPC: + return __libunwind_arch__unwind_step_ppc32(ui); + case EM_PPC64: + return __libunwind_arch__unwind_step_ppc64(ui); + case EM_S390: + return __libunwind_arch__unwind_step_s390(ui); + case EM_386: + return __libunwind_arch__unwind_step_i386(ui); + case EM_X86_64: + return __libunwind_arch__unwind_step_x86_64(ui); + default: + pr_err("ELF MACHINE %x is not supported.\n", ui->e_machine); + return -EINVAL; + } +} diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.h b/tools/perf/u= til/libunwind-arch/libunwind-arch.h index c00277a5e914..2bf7fc33313b 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arch.h +++ b/tools/perf/util/libunwind-arch/libunwind-arch.h @@ -2,7 +2,36 @@ #ifndef __LIBUNWIND_ARCH_H #define __LIBUNWIND_ARCH_H =20 +#include +#include +#include + +struct machine; struct maps; +struct perf_sample; +struct thread; + +struct unwind_info { + struct machine *machine; + struct thread *thread; + struct perf_sample *sample; + void *cursor; + uint64_t *ips; + int cur_ip; + int max_ips; + unsigned int unw_word_t_size; + uint16_t e_machine; + bool best_effort; +}; + +struct libarch_unwind__dyn_info { + uint64_t start_ip; + uint64_t end_ip; + uint64_t segbase; + uint64_t table_data; + uint64_t table_len; +}; +struct libarch_unwind__proc_info; =20 int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum); int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum); @@ -37,4 +66,209 @@ void __libunwind_arch__finish_access_i386(struct maps *= maps); void __libunwind_arch__finish_access_x86_64(struct maps *maps); void libunwind_arch__finish_access(struct maps *maps); =20 +void *__libunwind_arch__create_addr_space_arm(void); +void *__libunwind_arch__create_addr_space_arm64(void); +void *__libunwind_arch__create_addr_space_loongarch(void); +void *__libunwind_arch__create_addr_space_mips(void); +void *__libunwind_arch__create_addr_space_ppc32(void); +void *__libunwind_arch__create_addr_space_ppc64(void); +void *__libunwind_arch__create_addr_space_s390(void); +void *__libunwind_arch__create_addr_space_i386(void); +void *__libunwind_arch__create_addr_space_x86_64(void); +void *libunwind_arch__create_addr_space(unsigned int e_machine); + +int __libunwind__find_proc_info(void *as, uint64_t ip, void *pi, int need_= unwind_info, void *arg); +int __libunwind__access_mem(void *as, uint64_t addr, void *valp_word, int = __write, void *arg); +int __libunwind__access_reg(void *as, int regnum, void *valp_word, int __w= rite, void *arg); + +int __libunwind_arch__dwarf_search_unwind_table_arm(void *as, uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_arm64(void *as, uint64_t i= p, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_loongarch(void *as, uint64= _t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_mips(void *as, uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_ppc32(void *as, uint64_t i= p, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_ppc64(void *as, uint64_t i= p, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_s390(void *as, uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_i386(void *as, uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_x86_64(void *as, uint64_t = ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int libunwind_arch__dwarf_search_unwind_table(unsigned int e_machine, + void *as, + uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); + +int __libunwind_arch__dwarf_find_debug_frame_arm(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_arm64(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_loongarch(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_mips(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_ppc32(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_ppc64(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_s390(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_i386(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_x86_64(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int libunwind_arch__dwarf_find_debug_frame(unsigned int e_machine, + int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); + +struct unwind_info *__libunwind_arch_unwind_info__new_arm(struct thread *t= hread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_arm64(struct thread = *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_loongarch(struct thr= ead *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_mips(struct thread *= thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_ppc32(struct thread = *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_ppc64(struct thread = *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_s390(struct thread *= thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_i386(struct thread *= thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_x86_64(struct thread= *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *libunwind_arch_unwind_info__new(struct thread *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint16_t e_machine, + uint64_t first_ip); + +void libunwind_arch_unwind_info__delete(struct unwind_info *ui); + +int __libunwind_arch__unwind_step_arm(struct unwind_info *ui); +int __libunwind_arch__unwind_step_arm64(struct unwind_info *ui); +int __libunwind_arch__unwind_step_loongarch(struct unwind_info *ui); +int __libunwind_arch__unwind_step_mips(struct unwind_info *ui); +int __libunwind_arch__unwind_step_ppc32(struct unwind_info *ui); +int __libunwind_arch__unwind_step_ppc64(struct unwind_info *ui); +int __libunwind_arch__unwind_step_s390(struct unwind_info *ui); +int __libunwind_arch__unwind_step_i386(struct unwind_info *ui); +int __libunwind_arch__unwind_step_x86_64(struct unwind_info *ui); +int libunwind_arch__unwind_step(struct unwind_info *ui); + #endif /* __LIBUNWIND_ARCH_H */ diff --git a/tools/perf/util/libunwind-arch/libunwind-arm.c b/tools/perf/ut= il/libunwind-arch/libunwind-arm.c index bbaf01406c52..b31aeeab1663 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arm.c +++ b/tools/perf/util/libunwind-arch/libunwind-arm.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/arm/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include =20 #ifdef HAVE_LIBUNWIND_ARM_SUPPORT @@ -32,3 +36,255 @@ void __libunwind_arch__finish_access_arm(struct maps *m= aps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + + +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_inf= o_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, siz= eof(unw_word_t)); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *va= lp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_w= ord_t)); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t= *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw= _word_t)); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_arm(void) +{ +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT + static unw_accessors_t accessors =3D { + .find_proc_info =3D find_proc_info, + .put_unwind_info =3D put_unwind_info, + .get_dyn_info_list_addr =3D get_dyn_info_list_addr, + .access_mem =3D access_mem, + .access_reg =3D access_reg, + .access_fpreg =3D access_fpreg, + .resume =3D resume, + .get_proc_name =3D get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space =3D unw_create_addr_space(&accessors, /*byte_order=3D*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_arm(void *as __maybe_unuse= d, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, = arg); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_ARM_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRA= ME_ARM) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_= debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_arm(int found __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_ARM_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRA= ME_ARM) + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, sta= rt, end); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_arm(struct thread *t= hread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps =3D thread__maps(thread); + void *addr_space =3D maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space =3D=3D NULL) + return NULL; + + ui =3D zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine =3D maps__machine(maps); + ui->ui.thread =3D thread; + ui->ui.sample =3D sample; + ui->ui.cursor =3D &ui->_cursor; + ui->ui.ips =3D &ui->_ips[0]; + ui->ui.ips[0] =3D first_ip; + ui->ui.cur_ip =3D 1; + ui->ui.max_ips =3D max_stack; + ui->ui.unw_word_t_size =3D sizeof(unw_word_t); + ui->ui.e_machine =3D EM_ARM; + ui->ui.best_effort =3D best_effort; + + ret =3D unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_arm(struct unwind_info *ui __maybe_unuse= d) +{ +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT + int ret; + + if (ui->cur_ip >=3D ui->max_ips) + return -1; + + ret =3D unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <=3D 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] =3D ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-arm64.c b/tools/perf/= util/libunwind-arch/libunwind-arm64.c index 8ba510089736..ceeae2d0f8e9 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arm64.c +++ b/tools/perf/util/libunwind-arch/libunwind-arm64.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/arm64/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include =20 #ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT @@ -32,3 +36,254 @@ void __libunwind_arch__finish_access_arm64(struct maps = *maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_inf= o_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, siz= eof(unw_word_t)); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *va= lp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_w= ord_t)); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t= *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw= _word_t)); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_arm64(void) +{ +#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT + static unw_accessors_t accessors =3D { + .find_proc_info =3D find_proc_info, + .put_unwind_info =3D put_unwind_info, + .get_dyn_info_list_addr =3D get_dyn_info_list_addr, + .access_mem =3D access_mem, + .access_reg =3D access_reg, + .access_fpreg =3D access_fpreg, + .resume =3D resume, + .get_proc_name =3D get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space =3D unw_create_addr_space(&accessors, /*byte_order=3D*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_arm64(void *as __maybe_unu= sed, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, = arg); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_ARM64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_F= RAME_ARM64) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_= debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_arm64(int found __maybe_unuse= d, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_ARM64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_F= RAME_ARM64) + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, sta= rt, end); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_arm64(struct thread = *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps =3D thread__maps(thread); + void *addr_space =3D maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space =3D=3D NULL) + return NULL; + + ui =3D zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine =3D maps__machine(maps); + ui->ui.thread =3D thread; + ui->ui.sample =3D sample; + ui->ui.cursor =3D &ui->_cursor; + ui->ui.ips =3D &ui->_ips[0]; + ui->ui.ips[0] =3D first_ip; + ui->ui.cur_ip =3D 1; + ui->ui.max_ips =3D max_stack; + ui->ui.unw_word_t_size =3D sizeof(unw_word_t); + ui->ui.e_machine =3D EM_AARCH64; + ui->ui.best_effort =3D best_effort; + + ret =3D unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_arm64(struct unwind_info *ui __maybe_unu= sed) +{ +#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT + int ret; + + if (ui->cur_ip >=3D ui->max_ips) + return -1; + + ret =3D unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <=3D 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] =3D ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-i386.c b/tools/perf/u= til/libunwind-arch/libunwind-i386.c index 45ff30c95c1b..fde40872a610 100644 --- a/tools/perf/util/libunwind-arch/libunwind-i386.c +++ b/tools/perf/util/libunwind-arch/libunwind-i386.c @@ -2,9 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/x86/include/uapi/asm/perf_regs.h" #include #include +#include +#include #include =20 #ifdef HAVE_LIBUNWIND_X86_SUPPORT @@ -56,3 +59,254 @@ void __libunwind_arch__finish_access_i386(struct maps *= maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_X86_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_inf= o_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, siz= eof(unw_word_t)); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *va= lp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_w= ord_t)); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t= *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw= _word_t)); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_i386(void) +{ +#ifdef HAVE_LIBUNWIND_X86_SUPPORT + static unw_accessors_t accessors =3D { + .find_proc_info =3D find_proc_info, + .put_unwind_info =3D put_unwind_info, + .get_dyn_info_list_addr =3D get_dyn_info_list_addr, + .access_mem =3D access_mem, + .access_reg =3D access_reg, + .access_fpreg =3D access_fpreg, + .resume =3D resume, + .get_proc_name =3D get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space =3D unw_create_addr_space(&accessors, /*byte_order=3D*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_X86_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_i386(void *as __maybe_unus= ed, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_SUPPORT + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, = arg); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_X86_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRA= ME_X86) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_= debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_i386(int found __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_X86_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRA= ME_X86) + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, sta= rt, end); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_i386(struct thread *= thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps =3D thread__maps(thread); + void *addr_space =3D maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space =3D=3D NULL) + return NULL; + + ui =3D zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine =3D maps__machine(maps); + ui->ui.thread =3D thread; + ui->ui.sample =3D sample; + ui->ui.cursor =3D &ui->_cursor; + ui->ui.ips =3D &ui->_ips[0]; + ui->ui.ips[0] =3D first_ip; + ui->ui.cur_ip =3D 1; + ui->ui.max_ips =3D max_stack; + ui->ui.unw_word_t_size =3D sizeof(unw_word_t); + ui->ui.e_machine =3D EM_I386; + ui->ui.best_effort =3D best_effort; + + ret =3D unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_i386(struct unwind_info *ui __maybe_unus= ed) +{ +#ifdef HAVE_LIBUNWIND_X86_SUPPORT + int ret; + + if (ui->cur_ip >=3D ui->max_ips) + return -1; + + ret =3D unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <=3D 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] =3D ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-loongarch.c b/tools/p= erf/util/libunwind-arch/libunwind-loongarch.c index 837aa11e2b9f..d86397598a9d 100644 --- a/tools/perf/util/libunwind-arch/libunwind-loongarch.c +++ b/tools/perf/util/libunwind-arch/libunwind-loongarch.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/loongarch/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include =20 #ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT @@ -40,3 +44,254 @@ void __libunwind_arch__finish_access_loongarch(struct m= aps *maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_inf= o_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, siz= eof(unw_word_t)); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *va= lp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_w= ord_t)); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t= *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw= _word_t)); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_loongarch(void) +{ +#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT + static unw_accessors_t accessors =3D { + .find_proc_info =3D find_proc_info, + .put_unwind_info =3D put_unwind_info, + .get_dyn_info_list_addr =3D get_dyn_info_list_addr, + .access_mem =3D access_mem, + .access_reg =3D access_reg, + .access_fpreg =3D access_fpreg, + .resume =3D resume, + .get_proc_name =3D get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space =3D unw_create_addr_space(&accessors, /*byte_order=3D*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_loongarch(void *as __maybe= _unused, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, = arg); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_LOONGARCH_SUPPORT) && !defined(NO_LIBUNWIND_DEB= UG_FRAME_LOONGARCH) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_= debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_loongarch(int found __maybe_u= nused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_LOONGARCH_SUPPORT) && !defined(NO_LIBUNWIND_DEB= UG_FRAME_LOONGARCH) + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, sta= rt, end); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_loongarch(struct thr= ead *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps =3D thread__maps(thread); + void *addr_space =3D maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space =3D=3D NULL) + return NULL; + + ui =3D zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine =3D maps__machine(maps); + ui->ui.thread =3D thread; + ui->ui.sample =3D sample; + ui->ui.cursor =3D &ui->_cursor; + ui->ui.ips =3D &ui->_ips[0]; + ui->ui.ips[0] =3D first_ip; + ui->ui.cur_ip =3D 1; + ui->ui.max_ips =3D max_stack; + ui->ui.unw_word_t_size =3D sizeof(unw_word_t); + ui->ui.e_machine =3D EM_LOONGARCH; + ui->ui.best_effort =3D best_effort; + + ret =3D unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_loongarch(struct unwind_info *ui __maybe= _unused) +{ +#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT + int ret; + + if (ui->cur_ip >=3D ui->max_ips) + return -1; + + ret =3D unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <=3D 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] =3D ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-mips.c b/tools/perf/u= til/libunwind-arch/libunwind-mips.c index 1fa81742ff4a..91ee3c27d159 100644 --- a/tools/perf/util/libunwind-arch/libunwind-mips.c +++ b/tools/perf/util/libunwind-arch/libunwind-mips.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/mips/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include =20 #ifdef HAVE_LIBUNWIND_MIPS_SUPPORT @@ -42,3 +46,254 @@ void __libunwind_arch__finish_access_mips(struct maps *= maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_inf= o_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, siz= eof(unw_word_t)); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *va= lp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_w= ord_t)); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t= *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw= _word_t)); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_mips(void) +{ +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT + static unw_accessors_t accessors =3D { + .find_proc_info =3D find_proc_info, + .put_unwind_info =3D put_unwind_info, + .get_dyn_info_list_addr =3D get_dyn_info_list_addr, + .access_mem =3D access_mem, + .access_reg =3D access_reg, + .access_fpreg =3D access_fpreg, + .resume =3D resume, + .get_proc_name =3D get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space =3D unw_create_addr_space(&accessors, /*byte_order=3D*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_mips(void *as __maybe_unus= ed, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, = arg); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_MIPS_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FR= AME_MIPS) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_= debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_mips(int found __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_MIPS_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FR= AME_MIPS) + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, sta= rt, end); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_mips(struct thread *= thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps =3D thread__maps(thread); + void *addr_space =3D maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space =3D=3D NULL) + return NULL; + + ui =3D zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine =3D maps__machine(maps); + ui->ui.thread =3D thread; + ui->ui.sample =3D sample; + ui->ui.cursor =3D &ui->_cursor; + ui->ui.ips =3D &ui->_ips[0]; + ui->ui.ips[0] =3D first_ip; + ui->ui.cur_ip =3D 1; + ui->ui.max_ips =3D max_stack; + ui->ui.unw_word_t_size =3D sizeof(unw_word_t); + ui->ui.e_machine =3D EM_MIPS; + ui->ui.best_effort =3D best_effort; + + ret =3D unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_mips(struct unwind_info *ui __maybe_unus= ed) +{ +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT + int ret; + + if (ui->cur_ip >=3D ui->max_ips) + return -1; + + ret =3D unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <=3D 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] =3D ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc32.c b/tools/perf/= util/libunwind-arch/libunwind-ppc32.c index fa8471c74bf3..b80d3184a28d 100644 --- a/tools/perf/util/libunwind-arch/libunwind-ppc32.c +++ b/tools/perf/util/libunwind-arch/libunwind-ppc32.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/powerpc/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include =20 #ifdef HAVE_LIBUNWIND_PPC32_SUPPORT @@ -44,3 +48,254 @@ void __libunwind_arch__finish_access_ppc32(struct maps = *maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_inf= o_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, siz= eof(unw_word_t)); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *va= lp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_w= ord_t)); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t= *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw= _word_t)); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_ppc32(void) +{ +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT + static unw_accessors_t accessors =3D { + .find_proc_info =3D find_proc_info, + .put_unwind_info =3D put_unwind_info, + .get_dyn_info_list_addr =3D get_dyn_info_list_addr, + .access_mem =3D access_mem, + .access_reg =3D access_reg, + .access_fpreg =3D access_fpreg, + .resume =3D resume, + .get_proc_name =3D get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space =3D unw_create_addr_space(&accessors, /*byte_order=3D*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_ppc32(void *as __maybe_unu= sed, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, = arg); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_PPC32_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_F= RAME_PPC32) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_= debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_ppc32(int found __maybe_unuse= d, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_PPC32_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_F= RAME_PPC32) + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, sta= rt, end); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_ppc32(struct thread = *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps =3D thread__maps(thread); + void *addr_space =3D maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space =3D=3D NULL) + return NULL; + + ui =3D zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine =3D maps__machine(maps); + ui->ui.thread =3D thread; + ui->ui.sample =3D sample; + ui->ui.cursor =3D &ui->_cursor; + ui->ui.ips =3D &ui->_ips[0]; + ui->ui.ips[0] =3D first_ip; + ui->ui.cur_ip =3D 1; + ui->ui.max_ips =3D max_stack; + ui->ui.unw_word_t_size =3D sizeof(unw_word_t); + ui->ui.e_machine =3D EM_PPC; + ui->ui.best_effort =3D best_effort; + + ret =3D unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_ppc32(struct unwind_info *ui __maybe_unu= sed) +{ +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT + int ret; + + if (ui->cur_ip >=3D ui->max_ips) + return -1; + + ret =3D unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <=3D 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] =3D ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc64.c b/tools/perf/= util/libunwind-arch/libunwind-ppc64.c index 2f746e347336..cbe3b476203f 100644 --- a/tools/perf/util/libunwind-arch/libunwind-ppc64.c +++ b/tools/perf/util/libunwind-arch/libunwind-ppc64.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/powerpc/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include =20 #ifdef HAVE_LIBUNWIND_PPC64_SUPPORT @@ -46,3 +50,254 @@ void __libunwind_arch__finish_access_ppc64(struct maps = *maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_inf= o_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, siz= eof(unw_word_t)); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *va= lp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_w= ord_t)); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t= *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw= _word_t)); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_ppc64(void) +{ +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT + static unw_accessors_t accessors =3D { + .find_proc_info =3D find_proc_info, + .put_unwind_info =3D put_unwind_info, + .get_dyn_info_list_addr =3D get_dyn_info_list_addr, + .access_mem =3D access_mem, + .access_reg =3D access_reg, + .access_fpreg =3D access_fpreg, + .resume =3D resume, + .get_proc_name =3D get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space =3D unw_create_addr_space(&accessors, /*byte_order=3D*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_ppc64(void *as __maybe_unu= sed, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, = arg); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_PPC64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_F= RAME_PPC64) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_= debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_ppc64(int found __maybe_unuse= d, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_PPC64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_F= RAME_PPC64) + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, sta= rt, end); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_ppc64(struct thread = *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps =3D thread__maps(thread); + void *addr_space =3D maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space =3D=3D NULL) + return NULL; + + ui =3D zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine =3D maps__machine(maps); + ui->ui.thread =3D thread; + ui->ui.sample =3D sample; + ui->ui.cursor =3D &ui->_cursor; + ui->ui.ips =3D &ui->_ips[0]; + ui->ui.ips[0] =3D first_ip; + ui->ui.cur_ip =3D 1; + ui->ui.max_ips =3D max_stack; + ui->ui.unw_word_t_size =3D sizeof(unw_word_t); + ui->ui.e_machine =3D EM_PPC64; + ui->ui.best_effort =3D best_effort; + + ret =3D unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_ppc64(struct unwind_info *ui __maybe_unu= sed) +{ +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT + int ret; + + if (ui->cur_ip >=3D ui->max_ips) + return -1; + + ret =3D unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <=3D 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] =3D ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-s390.c b/tools/perf/u= til/libunwind-arch/libunwind-s390.c index 9f68d15438b2..ce3788e86d1f 100644 --- a/tools/perf/util/libunwind-arch/libunwind-s390.c +++ b/tools/perf/util/libunwind-arch/libunwind-s390.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/s390/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include =20 #ifdef HAVE_LIBUNWIND_S390X_SUPPORT @@ -42,3 +46,254 @@ void __libunwind_arch__finish_access_s390(struct maps *= maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_inf= o_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, siz= eof(unw_word_t)); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *va= lp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_w= ord_t)); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t= *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw= _word_t)); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_s390(void) +{ +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT + static unw_accessors_t accessors =3D { + .find_proc_info =3D find_proc_info, + .put_unwind_info =3D put_unwind_info, + .get_dyn_info_list_addr =3D get_dyn_info_list_addr, + .access_mem =3D access_mem, + .access_reg =3D access_reg, + .access_fpreg =3D access_fpreg, + .resume =3D resume, + .get_proc_name =3D get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space =3D unw_create_addr_space(&accessors, /*byte_order=3D*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_s390(void *as __maybe_unus= ed, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, = arg); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_S390X_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_F= RAME_S390X) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_= debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_s390(int found __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_S390X_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_F= RAME_S390X) + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, sta= rt, end); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_s390(struct thread *= thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps =3D thread__maps(thread); + void *addr_space =3D maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space =3D=3D NULL) + return NULL; + + ui =3D zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine =3D maps__machine(maps); + ui->ui.thread =3D thread; + ui->ui.sample =3D sample; + ui->ui.cursor =3D &ui->_cursor; + ui->ui.ips =3D &ui->_ips[0]; + ui->ui.ips[0] =3D first_ip; + ui->ui.cur_ip =3D 1; + ui->ui.max_ips =3D max_stack; + ui->ui.unw_word_t_size =3D sizeof(unw_word_t); + ui->ui.e_machine =3D EM_S390; + ui->ui.best_effort =3D best_effort; + + ret =3D unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_s390(struct unwind_info *ui __maybe_unus= ed) +{ +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT + int ret; + + if (ui->cur_ip >=3D ui->max_ips) + return -1; + + ret =3D unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <=3D 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] =3D ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-x86_64.c b/tools/perf= /util/libunwind-arch/libunwind-x86_64.c index 25e326bd3e15..8ca66d442ffc 100644 --- a/tools/perf/util/libunwind-arch/libunwind-x86_64.c +++ b/tools/perf/util/libunwind-arch/libunwind-x86_64.c @@ -2,9 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/x86/include/uapi/asm/perf_regs.h" #include #include +#include +#include #include =20 #ifdef HAVE_LIBUNWIND_X86_64_SUPPORT @@ -65,3 +68,253 @@ void __libunwind_arch__finish_access_x86_64(struct maps= *maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_inf= o_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *va= lp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t= *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_x86_64(void) +{ +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT + static unw_accessors_t accessors =3D { + .find_proc_info =3D find_proc_info, + .put_unwind_info =3D put_unwind_info, + .get_dyn_info_list_addr =3D get_dyn_info_list_addr, + .access_mem =3D access_mem, + .access_reg =3D access_reg, + .access_fpreg =3D access_fpreg, + .resume =3D resume, + .get_proc_name =3D get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space =3D unw_create_addr_space(&accessors, /*byte_order=3D*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_x86_64(void *as __maybe_un= used, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, = arg); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_X86_64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_= FRAME_X86_64) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_= debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_x86_64(int found __maybe_unus= ed, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_X86_64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_= FRAME_X86_64) + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, sta= rt, end); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_x86_64(struct thread= *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps =3D thread__maps(thread); + void *addr_space =3D maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space =3D=3D NULL) + return NULL; + + ui =3D zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine =3D maps__machine(maps); + ui->ui.thread =3D thread; + ui->ui.sample =3D sample; + ui->ui.cursor =3D &ui->_cursor; + ui->ui.ips =3D &ui->_ips[0]; + ui->ui.ips[0] =3D first_ip; + ui->ui.cur_ip =3D 1; + ui->ui.max_ips =3D max_stack; + ui->ui.unw_word_t_size =3D sizeof(unw_word_t); + ui->ui.e_machine =3D EM_X86_64; + ui->ui.best_effort =3D best_effort; + + ret =3D unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_x86_64(struct unwind_info *ui __maybe_un= used) +{ +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT + int ret; + + if (ui->cur_ip >=3D ui->max_ips) + return 0; + + ret =3D unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <=3D 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] =3D ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind/arm64.c b/tools/perf/util/libunwind/= arm64.c deleted file mode 100644 index 15670a964495..000000000000 --- a/tools/perf/util/libunwind/arm64.c +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * This file setups defines to compile arch specific binary from the - * generic one. - * - * The function 'LIBUNWIND__ARCH_REG_ID' name is set according to arch - * name and the definition of this function is included directly from - * 'arch/arm64/util/unwind-libunwind.c', to make sure that this function - * is defined no matter what arch the host is. - * - * Finally, the arch specific unwind methods are exported which will - * be assigned to each arm64 thread. - */ - -#define REMOTE_UNWIND_LIBUNWIND - -#include "unwind.h" -#include "libunwind-aarch64.h" -#define perf_event_arm_regs perf_event_arm64_regs -#include <../../../arch/arm64/include/uapi/asm/perf_regs.h> -#undef perf_event_arm_regs -#include "../../arch/arm64/util/unwind-libunwind.c" - -/* NO_LIBUNWIND_DEBUG_FRAME is a feature flag for local libunwind, - * assign NO_LIBUNWIND_DEBUG_FRAME_AARCH64 to it for compiling arm64 - * unwind methods. - */ -#undef NO_LIBUNWIND_DEBUG_FRAME -#ifdef NO_LIBUNWIND_DEBUG_FRAME_AARCH64 -#define NO_LIBUNWIND_DEBUG_FRAME -#endif -#include "util/unwind-libunwind-local.c" - -struct unwind_libunwind_ops * -arm64_unwind_libunwind_ops =3D &_unwind_libunwind_ops; diff --git a/tools/perf/util/libunwind/x86_32.c b/tools/perf/util/libunwind= /x86_32.c deleted file mode 100644 index 1e9fb8bfec44..000000000000 --- a/tools/perf/util/libunwind/x86_32.c +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * This file setups defines to compile arch specific binary from the - * generic one. - * - * The function 'LIBUNWIND__ARCH_REG_ID' name is set according to arch - * name and the definition of this function is included directly from - * 'arch/x86/util/unwind-libunwind.c', to make sure that this function - * is defined no matter what arch the host is. - * - * Finally, the arch specific unwind methods are exported which will - * be assigned to each x86 thread. - */ - -#define REMOTE_UNWIND_LIBUNWIND - -#include "unwind.h" -#include "libunwind-x86.h" - -/* Explicitly define NO_LIBUNWIND_DEBUG_FRAME, because non-ARM has no - * dwarf_find_debug_frame() function. - */ -#ifndef NO_LIBUNWIND_DEBUG_FRAME -#define NO_LIBUNWIND_DEBUG_FRAME -#endif -#include "util/unwind-libunwind-local.c" - -struct unwind_libunwind_ops * -x86_32_unwind_libunwind_ops =3D &_unwind_libunwind_ops; diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c index 8c7b2a1e7642..7ba82024fb11 100644 --- a/tools/perf/util/maps.c +++ b/tools/perf/util/maps.c @@ -198,16 +198,6 @@ void maps__set_addr_space(struct maps *maps, void *add= r_space) RC_CHK_ACCESS(maps)->addr_space =3D addr_space; } =20 -const struct unwind_libunwind_ops *maps__unwind_libunwind_ops(const struct= maps *maps) -{ - return RC_CHK_ACCESS(maps)->unwind_libunwind_ops; -} - -void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind= _libunwind_ops *ops) -{ - RC_CHK_ACCESS(maps)->unwind_libunwind_ops =3D ops; -} - uint16_t maps__e_machine(const struct maps *maps) { return RC_CHK_ACCESS(maps)->e_machine; diff --git a/tools/perf/util/maps.h b/tools/perf/util/maps.h index 6469f62c41a8..5b80b199685e 100644 --- a/tools/perf/util/maps.h +++ b/tools/perf/util/maps.h @@ -49,8 +49,6 @@ refcount_t *maps__refcnt(struct maps *maps); /* Test only= . */ #ifdef HAVE_LIBUNWIND_SUPPORT void *maps__addr_space(const struct maps *maps); void maps__set_addr_space(struct maps *maps, void *addr_space); -const struct unwind_libunwind_ops *maps__unwind_libunwind_ops(const struct= maps *maps); -void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind= _libunwind_ops *ops); uint16_t maps__e_machine(const struct maps *maps); void maps__set_e_machine(struct maps *maps, uint16_t e_machine); #endif diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unw= ind-libunwind-local.c deleted file mode 100644 index 8f0128ba05a7..000000000000 --- a/tools/perf/util/unwind-libunwind-local.c +++ /dev/null @@ -1,820 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Post mortem Dwarf CFI based unwinding on top of regs and stack dumps. - * - * Lots of this code have been borrowed or heavily inspired from parts of - * the libunwind 0.99 code which are (amongst other contributors I may have - * forgotten): - * - * Copyright (C) 2002-2007 Hewlett-Packard Co - * Contributed by David Mosberger-Tang - * - * And the bugs have been added by: - * - * Copyright (C) 2010, Frederic Weisbecker - * Copyright (C) 2012, Jiri Olsa - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifndef REMOTE_UNWIND_LIBUNWIND -#include -#include -#endif -#include "callchain.h" -#include "thread.h" -#include "session.h" -#include "perf_regs.h" -#include "unwind.h" -#include "map.h" -#include "symbol.h" -#include "debug.h" -#include "asm/bug.h" -#include "dso.h" -#include "libunwind-arch/libunwind-arch.h" - -extern int -UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, - unw_word_t ip, - unw_dyn_info_t *di, - unw_proc_info_t *pi, - int need_unwind_info, void *arg); - -#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) - -extern int -UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, - unw_word_t ip, - unw_word_t segbase, - const char *obj_name, unw_word_t start, - unw_word_t end); - -#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) - -#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */ -#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */ - -/* Pointer-encoding formats: */ -#define DW_EH_PE_omit 0xff -#define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */ -#define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */ -#define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */ -#define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */ -#define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */ - -/* Pointer-encoding application: */ -#define DW_EH_PE_absptr 0x00 /* absolute value */ -#define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */ - -/* - * The following are not documented by LSB v1.3, yet they are used by - * GCC, presumably they aren't documented by LSB since they aren't - * used on Linux: - */ -#define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */ -#define DW_EH_PE_aligned 0x50 /* aligned pointer */ - -/* Flags intentionally not handled, since they're not needed: - * #define DW_EH_PE_indirect 0x80 - * #define DW_EH_PE_uleb128 0x01 - * #define DW_EH_PE_udata2 0x02 - * #define DW_EH_PE_sleb128 0x09 - * #define DW_EH_PE_sdata2 0x0a - * #define DW_EH_PE_textrel 0x20 - * #define DW_EH_PE_datarel 0x30 - */ - -struct unwind_info { - struct perf_sample *sample; - struct machine *machine; - struct thread *thread; - uint16_t e_machine; - bool best_effort; -}; - -#define dw_read(ptr, type, end) ({ \ - type *__p =3D (type *) ptr; \ - type __v; \ - if ((__p + 1) > (type *) end) \ - return -EINVAL; \ - __v =3D *__p++; \ - ptr =3D (typeof(ptr)) __p; \ - __v; \ - }) - -static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val, - u8 encoding) -{ - u8 *cur =3D *p; - *val =3D 0; - - switch (encoding) { - case DW_EH_PE_omit: - *val =3D 0; - goto out; - case DW_EH_PE_ptr: - *val =3D dw_read(cur, unsigned long, end); - goto out; - default: - break; - } - - switch (encoding & DW_EH_PE_APPL_MASK) { - case DW_EH_PE_absptr: - break; - case DW_EH_PE_pcrel: - *val =3D (unsigned long) cur; - break; - default: - return -EINVAL; - } - - if ((encoding & 0x07) =3D=3D 0x00) - encoding |=3D DW_EH_PE_udata4; - - switch (encoding & DW_EH_PE_FORMAT_MASK) { - case DW_EH_PE_sdata4: - *val +=3D dw_read(cur, s32, end); - break; - case DW_EH_PE_udata4: - *val +=3D dw_read(cur, u32, end); - break; - case DW_EH_PE_sdata8: - *val +=3D dw_read(cur, s64, end); - break; - case DW_EH_PE_udata8: - *val +=3D dw_read(cur, u64, end); - break; - default: - return -EINVAL; - } - - out: - *p =3D cur; - return 0; -} - -#define dw_read_encoded_value(ptr, end, enc) ({ \ - u64 __v; \ - if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \ - return -EINVAL; \ - } \ - __v; \ - }) - -static int elf_section_address_and_offset(int fd, const char *name, u64 *a= ddress, u64 *offset) -{ - Elf *elf; - GElf_Ehdr ehdr; - GElf_Shdr shdr; - int ret =3D -1; - - elf =3D elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); - if (elf =3D=3D NULL) - return -1; - - if (gelf_getehdr(elf, &ehdr) =3D=3D NULL) - goto out_err; - - if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL)) - goto out_err; - - *address =3D shdr.sh_addr; - *offset =3D shdr.sh_offset; - ret =3D 0; -out_err: - elf_end(elf); - return ret; -} - -#ifndef NO_LIBUNWIND_DEBUG_FRAME -static u64 elf_section_offset(int fd, const char *name) -{ - u64 address, offset =3D 0; - - if (elf_section_address_and_offset(fd, name, &address, &offset)) - return 0; - - return offset; -} -#endif - -static u64 elf_base_address(int fd) -{ - Elf *elf =3D elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); - GElf_Phdr phdr; - u64 retval =3D 0; - size_t i, phdrnum =3D 0; - - if (elf =3D=3D NULL) - return 0; - (void)elf_getphdrnum(elf, &phdrnum); - /* PT_LOAD segments are sorted by p_vaddr, so the first has the minimum p= _vaddr. */ - for (i =3D 0; i < phdrnum; i++) { - if (gelf_getphdr(elf, i, &phdr) && phdr.p_type =3D=3D PT_LOAD) { - retval =3D phdr.p_vaddr & -getpagesize(); - break; - } - } - - elf_end(elf); - return retval; -} - -#ifndef NO_LIBUNWIND_DEBUG_FRAME -static int elf_is_exec(int fd, const char *name) -{ - Elf *elf; - GElf_Ehdr ehdr; - int retval =3D 0; - - elf =3D elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); - if (elf =3D=3D NULL) - return 0; - if (gelf_getehdr(elf, &ehdr) =3D=3D NULL) - goto out; - - retval =3D (ehdr.e_type =3D=3D ET_EXEC); - -out: - elf_end(elf); - pr_debug("unwind: elf_is_exec(%s): %d\n", name, retval); - return retval; -} -#endif - -struct table_entry { - u32 start_ip_offset; - u32 fde_offset; -}; - -struct eh_frame_hdr { - unsigned char version; - unsigned char eh_frame_ptr_enc; - unsigned char fde_count_enc; - unsigned char table_enc; - - /* - * The rest of the header is variable-length and consists of the - * following members: - * - * encoded_t eh_frame_ptr; - * encoded_t fde_count; - */ - - /* A single encoded pointer should not be more than 8 bytes. */ - u64 enc[2]; - - /* - * struct { - * encoded_t start_ip; - * encoded_t fde_addr; - * } binary_search_table[fde_count]; - */ - char data[]; -} __packed; - -static int unwind_spec_ehframe(struct dso *dso, struct machine *machine, - u64 offset, u64 *table_data_offset, u64 *fde_count) -{ - struct eh_frame_hdr hdr; - u8 *enc =3D (u8 *) &hdr.enc; - u8 *end =3D (u8 *) &hdr.data; - ssize_t r; - - r =3D dso__data_read_offset(dso, machine, offset, - (u8 *) &hdr, sizeof(hdr)); - if (r !=3D sizeof(hdr)) - return -EINVAL; - - /* We dont need eh_frame_ptr, just skip it. */ - dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc); - - *fde_count =3D dw_read_encoded_value(enc, end, hdr.fde_count_enc); - *table_data_offset =3D enc - (u8 *) &hdr; - return 0; -} - -struct read_unwind_spec_eh_frame_maps_cb_args { - struct dso *dso; - u64 base_addr; -}; - -static int read_unwind_spec_eh_frame_maps_cb(struct map *map, void *data) -{ - - struct read_unwind_spec_eh_frame_maps_cb_args *args =3D data; - - if (map__dso(map) =3D=3D args->dso && map__start(map) - map__pgoff(map) <= args->base_addr) - args->base_addr =3D map__start(map) - map__pgoff(map); - - return 0; -} - - -static int read_unwind_spec_eh_frame(struct dso *dso, struct unwind_info *= ui, - u64 *table_data, u64 *segbase, - u64 *fde_count) -{ - struct read_unwind_spec_eh_frame_maps_cb_args args =3D { - .dso =3D dso, - .base_addr =3D UINT64_MAX, - }; - int ret, fd; - - if (dso__data(dso)->eh_frame_hdr_offset =3D=3D 0) { - if (!dso__data_get_fd(dso, ui->machine, &fd)) - return -EINVAL; - - /* Check the .eh_frame section for unwinding info */ - ret =3D elf_section_address_and_offset(fd, ".eh_frame_hdr", - &dso__data(dso)->eh_frame_hdr_addr, - &dso__data(dso)->eh_frame_hdr_offset); - dso__data(dso)->elf_base_addr =3D elf_base_address(fd); - dso__data_put_fd(dso); - if (ret || dso__data(dso)->eh_frame_hdr_offset =3D=3D 0) - return -EINVAL; - } - - maps__for_each_map(thread__maps(ui->thread), read_unwind_spec_eh_frame_ma= ps_cb, &args); - - args.base_addr -=3D dso__data(dso)->elf_base_addr; - /* Address of .eh_frame_hdr */ - *segbase =3D args.base_addr + dso__data(dso)->eh_frame_hdr_addr; - ret =3D unwind_spec_ehframe(dso, ui->machine, dso__data(dso)->eh_frame_hd= r_offset, - table_data, fde_count); - if (ret) - return ret; - /* binary_search_table offset plus .eh_frame_hdr address */ - *table_data +=3D *segbase; - return 0; -} - -#ifndef NO_LIBUNWIND_DEBUG_FRAME -static int read_unwind_spec_debug_frame(struct dso *dso, - struct machine *machine, u64 *offset) -{ - int fd; - u64 ofs =3D dso__data(dso)->debug_frame_offset; - - /* debug_frame can reside in: - * - dso - * - debug pointed by symsrc_filename - * - gnu_debuglink, which doesn't necessary - * has to be pointed by symsrc_filename - */ - if (ofs =3D=3D 0) { - if (dso__data_get_fd(dso, machine, &fd)) { - ofs =3D elf_section_offset(fd, ".debug_frame"); - dso__data_put_fd(dso); - } - - if (ofs <=3D 0) { - fd =3D open(dso__symsrc_filename(dso), O_RDONLY); - if (fd >=3D 0) { - ofs =3D elf_section_offset(fd, ".debug_frame"); - close(fd); - } - } - - if (ofs <=3D 0) { - char *debuglink =3D malloc(PATH_MAX); - int ret =3D 0; - - if (debuglink =3D=3D NULL) { - pr_err("unwind: Can't read unwind spec debug frame.\n"); - return -ENOMEM; - } - - ret =3D dso__read_binary_type_filename( - dso, DSO_BINARY_TYPE__DEBUGLINK, - machine->root_dir, debuglink, PATH_MAX); - if (!ret) { - fd =3D open(debuglink, O_RDONLY); - if (fd >=3D 0) { - ofs =3D elf_section_offset(fd, - ".debug_frame"); - close(fd); - } - } - if (ofs > 0) { - if (dso__symsrc_filename(dso) !=3D NULL) { - pr_warning( - "%s: overwrite symsrc(%s,%s)\n", - __func__, - dso__symsrc_filename(dso), - debuglink); - dso__free_symsrc_filename(dso); - } - dso__set_symsrc_filename(dso, debuglink); - } else { - free(debuglink); - } - } - - dso__data(dso)->debug_frame_offset =3D ofs; - } - - *offset =3D ofs; - if (*offset) - return 0; - - return -EINVAL; -} -#endif - -static struct map *find_map(unw_word_t ip, struct unwind_info *ui) -{ - struct addr_location al; - struct map *ret; - - addr_location__init(&al); - thread__find_map(ui->thread, PERF_RECORD_MISC_USER, ip, &al); - ret =3D map__get(al.map); - addr_location__exit(&al); - return ret; -} - -static int -find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, - int need_unwind_info, void *arg) -{ - struct unwind_info *ui =3D arg; - struct map *map; - struct dso *dso; - unw_dyn_info_t di; - u64 table_data, segbase, fde_count; - int ret =3D -EINVAL; - - map =3D find_map(ip, ui); - if (!map) - return -EINVAL; - - dso =3D map__dso(map); - if (!dso) { - map__put(map); - return -EINVAL; - } - - pr_debug("unwind: find_proc_info dso %s\n", dso__name(dso)); - - /* Check the .eh_frame section for unwinding info */ - if (!read_unwind_spec_eh_frame(dso, ui, &table_data, &segbase, &fde_count= )) { - memset(&di, 0, sizeof(di)); - di.format =3D UNW_INFO_FORMAT_REMOTE_TABLE; - di.start_ip =3D map__start(map); - di.end_ip =3D map__end(map); - di.u.rti.segbase =3D segbase; - di.u.rti.table_data =3D table_data; - di.u.rti.table_len =3D fde_count * sizeof(struct table_entry) - / sizeof(unw_word_t); - ret =3D dwarf_search_unwind_table(as, ip, &di, pi, - need_unwind_info, arg); - } - -#ifndef NO_LIBUNWIND_DEBUG_FRAME - /* Check the .debug_frame section for unwinding info */ - if (ret < 0 && - !read_unwind_spec_debug_frame(dso, ui->machine, &segbase)) { - int fd; - u64 start =3D map__start(map); - unw_word_t base =3D start; - const char *symfile; - - if (dso__data_get_fd(dso, ui->machine, &fd)) { - if (elf_is_exec(fd, dso__name(dso))) - base =3D 0; - dso__data_put_fd(dso); - } - - symfile =3D dso__symsrc_filename(dso) ?: dso__name(dso); - - memset(&di, 0, sizeof(di)); - if (dwarf_find_debug_frame(0, &di, ip, base, symfile, start, map__end(ma= p))) - ret =3D dwarf_search_unwind_table(as, ip, &di, pi, - need_unwind_info, arg); - } -#endif - map__put(map); - return ret; -} - -static int access_fpreg(unw_addr_space_t __maybe_unused as, - unw_regnum_t __maybe_unused num, - unw_fpreg_t __maybe_unused *val, - int __maybe_unused __write, - void __maybe_unused *arg) -{ - pr_err("unwind: access_fpreg unsupported\n"); - return -UNW_EINVAL; -} - -static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, - unw_word_t __maybe_unused *dil_addr, - void __maybe_unused *arg) -{ - return -UNW_ENOINFO; -} - -static int resume(unw_addr_space_t __maybe_unused as, - unw_cursor_t __maybe_unused *cu, - void __maybe_unused *arg) -{ - pr_err("unwind: resume unsupported\n"); - return -UNW_EINVAL; -} - -static int -get_proc_name(unw_addr_space_t __maybe_unused as, - unw_word_t __maybe_unused addr, - char __maybe_unused *bufp, size_t __maybe_unused buf_len, - unw_word_t __maybe_unused *offp, void __maybe_unused *arg) -{ - pr_err("unwind: get_proc_name unsupported\n"); - return -UNW_EINVAL; -} - -static int access_dso_mem(struct unwind_info *ui, unw_word_t addr, - unw_word_t *data) -{ - struct map *map; - struct dso *dso; - ssize_t size; - - map =3D find_map(addr, ui); - if (!map) { - pr_debug("unwind: no map for %lx\n", (unsigned long)addr); - return -1; - } - - dso =3D map__dso(map); - - if (!dso) { - map__put(map); - return -1; - } - - size =3D dso__data_read_addr(dso, map, ui->machine, - addr, (u8 *) data, sizeof(*data)); - map__put(map); - return !(size =3D=3D sizeof(*data)); -} - -static int access_mem(unw_addr_space_t __maybe_unused as, - unw_word_t addr, unw_word_t *valp, - int __write, void *arg) -{ - struct unwind_info *ui =3D arg; - struct stack_dump *stack =3D &ui->sample->user_stack; - u64 start, end; - int offset; - int ret; - - /* Don't support write, probably not needed. */ - if (__write || !stack || !ui->sample->user_regs || !ui->sample->user_regs= ->regs) { - *valp =3D 0; - return 0; - } - - ret =3D perf_reg_value(&start, perf_sample__user_regs(ui->sample), - perf_arch_reg_sp(ui->e_machine)); - if (ret) - return ret; - - end =3D start + stack->size; - - /* Check overflow. */ - if (addr + sizeof(unw_word_t) < addr) - return -EINVAL; - - if (addr < start || addr + sizeof(unw_word_t) >=3D end) { - ret =3D access_dso_mem(ui, addr, valp); - if (ret) { - pr_debug("unwind: access_mem %p not inside range" - " 0x%" PRIx64 "-0x%" PRIx64 "\n", - (void *) (uintptr_t) addr, start, end); - *valp =3D 0; - return ret; - } - return 0; - } - - offset =3D addr - start; - *valp =3D *(unw_word_t *)&stack->data[offset]; - pr_debug("unwind: access_mem addr %p val %lx, offset %d\n", - (void *) (uintptr_t) addr, (unsigned long)*valp, offset); - return 0; -} - -static int access_reg(unw_addr_space_t __maybe_unused as, - unw_regnum_t regnum, unw_word_t *valp, - int __write, void *arg) -{ - struct unwind_info *ui =3D arg; - int id, ret; - u64 val; - - /* Don't support write, I suspect we don't need it. */ - if (__write) { - pr_err("unwind: access_reg w %d\n", regnum); - return 0; - } - - if (!ui->sample->user_regs || !ui->sample->user_regs->regs) { - *valp =3D 0; - return 0; - } - - id =3D get_perf_regnum_for_unw_regnum(ui->e_machine, regnum); - if (id < 0) - return -EINVAL; - - ret =3D perf_reg_value(&val, perf_sample__user_regs(ui->sample), id); - if (ret) { - if (!ui->best_effort) - pr_err("unwind: can't read reg %d\n", regnum); - return ret; - } - - *valp =3D (unw_word_t) val; - pr_debug("unwind: reg %d, val %lx\n", regnum, (unsigned long)*valp); - return 0; -} - -static void put_unwind_info(unw_addr_space_t __maybe_unused as, - unw_proc_info_t *pi __maybe_unused, - void *arg __maybe_unused) -{ - pr_debug("unwind: put_unwind_info called\n"); -} - -static int entry(u64 ip, struct thread *thread, - unwind_entry_cb_t cb, void *arg) -{ - struct unwind_entry e; - struct addr_location al; - int ret; - - addr_location__init(&al); - e.ms.sym =3D thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al); - e.ip =3D ip; - e.ms.map =3D al.map; - e.ms.thread =3D thread__get(al.thread); - - pr_debug("unwind: %s:ip =3D 0x%" PRIx64 " (0x%" PRIx64 ")\n", - al.sym ? al.sym->name : "''", - ip, - al.map ? map__map_ip(al.map, ip) : (u64) 0); - - ret =3D cb(&e, arg); - addr_location__exit(&al); - return ret; -} - -static void display_error(int err) -{ - switch (err) { - case UNW_EINVAL: - pr_err("unwind: Only supports local.\n"); - break; - case UNW_EUNSPEC: - pr_err("unwind: Unspecified error.\n"); - break; - case UNW_EBADREG: - pr_err("unwind: Register unavailable.\n"); - break; - default: - break; - } -} - -static unw_accessors_t accessors =3D { - .find_proc_info =3D find_proc_info, - .put_unwind_info =3D put_unwind_info, - .get_dyn_info_list_addr =3D get_dyn_info_list_addr, - .access_mem =3D access_mem, - .access_reg =3D access_reg, - .access_fpreg =3D access_fpreg, - .resume =3D resume, - .get_proc_name =3D get_proc_name, -}; - -static int _unwind__prepare_access(struct maps *maps) -{ - void *addr_space =3D unw_create_addr_space(&accessors, 0); - - maps__set_addr_space(maps, addr_space); - if (!addr_space) { - pr_err("unwind: Can't create unwind address space.\n"); - return -ENOMEM; - } - - unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); - return 0; -} - -static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, - void *arg, int max_stack) -{ - u64 val; - unw_word_t ips[max_stack]; - unw_addr_space_t addr_space; - unw_cursor_t c; - int ret, i =3D 0; - - ret =3D perf_reg_value(&val, perf_sample__user_regs(ui->sample), - perf_arch_reg_ip(ui->e_machine)); - if (ret) - return ret; - - ips[i++] =3D (unw_word_t) val; - - /* - * If we need more than one entry, do the DWARF - * unwind itself. - */ - if (max_stack - 1 > 0) { - WARN_ONCE(!ui->thread, "WARNING: ui->thread is NULL"); - addr_space =3D maps__addr_space(thread__maps(ui->thread)); - - if (addr_space =3D=3D NULL) - return -1; - - ret =3D unw_init_remote(&c, addr_space, ui); - if (ret && !ui->best_effort) - display_error(ret); - - while (!ret && (unw_step(&c) > 0) && i < max_stack) { - unw_get_reg(&c, UNW_REG_IP, &ips[i]); - - /* - * Decrement the IP for any non-activation frames. - * this is required to properly find the srcline - * for caller frames. - * See also the documentation for dwfl_frame_pc(), - * which this code tries to replicate. - */ - if (unw_is_signal_frame(&c) <=3D 0) - --ips[i]; - - ++i; - } - - max_stack =3D i; - } - - /* - * Display what we got based on the order setup. - */ - for (i =3D 0; i < max_stack && !ret; i++) { - int j =3D i; - - if (callchain_param.order =3D=3D ORDER_CALLER) - j =3D max_stack - i - 1; - ret =3D ips[j] ? entry(ips[j], ui->thread, cb, arg) : 0; - } - - return ret; -} - -static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg, - struct thread *thread, - struct perf_sample *data, int max_stack, - bool best_effort) -{ - struct unwind_info ui =3D { - .sample =3D data, - .thread =3D thread, - .machine =3D maps__machine(thread__maps(thread)), - .e_machine =3D thread__e_machine(thread, /*machine=3D*/NULL, /*e_flag= s=3D*/NULL), - .best_effort =3D best_effort - }; - - if (!data->user_regs || !data->user_regs->regs) - return -EINVAL; - - if (max_stack <=3D 0) - return -EINVAL; - - return get_entries(&ui, cb, arg, max_stack); -} - -static struct unwind_libunwind_ops -_unwind_libunwind_ops =3D { - .prepare_access =3D _unwind__prepare_access, - .get_entries =3D _unwind__get_entries, -}; - -#ifndef REMOTE_UNWIND_LIBUNWIND -struct unwind_libunwind_ops * -local_unwind_libunwind_ops =3D &_unwind_libunwind_ops; -#endif diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-li= bunwind.c index eaee7b78d87c..66e7b7a26aad 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -1,23 +1,557 @@ // SPDX-License-Identifier: GPL-2.0 -#include "unwind.h" +#include "callchain.h" +#include "debug.h" #include "dso.h" +#include "env.h" #include "map.h" -#include "thread.h" +#include "perf_regs.h" #include "session.h" -#include "debug.h" -#include "env.h" -#include "callchain.h" +#include "symbol.h" +#include "thread.h" +#include "unwind.h" #include "libunwind-arch/libunwind-arch.h" #include #include +#include +#include +#include + +#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */ +#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */ + +/* Pointer-encoding formats: */ +#define DW_EH_PE_omit 0xff +#define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */ +#define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */ +#define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */ +#define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */ +#define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */ + +/* Pointer-encoding application: */ +#define DW_EH_PE_absptr 0x00 /* absolute value */ +#define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */ + +/* + * The following are not documented by LSB v1.3, yet they are used by + * GCC, presumably they aren't documented by LSB since they aren't + * used on Linux: + */ +#define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */ +#define DW_EH_PE_aligned 0x50 /* aligned pointer */ + +/* Flags intentionally not handled, since they're not needed: + * #define DW_EH_PE_indirect 0x80 + * #define DW_EH_PE_uleb128 0x01 + * #define DW_EH_PE_udata2 0x02 + * #define DW_EH_PE_sleb128 0x09 + * #define DW_EH_PE_sdata2 0x0a + * #define DW_EH_PE_textrel 0x20 + * #define DW_EH_PE_datarel 0x30 + */ + +#define dw_read(ptr, type, end) ({ \ + type *__p =3D (type *) ptr; \ + type __v; \ + if ((__p + 1) > (type *) end) \ + return -EINVAL; \ + __v =3D *__p++; \ + ptr =3D (typeof(ptr)) __p; \ + __v; \ + }) + +static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val, + u8 encoding) +{ + u8 *cur =3D *p; + *val =3D 0; + + switch (encoding) { + case DW_EH_PE_omit: + *val =3D 0; + goto out; + case DW_EH_PE_ptr: + *val =3D dw_read(cur, unsigned long, end); + goto out; + default: + break; + } + + switch (encoding & DW_EH_PE_APPL_MASK) { + case DW_EH_PE_absptr: + break; + case DW_EH_PE_pcrel: + *val =3D (unsigned long) cur; + break; + default: + return -EINVAL; + } + + if ((encoding & 0x07) =3D=3D 0x00) + encoding |=3D DW_EH_PE_udata4; + + switch (encoding & DW_EH_PE_FORMAT_MASK) { + case DW_EH_PE_sdata4: + *val +=3D dw_read(cur, s32, end); + break; + case DW_EH_PE_udata4: + *val +=3D dw_read(cur, u32, end); + break; + case DW_EH_PE_sdata8: + *val +=3D dw_read(cur, s64, end); + break; + case DW_EH_PE_udata8: + *val +=3D dw_read(cur, u64, end); + break; + default: + return -EINVAL; + } + + out: + *p =3D cur; + return 0; +} + +#define dw_read_encoded_value(ptr, end, enc) ({ \ + u64 __v; \ + if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \ + return -EINVAL; \ + } \ + __v; \ + }) + +static u64 elf_base_address(int fd) +{ + Elf *elf =3D elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); + GElf_Phdr phdr; + u64 retval =3D 0; + size_t i, phdrnum =3D 0; + + if (elf =3D=3D NULL) + return 0; + (void)elf_getphdrnum(elf, &phdrnum); + /* PT_LOAD segments are sorted by p_vaddr, so the first has the minimum p= _vaddr. */ + for (i =3D 0; i < phdrnum; i++) { + if (gelf_getphdr(elf, i, &phdr) && phdr.p_type =3D=3D PT_LOAD) { + retval =3D phdr.p_vaddr & -getpagesize(); + break; + } + } + + elf_end(elf); + return retval; +} + +static int unwind_spec_ehframe(struct dso *dso, struct machine *machine, + u64 offset, u64 *table_data_offset, u64 *fde_count) +{ + struct eh_frame_hdr { + unsigned char version; + unsigned char eh_frame_ptr_enc; + unsigned char fde_count_enc; + unsigned char table_enc; + + /* + * The rest of the header is variable-length and consists of the + * following members: + * + * encoded_t eh_frame_ptr; + * encoded_t fde_count; + */ + + /* A single encoded pointer should not be more than 8 bytes. */ + u64 enc[2]; + + /* + * struct { + * encoded_t start_ip; + * encoded_t fde_addr; + * } binary_search_table[fde_count]; + */ + char data[]; + } __packed hdr; + u8 *enc =3D (u8 *) &hdr.enc; + u8 *end =3D (u8 *) &hdr.data; + ssize_t r; + + r =3D dso__data_read_offset(dso, machine, offset, (u8 *) &hdr, sizeof(hdr= )); + if (r !=3D sizeof(hdr)) + return -EINVAL; + + /* We dont need eh_frame_ptr, just skip it. */ + dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc); + + *fde_count =3D dw_read_encoded_value(enc, end, hdr.fde_count_enc); + *table_data_offset =3D enc - (u8 *) &hdr; + return 0; +} + +struct read_unwind_spec_eh_frame_maps_cb_args { + struct dso *dso; + u64 base_addr; +}; + +static int read_unwind_spec_eh_frame_maps_cb(struct map *map, void *data) +{ + + struct read_unwind_spec_eh_frame_maps_cb_args *args =3D data; + + if (map__dso(map) =3D=3D args->dso && map__start(map) - map__pgoff(map) <= args->base_addr) + args->base_addr =3D map__start(map) - map__pgoff(map); + + return 0; +} + +static int elf_section_address_and_offset(int fd, const char *name, u64 *a= ddress, u64 *offset) +{ + Elf *elf; + GElf_Ehdr ehdr; + GElf_Shdr shdr; + int ret =3D -1; + + elf =3D elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); + if (elf =3D=3D NULL) + return -1; =20 -struct unwind_libunwind_ops __weak *local_unwind_libunwind_ops; -struct unwind_libunwind_ops __weak *x86_32_unwind_libunwind_ops; -struct unwind_libunwind_ops __weak *arm64_unwind_libunwind_ops; + if (gelf_getehdr(elf, &ehdr) =3D=3D NULL) + goto out_err; + + if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL)) + goto out_err; + + *address =3D shdr.sh_addr; + *offset =3D shdr.sh_offset; + ret =3D 0; +out_err: + elf_end(elf); + return ret; +} + +static int read_unwind_spec_eh_frame(struct dso *dso, struct unwind_info *= ui, + u64 *table_data, u64 *segbase, + u64 *fde_count) +{ + struct read_unwind_spec_eh_frame_maps_cb_args args =3D { + .dso =3D dso, + .base_addr =3D UINT64_MAX, + }; + int ret, fd; + + if (dso__data(dso)->eh_frame_hdr_offset =3D=3D 0) { + if (!dso__data_get_fd(dso, ui->machine, &fd)) + return -EINVAL; + + /* Check the .eh_frame section for unwinding info */ + ret =3D elf_section_address_and_offset(fd, ".eh_frame_hdr", + &dso__data(dso)->eh_frame_hdr_addr, + &dso__data(dso)->eh_frame_hdr_offset); + dso__data(dso)->elf_base_addr =3D elf_base_address(fd); + dso__data_put_fd(dso); + if (ret || dso__data(dso)->eh_frame_hdr_offset =3D=3D 0) + return -EINVAL; + } + + maps__for_each_map(thread__maps(ui->thread), read_unwind_spec_eh_frame_ma= ps_cb, &args); + + args.base_addr -=3D dso__data(dso)->elf_base_addr; + /* Address of .eh_frame_hdr */ + *segbase =3D args.base_addr + dso__data(dso)->eh_frame_hdr_addr; + ret =3D unwind_spec_ehframe(dso, ui->machine, dso__data(dso)->eh_frame_hd= r_offset, + table_data, fde_count); + if (ret) + return ret; + /* binary_search_table offset plus .eh_frame_hdr address */ + *table_data +=3D *segbase; + return 0; +} + +static u64 elf_section_offset(int fd, const char *name) +{ + u64 address, offset =3D 0; + + if (elf_section_address_and_offset(fd, name, &address, &offset)) + return 0; + + return offset; +} + +static int read_unwind_spec_debug_frame(struct dso *dso, + struct machine *machine, u64 *offset) +{ + int fd; + u64 ofs =3D dso__data(dso)->debug_frame_offset; + + /* debug_frame can reside in: + * - dso + * - debug pointed by symsrc_filename + * - gnu_debuglink, which doesn't necessary + * has to be pointed by symsrc_filename + */ + if (ofs =3D=3D 0) { + if (dso__data_get_fd(dso, machine, &fd)) { + ofs =3D elf_section_offset(fd, ".debug_frame"); + dso__data_put_fd(dso); + } + + if (ofs <=3D 0) { + fd =3D open(dso__symsrc_filename(dso), O_RDONLY); + if (fd >=3D 0) { + ofs =3D elf_section_offset(fd, ".debug_frame"); + close(fd); + } + } + + if (ofs <=3D 0) { + char *debuglink =3D malloc(PATH_MAX); + int ret =3D 0; + + if (debuglink =3D=3D NULL) { + pr_err("unwind: Can't read unwind spec debug frame.\n"); + return -ENOMEM; + } + + ret =3D dso__read_binary_type_filename( + dso, DSO_BINARY_TYPE__DEBUGLINK, + machine->root_dir, debuglink, PATH_MAX); + if (!ret) { + fd =3D open(debuglink, O_RDONLY); + if (fd >=3D 0) { + ofs =3D elf_section_offset(fd, + ".debug_frame"); + close(fd); + } + } + if (ofs > 0) { + if (dso__symsrc_filename(dso) !=3D NULL) { + pr_warning( + "%s: overwrite symsrc(%s,%s)\n", + __func__, + dso__symsrc_filename(dso), + debuglink); + dso__free_symsrc_filename(dso); + } + dso__set_symsrc_filename(dso, debuglink); + } else { + free(debuglink); + } + } + + dso__data(dso)->debug_frame_offset =3D ofs; + } + + *offset =3D ofs; + if (*offset) + return 0; + + return -EINVAL; +} + +static struct map *find_map(uint64_t ip, struct unwind_info *ui) +{ + struct addr_location al; + struct map *ret; + + addr_location__init(&al); + thread__find_map(ui->thread, PERF_RECORD_MISC_USER, ip, &al); + ret =3D map__get(al.map); + addr_location__exit(&al); + return ret; +} + +static int elf_is_exec(int fd, const char *name) +{ + Elf *elf; + GElf_Ehdr ehdr; + int retval =3D 0; + + elf =3D elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); + if (elf =3D=3D NULL) + return 0; + if (gelf_getehdr(elf, &ehdr) =3D=3D NULL) + goto out; + + retval =3D (ehdr.e_type =3D=3D ET_EXEC); + +out: + elf_end(elf); + pr_debug3("unwind: elf_is_exec(%s): %d\n", name, retval); + return retval; +} + +int __libunwind__find_proc_info(void *as, uint64_t ip, void *pi, int need_= unwind_info, void *arg) +{ + struct unwind_info *ui =3D arg; + struct map *map; + struct dso *dso; + u64 table_data, segbase, fde_count; + int ret =3D -EINVAL; + + map =3D find_map(ip, ui); + if (!map) + return -EINVAL; + + dso =3D map__dso(map); + if (!dso) { + map__put(map); + return -EINVAL; + } + + pr_debug3("unwind: find_proc_info dso %s\n", dso__name(dso)); + + /* Check the .eh_frame section for unwinding info */ + if (!read_unwind_spec_eh_frame(dso, ui, &table_data, &segbase, &fde_count= )) { + struct table_entry { + u32 start_ip_offset; + u32 fde_offset; + }; + struct libarch_unwind__dyn_info di =3D { + .start_ip =3D map__start(map), + .end_ip =3D map__end(map), + .segbase =3D segbase, + .table_data =3D table_data, + .table_len =3D fde_count * sizeof(struct table_entry) / ui->unw_word_t= _size, + }; + + ret =3D libunwind_arch__dwarf_search_unwind_table(ui->e_machine, as, ip,= &di, pi, + need_unwind_info, arg); + } + + /* Check the .debug_frame section for unwinding info */ + if (ret < 0 && !read_unwind_spec_debug_frame(dso, ui->machine, &segbase))= { + int fd; + u64 start =3D map__start(map); + u64 base =3D start; + const char *symfile; + struct libarch_unwind__dyn_info di =3D {}; + + if (dso__data_get_fd(dso, ui->machine, &fd)) { + if (elf_is_exec(fd, dso__name(dso))) + base =3D 0; + dso__data_put_fd(dso); + } + + symfile =3D dso__symsrc_filename(dso) ?: dso__name(dso); + + if (libunwind_arch__dwarf_find_debug_frame(ui->e_machine, /*found=3D*/0,= &di, ip, + base, symfile, start, map__end(map))) { + ret =3D libunwind_arch__dwarf_search_unwind_table(ui->e_machine, as, ip= , &di, pi, + need_unwind_info, arg); + } + } + map__put(map); + return ret; +} + +static int access_dso_mem(struct unwind_info *ui, uint64_t addr, void *dat= a_word) +{ + struct map *map; + struct dso *dso; + ssize_t size; + + map =3D find_map(addr, ui); + if (!map) { + pr_debug("unwind: no map for %lx\n", (unsigned long)addr); + return -1; + } + + dso =3D map__dso(map); + + if (!dso) { + map__put(map); + return -1; + } + + size =3D dso__data_read_addr(dso, map, ui->machine, + addr, + (u8 *) data_word, + ui->unw_word_t_size); + map__put(map); + return !((size_t)size =3D=3D ui->unw_word_t_size); +} + +int __libunwind__access_mem(void *as __maybe_unused, uint64_t addr, void *= valp_word, + int __write, void *arg) +{ + struct unwind_info *ui =3D arg; + struct stack_dump *stack =3D &ui->sample->user_stack; + u64 start, end; + int offset; + int ret; + + /* Don't support write, probably not needed. */ + if (__write || !stack || !ui->sample->user_regs || !ui->sample->user_regs= ->regs) { + uint64_t zero =3D 0; + + memcpy(valp_word, &zero, ui->unw_word_t_size); + return 0; + } + + ret =3D perf_reg_value(&start, perf_sample__user_regs(ui->sample), + perf_arch_reg_sp(ui->e_machine)); + if (ret) + return ret; + + end =3D start + stack->size; + + /* Check overflow. */ + if (addr + ui->unw_word_t_size < addr) + return -EINVAL; + + if (addr < start || addr + ui->unw_word_t_size >=3D end) { + ret =3D access_dso_mem(ui, addr, valp_word); + if (ret) { + pr_debug3("unwind: access_mem %p not inside range" + " 0x%" PRIx64 "-0x%" PRIx64 "\n", + (void *) (uintptr_t) addr, start, end); + memset(valp_word, 0, ui->unw_word_t_size); + return ret; + } + return 0; + } + + offset =3D addr - start; + memcpy(valp_word, &stack->data[offset], ui->unw_word_t_size); + pr_debug3("unwind: access_mem addr %p val %lx, offset %d\n", + (void *) (uintptr_t) addr, *((unsigned long *)valp_word), offset); + return 0; +} + +int __libunwind__access_reg(void *as __maybe_unused, int regnum, void *val= p_word, int __write, + void *arg) +{ + struct unwind_info *ui =3D arg; + int id, ret; + u64 val; + + /* Don't support write, I suspect we don't need it. */ + if (__write) { + pr_err("unwind: access_reg w %d\n", regnum); + return 0; + } + + if (!ui->sample->user_regs || !ui->sample->user_regs->regs) { + memset(valp_word, 0, ui->unw_word_t_size); + return 0; + } + + id =3D get_perf_regnum_for_unw_regnum(ui->e_machine, regnum); + if (id < 0) + return -EINVAL; + + ret =3D perf_reg_value(&val, perf_sample__user_regs(ui->sample), id); + if (ret) { + if (!ui->best_effort) + pr_err("unwind: can't read reg %d\n", regnum); + return ret; + } + + memcpy(valp_word, &val, ui->unw_word_t_size); + pr_debug3("unwind: reg %d, val %lx\n", regnum, val); + return 0; +} =20 int unwind__prepare_access(struct maps *maps, uint16_t e_machine) { - struct unwind_libunwind_ops *ops =3D local_unwind_libunwind_ops; + void *addr_space; =20 if (!dwarf_callchain_users) return 0; @@ -27,25 +561,16 @@ int unwind__prepare_access(struct maps *maps, uint16_t= e_machine) return 0; } =20 - if (e_machine !=3D EM_HOST) { - /* If not live/local mode. */ - switch (e_machine) { - case EM_386: - ops =3D x86_32_unwind_libunwind_ops; - break; - case EM_AARCH64: - ops =3D arm64_unwind_libunwind_ops; - break; - default: - pr_warning_once("unwind: ELF machine type %d is not supported\n", - e_machine); - return 0; - } - } - maps__set_unwind_libunwind_ops(maps, ops); maps__set_e_machine(maps, e_machine); + addr_space =3D libunwind_arch__create_addr_space(e_machine); + + maps__set_addr_space(maps, addr_space); + if (!addr_space) { + pr_err("unwind: Can't create unwind address space.\n"); + return -ENOMEM; + } =20 - return maps__unwind_libunwind_ops(maps)->prepare_access(maps); + return 0; } =20 void unwind__flush_access(struct maps *maps) @@ -58,14 +583,81 @@ void unwind__finish_access(struct maps *maps) libunwind_arch__finish_access(maps); } =20 +static int entry(uint64_t ip, struct thread *thread, unwind_entry_cb_t cb,= void *arg) +{ + struct unwind_entry e; + struct addr_location al; + int ret; + + addr_location__init(&al); + e.ms.sym =3D thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al); + e.ip =3D ip; + e.ms.map =3D al.map; + e.ms.thread =3D thread__get(al.thread); + + pr_debug("unwind: %s:ip =3D 0x%" PRIx64 " (0x%" PRIx64 ")\n", + al.sym ? al.sym->name : "''", + ip, + al.map ? map__map_ip(al.map, ip) : (u64) 0); + + ret =3D cb(&e, arg); + addr_location__exit(&al); + return ret; +} + int libunwind__get_entries(unwind_entry_cb_t cb, void *arg, struct thread *thread, - struct perf_sample *data, int max_stack, + struct perf_sample *sample, int max_stack, bool best_effort) { - const struct unwind_libunwind_ops *ops =3D maps__unwind_libunwind_ops(thr= ead__maps(thread)); + struct unwind_info *ui; + uint64_t first_ip; + int ret, i =3D 0; + uint16_t e_machine; =20 - if (ops) - return ops->get_entries(cb, arg, thread, data, max_stack, best_effort); - return 0; + if (!sample->user_regs || !sample->user_regs->regs) + return -EINVAL; + + if (max_stack <=3D 0) + return -EINVAL; + + if (!thread) { + pr_warning_once("WARNING: thread is NULL"); + return -EINVAL; + } + + e_machine =3D thread__e_machine(thread, /*machine=3D*/NULL, /*e_flags=3D*= /NULL); + ret =3D perf_reg_value(&first_ip, perf_sample__user_regs(sample), + perf_arch_reg_ip(e_machine)); + if (ret) + return ret; + + if (max_stack =3D=3D 1) { + /* Special case for a single entry. */ + return entry(first_ip, thread, cb, arg); + } + + ui =3D libunwind_arch_unwind_info__new(thread, sample, max_stack, best_ef= fort, e_machine, first_ip); + if (!ui) + return -1; + + do { + ret =3D libunwind_arch__unwind_step(ui); + if (ret < 0) + goto out; + + } while (ret); + + /* + * Display what we got based on the order setup. + */ + for (i =3D 0; i < ui->cur_ip; i++) { + int j =3D callchain_param.order =3D=3D ORDER_CALLEE ? i : ui->cur_ip - i= - 1; + + if (ui->ips[j]) + ret =3D entry(ui->ips[j], thread, cb, arg); + } +out: + libunwind_arch_unwind_info__delete(ui); + return ret; } diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index ad610c5a241c..47ce12408c66 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h @@ -19,13 +19,6 @@ struct unwind_entry { =20 typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); =20 -struct unwind_libunwind_ops { - int (*prepare_access)(struct maps *maps); - int (*get_entries)(unwind_entry_cb_t cb, void *arg, - struct thread *thread, - struct perf_sample *data, int max_stack, bool best_effort); -}; - int unwind__configure(const char *var, const char *value, void *cb); int unwind__option(const struct option *opt, const char *arg, int unset); =20 --=20 2.53.0.1213.gd9a14994de-goog From nobody Thu Apr 9 20:26:05 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B6B74374198 for ; Sat, 4 Apr 2026 05:41:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775281265; cv=none; b=VX+kWafBzetK1LxpKK9Hhz20uqYd4gcPxb3omTV4QtEEgLCfNYjW3XBNvpCVrhVVFlWacKXsQvsymuWaww7DoAqkDUKLgq2ex6yfP5OZZ+nv17q/l+4BFINhJpIUWN4ueSlv9v20e5cYa+lYA/H2CLDZPOMGbfZUd/EmrVZjZVo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775281265; c=relaxed/simple; bh=tratQNx+xvzaTcpiB/+muqN8WpoxS6TuizqLn1CKw7w=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=j2HBCdHmSEyYtKcA/+mhTwVZP0NfR+p+JxDI99BYiopG8eC61HmdsKmPCoq8GeMb0+b5Jzr4uA/659ZAVKyHpfJNXdrOUbrEMsExiVXIFfnGc60UM9muwaREXtF/r13UfkB6x6fRnuGfSdyNl7j62xp0cmn+mtZrJFSOVLJIuBk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=EpsjB0ma; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="EpsjB0ma" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2c8ac8e422dso3973519eec.0 for ; Fri, 03 Apr 2026 22:41:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1775281263; x=1775886063; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=KQVkcwEiLCnN6dwxfewA2WuOZTkC+/YHvO/Den3A6Hw=; b=EpsjB0ma+XAVQyXo1eIeotPcMNNw80eRKnrPugG/4/zNNuObbLx/RPSmzFYPFC8GEZ DvOY2i27TFDunsyyHpfQxfOFhrxqyzcj+Xz1Hi5aKSOdFR3WuiO6GqW6VVzbrVlU3YEw RAKImXGbyJqywRRF76rUb9eelDGaWaMtgTMHi2Ha6X4TFB3anMUHlZlA2e6i+mn77Quh cUcuDt2DlzbzQMTFU297zvtgJmfw54MbPo0o0zWx8M9481C71JZ9WYez0dWJOtrfuWcn 0bUhJmv8Uw41fBICnG4yb4cRwyE2Hv6GdiNotTllowe1G6r2rPJf33uPfzmpSVVSGz/+ coQQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775281263; x=1775886063; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=KQVkcwEiLCnN6dwxfewA2WuOZTkC+/YHvO/Den3A6Hw=; b=Rp+NvgYPkXp3NmERKhV9uij5Jw23dsf+A7vgV6TgNYbrM/GqQo6LoNs+j6mrJhN8CB 54ox8+3Mm9zfRJ/qzmms/byJ+4P9Jaq43JtCvIQLP9J/lXeGuWxr0nWmq4ynx9zwGP4I 7LBz9TZ6OnYHufn9H8W0DgDaU1gE4MLdnl/tWZmfHO8IIbXySiyVhLc0Qi7kTbzrZwRf IGxCZQ69hhLuBGdclU/rac+0mMydcykyE7mAIAyBIbbl4/Aaq71SnYMXZi9Bi02LY8Zi 351GzzDAq6qDt8ZbzlfCrnEbLz157UokhGPOyCQxDm28l1RrqnoMlIlxjjE9pHuOY+3B FnAg== X-Forwarded-Encrypted: i=1; AJvYcCWYZCHbbg9s5lCt689SK6lF1e47i4V2Q4k/wxtv9UatzjkjY7zQV0CtZBse82TXoKk4NiR1W2dJCEix/Xg=@vger.kernel.org X-Gm-Message-State: AOJu0Yxgy4Qmoaj+2rqyivfbWeCAxeTwLyMaxIn61ufSm/hdV54euy7j E6ielV90m2xPwtiqea18KrBSy1Oa5hGMS18jjyRL1VbfggtwhMhSUe6GQDZaYdTJ+0tMXHH1frk fmmM61fVcOA== X-Received: from dlbqq2.prod.google.com ([2002:a05:7022:ed02:b0:12a:bc7d:34e3]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:911:b0:11f:2c9e:87f8 with SMTP id a92af1059eb24-12bfb787755mr2328701c88.34.1775281262742; Fri, 03 Apr 2026 22:41:02 -0700 (PDT) Date: Fri, 3 Apr 2026 22:40:32 -0700 In-Reply-To: <20260404054032.1538095-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260305221927.3237145-1-irogers@google.com> <20260404054032.1538095-1-irogers@google.com> X-Mailer: git-send-email 2.53.0.1213.gd9a14994de-goog Message-ID: <20260404054032.1538095-9-irogers@google.com> Subject: [PATCH v3 8/8] perf unwind-libunwind: Add RISC-V libunwind support From: Ian Rogers To: acme@kernel.org Cc: irogers@google.com, 9erthalion6@gmail.com, adrian.hunter@intel.com, alex@ghiti.fr, alexander.shishkin@linux.intel.com, andrew.jones@oss.qualcomm.com, aou@eecs.berkeley.edu, atrajeev@linux.ibm.com, blakejones@google.com, ctshao@google.com, dapeng1.mi@linux.intel.com, howardchu95@gmail.com, james.clark@linaro.org, john.g.garry@oracle.com, jolsa@kernel.org, leo.yan@linux.dev, libunwind-devel@nongnu.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-riscv@lists.infradead.org, mingo@redhat.com, namhyung@kernel.org, palmer@dabbelt.com, peterz@infradead.org, pjw@kernel.org, shimin.guo@skydio.com, tglozar@redhat.com, tmricht@linux.ibm.com, will@kernel.org, yuzhuo@google.com Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a RISC-V implementation for unwinding. Signed-off-by: Ian Rogers --- tools/perf/util/libunwind-arch/Build | 1 + .../perf/util/libunwind-arch/libunwind-arch.c | 21 ++ .../perf/util/libunwind-arch/libunwind-arch.h | 22 ++ .../util/libunwind-arch/libunwind-riscv.c | 297 ++++++++++++++++++ 4 files changed, 341 insertions(+) create mode 100644 tools/perf/util/libunwind-arch/libunwind-riscv.c diff --git a/tools/perf/util/libunwind-arch/Build b/tools/perf/util/libunwi= nd-arch/Build index 87fd657a3248..80d3571918b1 100644 --- a/tools/perf/util/libunwind-arch/Build +++ b/tools/perf/util/libunwind-arch/Build @@ -5,6 +5,7 @@ perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-loongarch.o perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-mips.o perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-ppc32.o perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-ppc64.o +perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-riscv.o perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-s390.o perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-i386.o perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-x86_64.o diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.c b/tools/perf/u= til/libunwind-arch/libunwind-arch.c index 8539b4233df4..9a74cf3c8729 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arch.c +++ b/tools/perf/util/libunwind-arch/libunwind-arch.c @@ -20,6 +20,8 @@ int get_perf_regnum_for_unw_regnum(unsigned int e_machine= , int unw_regnum) return __get_perf_regnum_for_unw_regnum_ppc32(unw_regnum); case EM_PPC64: return __get_perf_regnum_for_unw_regnum_ppc64(unw_regnum); + case EM_RISCV: + return __get_perf_regnum_for_unw_regnum_riscv(unw_regnum); case EM_S390: return __get_perf_regnum_for_unw_regnum_s390(unw_regnum); case EM_386: @@ -58,6 +60,9 @@ void libunwind_arch__flush_access(struct maps *maps) case EM_PPC64: __libunwind_arch__flush_access_ppc64(maps); break; + case EM_RISCV: + __libunwind_arch__flush_access_riscv(maps); + break; case EM_S390: __libunwind_arch__flush_access_s390(maps); break; @@ -98,6 +103,9 @@ void libunwind_arch__finish_access(struct maps *maps) case EM_PPC64: __libunwind_arch__finish_access_ppc64(maps); break; + case EM_RISCV: + __libunwind_arch__finish_access_riscv(maps); + break; case EM_S390: __libunwind_arch__finish_access_s390(maps); break; @@ -128,6 +136,8 @@ void *libunwind_arch__create_addr_space(unsigned int e_= machine) return __libunwind_arch__create_addr_space_ppc32(); case EM_PPC64: return __libunwind_arch__create_addr_space_ppc64(); + case EM_RISCV: + return __libunwind_arch__create_addr_space_riscv(); case EM_S390: return __libunwind_arch__create_addr_space_s390(); case EM_386: @@ -167,6 +177,9 @@ int libunwind_arch__dwarf_search_unwind_table(unsigned = int e_machine, case EM_PPC64: return __libunwind_arch__dwarf_search_unwind_table_ppc64(as, ip, di, pi, need_unwind_info, arg); + case EM_RISCV: + return __libunwind_arch__dwarf_search_unwind_table_riscv(as, ip, di, pi, + need_unwind_info, arg); case EM_S390: return __libunwind_arch__dwarf_search_unwind_table_s390(as, ip, di, pi, need_unwind_info, arg); @@ -211,6 +224,9 @@ int libunwind_arch__dwarf_find_debug_frame(unsigned int= e_machine, case EM_PPC64: return __libunwind_arch__dwarf_find_debug_frame_ppc64(found, di_debug, i= p, segbase, obj_name, start, end); + case EM_RISCV: + return __libunwind_arch__dwarf_find_debug_frame_riscv(found, di_debug, i= p, segbase, + obj_name, start, end); case EM_S390: return __libunwind_arch__dwarf_find_debug_frame_s390(found, di_debug, ip= , segbase, obj_name, start, end); @@ -250,6 +266,9 @@ struct unwind_info *libunwind_arch_unwind_info__new(str= uct thread *thread, case EM_PPC64: return __libunwind_arch_unwind_info__new_ppc64(thread, sample, max_stack, best_effort, first_ip); + case EM_RISCV: + return __libunwind_arch_unwind_info__new_riscv(thread, sample, max_stack, + best_effort, first_ip); case EM_S390: return __libunwind_arch_unwind_info__new_s390(thread, sample, max_stack, best_effort, first_ip); @@ -285,6 +304,8 @@ int libunwind_arch__unwind_step(struct unwind_info *ui) return __libunwind_arch__unwind_step_ppc32(ui); case EM_PPC64: return __libunwind_arch__unwind_step_ppc64(ui); + case EM_RISCV: + return __libunwind_arch__unwind_step_riscv(ui); case EM_S390: return __libunwind_arch__unwind_step_s390(ui); case EM_386: diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.h b/tools/perf/u= til/libunwind-arch/libunwind-arch.h index 2bf7fc33313b..74a09cd58f38 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arch.h +++ b/tools/perf/util/libunwind-arch/libunwind-arch.h @@ -39,6 +39,7 @@ int __get_perf_regnum_for_unw_regnum_loongarch(int unw_re= gnum); int __get_perf_regnum_for_unw_regnum_mips(int unw_regnum); int __get_perf_regnum_for_unw_regnum_ppc32(int unw_regnum); int __get_perf_regnum_for_unw_regnum_ppc64(int unw_regnum); +int __get_perf_regnum_for_unw_regnum_riscv(int unw_regnum); int __get_perf_regnum_for_unw_regnum_s390(int unw_regnum); int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum); int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum); @@ -50,6 +51,7 @@ void __libunwind_arch__flush_access_loongarch(struct maps= *maps); void __libunwind_arch__flush_access_mips(struct maps *maps); void __libunwind_arch__flush_access_ppc32(struct maps *maps); void __libunwind_arch__flush_access_ppc64(struct maps *maps); +void __libunwind_arch__flush_access_riscv(struct maps *maps); void __libunwind_arch__flush_access_s390(struct maps *maps); void __libunwind_arch__flush_access_i386(struct maps *maps); void __libunwind_arch__flush_access_x86_64(struct maps *maps); @@ -61,6 +63,7 @@ void __libunwind_arch__finish_access_loongarch(struct map= s *maps); void __libunwind_arch__finish_access_mips(struct maps *maps); void __libunwind_arch__finish_access_ppc32(struct maps *maps); void __libunwind_arch__finish_access_ppc64(struct maps *maps); +void __libunwind_arch__finish_access_riscv(struct maps *maps); void __libunwind_arch__finish_access_s390(struct maps *maps); void __libunwind_arch__finish_access_i386(struct maps *maps); void __libunwind_arch__finish_access_x86_64(struct maps *maps); @@ -72,6 +75,7 @@ void *__libunwind_arch__create_addr_space_loongarch(void); void *__libunwind_arch__create_addr_space_mips(void); void *__libunwind_arch__create_addr_space_ppc32(void); void *__libunwind_arch__create_addr_space_ppc64(void); +void *__libunwind_arch__create_addr_space_riscv(void); void *__libunwind_arch__create_addr_space_s390(void); void *__libunwind_arch__create_addr_space_i386(void); void *__libunwind_arch__create_addr_space_x86_64(void); @@ -111,6 +115,11 @@ int __libunwind_arch__dwarf_search_unwind_table_ppc64(= void *as, uint64_t ip, void *pi, int need_unwind_info, void *arg); +int __libunwind_arch__dwarf_search_unwind_table_riscv(void *as, uint64_t i= p, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); int __libunwind_arch__dwarf_search_unwind_table_s390(void *as, uint64_t ip, struct libarch_unwind__dyn_info *di, void *pi, @@ -176,6 +185,13 @@ int __libunwind_arch__dwarf_find_debug_frame_ppc64(int= found, const char *obj_name, uint64_t start, uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_riscv(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); int __libunwind_arch__dwarf_find_debug_frame_s390(int found, struct libarch_unwind__dyn_info *di_debug, uint64_t ip, @@ -236,6 +252,11 @@ struct unwind_info *__libunwind_arch_unwind_info__new_= ppc64(struct thread *threa int max_stack, bool best_effort, uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_riscv(struct thread = *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); struct unwind_info *__libunwind_arch_unwind_info__new_s390(struct thread *= thread, struct perf_sample *sample, int max_stack, @@ -266,6 +287,7 @@ int __libunwind_arch__unwind_step_loongarch(struct unwi= nd_info *ui); int __libunwind_arch__unwind_step_mips(struct unwind_info *ui); int __libunwind_arch__unwind_step_ppc32(struct unwind_info *ui); int __libunwind_arch__unwind_step_ppc64(struct unwind_info *ui); +int __libunwind_arch__unwind_step_riscv(struct unwind_info *ui); int __libunwind_arch__unwind_step_s390(struct unwind_info *ui); int __libunwind_arch__unwind_step_i386(struct unwind_info *ui); int __libunwind_arch__unwind_step_x86_64(struct unwind_info *ui); diff --git a/tools/perf/util/libunwind-arch/libunwind-riscv.c b/tools/perf/= util/libunwind-arch/libunwind-riscv.c new file mode 100644 index 000000000000..a70a2ea96644 --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-riscv.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libunwind-arch.h" +#include "../debug.h" +#include "../maps.h" +#include "../thread.h" +#include "../../../arch/riscv/include/uapi/asm/perf_regs.h" +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT +#include +#endif + +int __get_perf_regnum_for_unw_regnum_riscv(int unw_regnum __maybe_unused) +{ +#ifndef HAVE_LIBUNWIND_RISCV_SUPPORT + return -EINVAL; +#else + switch (unw_regnum) { + case UNW_RISCV_X1 ... UNW_RISCV_X31: + return unw_regnum - UNW_RISCV_X1 + PERF_REG_RISCV_RA; + case UNW_RISCV_PC: + return PERF_REG_RISCV_PC; + default: + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } +#endif // HAVE_LIBUNWIND_RISCV_SUPPORT +} + +void __libunwind_arch__flush_access_riscv(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_riscv(struct maps *maps __maybe_unuse= d) +{ +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} + +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_inf= o_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, siz= eof(unw_word_t)); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *va= lp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_w= ord_t)); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t= *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw= _word_t)); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_riscv(void) +{ +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT + static unw_accessors_t accessors =3D { + .find_proc_info =3D find_proc_info, + .put_unwind_info =3D put_unwind_info, + .get_dyn_info_list_addr =3D get_dyn_info_list_addr, + .access_mem =3D access_mem, + .access_reg =3D access_reg, + .access_fpreg =3D access_fpreg, + .resume =3D resume, + .get_proc_name =3D get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space =3D unw_create_addr_space(&accessors, /*byte_order=3D*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_riscv(void *as __maybe_unu= sed, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, = arg); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_RISCV_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_F= RAME_RISCV) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_= debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_riscv(int found __maybe_unuse= d, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_RISCV_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_F= RAME_RISCV) + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, sta= rt, end); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_riscv(struct thread = *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps =3D thread__maps(thread); + void *addr_space =3D maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space =3D=3D NULL) + return NULL; + + ui =3D zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine =3D maps__machine(maps); + ui->ui.thread =3D thread; + ui->ui.sample =3D sample; + ui->ui.cursor =3D &ui->_cursor; + ui->ui.ips =3D &ui->_ips[0]; + ui->ui.ips[0] =3D first_ip; + ui->ui.cur_ip =3D 1; + ui->ui.max_ips =3D max_stack; + ui->ui.unw_word_t_size =3D sizeof(unw_word_t); + ui->ui.e_machine =3D EM_RISCV; + ui->ui.best_effort =3D best_effort; + + ret =3D unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_riscv(struct unwind_info *ui __maybe_unu= sed) +{ +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT + int ret; + + if (ui->cur_ip >=3D ui->max_ips) + return -1; + + ret =3D unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <=3D 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] =3D ip; + } + return ret; +#else + return -EINVAL; +#endif +} --=20 2.53.0.1213.gd9a14994de-goog