perf parse-regs: Introduce functions perf_arch_reg_{ip|sp}()
authorLeo Yan <leo.yan@linaro.org>
Tue, 6 Jun 2023 01:45:55 +0000 (09:45 +0800)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 16 Aug 2023 11:46:45 +0000 (08:46 -0300)
The current code uses macros PERF_REG_IP and PERF_REG_SP for parsing
registers and we build perf with these macros statically, which means it
only can correctly analyze CPU registers for the native architecture and
fails to support cross analysis (e.g. we build perf on x86 and cannot
analyze Arm64's registers).

We need to generalize util/perf_regs.c for support multi architectures,
as a first step, this commit introduces new functions perf_arch_reg_ip()
and perf_arch_reg_sp(), these two functions dynamically return IP and SP
register index respectively according to the parameter "arch".

Every architecture has its own functions (like __perf_reg_ip_arm64 and
__perf_reg_sp_arm64), these architecture specific functions are defined
in each arch source file under folder util/perf-regs-arch; at the end
all of them are built into the tool for cross analysis.

Committer notes:

Make DWARF_MINIMAL_REGS() an inline function, so that we can use the
__maybe_unused attribute for the 'arch' parameter, as this will avoid a
build failure when that variable is unused in the callers. That happens
when building on unsupported architectures, the ones without
HAVE_PERF_REGS_SUPPORT defined.

Signed-off-by: Leo Yan <leo.yan@linaro.org>
Acked-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Eric Lin <eric.lin@sifive.com>
Cc: Fangrui Song <maskray@google.com>
Cc: Guo Ren <guoren@kernel.org>
Cc: Huacai Chen <chenhuacai@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Ivan Babrou <ivan@cloudflare.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.g.garry@oracle.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mike Leach <mike.leach@linaro.org>
Cc: Ming Wang <wangming01@loongson.cn>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Paul Walmsley <paul.walmsley@sifive.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sandipan Das <sandipan.das@amd.com>
Cc: Will Deacon <will@kernel.org>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-csky@vger.kernel.org
Cc: linux-riscv@lists.infradead.org
Link: https://lore.kernel.org/r/20230606014559.21783-3-leo.yan@linaro.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
12 files changed:
tools/perf/util/evsel.c
tools/perf/util/perf-regs-arch/perf_regs_aarch64.c
tools/perf/util/perf-regs-arch/perf_regs_arm.c
tools/perf/util/perf-regs-arch/perf_regs_csky.c
tools/perf/util/perf-regs-arch/perf_regs_loongarch.c
tools/perf/util/perf-regs-arch/perf_regs_mips.c
tools/perf/util/perf-regs-arch/perf_regs_powerpc.c
tools/perf/util/perf-regs-arch/perf_regs_riscv.c
tools/perf/util/perf-regs-arch/perf_regs_s390.c
tools/perf/util/perf-regs-arch/perf_regs_x86.c
tools/perf/util/perf_regs.c
tools/perf/util/perf_regs.h

index 0c50c443d45604f50528994e490cd5fbd1e0fe2b..e3be12596cc3f6e3772602a4e5fbc097e1f836a8 100644 (file)
@@ -845,6 +845,7 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o
 {
        bool function = evsel__is_function_event(evsel);
        struct perf_event_attr *attr = &evsel->core.attr;
+       const char *arch = perf_env__arch(evsel__env(evsel));
 
        evsel__set_sample_bit(evsel, CALLCHAIN);
 
@@ -877,8 +878,9 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o
                if (!function) {
                        evsel__set_sample_bit(evsel, REGS_USER);
                        evsel__set_sample_bit(evsel, STACK_USER);
-                       if (opts->sample_user_regs && DWARF_MINIMAL_REGS != PERF_REGS_MASK) {
-                               attr->sample_regs_user |= DWARF_MINIMAL_REGS;
+                       if (opts->sample_user_regs &&
+                           DWARF_MINIMAL_REGS(arch) != PERF_REGS_MASK) {
+                               attr->sample_regs_user |= DWARF_MINIMAL_REGS(arch);
                                pr_warning("WARNING: The use of --call-graph=dwarf may require all the user registers, "
                                           "specifying a subset with --user-regs may render DWARF unwinding unreliable, "
                                           "so the minimal registers set (IP, SP) is explicitly forced.\n");
index c02c045af46e170dd50c038b99e5a771095c889f..696566c54768775f8dafd75ab1692b1ad0242ca7 100644 (file)
@@ -83,4 +83,14 @@ const char *__perf_reg_name_arm64(int id)
        return NULL;
 }
 
+uint64_t __perf_reg_ip_arm64(void)
+{
+       return PERF_REG_ARM64_PC;
+}
+
+uint64_t __perf_reg_sp_arm64(void)
+{
+       return PERF_REG_ARM64_SP;
+}
+
 #endif
index e8b0fcd72f34988e04d4b6f6fd76a1d5093d14f0..700fd07cd2aa32097960e369053938a70f11367f 100644 (file)
@@ -47,4 +47,14 @@ const char *__perf_reg_name_arm(int id)
        return NULL;
 }
 
+uint64_t __perf_reg_ip_arm(void)
+{
+       return PERF_REG_ARM_PC;
+}
+
+uint64_t __perf_reg_sp_arm(void)
+{
+       return PERF_REG_ARM_SP;
+}
+
 #endif
index e343b1cef7ba25a04266fa48933b117cf9122322..a2841094e096e75af4c42e22d81bdc2b08ccb0a7 100644 (file)
@@ -87,4 +87,14 @@ const char *__perf_reg_name_csky(int id)
        return NULL;
 }
 
+uint64_t __perf_reg_ip_csky(void)
+{
+       return PERF_REG_CSKY_PC;
+}
+
+uint64_t __perf_reg_sp_csky(void)
+{
+       return PERF_REG_CSKY_SP;
+}
+
 #endif
index 6f937464067bed9f8010881fe7e274cd0fb6a49d..a9ba0f934123db0ebc7fdec70735984afbf6a537 100644 (file)
@@ -78,4 +78,14 @@ const char *__perf_reg_name_loongarch(int id)
        return NULL;
 }
 
+uint64_t __perf_reg_ip_loongarch(void)
+{
+       return PERF_REG_LOONGARCH_PC;
+}
+
+uint64_t __perf_reg_sp_loongarch(void)
+{
+       return PERF_REG_LOONGARCH_R3;
+}
+
 #endif
index f48fbca2f947ef3efb8fb9d16df9a99ce13aafe8..5a45830cfbf58bf0dccd24a1b3650f4713905a6d 100644 (file)
@@ -74,4 +74,14 @@ const char *__perf_reg_name_mips(int id)
        return NULL;
 }
 
+uint64_t __perf_reg_ip_mips(void)
+{
+       return PERF_REG_MIPS_PC;
+}
+
+uint64_t __perf_reg_sp_mips(void)
+{
+       return PERF_REG_MIPS_R29;
+}
+
 #endif
index dda1b4b169fc6aa7b53fa4bf85f9386e453cdf05..1f0d682db74a76396ab0469ed8014b891aafbecb 100644 (file)
@@ -132,4 +132,14 @@ const char *__perf_reg_name_powerpc(int id)
        return NULL;
 }
 
+uint64_t __perf_reg_ip_powerpc(void)
+{
+       return PERF_REG_POWERPC_NIP;
+}
+
+uint64_t __perf_reg_sp_powerpc(void)
+{
+       return PERF_REG_POWERPC_R1;
+}
+
 #endif
index c504b047cac2bac1f60560766e3e8a735c521a3b..e432630be4c56e625ca2e26db5b01b52d7ec742f 100644 (file)
@@ -79,4 +79,14 @@ const char *__perf_reg_name_riscv(int id)
        return NULL;
 }
 
+uint64_t __perf_reg_ip_riscv(void)
+{
+       return PERF_REG_RISCV_PC;
+}
+
+uint64_t __perf_reg_sp_riscv(void)
+{
+       return PERF_REG_RISCV_SP;
+}
+
 #endif
index e71e2302394c4fef3f5854be644c749c63da8328..1c7a46db778c88fd6f753257d8298fe6609658f4 100644 (file)
@@ -83,4 +83,14 @@ const char *__perf_reg_name_s390(int id)
        return NULL;
 }
 
+uint64_t __perf_reg_ip_s390(void)
+{
+       return PERF_REG_S390_PC;
+}
+
+uint64_t __perf_reg_sp_s390(void)
+{
+       return PERF_REG_S390_R15;
+}
+
 #endif
index eb5d249afa70802aee9dae3b2487e30e69da4aaa..873c620f063426c5bb2a877533ba9bd24f14f9af 100644 (file)
@@ -85,4 +85,14 @@ const char *__perf_reg_name_x86(int id)
        return NULL;
 }
 
+uint64_t __perf_reg_ip_x86(void)
+{
+       return PERF_REG_X86_IP;
+}
+
+uint64_t __perf_reg_sp_x86(void)
+{
+       return PERF_REG_X86_SP;
+}
+
 #endif
index 5af1b95c3d012c4c1b689df261458cb0ffd7df26..23584efd4886eb6f4b1ef8aad463c3d173eef33f 100644 (file)
@@ -3,6 +3,7 @@
 #include <string.h>
 #include "perf_regs.h"
 #include "util/sample.h"
+#include "debug.h"
 
 int __weak arch_sdt_arg_parse_op(char *old_op __maybe_unused,
                                 char **new_op __maybe_unused)
@@ -74,4 +75,55 @@ out:
        *valp = regs->cache_regs[id];
        return 0;
 }
+
+uint64_t perf_arch_reg_ip(const char *arch)
+{
+       if (!strcmp(arch, "arm"))
+               return __perf_reg_ip_arm();
+       else if (!strcmp(arch, "arm64"))
+               return __perf_reg_ip_arm64();
+       else if (!strcmp(arch, "csky"))
+               return __perf_reg_ip_csky();
+       else if (!strcmp(arch, "loongarch"))
+               return __perf_reg_ip_loongarch();
+       else if (!strcmp(arch, "mips"))
+               return __perf_reg_ip_mips();
+       else if (!strcmp(arch, "powerpc"))
+               return __perf_reg_ip_powerpc();
+       else if (!strcmp(arch, "riscv"))
+               return __perf_reg_ip_riscv();
+       else if (!strcmp(arch, "s390"))
+               return __perf_reg_ip_s390();
+       else if (!strcmp(arch, "x86"))
+               return __perf_reg_ip_x86();
+
+       pr_err("Fail to find IP register for arch %s, returns 0\n", arch);
+       return 0;
+}
+
+uint64_t perf_arch_reg_sp(const char *arch)
+{
+       if (!strcmp(arch, "arm"))
+               return __perf_reg_sp_arm();
+       else if (!strcmp(arch, "arm64"))
+               return __perf_reg_sp_arm64();
+       else if (!strcmp(arch, "csky"))
+               return __perf_reg_sp_csky();
+       else if (!strcmp(arch, "loongarch"))
+               return __perf_reg_sp_loongarch();
+       else if (!strcmp(arch, "mips"))
+               return __perf_reg_sp_mips();
+       else if (!strcmp(arch, "powerpc"))
+               return __perf_reg_sp_powerpc();
+       else if (!strcmp(arch, "riscv"))
+               return __perf_reg_sp_riscv();
+       else if (!strcmp(arch, "s390"))
+               return __perf_reg_sp_s390();
+       else if (!strcmp(arch, "x86"))
+               return __perf_reg_sp_x86();
+
+       pr_err("Fail to find SP register for arch %s, returns 0\n", arch);
+       return 0;
+}
+
 #endif
index 6b19a2867171940149b9ff33741464116292d11f..de1673057e502de9438c7cf69259c53291c2649c 100644 (file)
@@ -32,25 +32,51 @@ extern const struct sample_reg sample_reg_masks[];
 
 #include <perf_regs.h>
 
-#define DWARF_MINIMAL_REGS ((1ULL << PERF_REG_IP) | (1ULL << PERF_REG_SP))
-
 const char *perf_reg_name(int id, const char *arch);
 int perf_reg_value(u64 *valp, struct regs_dump *regs, int id);
+uint64_t perf_arch_reg_ip(const char *arch);
+uint64_t perf_arch_reg_sp(const char *arch);
 const char *__perf_reg_name_arm64(int id);
+uint64_t __perf_reg_ip_arm64(void);
+uint64_t __perf_reg_sp_arm64(void);
 const char *__perf_reg_name_arm(int id);
+uint64_t __perf_reg_ip_arm(void);
+uint64_t __perf_reg_sp_arm(void);
 const char *__perf_reg_name_csky(int id);
+uint64_t __perf_reg_ip_csky(void);
+uint64_t __perf_reg_sp_csky(void);
 const char *__perf_reg_name_loongarch(int id);
+uint64_t __perf_reg_ip_loongarch(void);
+uint64_t __perf_reg_sp_loongarch(void);
 const char *__perf_reg_name_mips(int id);
+uint64_t __perf_reg_ip_mips(void);
+uint64_t __perf_reg_sp_mips(void);
 const char *__perf_reg_name_powerpc(int id);
+uint64_t __perf_reg_ip_powerpc(void);
+uint64_t __perf_reg_sp_powerpc(void);
 const char *__perf_reg_name_riscv(int id);
+uint64_t __perf_reg_ip_riscv(void);
+uint64_t __perf_reg_sp_riscv(void);
 const char *__perf_reg_name_s390(int id);
+uint64_t __perf_reg_ip_s390(void);
+uint64_t __perf_reg_sp_s390(void);
 const char *__perf_reg_name_x86(int id);
+uint64_t __perf_reg_ip_x86(void);
+uint64_t __perf_reg_sp_x86(void);
+
+static inline uint64_t DWARF_MINIMAL_REGS(const char *arch)
+{
+       return (1ULL << perf_arch_reg_ip(arch)) | (1ULL << perf_arch_reg_sp(arch));
+}
 
 #else
 #define PERF_REGS_MASK 0
 #define PERF_REGS_MAX  0
 
-#define DWARF_MINIMAL_REGS PERF_REGS_MASK
+static inline uint64_t DWARF_MINIMAL_REGS(const char *arch __maybe_unused)
+{
+       return PERF_REGS_MASK;
+}
 
 static inline const char *perf_reg_name(int id __maybe_unused, const char *arch __maybe_unused)
 {
@@ -63,5 +89,16 @@ static inline int perf_reg_value(u64 *valp __maybe_unused,
 {
        return 0;
 }
+
+static inline uint64_t perf_arch_reg_ip(const char *arch __maybe_unused)
+{
+       return 0;
+}
+
+static inline uint64_t perf_arch_reg_sp(const char *arch __maybe_unused)
+{
+       return 0;
+}
+
 #endif /* HAVE_PERF_REGS_SUPPORT */
 #endif /* __PERF_REGS_H */