perf script: Fix script_fetch_insn for more than just x86

The script_fetch_insn code was only supported on natively running x86.

Implement a crude elf_machine_max_instruction_length function and use to
give an instruction length on more than just x86.

Use the ELF machine to determine the length to use to support
cross-architecture development.

Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Leo Yan <leo.yan@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Shimin Guo <shimin.guo@skydio.com>
Cc: Yujie Liu <yujie.liu@intel.com>
[ Conditionally define EM_CSKY and EM_LOONGARCH for older distros ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Ian Rogers
2026-01-26 14:05:48 -08:00
committed by Arnaldo Carvalho de Melo
parent 0a6fb66047
commit 7ce6dfc603
11 changed files with 87 additions and 78 deletions

View File

@@ -14,6 +14,5 @@ perf-util-y += iostat.o
perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
perf-util-y += auxtrace.o
perf-util-y += archinsn.o
perf-util-y += intel-pt.o
perf-util-y += intel-bts.o

View File

@@ -1,27 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#include "archinsn.h"
#include "event.h"
#include "machine.h"
#include "thread.h"
#include "symbol.h"
#include "../../../../arch/x86/include/asm/insn.h"
void arch_fetch_insn(struct perf_sample *sample,
struct thread *thread,
struct machine *machine)
{
struct insn insn;
int len, ret;
bool is64bit = false;
if (!sample->ip)
return;
len = thread__memcpy(thread, machine, sample->insn, sample->ip, sizeof(sample->insn), &is64bit);
if (len <= 0)
return;
ret = insn_decode(&insn, sample->insn, len,
is64bit ? INSN_MODE_64 : INSN_MODE_32);
if (ret >= 0 && insn.length <= len)
sample->insn_len = insn.length;
}

View File

@@ -37,7 +37,6 @@
#include "ui/ui.h"
#include "print_binary.h"
#include "print_insn.h"
#include "archinsn.h"
#include <linux/bitmap.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
@@ -90,7 +89,6 @@ static bool print_flags;
static const char *cpu_list;
static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
static int max_blocks;
static bool native_arch;
static struct dlfilter *dlfilter;
static int dlargc;
static char **dlargv;
@@ -1627,7 +1625,7 @@ static int perf_sample__fprintf_insn(struct perf_sample *sample,
{
int printed = 0;
script_fetch_insn(sample, thread, machine, native_arch);
perf_sample__fetch_insn(sample, thread, machine);
if (PRINT_FIELD(INSNLEN))
printed += fprintf(fp, " ilen: %d", sample->insn_len);
@@ -4034,7 +4032,6 @@ int cmd_script(int argc, const char **argv)
.set = false,
.default_no_sample = true,
};
struct utsname uts;
char *script_path = NULL;
const char *dlfilter_file = NULL;
const char **__argv;
@@ -4456,17 +4453,6 @@ script_found:
if (symbol__init(env) < 0)
goto out_delete;
uname(&uts);
if (data.is_pipe) { /* Assume pipe_mode indicates native_arch */
native_arch = true;
} else if (env->arch) {
if (!strcmp(uts.machine, env->arch))
native_arch = true;
else if (!strcmp(uts.machine, "x86_64") &&
!strcmp(env->arch, "i386"))
native_arch = true;
}
script.session = session;
script__setup_sample_type(&script);

View File

@@ -93,7 +93,7 @@ static PyObject *perf_sample_insn(PyObject *obj, PyObject *args)
if (c->sample->ip && !c->sample->insn_len && thread__maps(c->al->thread)) {
struct machine *machine = maps__machine(thread__maps(c->al->thread));
script_fetch_insn(c->sample, c->al->thread, machine, /*native_arch=*/true);
perf_sample__fetch_insn(c->sample, c->al->thread, machine);
}
if (!c->sample->insn_len)
Py_RETURN_NONE; /* N.B. This is a return statement */

View File

@@ -30,7 +30,6 @@
#include "symbol.h"
#include "synthetic-events.h"
#include "util.h"
#include "archinsn.h"
#include "dlfilter.h"
#include "tests.h"
#include "util/sample.h"

View File

@@ -1,12 +0,0 @@
#ifndef INSN_H
#define INSN_H 1
struct perf_sample;
struct machine;
struct thread;
void arch_fetch_insn(struct perf_sample *sample,
struct thread *thread,
struct machine *machine);
#endif

View File

@@ -234,8 +234,7 @@ static const __u8 *dlfilter__insn(void *ctx, __u32 *len)
struct machine *machine = maps__machine(thread__maps(al->thread));
if (machine)
script_fetch_insn(d->sample, al->thread, machine,
/*native_arch=*/true);
perf_sample__fetch_insn(d->sample, al->thread, machine);
}
}

View File

@@ -1,9 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include "sample.h"
#include "debug.h"
#include "thread.h"
#include <elf.h>
#ifndef EM_CSKY
#define EM_CSKY 252
#endif
#ifndef EM_LOONGARCH
#define EM_LOONGARCH 258
#endif
#include <linux/zalloc.h>
#include <stdlib.h>
#include <string.h>
#include "../../arch/x86/include/asm/insn.h"
void perf_sample__init(struct perf_sample *sample, bool all)
{
@@ -41,3 +50,71 @@ struct regs_dump *perf_sample__intr_regs(struct perf_sample *sample)
}
return sample->intr_regs;
}
static int elf_machine_max_instruction_length(uint16_t e_machine)
{
switch (e_machine) {
/* Fixed 4-byte (32-bit) architectures */
case EM_AARCH64:
case EM_PPC:
case EM_PPC64:
case EM_MIPS:
case EM_SPARC:
case EM_SPARCV9:
case EM_ALPHA:
case EM_LOONGARCH:
case EM_PARISC:
case EM_SH:
return 4;
/* Variable length or mixed-mode architectures */
case EM_ARM: /* Variable due to Thumb/Thumb-2 */
case EM_RISCV: /* Variable due to Compressed (C) extension */
case EM_CSKY: /* Variable (16 or 32 bit) */
case EM_ARC: /* Variable (ARCompact) */
return 4;
case EM_S390: /* Variable (2, 4, or 6 bytes) */
return 6;
case EM_68K:
return 10;
case EM_386:
case EM_X86_64:
return 15;
case EM_XTENSA: /* Variable (FLIX) */
return 16;
default:
return MAX_INSN;
}
}
void perf_sample__fetch_insn(struct perf_sample *sample,
struct thread *thread,
struct machine *machine)
{
int ret, len;
bool is64bit = false;
uint16_t e_machine;
if (!sample->ip || sample->insn_len != 0)
return;
e_machine = thread__e_machine(thread, machine);
len = elf_machine_max_instruction_length(e_machine);
len = thread__memcpy(thread, machine, sample->insn,
sample->ip, len,
&is64bit);
if (len <= 0)
return;
sample->insn_len = len;
if (e_machine == EM_386 || e_machine == EM_X86_64) {
/* Refine the x86 instruction length with the decoder. */
struct insn insn;
ret = insn_decode(&insn, sample->insn, len,
is64bit ? INSN_MODE_64 : INSN_MODE_32);
if (ret >= 0 && insn.length <= len)
sample->insn_len = insn.length;
}
}

View File

@@ -5,6 +5,9 @@
#include <linux/perf_event.h>
#include <linux/types.h>
struct machine;
struct thread;
/* number of register is bound by the number of bits in regs_dump::mask (64) */
#define PERF_SAMPLE_REGS_CACHE_SIZE (8 * sizeof(u64))
@@ -127,6 +130,10 @@ void perf_sample__exit(struct perf_sample *sample);
struct regs_dump *perf_sample__user_regs(struct perf_sample *sample);
struct regs_dump *perf_sample__intr_regs(struct perf_sample *sample);
void perf_sample__fetch_insn(struct perf_sample *sample,
struct thread *thread,
struct machine *machine);
/*
* raw_data is always 4 bytes from an 8-byte boundary, so subtract 4 to get
* 8-byte alignment.

View File

@@ -13,7 +13,6 @@
#include <event-parse.h>
#endif
#include "archinsn.h"
#include "debug.h"
#include "event.h"
#include "trace-event.h"
@@ -274,21 +273,6 @@ void setup_perl_scripting(void)
#endif
#endif
#if !defined(__i386__) && !defined(__x86_64__)
void arch_fetch_insn(struct perf_sample *sample __maybe_unused,
struct thread *thread __maybe_unused,
struct machine *machine __maybe_unused)
{
}
#endif
void script_fetch_insn(struct perf_sample *sample, struct thread *thread,
struct machine *machine, bool native_arch)
{
if (sample->insn_len == 0 && native_arch)
arch_fetch_insn(sample, thread, machine);
}
static const struct {
u32 flags;
const char *name;

View File

@@ -116,9 +116,6 @@ extern unsigned int scripting_max_stack;
struct scripting_ops *script_spec__lookup(const char *spec);
int script_spec__for_each(int (*cb)(struct scripting_ops *ops, const char *spec));
void script_fetch_insn(struct perf_sample *sample, struct thread *thread,
struct machine *machine, bool native_arch);
void setup_perl_scripting(void);
void setup_python_scripting(void);