Commit a457ef08 authored by Ian Rogers's avatar Ian Rogers Committed by Arnaldo Carvalho de Melo
Browse files

perf perf_regs: Switch from arch string to int e_machine



The arch string requires multiple strcmp to identify things like the
IP and SP.

Switch to passing in an e_machine that in the bulk of cases is computed
using a current thread load.

The e_machine also allows identification of 32-bit vs 64-bit processes.

Signed-off-by: default avatarIan Rogers <irogers@google.com>
Cc: Aditya Bodkhe <aditya.b1@linux.ibm.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Alexandre Ghiti <alex@ghiti.fr>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Athira Rajeev <atrajeev@linux.ibm.com>
Cc: Chun-Tse Shao <ctshao@google.com>
Cc: Dmitriy Vyukov <dvyukov@google.com>
Cc: Dr. David Alan Gilbert <linux@treblig.org>
Cc: Guo Ren <guoren@kernel.org>
Cc: Haibo Xu <haibo1.xu@intel.com>
Cc: Howard Chu <howardchu95@gmail.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.g.garry@oracle.com>
Cc: Krzysztof Łopatowski <krzysztof.m.lopatowski@gmail.com>
Cc: Leo Yan <leo.yan@linux.dev>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Paul Walmsley <pjw@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sergei Trofimovich <slyich@gmail.com>
Cc: Shimin Guo <shimin.guo@skydio.com>
Cc: Stephen Brennan <stephen.s.brennan@oracle.com>
Cc: Thomas Falcon <thomas.falcon@intel.com>
Cc: Will Deacon <will@kernel.org>
[ Include dwarf-regs.h to get conditional defines for EM_CSKY and EM_LOONGARCH, not available in old distros ]
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent b7a2b011
Loading
Loading
Loading
Loading
+8 −9
Original line number Diff line number Diff line
@@ -717,7 +717,7 @@ static int perf_session__check_output_opt(struct perf_session *session)
	return 0;
}

static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask, const char *arch,
static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask, uint16_t e_machine,
				     FILE *fp)
{
	unsigned i = 0, r;
@@ -730,7 +730,7 @@ static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask, cons

	for_each_set_bit(r, (unsigned long *) &mask, sizeof(mask) * 8) {
		u64 val = regs->regs[i++];
		printed += fprintf(fp, "%5s:0x%"PRIx64" ", perf_reg_name(r, arch), val);
		printed += fprintf(fp, "%5s:0x%"PRIx64" ", perf_reg_name(r, e_machine), val);
	}

	return printed;
@@ -787,23 +787,23 @@ tod_scnprintf(struct perf_script *script, char *buf, int buflen,
}

static int perf_sample__fprintf_iregs(struct perf_sample *sample,
				      struct perf_event_attr *attr, const char *arch, FILE *fp)
				      struct perf_event_attr *attr, uint16_t e_machine, FILE *fp)
{
	if (!sample->intr_regs)
		return 0;

	return perf_sample__fprintf_regs(perf_sample__intr_regs(sample),
					 attr->sample_regs_intr, arch, fp);
					 attr->sample_regs_intr, e_machine, fp);
}

static int perf_sample__fprintf_uregs(struct perf_sample *sample,
				      struct perf_event_attr *attr, const char *arch, FILE *fp)
				      struct perf_event_attr *attr, uint16_t e_machine, FILE *fp)
{
	if (!sample->user_regs)
		return 0;

	return perf_sample__fprintf_regs(perf_sample__user_regs(sample),
					 attr->sample_regs_user, arch, fp);
					 attr->sample_regs_user, e_machine, fp);
}

static int perf_sample__fprintf_start(struct perf_script *script,
@@ -2418,7 +2418,6 @@ static void process_event(struct perf_script *script,
	struct evsel_script *es = evsel->priv;
	FILE *fp = es->fp;
	char str[PAGE_SIZE_NAME_LEN];
	const char *arch = perf_env__arch(machine->env);

	if (output[type].fields == 0)
		return;
@@ -2506,10 +2505,10 @@ static void process_event(struct perf_script *script,
	}

	if (PRINT_FIELD(IREGS))
		perf_sample__fprintf_iregs(sample, attr, arch, fp);
		perf_sample__fprintf_iregs(sample, attr, thread__e_machine(thread, machine), fp);

	if (PRINT_FIELD(UREGS))
		perf_sample__fprintf_uregs(sample, attr, arch, fp);
		perf_sample__fprintf_uregs(sample, attr, thread__e_machine(thread, machine), fp);

	if (PRINT_FIELD(BRSTACK))
		perf_sample__fprintf_brstack(sample, thread, evsel, fp);
+11 −3
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@
#include "callchain.h"
#include "cgroup.h"
#include "counts.h"
#include "dwarf-regs.h"
#include "event.h"
#include "evsel.h"
#include "time-utils.h"
@@ -1007,6 +1008,13 @@ int evsel__group_desc(struct evsel *evsel, char *buf, size_t size)
	return ret;
}

static uint16_t evsel__e_machine(struct evsel *evsel)
{
	struct perf_session *session = evsel__session(evsel);

	return session ? perf_session__e_machine(session) : EM_HOST;
}

static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *opts,
				      struct callchain_param *param)
{
@@ -1042,13 +1050,13 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o

	if (param->record_mode == CALLCHAIN_DWARF) {
		if (!function) {
			const char *arch = perf_env__arch(evsel__env(evsel));
			uint16_t e_machine = evsel__e_machine(evsel);

			evsel__set_sample_bit(evsel, REGS_USER);
			evsel__set_sample_bit(evsel, STACK_USER);
			if (opts->sample_user_regs &&
			    DWARF_MINIMAL_REGS(arch) != arch__user_reg_mask()) {
				attr->sample_regs_user |= DWARF_MINIMAL_REGS(arch);
			    DWARF_MINIMAL_REGS(e_machine) != arch__user_reg_mask()) {
				attr->sample_regs_user |= DWARF_MINIMAL_REGS(e_machine);
				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");
+67 −39
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
#include <elf.h>
#include <errno.h>
#include <string.h>
#include "dwarf-regs.h"
#include "perf_regs.h"
#include "util/sample.h"
#include "debug.h"
@@ -30,30 +32,48 @@ const struct sample_reg * __weak arch__sample_reg_masks(void)
	return sample_reg_masks;
}

const char *perf_reg_name(int id, const char *arch)
const char *perf_reg_name(int id, uint16_t e_machine)
{
	const char *reg_name = NULL;

	if (!strcmp(arch, "csky"))
	switch (e_machine) {
	case EM_ARM:
		reg_name = __perf_reg_name_arm(id);
		break;
	case EM_AARCH64:
		reg_name = __perf_reg_name_arm64(id);
		break;
	case EM_CSKY:
		reg_name = __perf_reg_name_csky(id);
	else if (!strcmp(arch, "loongarch"))
		break;
	case EM_LOONGARCH:
		reg_name = __perf_reg_name_loongarch(id);
	else if (!strcmp(arch, "mips"))
		break;
	case EM_MIPS:
		reg_name = __perf_reg_name_mips(id);
	else if (!strcmp(arch, "powerpc"))
		break;
	case EM_PPC:
	case EM_PPC64:
		reg_name = __perf_reg_name_powerpc(id);
	else if (!strcmp(arch, "riscv"))
		break;
	case EM_RISCV:
		reg_name = __perf_reg_name_riscv(id);
	else if (!strcmp(arch, "s390"))
		break;
	case EM_S390:
		reg_name = __perf_reg_name_s390(id);
	else if (!strcmp(arch, "x86"))
		break;
	case EM_386:
	case EM_X86_64:
		reg_name = __perf_reg_name_x86(id);
	else if (!strcmp(arch, "arm"))
		reg_name = __perf_reg_name_arm(id);
	else if (!strcmp(arch, "arm64"))
		reg_name = __perf_reg_name_arm64(id);
		break;
	default:
		break;
	}
	if (reg_name)
		return reg_name;

	return reg_name ?: "unknown";
	pr_debug("Failed to find register %d for ELF machine type %u\n", id, e_machine);
	return "unknown";
}

int perf_reg_value(u64 *valp, struct regs_dump *regs, int id)
@@ -83,52 +103,60 @@ int perf_reg_value(u64 *valp, struct regs_dump *regs, int id)
	return 0;
}

uint64_t perf_arch_reg_ip(const char *arch)
uint64_t perf_arch_reg_ip(uint16_t e_machine)
{
	if (!strcmp(arch, "arm"))
	switch (e_machine) {
	case EM_ARM:
		return __perf_reg_ip_arm();
	else if (!strcmp(arch, "arm64"))
	case EM_AARCH64:
		return __perf_reg_ip_arm64();
	else if (!strcmp(arch, "csky"))
	case EM_CSKY:
		return __perf_reg_ip_csky();
	else if (!strcmp(arch, "loongarch"))
	case EM_LOONGARCH:
		return __perf_reg_ip_loongarch();
	else if (!strcmp(arch, "mips"))
	case EM_MIPS:
		return __perf_reg_ip_mips();
	else if (!strcmp(arch, "powerpc"))
	case EM_PPC:
	case EM_PPC64:
		return __perf_reg_ip_powerpc();
	else if (!strcmp(arch, "riscv"))
	case EM_RISCV:
		return __perf_reg_ip_riscv();
	else if (!strcmp(arch, "s390"))
	case EM_S390:
		return __perf_reg_ip_s390();
	else if (!strcmp(arch, "x86"))
	case EM_386:
	case EM_X86_64:
		return __perf_reg_ip_x86();

	pr_err("Fail to find IP register for arch %s, returns 0\n", arch);
	default:
		pr_err("Failed to find IP register for ELF machine type %u\n", e_machine);
		return 0;
	}
}

uint64_t perf_arch_reg_sp(const char *arch)
uint64_t perf_arch_reg_sp(uint16_t e_machine)
{
	if (!strcmp(arch, "arm"))
	switch (e_machine) {
	case EM_ARM:
		return __perf_reg_sp_arm();
	else if (!strcmp(arch, "arm64"))
	case EM_AARCH64:
		return __perf_reg_sp_arm64();
	else if (!strcmp(arch, "csky"))
	case EM_CSKY:
		return __perf_reg_sp_csky();
	else if (!strcmp(arch, "loongarch"))
	case EM_LOONGARCH:
		return __perf_reg_sp_loongarch();
	else if (!strcmp(arch, "mips"))
	case EM_MIPS:
		return __perf_reg_sp_mips();
	else if (!strcmp(arch, "powerpc"))
	case EM_PPC:
	case EM_PPC64:
		return __perf_reg_sp_powerpc();
	else if (!strcmp(arch, "riscv"))
	case EM_RISCV:
		return __perf_reg_sp_riscv();
	else if (!strcmp(arch, "s390"))
	case EM_S390:
		return __perf_reg_sp_s390();
	else if (!strcmp(arch, "x86"))
	case EM_386:
	case EM_X86_64:
		return __perf_reg_sp_x86();

	pr_err("Fail to find SP register for arch %s, returns 0\n", arch);
	default:
		pr_err("Failed to find SP register for ELF machine type %u\n", e_machine);
		return 0;
	}
}
+5 −5
Original line number Diff line number Diff line
@@ -28,10 +28,10 @@ uint64_t arch__intr_reg_mask(void);
uint64_t arch__user_reg_mask(void);
const struct sample_reg *arch__sample_reg_masks(void);

const char *perf_reg_name(int id, const char *arch);
const char *perf_reg_name(int id, uint16_t e_machine);
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);
uint64_t perf_arch_reg_ip(uint16_t e_machine);
uint64_t perf_arch_reg_sp(uint16_t e_machine);
const char *__perf_reg_name_arm64(int id);
uint64_t __perf_reg_ip_arm64(void);
uint64_t __perf_reg_sp_arm64(void);
@@ -60,9 +60,9 @@ 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)
static inline uint64_t DWARF_MINIMAL_REGS(uint16_t e_machine)
{
	return (1ULL << perf_arch_reg_ip(arch)) | (1ULL << perf_arch_reg_sp(arch));
	return (1ULL << perf_arch_reg_ip(e_machine)) | (1ULL << perf_arch_reg_sp(e_machine));
}

#endif /* __PERF_REGS_H */
+14 −7
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@
#include "../thread-stack.h"
#include "../trace-event.h"
#include "../call-path.h"
#include "dwarf-regs.h"
#include "map.h"
#include "symbol.h"
#include "thread_map.h"
@@ -713,7 +714,7 @@ static void set_sample_datasrc_in_dict(PyObject *dict,
			_PyUnicode_FromString(decode));
}

static void regs_map(struct regs_dump *regs, uint64_t mask, const char *arch, char *bf, int size)
static void regs_map(struct regs_dump *regs, uint64_t mask, uint16_t e_machine, char *bf, int size)
{
	unsigned int i = 0, r;
	int printed = 0;
@@ -731,7 +732,7 @@ static void regs_map(struct regs_dump *regs, uint64_t mask, const char *arch, ch

		printed += scnprintf(bf + printed, size - printed,
				     "%5s:0x%" PRIx64 " ",
				     perf_reg_name(r, arch), val);
				     perf_reg_name(r, e_machine), val);
	}
}

@@ -739,10 +740,10 @@ static void regs_map(struct regs_dump *regs, uint64_t mask, const char *arch, ch

static int set_regs_in_dict(PyObject *dict,
			     struct perf_sample *sample,
			     struct evsel *evsel)
			     struct evsel *evsel,
			     uint16_t e_machine)
{
	struct perf_event_attr *attr = &evsel->core.attr;
	const char *arch = perf_env__arch(evsel__env(evsel));

	int size = (__sw_hweight64(attr->sample_regs_intr) * MAX_REG_SIZE) + 1;
	char *bf = NULL;
@@ -752,7 +753,7 @@ static int set_regs_in_dict(PyObject *dict,
		if (!bf)
			return -1;

		regs_map(sample->intr_regs, attr->sample_regs_intr, arch, bf, size);
		regs_map(sample->intr_regs, attr->sample_regs_intr, e_machine, bf, size);

		pydict_set_item_string_decref(dict, "iregs",
					_PyUnicode_FromString(bf));
@@ -764,7 +765,7 @@ static int set_regs_in_dict(PyObject *dict,
			if (!bf)
				return -1;
		}
		regs_map(sample->user_regs, attr->sample_regs_user, arch, bf, size);
		regs_map(sample->user_regs, attr->sample_regs_user, e_machine, bf, size);

		pydict_set_item_string_decref(dict, "uregs",
					_PyUnicode_FromString(bf));
@@ -834,6 +835,8 @@ static PyObject *get_perf_sample_dict(struct perf_sample *sample,
					 PyObject *callchain)
{
	PyObject *dict, *dict_sample, *brstack, *brstacksym;
	struct machine *machine;
	uint16_t e_machine = EM_HOST;

	dict = PyDict_New();
	if (!dict)
@@ -920,7 +923,11 @@ static PyObject *get_perf_sample_dict(struct perf_sample *sample,
			PyLong_FromUnsignedLongLong(sample->cyc_cnt));
	}

	if (set_regs_in_dict(dict, sample, evsel))
	if (al->thread) {
		machine = maps__machine(thread__maps(al->thread));
		e_machine = thread__e_machine(al->thread, machine);
	}
	if (set_regs_in_dict(dict, sample, evsel, e_machine))
		Py_FatalError("Failed to setting regs in dict");

	return dict;
Loading