* [PATCH 0/2] perf: Alpha annotation support
@ 2026-09-05 22:54 Matt Turner
2026-09-05 22:54 ` [PATCH 1/2] perf thread: Fix live-session detection in thread__e_machine() Matt Turner
2026-09-05 22:54 ` [PATCH 2/2] perf annotate: add Alpha instruction support Matt Turner
0 siblings, 2 replies; 8+ messages in thread
From: Matt Turner @ 2026-09-05 22:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, James Clark
Cc: linux-perf-users, linux-kernel, Matt Turner
Alpha has no arch support in perf annotate, so annotating an Alpha
perf.data resolves no call targets and draws no jump arrows. Patch 2 adds
it. Patch 1 is a prerequisite for reading such a perf.data anywhere other
than on Alpha itself: thread__e_machine() currently misdetects a
file-backed session as live and falls back to EM_HOST, which silently
annotates the Alpha disassembly with the x86 instruction table.
Alpha's calls need a little care. Only bsr has a real PC-relative target.
jsr is register-indirect, and its trailing operand is a branch prediction
hint that objdump prints as an address, so it looks like a call target
without being one: in a vmlinux built from this tree, only 23093 of the
213750 jsr hints land on a symbol, while 157204 point into the middle of an
unrelated function. Those are therefore left unresolved rather than
reported as calls to whatever the hint happens to name.
Tested on an EV7 Marvel, both natively and by annotating its perf.data on
an x86_64 host, over bsr to a local function, jsr through the PLT and
kernel-mode jsr. The two hosts produce identical output.
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
Matt Turner (2):
perf thread: Fix live-session detection in thread__e_machine()
perf annotate: add Alpha instruction support
tools/perf/util/annotate-arch/Build | 1 +
tools/perf/util/annotate-arch/annotate-alpha.c | 185 +++++++++++++++++++++++++
tools/perf/util/disasm.c | 15 +-
tools/perf/util/disasm.h | 1 +
tools/perf/util/machine.c | 1 +
tools/perf/util/thread.c | 2 +-
6 files changed, 202 insertions(+), 3 deletions(-)
---
base-commit: 9f0346dcbea363787186c94ef94dd01aaa215afa
change-id: 20260905-perf-alpha-annotate-b49a2b8b6d99
Best regards,
--
Matt Turner <mattst88@gmail.com>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/2] perf thread: Fix live-session detection in thread__e_machine()
2026-09-05 22:54 [PATCH 0/2] perf: Alpha annotation support Matt Turner
@ 2026-09-05 22:54 ` Matt Turner
2026-09-05 23:05 ` sashiko-bot
2026-09-08 17:34 ` Ian Rogers
2026-09-05 22:54 ` [PATCH 2/2] perf annotate: add Alpha instruction support Matt Turner
1 sibling, 2 replies; 8+ messages in thread
From: Matt Turner @ 2026-09-05 22:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, James Clark
Cc: linux-perf-users, linux-kernel, Matt Turner
When the per-thread e_machine cannot be determined from the DSOs in the
thread's maps, thread__e_machine_endian() decides between reading
/proc/<pid>/exe and falling back to the recorded session environment:
bool is_live = machine->machines == NULL;
if (!is_live) {
/* Check if the session has a data file. */
struct perf_session *session = container_of(...);
is_live = !!session->data;
}
Neither half of that works.
The back pointer added by commit a088031c4998 ("perf tools: Add machine to
machines back pointer") is set by machines__add(), which only ever adds
guests; the host machine never gets one. Host-machine threads, which is to
say almost all of them, therefore see machine->machines == NULL and are
declared live before the session is consulted at all.
The session test is also inverted. A session with a perf_data attached is
one being read from a perf.data file, i.e. exactly the case that is not
live, while a live session such as 'perf top' passes data=NULL to
__perf_session__new().
So a file-based session takes the live path and reads /proc/<pid>/exe on
the analysing host, which at best describes an unrelated process that has
since been given the recorded pid, and normally just fails, leaving
e_machine as EM_NONE. The perf_env fallback that would have supplied the
recorded architecture is never reached, and thread__e_machine() returns
EM_HOST.
For a same-architecture recording this is invisible, since EM_HOST is the
right answer anyway. Cross-architecture it is not: annotating an Alpha
perf.data on an x86_64 host selects the x86 struct arch, so the Alpha
disassembly is matched against the x86 instruction table. Alpha's 'ret'
collides with x86's and gets ret_ops, while its calls and branches match
nothing and are left unparsed, so no call target is resolved and no jump
arrows are drawn.
Set the back pointer for the host machine and correct the session test.
The new back pointer does not disturb the other reader of the field,
machine__findnew_guest_code(), which machine__resolve() only calls when
!machine__is_host(machine).
Fixes: 70351029b556 ("perf thread: Add support for reading the e_machine type for a thread")
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
tools/perf/util/machine.c | 1 +
tools/perf/util/thread.c | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index f86b3b7df742..a1288fbed833 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -237,6 +237,7 @@ int machines__init(struct machines *machines)
{
int err = machine__init(&machines->host, "", HOST_KERNEL_ID);
+ machines->host.machines = machines;
machines->guests = RB_ROOT_CACHED;
return err;
}
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index e483ffcb5d93..f0d3773d87db 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -536,7 +536,7 @@ uint16_t thread__e_machine_endian(struct thread *thread, struct machine *machine
struct perf_session,
machines);
- is_live = !!session->data;
+ is_live = !session->data;
}
/* Read from /proc/pid/exe if live. */
if (is_live) {
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/2] perf annotate: add Alpha instruction support
2026-09-05 22:54 [PATCH 0/2] perf: Alpha annotation support Matt Turner
2026-09-05 22:54 ` [PATCH 1/2] perf thread: Fix live-session detection in thread__e_machine() Matt Turner
@ 2026-09-05 22:54 ` Matt Turner
2026-09-05 22:59 ` sashiko-bot
2026-09-08 17:35 ` Ian Rogers
1 sibling, 2 replies; 8+ messages in thread
From: Matt Turner @ 2026-09-05 22:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, James Clark
Cc: linux-perf-users, linux-kernel, Matt Turner
Teach perf annotate about the Alpha control-transfer instructions, so that
an Alpha perf.data gets call and jump arrows and resolved call targets,
whether it is read on Alpha or on another host.
Add tools/perf/util/annotate-arch/annotate-alpha.c with arch__new_alpha()
and an associate_instruction_ops() that classifies:
call: bsr, plus jsr and jcr as indirect calls
ret: ret
jump: br, the conditional branches beq/bne/blt/ble/bgt/bge/blbc/blbs
and fbeq/fbne/fblt/fble/fbgt/fbge, plus jmp as an indirect jump
mov: mov, fmov (objdump pseudos)
That is every mnemonic binutils can print for the branch and JSR formats.
jcr rather than jsr_coroutine, because both name the same MBR(0x1a,3)
encoding and print_insn_alpha() takes the first match in the table, where
the jcr alias has come first since the sources were imported in 1999.
bsr needs an Alpha-specific parse routine. The generic call__parse()
expects the operand string to begin with the target address, but a bsr
prints its return-address register first:
bsr t0,fffffc0001031dc0 <cserve_ena>
strtoull() then stops on the leading register name, leaving the target
address as 0, which makes call__scnprintf() fall back to printing the raw
operands and leaves target.sym unresolved so the browser cannot follow the
call. alpha_call__parse() takes the address from after the comma instead,
as s390_call__parse() does for the same reason. The PC-relative branches
need no such handling, as jump__parse() already skips up to two operands.
jsr and jmp get ins_ops that resolve no target at all. They transfer
control to a register, and their trailing operand is only a branch
prediction hint:
jsr ra,(t12),fffffc0001014ee8 <_printk>
binutils extracts that hint as a 14-bit signed field scaled by four and
prints it relative to the next instruction (extract_jhint() in alpha-opc.c,
print_insn_alpha() in alpha-dis.c), so it can name the callee only when the
callee lies within the resulting +-32KB. It also defaults to zero, which
prints as the next instruction. Of the 213750 jsr in a vmlinux built from
this tree, only 23093 hints land on a symbol; 157204 point into the middle
of an unrelated function and 33453 are that default. Parsing the hint
would therefore invent a call target for the majority of calls, so these
keep their operands, as an indirect call does elsewhere.
EM_ALPHA is 0x9026, far too large to index the e_machine-keyed
arch_new_fn[] table in arch__find(), so select arch__new_alpha explicitly
before the table lookup. Declare it in disasm.h and add the object to the
annotate-arch Build.
Disassembly itself comes from objdump/binutils, which already supports
Alpha; this provides perf's instruction-class metadata for annotation.
Tested on an EV7 Marvel, both natively and by annotating its perf.data on
an x86_64 host, over bsr to a local function, jsr through the PLT and
kernel-mode jsr; the two hosts produce identical output.
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
tools/perf/util/annotate-arch/Build | 1 +
tools/perf/util/annotate-arch/annotate-alpha.c | 185 +++++++++++++++++++++++++
tools/perf/util/disasm.c | 15 +-
tools/perf/util/disasm.h | 1 +
4 files changed, 200 insertions(+), 2 deletions(-)
diff --git a/tools/perf/util/annotate-arch/Build b/tools/perf/util/annotate-arch/Build
index 23316743fdc5..0a74e1a9f17b 100644
--- a/tools/perf/util/annotate-arch/Build
+++ b/tools/perf/util/annotate-arch/Build
@@ -1,3 +1,4 @@
+perf-util-y += annotate-alpha.o
perf-util-y += annotate-arc.o
perf-util-y += annotate-arm.o
perf-util-y += annotate-arm64.o
diff --git a/tools/perf/util/annotate-arch/annotate-alpha.c b/tools/perf/util/annotate-arch/annotate-alpha.c
new file mode 100644
index 000000000000..ccb13e2338a2
--- /dev/null
+++ b/tools/perf/util/annotate-arch/annotate-alpha.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <stdlib.h>
+#include <string.h>
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include "../../util/disasm.h"
+#include "../../util/map.h"
+#include "../../util/maps.h"
+#include "../../util/symbol.h"
+#include "../../util/thread.h"
+
+/*
+ * Alpha control-transfer instructions, as printed by objdump:
+ *
+ * PC-relative (opcode group 0x30-0x3f), 21-bit displacement:
+ * br, bsr unconditional / to-subroutine
+ * beq bne blt ble bgt bge blbc blbs integer conditional
+ * fbeq fbne fblt fble fbgt fbge floating conditional
+ *
+ * Register-indirect (JSR group, opcode 0x1a):
+ * jmp, jsr, ret, jcr
+ *
+ * bsr/jsr (and jcr, the coroutine form, which binutils prints in preference to
+ * the jsr_coroutine spelling) save a return address, so they are calls; ret
+ * returns; everything else that transfers control is a jump.
+ *
+ * Alpha has no machine "mov"; objdump prints "mov"/"fmov" as pseudos for
+ * bis/cpys, so map them to mov_ops when present. The no-ops are deliberately
+ * left alone: nop_ops would let delete_last_nop() trim the padding gcc leaves
+ * at the end of a function, but its scnprintf() prints the literal "nop", and
+ * Alpha pads with unop (ldq_u $31) rather than nop.
+ */
+
+/*
+ * The generic call__parse() expects the target address to be the first thing
+ * in the operand string, but a bsr prints its return-address register first:
+ *
+ * bsr t0,fffffc0001031dc0 <cserve_ena>
+ *
+ * so take the address from after the comma. Without this the address comes
+ * out as 0, and neither the callee symbol nor the annotation browser's
+ * "go to target" work.
+ */
+static int alpha_call__parse(const struct arch *arch, struct ins_operands *ops,
+ struct map_symbol *ms,
+ struct disasm_line *dl __maybe_unused)
+{
+ char *endptr, *tok, *name;
+ struct map *map = ms->map;
+ struct addr_map_symbol target;
+
+ tok = strchr(ops->raw, ',');
+ if (tok == NULL)
+ return -1;
+
+ ops->target.addr = strtoull(tok + 1, &endptr, 16);
+ if (endptr == tok + 1)
+ return -1;
+
+ /* A stripped object has no "<symbol>" to name the target with. */
+ name = strchr(endptr, '<');
+ if (name == NULL)
+ goto find_target;
+
+ name++;
+
+ if (arch->objdump.skip_functions_char &&
+ strchr(name, arch->objdump.skip_functions_char))
+ return -1;
+
+ tok = strchr(name, '>');
+ if (tok == NULL)
+ return -1;
+
+ *tok = '\0';
+ ops->target.name = strdup(name);
+ *tok = '>';
+
+ if (ops->target.name == NULL)
+ return -1;
+
+find_target:
+ target = (struct addr_map_symbol) {
+ .ms = { .map = map__get(map), },
+ .addr = map__objdump_2mem(map, ops->target.addr),
+ };
+
+ if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
+ map__rip_2objdump(target.ms.map,
+ map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
+ ops->target.sym = target.ms.sym;
+
+ addr_map_symbol__exit(&target);
+ return 0;
+}
+
+static const struct ins_ops alpha_call_ops = {
+ .parse = alpha_call__parse,
+ .scnprintf = call__scnprintf,
+ .is_call = true,
+};
+
+/*
+ * jsr and jmp transfer control to a register, and their trailing operand is
+ * only a branch prediction hint:
+ *
+ * jsr ra,(t12),fffffc0001014ee8 <_printk>
+ *
+ * binutils extracts that hint as a 14-bit signed field scaled by four and
+ * prints it relative to the next instruction (extract_jhint() in alpha-opc.c,
+ * print_insn_alpha() in alpha-dis.c), so it can name the callee only when the
+ * callee lies within the resulting +-32KB. It also defaults to zero, which
+ * prints as the next instruction. Most hints are therefore not the callee at
+ * all, and parsing one would invent a call target, so these resolve no target
+ * and keep their operands, as an indirect call does elsewhere. The hint on
+ * jcr is not even an address.
+ */
+static const struct ins_ops alpha_indirect_call_ops = {
+ .scnprintf = ins__raw_scnprintf,
+ .is_call = true,
+};
+
+static const struct ins_ops alpha_indirect_jump_ops = {
+ .scnprintf = ins__raw_scnprintf,
+ .is_jump = true,
+};
+
+static int is_alpha_cond_branch(const char *name)
+{
+ static const char *const branches[] = {
+ "beq", "bne", "blt", "ble", "bgt", "bge", "blbc", "blbs",
+ "fbeq", "fbne", "fblt", "fble", "fbgt", "fbge",
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(branches); i++) {
+ if (!strcmp(name, branches[i]))
+ return 1;
+ }
+ return 0;
+}
+
+static const struct ins_ops *alpha__associate_instruction_ops(struct arch *arch, const char *name)
+{
+ const struct ins_ops *ops = NULL;
+
+ if (!strcmp(name, "bsr")) {
+ ops = &alpha_call_ops;
+ } else if (!strcmp(name, "jsr") ||
+ !strcmp(name, "jcr")) {
+ ops = &alpha_indirect_call_ops;
+ } else if (!strcmp(name, "ret")) {
+ ops = &ret_ops;
+ } else if (!strcmp(name, "jmp")) {
+ ops = &alpha_indirect_jump_ops;
+ } else if (!strcmp(name, "br") ||
+ is_alpha_cond_branch(name)) {
+ ops = &jump_ops;
+ } else if (!strcmp(name, "mov") ||
+ !strcmp(name, "fmov")) {
+ ops = &mov_ops;
+ }
+
+ if (ops)
+ arch__associate_ins_ops(arch, name, ops);
+
+ return ops;
+}
+
+const struct arch *arch__new_alpha(const struct e_machine_and_e_flags *id,
+ const char *cpuid __maybe_unused)
+{
+ struct arch *arch = zalloc(sizeof(*arch));
+
+ if (!arch)
+ return NULL;
+
+ arch->name = "alpha";
+ arch->id = *id;
+ arch->associate_instruction_ops = alpha__associate_instruction_ops;
+ /* objdump emits no comments for Alpha; '#' is what the assembler uses. */
+ arch->objdump.comment_char = '#';
+ return arch;
+}
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index 6cfdbabbb8c7..49c206a88eec 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -161,6 +161,8 @@ const struct arch *arch__find(uint16_t e_machine, uint32_t e_flags, const char *
.e_flags = e_flags,
};
const struct arch *result = NULL, **tmp;
+ const struct arch *(*new_fn)(const struct e_machine_and_e_flags *id,
+ const char *cpuid) = NULL;
if (num_archs > 0) {
tmp = bsearch(&key, archs, num_archs, sizeof(*archs), arch__key_cmp);
@@ -171,7 +173,16 @@ const struct arch *arch__find(uint16_t e_machine, uint32_t e_flags, const char *
if (result)
return result;
- if (e_machine >= ARRAY_SIZE(arch_new_fn) || arch_new_fn[e_machine] == NULL) {
+ /*
+ * EM_ALPHA (0x9026) is far too large to index arch_new_fn[], so it is
+ * selected explicitly; everything else uses the e_machine-indexed table.
+ */
+ if (e_machine == EM_ALPHA)
+ new_fn = arch__new_alpha;
+ else if (e_machine < ARRAY_SIZE(arch_new_fn))
+ new_fn = arch_new_fn[e_machine];
+
+ if (new_fn == NULL) {
errno = ENOTSUP;
return NULL;
}
@@ -182,7 +193,7 @@ const struct arch *arch__find(uint16_t e_machine, uint32_t e_flags, const char *
archs = tmp;
- result = arch_new_fn[e_machine](&key, cpuid);
+ result = new_fn(&key, cpuid);
if (!result) {
pr_err("%s: failed to initialize %u arch priv area\n",
__func__, e_machine);
diff --git a/tools/perf/util/disasm.h b/tools/perf/util/disasm.h
index 25756e3f47e4..adbdbf8f1f35 100644
--- a/tools/perf/util/disasm.h
+++ b/tools/perf/util/disasm.h
@@ -122,6 +122,7 @@ extern const struct ins_ops ret_ops;
int arch__associate_ins_ops(struct arch *arch, const char *name, const struct ins_ops *ops);
+const struct arch *arch__new_alpha(const struct e_machine_and_e_flags *id, const char *cpuid);
const struct arch *arch__new_arc(const struct e_machine_and_e_flags *id, const char *cpuid);
const struct arch *arch__new_arm(const struct e_machine_and_e_flags *id, const char *cpuid);
const struct arch *arch__new_arm64(const struct e_machine_and_e_flags *id, const char *cpuid);
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] perf annotate: add Alpha instruction support
2026-09-05 22:54 ` [PATCH 2/2] perf annotate: add Alpha instruction support Matt Turner
@ 2026-09-05 22:59 ` sashiko-bot
2026-09-08 17:35 ` Ian Rogers
1 sibling, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-09-05 22:59 UTC (permalink / raw)
To: Matt Turner; +Cc: linux-perf-users
> Teach perf annotate about the Alpha control-transfer instructions, so that
> an Alpha perf.data gets call and jump arrows and resolved call targets,
> whether it is read on Alpha or on another host.
>
> Add tools/perf/util/annotate-arch/annotate-alpha.c with arch__new_alpha()
> and an associate_instruction_ops() that classifies:
>
> call: bsr, plus jsr and jcr as indirect calls
> ret: ret
> jump: br, the conditional branches beq/bne/blt/ble/bgt/bge/blbc/blbs
> and fbeq/fbne/fblt/fble/fbgt/fbge, plus jmp as an indirect jump
> mov: mov, fmov (objdump pseudos)
>
> That is every mnemonic binutils can print for the branch and JSR formats.
> jcr rather than jsr_coroutine, because both name the same MBR(0x1a,3)
> [ ... ]
> Tested on an EV7 Marvel, both natively and by annotating its perf.data on
> an x86_64 host, over bsr to a local function, jsr through the PLT and
> kernel-mode jsr; the two hosts produce identical output.
>
> Signed-off-by: Matt Turner <mattst88@gmail.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260905-perf-alpha-annotate-v1-0-97380433ccb6@gmail.com?part=2
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] perf thread: Fix live-session detection in thread__e_machine()
2026-09-05 22:54 ` [PATCH 1/2] perf thread: Fix live-session detection in thread__e_machine() Matt Turner
@ 2026-09-05 23:05 ` sashiko-bot
2026-09-08 17:34 ` Ian Rogers
1 sibling, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-09-05 23:05 UTC (permalink / raw)
To: Matt Turner; +Cc: linux-perf-users
> When the per-thread e_machine cannot be determined from the DSOs in the
> thread's maps, thread__e_machine_endian() decides between reading
> /proc/<pid>/exe and falling back to the recorded session environment:
>
> bool is_live = machine->machines == NULL;
>
> if (!is_live) {
> /* Check if the session has a data file. */
> struct perf_session *session = container_of(...);
>
> is_live = !!session->data;
> }
>
> Neither half of that works.
>
> [ ... ]
> machine__findnew_guest_code(), which machine__resolve() only calls when
> !machine__is_host(machine).
>
> Fixes: 70351029b556 ("perf thread: Add support for reading the e_machine type for a thread")
> Signed-off-by: Matt Turner <mattst88@gmail.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260905-perf-alpha-annotate-v1-0-97380433ccb6@gmail.com?part=1
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] perf thread: Fix live-session detection in thread__e_machine()
2026-09-05 22:54 ` [PATCH 1/2] perf thread: Fix live-session detection in thread__e_machine() Matt Turner
2026-09-05 23:05 ` sashiko-bot
@ 2026-09-08 17:34 ` Ian Rogers
1 sibling, 0 replies; 8+ messages in thread
From: Ian Rogers @ 2026-09-08 17:34 UTC (permalink / raw)
To: Matt Turner
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Adrian Hunter, James Clark, linux-perf-users, linux-kernel
On Sat, Sep 5, 2026 at 3:54 PM Matt Turner <mattst88@gmail.com> wrote:
>
> When the per-thread e_machine cannot be determined from the DSOs in the
> thread's maps, thread__e_machine_endian() decides between reading
> /proc/<pid>/exe and falling back to the recorded session environment:
>
> bool is_live = machine->machines == NULL;
>
> if (!is_live) {
> /* Check if the session has a data file. */
> struct perf_session *session = container_of(...);
>
> is_live = !!session->data;
> }
>
> Neither half of that works.
>
> The back pointer added by commit a088031c4998 ("perf tools: Add machine to
> machines back pointer") is set by machines__add(), which only ever adds
> guests; the host machine never gets one. Host-machine threads, which is to
> say almost all of them, therefore see machine->machines == NULL and are
> declared live before the session is consulted at all.
>
> The session test is also inverted. A session with a perf_data attached is
> one being read from a perf.data file, i.e. exactly the case that is not
> live, while a live session such as 'perf top' passes data=NULL to
> __perf_session__new().
>
> So a file-based session takes the live path and reads /proc/<pid>/exe on
> the analysing host, which at best describes an unrelated process that has
> since been given the recorded pid, and normally just fails, leaving
> e_machine as EM_NONE. The perf_env fallback that would have supplied the
> recorded architecture is never reached, and thread__e_machine() returns
> EM_HOST.
>
> For a same-architecture recording this is invisible, since EM_HOST is the
> right answer anyway. Cross-architecture it is not: annotating an Alpha
> perf.data on an x86_64 host selects the x86 struct arch, so the Alpha
> disassembly is matched against the x86 instruction table. Alpha's 'ret'
> collides with x86's and gets ret_ops, while its calls and branches match
> nothing and are left unparsed, so no call target is resolved and no jump
> arrows are drawn.
>
> Set the back pointer for the host machine and correct the session test.
> The new back pointer does not disturb the other reader of the field,
> machine__findnew_guest_code(), which machine__resolve() only calls when
> !machine__is_host(machine).
>
> Fixes: 70351029b556 ("perf thread: Add support for reading the e_machine type for a thread")
> Signed-off-by: Matt Turner <mattst88@gmail.com>
Reviewed-by: Ian Rogers <irogers@google.com>
Thanks!
Ian
> ---
> tools/perf/util/machine.c | 1 +
> tools/perf/util/thread.c | 2 +-
> 2 files changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
> index f86b3b7df742..a1288fbed833 100644
> --- a/tools/perf/util/machine.c
> +++ b/tools/perf/util/machine.c
> @@ -237,6 +237,7 @@ int machines__init(struct machines *machines)
> {
> int err = machine__init(&machines->host, "", HOST_KERNEL_ID);
>
> + machines->host.machines = machines;
> machines->guests = RB_ROOT_CACHED;
> return err;
> }
> diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
> index e483ffcb5d93..f0d3773d87db 100644
> --- a/tools/perf/util/thread.c
> +++ b/tools/perf/util/thread.c
> @@ -536,7 +536,7 @@ uint16_t thread__e_machine_endian(struct thread *thread, struct machine *machine
> struct perf_session,
> machines);
>
> - is_live = !!session->data;
> + is_live = !session->data;
> }
> /* Read from /proc/pid/exe if live. */
> if (is_live) {
>
> --
> 2.54.0
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] perf annotate: add Alpha instruction support
2026-09-05 22:54 ` [PATCH 2/2] perf annotate: add Alpha instruction support Matt Turner
2026-09-05 22:59 ` sashiko-bot
@ 2026-09-08 17:35 ` Ian Rogers
2026-09-08 21:11 ` Arnaldo Carvalho de Melo
1 sibling, 1 reply; 8+ messages in thread
From: Ian Rogers @ 2026-09-08 17:35 UTC (permalink / raw)
To: Matt Turner
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Adrian Hunter, James Clark, linux-perf-users, linux-kernel
On Sat, Sep 5, 2026 at 3:54 PM Matt Turner <mattst88@gmail.com> wrote:
>
> Teach perf annotate about the Alpha control-transfer instructions, so that
> an Alpha perf.data gets call and jump arrows and resolved call targets,
> whether it is read on Alpha or on another host.
>
> Add tools/perf/util/annotate-arch/annotate-alpha.c with arch__new_alpha()
> and an associate_instruction_ops() that classifies:
>
> call: bsr, plus jsr and jcr as indirect calls
> ret: ret
> jump: br, the conditional branches beq/bne/blt/ble/bgt/bge/blbc/blbs
> and fbeq/fbne/fblt/fble/fbgt/fbge, plus jmp as an indirect jump
> mov: mov, fmov (objdump pseudos)
>
> That is every mnemonic binutils can print for the branch and JSR formats.
> jcr rather than jsr_coroutine, because both name the same MBR(0x1a,3)
> encoding and print_insn_alpha() takes the first match in the table, where
> the jcr alias has come first since the sources were imported in 1999.
>
> bsr needs an Alpha-specific parse routine. The generic call__parse()
> expects the operand string to begin with the target address, but a bsr
> prints its return-address register first:
>
> bsr t0,fffffc0001031dc0 <cserve_ena>
>
> strtoull() then stops on the leading register name, leaving the target
> address as 0, which makes call__scnprintf() fall back to printing the raw
> operands and leaves target.sym unresolved so the browser cannot follow the
> call. alpha_call__parse() takes the address from after the comma instead,
> as s390_call__parse() does for the same reason. The PC-relative branches
> need no such handling, as jump__parse() already skips up to two operands.
>
> jsr and jmp get ins_ops that resolve no target at all. They transfer
> control to a register, and their trailing operand is only a branch
> prediction hint:
>
> jsr ra,(t12),fffffc0001014ee8 <_printk>
>
> binutils extracts that hint as a 14-bit signed field scaled by four and
> prints it relative to the next instruction (extract_jhint() in alpha-opc.c,
> print_insn_alpha() in alpha-dis.c), so it can name the callee only when the
> callee lies within the resulting +-32KB. It also defaults to zero, which
> prints as the next instruction. Of the 213750 jsr in a vmlinux built from
> this tree, only 23093 hints land on a symbol; 157204 point into the middle
> of an unrelated function and 33453 are that default. Parsing the hint
> would therefore invent a call target for the majority of calls, so these
> keep their operands, as an indirect call does elsewhere.
>
> EM_ALPHA is 0x9026, far too large to index the e_machine-keyed
> arch_new_fn[] table in arch__find(), so select arch__new_alpha explicitly
> before the table lookup. Declare it in disasm.h and add the object to the
> annotate-arch Build.
>
> Disassembly itself comes from objdump/binutils, which already supports
> Alpha; this provides perf's instruction-class metadata for annotation.
>
> Tested on an EV7 Marvel, both natively and by annotating its perf.data on
> an x86_64 host, over bsr to a local function, jsr through the PLT and
> kernel-mode jsr; the two hosts produce identical output.
>
> Signed-off-by: Matt Turner <mattst88@gmail.com>
Reviewed-by: Ian Rogers <irogers@google.com>
Thanks!
Ian
> ---
> tools/perf/util/annotate-arch/Build | 1 +
> tools/perf/util/annotate-arch/annotate-alpha.c | 185 +++++++++++++++++++++++++
> tools/perf/util/disasm.c | 15 +-
> tools/perf/util/disasm.h | 1 +
> 4 files changed, 200 insertions(+), 2 deletions(-)
>
> diff --git a/tools/perf/util/annotate-arch/Build b/tools/perf/util/annotate-arch/Build
> index 23316743fdc5..0a74e1a9f17b 100644
> --- a/tools/perf/util/annotate-arch/Build
> +++ b/tools/perf/util/annotate-arch/Build
> @@ -1,3 +1,4 @@
> +perf-util-y += annotate-alpha.o
> perf-util-y += annotate-arc.o
> perf-util-y += annotate-arm.o
> perf-util-y += annotate-arm64.o
> diff --git a/tools/perf/util/annotate-arch/annotate-alpha.c b/tools/perf/util/annotate-arch/annotate-alpha.c
> new file mode 100644
> index 000000000000..ccb13e2338a2
> --- /dev/null
> +++ b/tools/perf/util/annotate-arch/annotate-alpha.c
> @@ -0,0 +1,185 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <stdlib.h>
> +#include <string.h>
> +#include <linux/compiler.h>
> +#include <linux/kernel.h>
> +#include <linux/zalloc.h>
> +#include "../../util/disasm.h"
> +#include "../../util/map.h"
> +#include "../../util/maps.h"
> +#include "../../util/symbol.h"
> +#include "../../util/thread.h"
> +
> +/*
> + * Alpha control-transfer instructions, as printed by objdump:
> + *
> + * PC-relative (opcode group 0x30-0x3f), 21-bit displacement:
> + * br, bsr unconditional / to-subroutine
> + * beq bne blt ble bgt bge blbc blbs integer conditional
> + * fbeq fbne fblt fble fbgt fbge floating conditional
> + *
> + * Register-indirect (JSR group, opcode 0x1a):
> + * jmp, jsr, ret, jcr
> + *
> + * bsr/jsr (and jcr, the coroutine form, which binutils prints in preference to
> + * the jsr_coroutine spelling) save a return address, so they are calls; ret
> + * returns; everything else that transfers control is a jump.
> + *
> + * Alpha has no machine "mov"; objdump prints "mov"/"fmov" as pseudos for
> + * bis/cpys, so map them to mov_ops when present. The no-ops are deliberately
> + * left alone: nop_ops would let delete_last_nop() trim the padding gcc leaves
> + * at the end of a function, but its scnprintf() prints the literal "nop", and
> + * Alpha pads with unop (ldq_u $31) rather than nop.
> + */
> +
> +/*
> + * The generic call__parse() expects the target address to be the first thing
> + * in the operand string, but a bsr prints its return-address register first:
> + *
> + * bsr t0,fffffc0001031dc0 <cserve_ena>
> + *
> + * so take the address from after the comma. Without this the address comes
> + * out as 0, and neither the callee symbol nor the annotation browser's
> + * "go to target" work.
> + */
> +static int alpha_call__parse(const struct arch *arch, struct ins_operands *ops,
> + struct map_symbol *ms,
> + struct disasm_line *dl __maybe_unused)
> +{
> + char *endptr, *tok, *name;
> + struct map *map = ms->map;
> + struct addr_map_symbol target;
> +
> + tok = strchr(ops->raw, ',');
> + if (tok == NULL)
> + return -1;
> +
> + ops->target.addr = strtoull(tok + 1, &endptr, 16);
> + if (endptr == tok + 1)
> + return -1;
> +
> + /* A stripped object has no "<symbol>" to name the target with. */
> + name = strchr(endptr, '<');
> + if (name == NULL)
> + goto find_target;
> +
> + name++;
> +
> + if (arch->objdump.skip_functions_char &&
> + strchr(name, arch->objdump.skip_functions_char))
> + return -1;
> +
> + tok = strchr(name, '>');
> + if (tok == NULL)
> + return -1;
> +
> + *tok = '\0';
> + ops->target.name = strdup(name);
> + *tok = '>';
> +
> + if (ops->target.name == NULL)
> + return -1;
> +
> +find_target:
> + target = (struct addr_map_symbol) {
> + .ms = { .map = map__get(map), },
> + .addr = map__objdump_2mem(map, ops->target.addr),
> + };
> +
> + if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
> + map__rip_2objdump(target.ms.map,
> + map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
> + ops->target.sym = target.ms.sym;
> +
> + addr_map_symbol__exit(&target);
> + return 0;
> +}
> +
> +static const struct ins_ops alpha_call_ops = {
> + .parse = alpha_call__parse,
> + .scnprintf = call__scnprintf,
> + .is_call = true,
> +};
> +
> +/*
> + * jsr and jmp transfer control to a register, and their trailing operand is
> + * only a branch prediction hint:
> + *
> + * jsr ra,(t12),fffffc0001014ee8 <_printk>
> + *
> + * binutils extracts that hint as a 14-bit signed field scaled by four and
> + * prints it relative to the next instruction (extract_jhint() in alpha-opc.c,
> + * print_insn_alpha() in alpha-dis.c), so it can name the callee only when the
> + * callee lies within the resulting +-32KB. It also defaults to zero, which
> + * prints as the next instruction. Most hints are therefore not the callee at
> + * all, and parsing one would invent a call target, so these resolve no target
> + * and keep their operands, as an indirect call does elsewhere. The hint on
> + * jcr is not even an address.
> + */
> +static const struct ins_ops alpha_indirect_call_ops = {
> + .scnprintf = ins__raw_scnprintf,
> + .is_call = true,
> +};
> +
> +static const struct ins_ops alpha_indirect_jump_ops = {
> + .scnprintf = ins__raw_scnprintf,
> + .is_jump = true,
> +};
> +
> +static int is_alpha_cond_branch(const char *name)
> +{
> + static const char *const branches[] = {
> + "beq", "bne", "blt", "ble", "bgt", "bge", "blbc", "blbs",
> + "fbeq", "fbne", "fblt", "fble", "fbgt", "fbge",
> + };
> + unsigned int i;
> +
> + for (i = 0; i < ARRAY_SIZE(branches); i++) {
> + if (!strcmp(name, branches[i]))
> + return 1;
> + }
> + return 0;
> +}
> +
> +static const struct ins_ops *alpha__associate_instruction_ops(struct arch *arch, const char *name)
> +{
> + const struct ins_ops *ops = NULL;
> +
> + if (!strcmp(name, "bsr")) {
> + ops = &alpha_call_ops;
> + } else if (!strcmp(name, "jsr") ||
> + !strcmp(name, "jcr")) {
> + ops = &alpha_indirect_call_ops;
> + } else if (!strcmp(name, "ret")) {
> + ops = &ret_ops;
> + } else if (!strcmp(name, "jmp")) {
> + ops = &alpha_indirect_jump_ops;
> + } else if (!strcmp(name, "br") ||
> + is_alpha_cond_branch(name)) {
> + ops = &jump_ops;
> + } else if (!strcmp(name, "mov") ||
> + !strcmp(name, "fmov")) {
> + ops = &mov_ops;
> + }
> +
> + if (ops)
> + arch__associate_ins_ops(arch, name, ops);
> +
> + return ops;
> +}
> +
> +const struct arch *arch__new_alpha(const struct e_machine_and_e_flags *id,
> + const char *cpuid __maybe_unused)
> +{
> + struct arch *arch = zalloc(sizeof(*arch));
> +
> + if (!arch)
> + return NULL;
> +
> + arch->name = "alpha";
> + arch->id = *id;
> + arch->associate_instruction_ops = alpha__associate_instruction_ops;
> + /* objdump emits no comments for Alpha; '#' is what the assembler uses. */
> + arch->objdump.comment_char = '#';
> + return arch;
> +}
> diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
> index 6cfdbabbb8c7..49c206a88eec 100644
> --- a/tools/perf/util/disasm.c
> +++ b/tools/perf/util/disasm.c
> @@ -161,6 +161,8 @@ const struct arch *arch__find(uint16_t e_machine, uint32_t e_flags, const char *
> .e_flags = e_flags,
> };
> const struct arch *result = NULL, **tmp;
> + const struct arch *(*new_fn)(const struct e_machine_and_e_flags *id,
> + const char *cpuid) = NULL;
>
> if (num_archs > 0) {
> tmp = bsearch(&key, archs, num_archs, sizeof(*archs), arch__key_cmp);
> @@ -171,7 +173,16 @@ const struct arch *arch__find(uint16_t e_machine, uint32_t e_flags, const char *
> if (result)
> return result;
>
> - if (e_machine >= ARRAY_SIZE(arch_new_fn) || arch_new_fn[e_machine] == NULL) {
> + /*
> + * EM_ALPHA (0x9026) is far too large to index arch_new_fn[], so it is
> + * selected explicitly; everything else uses the e_machine-indexed table.
> + */
> + if (e_machine == EM_ALPHA)
> + new_fn = arch__new_alpha;
> + else if (e_machine < ARRAY_SIZE(arch_new_fn))
> + new_fn = arch_new_fn[e_machine];
> +
> + if (new_fn == NULL) {
> errno = ENOTSUP;
> return NULL;
> }
> @@ -182,7 +193,7 @@ const struct arch *arch__find(uint16_t e_machine, uint32_t e_flags, const char *
>
> archs = tmp;
>
> - result = arch_new_fn[e_machine](&key, cpuid);
> + result = new_fn(&key, cpuid);
> if (!result) {
> pr_err("%s: failed to initialize %u arch priv area\n",
> __func__, e_machine);
> diff --git a/tools/perf/util/disasm.h b/tools/perf/util/disasm.h
> index 25756e3f47e4..adbdbf8f1f35 100644
> --- a/tools/perf/util/disasm.h
> +++ b/tools/perf/util/disasm.h
> @@ -122,6 +122,7 @@ extern const struct ins_ops ret_ops;
>
> int arch__associate_ins_ops(struct arch *arch, const char *name, const struct ins_ops *ops);
>
> +const struct arch *arch__new_alpha(const struct e_machine_and_e_flags *id, const char *cpuid);
> const struct arch *arch__new_arc(const struct e_machine_and_e_flags *id, const char *cpuid);
> const struct arch *arch__new_arm(const struct e_machine_and_e_flags *id, const char *cpuid);
> const struct arch *arch__new_arm64(const struct e_machine_and_e_flags *id, const char *cpuid);
>
> --
> 2.54.0
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] perf annotate: add Alpha instruction support
2026-09-08 17:35 ` Ian Rogers
@ 2026-09-08 21:11 ` Arnaldo Carvalho de Melo
0 siblings, 0 replies; 8+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-09-08 21:11 UTC (permalink / raw)
To: Ian Rogers
Cc: Matt Turner, Peter Zijlstra, Ingo Molnar, Namhyung Kim,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Adrian Hunter,
James Clark, linux-perf-users, linux-kernel
On Tue, Sep 08, 2026 at 10:35:57AM -0700, Ian Rogers wrote:
> On Sat, Sep 5, 2026 at 3:54 PM Matt Turner <mattst88@gmail.com> wrote:
> >
> > Teach perf annotate about the Alpha control-transfer instructions, so that
> > an Alpha perf.data gets call and jump arrows and resolved call targets,
> > whether it is read on Alpha or on another host.
> >
> > Add tools/perf/util/annotate-arch/annotate-alpha.c with arch__new_alpha()
> > and an associate_instruction_ops() that classifies:
> >
> > call: bsr, plus jsr and jcr as indirect calls
> > ret: ret
> > jump: br, the conditional branches beq/bne/blt/ble/bgt/bge/blbc/blbs
> > and fbeq/fbne/fblt/fble/fbgt/fbge, plus jmp as an indirect jump
> > mov: mov, fmov (objdump pseudos)
> >
> > That is every mnemonic binutils can print for the branch and JSR formats.
> > jcr rather than jsr_coroutine, because both name the same MBR(0x1a,3)
> > encoding and print_insn_alpha() takes the first match in the table, where
> > the jcr alias has come first since the sources were imported in 1999.
> >
> > bsr needs an Alpha-specific parse routine. The generic call__parse()
> > expects the operand string to begin with the target address, but a bsr
> > prints its return-address register first:
> >
> > bsr t0,fffffc0001031dc0 <cserve_ena>
> >
> > strtoull() then stops on the leading register name, leaving the target
> > address as 0, which makes call__scnprintf() fall back to printing the raw
> > operands and leaves target.sym unresolved so the browser cannot follow the
> > call. alpha_call__parse() takes the address from after the comma instead,
> > as s390_call__parse() does for the same reason. The PC-relative branches
> > need no such handling, as jump__parse() already skips up to two operands.
> >
> > jsr and jmp get ins_ops that resolve no target at all. They transfer
> > control to a register, and their trailing operand is only a branch
> > prediction hint:
> >
> > jsr ra,(t12),fffffc0001014ee8 <_printk>
> >
> > binutils extracts that hint as a 14-bit signed field scaled by four and
> > prints it relative to the next instruction (extract_jhint() in alpha-opc.c,
> > print_insn_alpha() in alpha-dis.c), so it can name the callee only when the
> > callee lies within the resulting +-32KB. It also defaults to zero, which
> > prints as the next instruction. Of the 213750 jsr in a vmlinux built from
> > this tree, only 23093 hints land on a symbol; 157204 point into the middle
> > of an unrelated function and 33453 are that default. Parsing the hint
> > would therefore invent a call target for the majority of calls, so these
> > keep their operands, as an indirect call does elsewhere.
> >
> > EM_ALPHA is 0x9026, far too large to index the e_machine-keyed
> > arch_new_fn[] table in arch__find(), so select arch__new_alpha explicitly
> > before the table lookup. Declare it in disasm.h and add the object to the
> > annotate-arch Build.
> >
> > Disassembly itself comes from objdump/binutils, which already supports
> > Alpha; this provides perf's instruction-class metadata for annotation.
> >
> > Tested on an EV7 Marvel, both natively and by annotating its perf.data on
> > an x86_64 host, over bsr to a local function, jsr through the PLT and
> > kernel-mode jsr; the two hosts produce identical output.
> >
> > Signed-off-by: Matt Turner <mattst88@gmail.com>
>
> Reviewed-by: Ian Rogers <irogers@google.com>
WOW! EV7 Marvel! :-)
Thanks, applied to perf-tools-next, for v7.4.
- Arnaldo
> Thanks!
> Ian
>
> > ---
> > tools/perf/util/annotate-arch/Build | 1 +
> > tools/perf/util/annotate-arch/annotate-alpha.c | 185 +++++++++++++++++++++++++
> > tools/perf/util/disasm.c | 15 +-
> > tools/perf/util/disasm.h | 1 +
> > 4 files changed, 200 insertions(+), 2 deletions(-)
> >
> > diff --git a/tools/perf/util/annotate-arch/Build b/tools/perf/util/annotate-arch/Build
> > index 23316743fdc5..0a74e1a9f17b 100644
> > --- a/tools/perf/util/annotate-arch/Build
> > +++ b/tools/perf/util/annotate-arch/Build
> > @@ -1,3 +1,4 @@
> > +perf-util-y += annotate-alpha.o
> > perf-util-y += annotate-arc.o
> > perf-util-y += annotate-arm.o
> > perf-util-y += annotate-arm64.o
> > diff --git a/tools/perf/util/annotate-arch/annotate-alpha.c b/tools/perf/util/annotate-arch/annotate-alpha.c
> > new file mode 100644
> > index 000000000000..ccb13e2338a2
> > --- /dev/null
> > +++ b/tools/perf/util/annotate-arch/annotate-alpha.c
> > @@ -0,0 +1,185 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include <linux/compiler.h>
> > +#include <linux/kernel.h>
> > +#include <linux/zalloc.h>
> > +#include "../../util/disasm.h"
> > +#include "../../util/map.h"
> > +#include "../../util/maps.h"
> > +#include "../../util/symbol.h"
> > +#include "../../util/thread.h"
> > +
> > +/*
> > + * Alpha control-transfer instructions, as printed by objdump:
> > + *
> > + * PC-relative (opcode group 0x30-0x3f), 21-bit displacement:
> > + * br, bsr unconditional / to-subroutine
> > + * beq bne blt ble bgt bge blbc blbs integer conditional
> > + * fbeq fbne fblt fble fbgt fbge floating conditional
> > + *
> > + * Register-indirect (JSR group, opcode 0x1a):
> > + * jmp, jsr, ret, jcr
> > + *
> > + * bsr/jsr (and jcr, the coroutine form, which binutils prints in preference to
> > + * the jsr_coroutine spelling) save a return address, so they are calls; ret
> > + * returns; everything else that transfers control is a jump.
> > + *
> > + * Alpha has no machine "mov"; objdump prints "mov"/"fmov" as pseudos for
> > + * bis/cpys, so map them to mov_ops when present. The no-ops are deliberately
> > + * left alone: nop_ops would let delete_last_nop() trim the padding gcc leaves
> > + * at the end of a function, but its scnprintf() prints the literal "nop", and
> > + * Alpha pads with unop (ldq_u $31) rather than nop.
> > + */
> > +
> > +/*
> > + * The generic call__parse() expects the target address to be the first thing
> > + * in the operand string, but a bsr prints its return-address register first:
> > + *
> > + * bsr t0,fffffc0001031dc0 <cserve_ena>
> > + *
> > + * so take the address from after the comma. Without this the address comes
> > + * out as 0, and neither the callee symbol nor the annotation browser's
> > + * "go to target" work.
> > + */
> > +static int alpha_call__parse(const struct arch *arch, struct ins_operands *ops,
> > + struct map_symbol *ms,
> > + struct disasm_line *dl __maybe_unused)
> > +{
> > + char *endptr, *tok, *name;
> > + struct map *map = ms->map;
> > + struct addr_map_symbol target;
> > +
> > + tok = strchr(ops->raw, ',');
> > + if (tok == NULL)
> > + return -1;
> > +
> > + ops->target.addr = strtoull(tok + 1, &endptr, 16);
> > + if (endptr == tok + 1)
> > + return -1;
> > +
> > + /* A stripped object has no "<symbol>" to name the target with. */
> > + name = strchr(endptr, '<');
> > + if (name == NULL)
> > + goto find_target;
> > +
> > + name++;
> > +
> > + if (arch->objdump.skip_functions_char &&
> > + strchr(name, arch->objdump.skip_functions_char))
> > + return -1;
> > +
> > + tok = strchr(name, '>');
> > + if (tok == NULL)
> > + return -1;
> > +
> > + *tok = '\0';
> > + ops->target.name = strdup(name);
> > + *tok = '>';
> > +
> > + if (ops->target.name == NULL)
> > + return -1;
> > +
> > +find_target:
> > + target = (struct addr_map_symbol) {
> > + .ms = { .map = map__get(map), },
> > + .addr = map__objdump_2mem(map, ops->target.addr),
> > + };
> > +
> > + if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
> > + map__rip_2objdump(target.ms.map,
> > + map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
> > + ops->target.sym = target.ms.sym;
> > +
> > + addr_map_symbol__exit(&target);
> > + return 0;
> > +}
> > +
> > +static const struct ins_ops alpha_call_ops = {
> > + .parse = alpha_call__parse,
> > + .scnprintf = call__scnprintf,
> > + .is_call = true,
> > +};
> > +
> > +/*
> > + * jsr and jmp transfer control to a register, and their trailing operand is
> > + * only a branch prediction hint:
> > + *
> > + * jsr ra,(t12),fffffc0001014ee8 <_printk>
> > + *
> > + * binutils extracts that hint as a 14-bit signed field scaled by four and
> > + * prints it relative to the next instruction (extract_jhint() in alpha-opc.c,
> > + * print_insn_alpha() in alpha-dis.c), so it can name the callee only when the
> > + * callee lies within the resulting +-32KB. It also defaults to zero, which
> > + * prints as the next instruction. Most hints are therefore not the callee at
> > + * all, and parsing one would invent a call target, so these resolve no target
> > + * and keep their operands, as an indirect call does elsewhere. The hint on
> > + * jcr is not even an address.
> > + */
> > +static const struct ins_ops alpha_indirect_call_ops = {
> > + .scnprintf = ins__raw_scnprintf,
> > + .is_call = true,
> > +};
> > +
> > +static const struct ins_ops alpha_indirect_jump_ops = {
> > + .scnprintf = ins__raw_scnprintf,
> > + .is_jump = true,
> > +};
> > +
> > +static int is_alpha_cond_branch(const char *name)
> > +{
> > + static const char *const branches[] = {
> > + "beq", "bne", "blt", "ble", "bgt", "bge", "blbc", "blbs",
> > + "fbeq", "fbne", "fblt", "fble", "fbgt", "fbge",
> > + };
> > + unsigned int i;
> > +
> > + for (i = 0; i < ARRAY_SIZE(branches); i++) {
> > + if (!strcmp(name, branches[i]))
> > + return 1;
> > + }
> > + return 0;
> > +}
> > +
> > +static const struct ins_ops *alpha__associate_instruction_ops(struct arch *arch, const char *name)
> > +{
> > + const struct ins_ops *ops = NULL;
> > +
> > + if (!strcmp(name, "bsr")) {
> > + ops = &alpha_call_ops;
> > + } else if (!strcmp(name, "jsr") ||
> > + !strcmp(name, "jcr")) {
> > + ops = &alpha_indirect_call_ops;
> > + } else if (!strcmp(name, "ret")) {
> > + ops = &ret_ops;
> > + } else if (!strcmp(name, "jmp")) {
> > + ops = &alpha_indirect_jump_ops;
> > + } else if (!strcmp(name, "br") ||
> > + is_alpha_cond_branch(name)) {
> > + ops = &jump_ops;
> > + } else if (!strcmp(name, "mov") ||
> > + !strcmp(name, "fmov")) {
> > + ops = &mov_ops;
> > + }
> > +
> > + if (ops)
> > + arch__associate_ins_ops(arch, name, ops);
> > +
> > + return ops;
> > +}
> > +
> > +const struct arch *arch__new_alpha(const struct e_machine_and_e_flags *id,
> > + const char *cpuid __maybe_unused)
> > +{
> > + struct arch *arch = zalloc(sizeof(*arch));
> > +
> > + if (!arch)
> > + return NULL;
> > +
> > + arch->name = "alpha";
> > + arch->id = *id;
> > + arch->associate_instruction_ops = alpha__associate_instruction_ops;
> > + /* objdump emits no comments for Alpha; '#' is what the assembler uses. */
> > + arch->objdump.comment_char = '#';
> > + return arch;
> > +}
> > diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
> > index 6cfdbabbb8c7..49c206a88eec 100644
> > --- a/tools/perf/util/disasm.c
> > +++ b/tools/perf/util/disasm.c
> > @@ -161,6 +161,8 @@ const struct arch *arch__find(uint16_t e_machine, uint32_t e_flags, const char *
> > .e_flags = e_flags,
> > };
> > const struct arch *result = NULL, **tmp;
> > + const struct arch *(*new_fn)(const struct e_machine_and_e_flags *id,
> > + const char *cpuid) = NULL;
> >
> > if (num_archs > 0) {
> > tmp = bsearch(&key, archs, num_archs, sizeof(*archs), arch__key_cmp);
> > @@ -171,7 +173,16 @@ const struct arch *arch__find(uint16_t e_machine, uint32_t e_flags, const char *
> > if (result)
> > return result;
> >
> > - if (e_machine >= ARRAY_SIZE(arch_new_fn) || arch_new_fn[e_machine] == NULL) {
> > + /*
> > + * EM_ALPHA (0x9026) is far too large to index arch_new_fn[], so it is
> > + * selected explicitly; everything else uses the e_machine-indexed table.
> > + */
> > + if (e_machine == EM_ALPHA)
> > + new_fn = arch__new_alpha;
> > + else if (e_machine < ARRAY_SIZE(arch_new_fn))
> > + new_fn = arch_new_fn[e_machine];
> > +
> > + if (new_fn == NULL) {
> > errno = ENOTSUP;
> > return NULL;
> > }
> > @@ -182,7 +193,7 @@ const struct arch *arch__find(uint16_t e_machine, uint32_t e_flags, const char *
> >
> > archs = tmp;
> >
> > - result = arch_new_fn[e_machine](&key, cpuid);
> > + result = new_fn(&key, cpuid);
> > if (!result) {
> > pr_err("%s: failed to initialize %u arch priv area\n",
> > __func__, e_machine);
> > diff --git a/tools/perf/util/disasm.h b/tools/perf/util/disasm.h
> > index 25756e3f47e4..adbdbf8f1f35 100644
> > --- a/tools/perf/util/disasm.h
> > +++ b/tools/perf/util/disasm.h
> > @@ -122,6 +122,7 @@ extern const struct ins_ops ret_ops;
> >
> > int arch__associate_ins_ops(struct arch *arch, const char *name, const struct ins_ops *ops);
> >
> > +const struct arch *arch__new_alpha(const struct e_machine_and_e_flags *id, const char *cpuid);
> > const struct arch *arch__new_arc(const struct e_machine_and_e_flags *id, const char *cpuid);
> > const struct arch *arch__new_arm(const struct e_machine_and_e_flags *id, const char *cpuid);
> > const struct arch *arch__new_arm64(const struct e_machine_and_e_flags *id, const char *cpuid);
> >
> > --
> > 2.54.0
> >
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-09-08 21:11 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-05 22:54 [PATCH 0/2] perf: Alpha annotation support Matt Turner
2026-09-05 22:54 ` [PATCH 1/2] perf thread: Fix live-session detection in thread__e_machine() Matt Turner
2026-09-05 23:05 ` sashiko-bot
2026-09-08 17:34 ` Ian Rogers
2026-09-05 22:54 ` [PATCH 2/2] perf annotate: add Alpha instruction support Matt Turner
2026-09-05 22:59 ` sashiko-bot
2026-09-08 17:35 ` Ian Rogers
2026-09-08 21:11 ` Arnaldo Carvalho de Melo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox