summaryrefslogtreecommitdiff
path: root/tools/perf/util
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/addr2line.c28
-rw-r--r--tools/perf/util/annotate-arch/annotate-arm64.c774
-rw-r--r--tools/perf/util/annotate-arch/annotate-x86.c38
-rw-r--r--tools/perf/util/annotate-data.c209
-rw-r--r--tools/perf/util/annotate-data.h14
-rw-r--r--tools/perf/util/auxtrace.c2
-rw-r--r--tools/perf/util/auxtrace.h1
-rw-r--r--tools/perf/util/cs-etm-decoder/cs-etm-decoder.c12
-rw-r--r--tools/perf/util/cs-etm-decoder/cs-etm-decoder.h2
-rw-r--r--tools/perf/util/cs-etm.c184
-rw-r--r--tools/perf/util/debug.h5
-rw-r--r--tools/perf/util/disasm.c124
-rw-r--r--tools/perf/util/dso.c145
-rw-r--r--tools/perf/util/dso.h15
-rw-r--r--tools/perf/util/evsel.c42
-rw-r--r--tools/perf/util/evsel.h1
-rw-r--r--tools/perf/util/hist.c6
-rw-r--r--tools/perf/util/libbfd.c8
-rw-r--r--tools/perf/util/libdw.c25
-rw-r--r--tools/perf/util/libdw.h5
-rw-r--r--tools/perf/util/llvm-c-helpers.cpp66
-rw-r--r--tools/perf/util/llvm.c12
-rw-r--r--tools/perf/util/machine.c2
-rw-r--r--tools/perf/util/mmap.c3
-rw-r--r--tools/perf/util/mutex.c101
-rw-r--r--tools/perf/util/mutex.h6
-rw-r--r--tools/perf/util/parse-events.c2
-rw-r--r--tools/perf/util/pfm.c1
-rw-r--r--tools/perf/util/print-events.c1
-rw-r--r--tools/perf/util/print_insn.h1
-rw-r--r--tools/perf/util/python.c2
-rw-r--r--tools/perf/util/srcline.c21
-rw-r--r--tools/perf/util/symbol-elf.c3
-rw-r--r--tools/perf/util/symbol.c58
34 files changed, 1556 insertions, 363 deletions
diff --git a/tools/perf/util/addr2line.c b/tools/perf/util/addr2line.c
index 6f64f6b42442..960dd48701bb 100644
--- a/tools/perf/util/addr2line.c
+++ b/tools/perf/util/addr2line.c
@@ -298,6 +298,7 @@ int cmd__addr2line(const char *dso_name, u64 addr,
struct io io = { .eof = false };
enum cmd_a2l_style cmd_a2l_style;
const char *current_dso_name;
+ bool a2l_synced = false;
mutex_lock(dso__lock(dso));
current_dso_name = dso__symsrc_filename(dso) ?: dso_name;
@@ -316,11 +317,14 @@ int cmd__addr2line(const char *dso_name, u64 addr,
if (a2l == NULL) {
if (!symbol_conf.addr2line_disable_warn)
pr_warning("%s %s: addr2line_subprocess_init failed\n", __func__, dso_name);
+ ret = -1;
goto out_unlock;
}
cmd_a2l_style = cmd_addr2line_configure(a2l, dso_name);
- if (cmd_a2l_style == BROKEN)
+ if (cmd_a2l_style == BROKEN) {
+ ret = -1;
goto out_unlock;
+ }
/*
* Take ownership of the a2l subprocess so we can safely perform
@@ -344,6 +348,7 @@ int cmd__addr2line(const char *dso_name, u64 addr,
if (written != len) {
if (!symbol_conf.addr2line_disable_warn)
pr_warning("%s %s: could not send request\n", __func__, dso_name);
+ ret = -1;
goto out;
}
io__init(&io, a2l->out, buf, sizeof(buf));
@@ -353,6 +358,7 @@ int cmd__addr2line(const char *dso_name, u64 addr,
case -1:
if (!symbol_conf.addr2line_disable_warn)
pr_warning("%s %s: could not read first record\n", __func__, dso_name);
+ ret = -1;
goto out;
case 0:
/*
@@ -373,6 +379,7 @@ int cmd__addr2line(const char *dso_name, u64 addr,
break;
case 0:
/* The sentinel as expected. */
+ a2l_synced = true;
break;
default:
if (!symbol_conf.addr2line_disable_warn)
@@ -388,17 +395,23 @@ int cmd__addr2line(const char *dso_name, u64 addr,
if (file) {
*file = strdup(record_filename);
- ret = 1;
+ if (!*file) {
+ ret = -ENOMEM;
+ goto out;
+ }
}
if (line_nr)
*line_nr = record_line_nr;
+ ret = 1;
if (unwind_inlines) {
if (node && inline_list__append_record(dso, node, sym,
record_function,
record_filename,
record_line_nr)) {
- ret = 0;
+ if (file && *file)
+ zfree(file);
+ ret = -ENOMEM;
goto out;
}
}
@@ -421,12 +434,17 @@ int cmd__addr2line(const char *dso_name, u64 addr,
record_function,
record_filename,
record_line_nr)) {
- ret = 0;
+ if (file && *file)
+ zfree(file);
+ inline_node__clear_frames(node);
+ ret = -ENOMEM;
goto out;
}
ret = 1; /* found at least one inline frame */
}
}
+ if (record_status == 0)
+ a2l_synced = true;
out:
mutex_lock(dso__lock(dso));
@@ -434,7 +452,7 @@ out:
free(record_filename);
current_dso_name = dso__symsrc_filename(dso) ?: dso__long_name(dso);
- if (!io.eof && dso__a2l(dso) == NULL && current_dso_name &&
+ if (a2l_synced && !io.eof && dso__a2l(dso) == NULL && current_dso_name &&
!strcmp(current_dso_name, dso_name))
dso__set_a2l(dso, a2l);
else
diff --git a/tools/perf/util/annotate-arch/annotate-arm64.c b/tools/perf/util/annotate-arch/annotate-arm64.c
index 6549535961a9..81369ae47290 100644
--- a/tools/perf/util/annotate-arch/annotate-arm64.c
+++ b/tools/perf/util/annotate-arch/annotate-arm64.c
@@ -1,14 +1,22 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
#include <errno.h>
+#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
+#include <strings.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/zalloc.h>
#include <regex.h>
#include "../annotate.h"
#include "../disasm.h"
+#include "../annotate-data.h"
+#include "../debug.h"
+#include "../dso.h"
+#include "../map.h"
+#include "../symbol.h"
+#include "../strbuf.h"
struct arch_arm64 {
struct arch arch;
@@ -448,6 +456,769 @@ check_modifiers:
}
}
+#ifdef HAVE_LIBDW_SUPPORT
+/* Check if the instruction is read-only (does not modify register state). */
+static bool is_readonly_insn(const char *name)
+{
+ return !strncmp(name, "cb", 2) || !strncmp(name, "tb", 2) ||
+ !strcmp(name, "tst") || !strcmp(name, "teq") ||
+ !strcmp(name, "cmp") || !strcmp(name, "cmn") ||
+ !strcmp(name, "ccmp") || !strcmp(name, "ccmn");
+}
+
+static void update_call_insn_state(struct type_state *state,
+ struct data_loc_info *dloc, Dwarf_Die *cu_die,
+ struct disasm_line *dl)
+{
+ struct symbol *func = dl->ops.target.sym;
+ const char *call_name;
+ u64 call_addr;
+ struct type_state_reg *tsr;
+ Dwarf_Die type_die;
+ u32 insn_offset = dl->al.offset;
+
+ call_name = func ? func->name : dl->ops.target.name;
+ pr_debug_dtp("call [%x] %s\n", insn_offset, call_name ?: "<unknown>");
+
+ /* Invalidate caller-saved registers after call */
+ call_addr = map__rip_2objdump(dloc->ms->map,
+ dloc->ms->sym->start + dl->al.offset);
+ for (unsigned int i = 0; i < ARRAY_SIZE(state->regs); i++) {
+ struct type_state_reg *reg = &state->regs[i];
+
+ if (!reg->caller_saved)
+ continue;
+ /* Keep register valid within DWARF location lifetime */
+ if (reg->lifetime_active && call_addr < reg->lifetime_end)
+ continue;
+ invalidate_reg_state(reg);
+ }
+
+ /* Update register with the return type (if any) */
+ if (call_name && die_find_func_rettype(cu_die, call_name, &type_die)) {
+ tsr = &state->regs[state->ret_reg];
+ invalidate_reg_state(tsr);
+ tsr->type = type_die;
+ tsr->kind = TSR_KIND_TYPE;
+ tsr->offset = 0;
+ tsr->ok = true;
+
+ pr_debug_dtp("call [%x] return -> reg%d", insn_offset, state->ret_reg);
+ pr_debug_type_name(&type_die, tsr->kind);
+ }
+}
+
+/* Invalidate registers explicitly written by an unhandled instruction. */
+static void invalidate_unhandled_insn_regs(struct type_state *state,
+ struct disasm_line *dl,
+ struct annotated_op_loc *src,
+ struct annotated_op_loc *dst)
+{
+ u32 insn_offset = dl->al.offset;
+
+ /* Invalidate destination registers */
+ if (has_reg_type(state, dst->reg1) && !dst->mem_ref) {
+ pr_debug_dtp("%s [%x] invalidate reg%d",
+ dl->ins.name, insn_offset, dst->reg1);
+ invalidate_reg_state(&state->regs[dst->reg1]);
+
+ if (dst->multi_regs && has_reg_type(state, dst->reg2)) {
+ pr_debug_dtp(" and reg%d", dst->reg2);
+ invalidate_reg_state(&state->regs[dst->reg2]);
+ }
+ pr_debug_dtp("\n");
+ }
+
+ /*
+ * Exclusive store instructions (stxr, stxp, stlxr, stlxp, etc.)
+ * write their execution status (0 or 1) into the status register
+ * (parsed into src->reg1). Invalidate this status register to prevent
+ * stale type propagation.
+ */
+ if (!strncmp(dl->ins.name, "stx", 3) || /* stxr, stxp, stxrb, stxrh */
+ !strncmp(dl->ins.name, "stlx", 4)) { /* stlxr, stlxp, stlxrb, stlxrh */
+ if (has_reg_type(state, src->reg1)) {
+ pr_debug_dtp("%s [%x] invalidate reg%d\n",
+ dl->ins.name, insn_offset, src->reg1);
+ invalidate_reg_state(&state->regs[src->reg1]);
+ }
+ }
+
+ /* TODO: Extend invalidation support to more special instructions. */
+}
+
+static int arm64__reg_size(const char *reg)
+{
+ if (!reg || !*reg || !arm64__is_reg(reg))
+ return -1;
+
+ if (reg[0] == 'w')
+ return 4;
+
+ if (reg[0] == 'x' || !strncmp(reg, "sp", 2))
+ return 8;
+
+ return -1;
+}
+
+/* Apply addressing mode (pre-index, post-index) to register state */
+static void adjust_reg_index_state(struct type_state *state,
+ struct data_loc_info *dloc,
+ struct disasm_line *dl,
+ struct annotated_op_loc *op_loc)
+{
+ struct type_state_reg *tsr;
+ int reg = op_loc->reg1;
+ int offset;
+
+ if (op_loc->addr_mode != PERF_AAM_PRE_INDEX &&
+ op_loc->addr_mode != PERF_AAM_POST_INDEX)
+ return;
+
+ if (!has_reg_type(state, reg) || !state->regs[reg].ok)
+ return;
+
+ tsr = &state->regs[reg];
+ tsr->copied_from = -1;
+
+ if (arch_get_reg_offset(dloc->arch, op_loc, reg, state, true, &offset)) {
+ invalidate_reg_state(tsr);
+ return;
+ }
+
+ tsr->offset += offset;
+
+ pr_debug_dtp("%s [%x] %s-index %#x(reg%d) -> reg%d", dl->ins.name,
+ (u32) dl->al.offset, op_loc->addr_mode == PERF_AAM_PRE_INDEX ?
+ "pre" : "post", offset, reg, reg);
+ pr_debug_type_name(&tsr->type, tsr->kind);
+}
+
+/*
+ * Match standard load variants (ldr, ldur, ldar, ldp) that follow
+ * straightforward load semantics: memory source on the right
+ * and target registers on the left (written). Excludes exclusive loads
+ * (ldxr, ldxp, etc.) and acquire/exclusive combination variants.
+ */
+static bool is_standard_load_insn(const char *name)
+{
+ return !strncmp(name, "ldr", 3) || /* ldr, ldrb, ldrh, ldrsb, ldrsh, ldrsw */
+ !strncmp(name, "ldur", 4) || /* ldur, ldurb, ldurh, ldursb, ldursh, ldursw */
+ !strncmp(name, "ldar", 4) || /* ldar, ldarb, ldarh */
+ !strncmp(name, "ldp", 3); /* ldp, ldpsw */
+}
+
+/*
+ * For load insns: propagate type from source reg state @src_states to @dreg.
+ *
+ * @mem_spacing accounts for the additional in-memory spacing between the two
+ * loads when handling multi-reg loads (e.g. the second target reg in 'ldp'),
+ * which is added to the reg offset.
+ */
+static int propagate_load_reg_state(struct type_state *state,
+ struct data_loc_info *dloc, Dwarf_Die *cu_die,
+ struct disasm_line *dl, int dreg,
+ struct annotated_op_loc *src,
+ struct type_state_reg **src_states,
+ int mem_spacing)
+{
+ struct type_state_reg *tsr;
+ struct type_state_reg *src_tsr = src_states[0];
+ Dwarf_Die type_die;
+ u32 insn_offset = dl->al.offset;
+ int sreg = src->reg1;
+ int reg_offset;
+ int fbreg = dloc->fbreg;
+ int fboff = 0;
+
+ if (!has_reg_type(state, dreg))
+ return -1;
+
+ tsr = &state->regs[dreg];
+ tsr->copied_from = -1;
+ tsr->lifetime_active = false;
+ tsr->lifetime_end = 0;
+
+ if (dloc->fb_cfa) {
+ u64 ip = dloc->ms->sym->start + dl->al.offset;
+ u64 pc = map__rip_2objdump(dloc->ms->map, ip);
+
+ if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0)
+ fbreg = -1;
+ }
+
+retry:
+ if (arch_get_reg_offset(dloc->arch, src, sreg, state, false, &reg_offset))
+ goto load_non_regoff;
+
+ reg_offset += mem_spacing;
+
+ /* Check stack variables with offset */
+ if (sreg != -1 && (sreg == fbreg || sreg == state->stack_reg)) {
+ struct type_state_stack *stack;
+ int offset = sreg == fbreg ? reg_offset - fboff : reg_offset;
+
+ stack = find_stack_state(state, offset);
+ if (stack == NULL) {
+ return -1;
+ } else if (!stack->compound) {
+ tsr->type = stack->type;
+ tsr->kind = stack->kind;
+ tsr->offset = stack->ptr_offset;
+ tsr->imm_value = stack->imm_value;
+ tsr->ok = true;
+ } else if (die_get_member_type(&stack->type,
+ offset - stack->offset,
+ &type_die)) {
+ tsr->type = type_die;
+ tsr->kind = TSR_KIND_TYPE;
+ tsr->offset = 0;
+ tsr->imm_value = 0;
+ tsr->ok = true;
+ } else {
+ return -1;
+ }
+
+ if (sreg == fbreg) {
+ pr_debug_dtp("%s [%x] -%#x(stack) -> reg%d",
+ dl->ins.name, insn_offset, -offset, dreg);
+ } else {
+ pr_debug_dtp("%s [%x] %#x(reg%d) -> reg%d",
+ dl->ins.name, insn_offset, offset, sreg, dreg);
+ }
+ pr_debug_type_name(&tsr->type, tsr->kind);
+ return 0;
+ }
+
+ if (!src_tsr || !src_tsr->ok)
+ return -1;
+
+ /* Dereference the pointer if it has one */
+ if (src_tsr->kind == TSR_KIND_TYPE &&
+ die_deref_ptr_type(&src_tsr->type,
+ src_tsr->offset + reg_offset, &type_die)) {
+ tsr->type = type_die;
+ tsr->kind = TSR_KIND_TYPE;
+ tsr->offset = 0;
+ tsr->imm_value = 0;
+ tsr->ok = true;
+
+ pr_debug_dtp("%s [%x] %#x(reg%d) -> reg%d",
+ dl->ins.name, insn_offset, reg_offset, sreg, dreg);
+ pr_debug_type_name(&tsr->type, tsr->kind);
+ return 0;
+ }
+
+ /* Handle dereference of TSR_KIND_POINTER registers */
+ if (src_tsr->kind == TSR_KIND_POINTER &&
+ die_get_member_type(&src_tsr->type,
+ src_tsr->offset + reg_offset, &type_die)) {
+ tsr->type = src_tsr->type;
+ tsr->kind = TSR_KIND_TYPE;
+ tsr->offset = src_tsr->offset + reg_offset;
+ tsr->imm_value = 0;
+ tsr->ok = true;
+
+ pr_debug_dtp("%s [%x] addr %#x(reg%d) -> reg%d",
+ dl->ins.name, insn_offset, reg_offset, sreg, dreg);
+ pr_debug_type_name(&tsr->type, tsr->kind);
+ return 0;
+ }
+
+ /* Or check if it's a global variable */
+ if (src_tsr->kind == TSR_KIND_GLOBAL_ADDR) {
+ u64 ip = dloc->ms->sym->start + dl->al.offset;
+ u64 addr = src_tsr->imm_value + reg_offset;
+ int offset;
+ u8 kind;
+ const char *var_name = NULL;
+
+ /* it might be per-cpu offset */
+ if (dso__kernel(map__dso(dloc->ms->map)) &&
+ get_global_var_info(dloc, addr, &var_name, &offset) &&
+ !strcmp(var_name, "__per_cpu_offset"))
+ kind = TSR_KIND_PERCPU_BASE;
+ else
+ kind = TSR_KIND_TYPE;
+
+ if (!get_global_var_type(cu_die, dloc, ip, addr, &offset, &type_die) ||
+ !die_get_member_type(&type_die, offset, &type_die))
+ return -1;
+
+ tsr->type = type_die;
+ tsr->kind = kind;
+ tsr->offset = 0;
+ tsr->imm_value = 0;
+ tsr->ok = true;
+
+ if (src->multi_regs) {
+ pr_debug_dtp("%s [%x] global (reg%d, reg%d) -> reg%d",
+ dl->ins.name, insn_offset, src->reg1, src->reg2, dreg);
+ } else {
+ pr_debug_dtp("%s [%x] global (reg%d) -> reg%d",
+ dl->ins.name, insn_offset, sreg, dreg);
+ }
+ pr_debug_type_name(&tsr->type, tsr->kind);
+ return 0;
+ }
+
+ /*
+ * If src_tsr->kind is TSR_KIND_PERCPU_BASE, treat it as an array.
+ * No special handling is needed here; just fall through to retry
+ * and resolve the type from the second register.
+ */
+
+load_non_regoff:
+ /* Try another register if any */
+ if (src->multi_regs && src->reg1 != src->reg2 && sreg != src->reg2 &&
+ !(src->extend_type || src->shift_type)) {
+ sreg = src->reg2;
+ src_tsr = src_states[1];
+ goto retry;
+ }
+
+ return -1;
+}
+
+static void update_load_insn_state(struct type_state *state,
+ struct data_loc_info *dloc, Dwarf_Die *cu_die,
+ struct disasm_line *dl,
+ struct annotated_op_loc *src,
+ struct annotated_op_loc *dst)
+{
+ struct type_state_reg snapshots[2];
+ struct type_state_reg *src_states[2] = {};
+ int mem_spacing = 0;
+
+ if (!src->mem_ref || /* exclude PC-relative loads */
+ !has_reg_type(state, src->reg1)) {
+ /* Invalidate tracked destination registers */
+ if (has_reg_type(state, dst->reg1))
+ invalidate_reg_state(&state->regs[dst->reg1]);
+ if (dst->multi_regs && has_reg_type(state, dst->reg2))
+ invalidate_reg_state(&state->regs[dst->reg2]);
+ goto out_adjust;
+ }
+
+ /*
+ * Snapshot source register states before updating destination registers.
+ * This preserves the original source state for aliased loads (e.g.,
+ * ldp x0, x1, [x0]), where updating the first destination may overwrite
+ * the source register.
+ */
+ snapshots[0] = state->regs[src->reg1];
+ src_states[0] = &snapshots[0];
+ if (src->multi_regs && has_reg_type(state, src->reg2)) {
+ snapshots[1] = state->regs[src->reg2];
+ src_states[1] = &snapshots[1];
+ }
+
+ /* Handle the first destination register */
+ if (has_reg_type(state, dst->reg1)) {
+ if (propagate_load_reg_state(state, dloc, cu_die, dl, dst->reg1,
+ src, src_states, mem_spacing))
+ invalidate_reg_state(&state->regs[dst->reg1]);
+ }
+
+ if (!dst->multi_regs || !has_reg_type(state, dst->reg2))
+ goto out_adjust;
+
+ /* Handle the second destination register */
+ if (!strcmp(dl->ins.name, "ldpsw"))
+ mem_spacing = 4; /* ldpsw loads two 32-bit signed words (4-byte spacing) */
+ else
+ mem_spacing = arm64__reg_size(dl->ops.target.raw);
+
+ if (mem_spacing < 0 ||
+ propagate_load_reg_state(state, dloc, cu_die, dl, dst->reg2,
+ src, src_states, mem_spacing))
+ invalidate_reg_state(&state->regs[dst->reg2]);
+
+out_adjust:
+ adjust_reg_index_state(state, dloc, dl, src);
+}
+
+/*
+ * Match standard store variants (str, stur, stlr, stp) that follow
+ * straightforward store semantics: source registers on the left (read-only)
+ * and memory target on the right. Excludes exclusive stores (stxr, stxp, etc.)
+ * which have status/result register side effects.
+ */
+static bool is_standard_store_insn(const char *name)
+{
+ return !strncmp(name, "str", 3) || /* str, strb, strh */
+ !strncmp(name, "stur", 4) || /* stur, sturb, sturh */
+ !strncmp(name, "stlr", 4) || /* stlr, stlrb, stlrh */
+ !strncmp(name, "stp", 3); /* stp */
+}
+
+/*
+ * For store insns: propagate type from @sreg to the memory location referenced
+ * by @dst, applying @mem_spacing to account for multi-regs stores (e.g. stp).
+ */
+static void propagate_store_reg_state(struct type_state *state,
+ struct data_loc_info *dloc,
+ struct disasm_line *dl, int sreg,
+ struct annotated_op_loc *dst,
+ int mem_spacing)
+{
+ struct type_state_reg *tsr;
+ u32 insn_offset = dl->al.offset;
+ int dreg = dst->reg1;
+ int reg_offset;
+ int fbreg = dloc->fbreg;
+ int fboff = 0;
+
+ if (arch_get_reg_offset(dloc->arch, dst, dreg, state, false, &reg_offset))
+ return;
+
+ reg_offset += mem_spacing;
+
+ if (dloc->fb_cfa) {
+ u64 ip = dloc->ms->sym->start + dl->al.offset;
+ u64 pc = map__rip_2objdump(dloc->ms->map, ip);
+
+ if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0)
+ fbreg = -1;
+ }
+
+ /* Check stack variables with offset */
+ if (dreg != -1 && (dreg == fbreg || dreg == state->stack_reg)) {
+ struct type_state_stack *stack;
+ int offset = dreg == fbreg ? reg_offset - fboff : reg_offset;
+
+ if (!has_reg_type(state, sreg) || !state->regs[sreg].ok) {
+ stack = find_stack_state(state, offset);
+ /* Preserve compound states when only a member is overwritten. */
+ if (stack && !stack->compound)
+ delete_stack_state(state, offset);
+
+ return;
+ }
+
+ tsr = &state->regs[sreg];
+
+ stack = find_stack_state(state, offset);
+ if (stack) {
+ if (!stack->compound)
+ set_stack_state(stack, offset, tsr->kind, &tsr->type,
+ tsr->offset, tsr->imm_value);
+ /*
+ * If it's a compound type, it means attempting to
+ * write to a member value of the compound type without
+ * changing the compound type itself, so do nothing.
+ */
+ } else {
+ findnew_stack_state(state, offset, tsr->kind, &tsr->type,
+ tsr->offset, tsr->imm_value);
+ }
+
+ if (dreg == fbreg) {
+ pr_debug_dtp("%s [%x] reg%d -> -%#x(stack)",
+ dl->ins.name, insn_offset, sreg, -offset);
+ } else {
+ pr_debug_dtp("%s [%x] reg%d -> %#x(reg%d)",
+ dl->ins.name, insn_offset, sreg, offset, dreg);
+ }
+ if (tsr->offset != 0)
+ pr_debug_dtp(" reg%d offset %#x ->", sreg, tsr->offset);
+
+ pr_debug_type_name(&tsr->type, tsr->kind);
+ }
+ /*
+ * Ignore other transfers since it'd set a value in a struct
+ * and won't change the type.
+ */
+}
+
+static void update_store_insn_state(struct type_state *state,
+ struct data_loc_info *dloc,
+ struct disasm_line *dl,
+ struct annotated_op_loc *src,
+ struct annotated_op_loc *dst)
+{
+ int mem_spacing = 0;
+
+ if (!has_reg_type(state, dst->reg1))
+ return;
+
+ /* Handle the first source register */
+ propagate_store_reg_state(state, dloc, dl, src->reg1, dst, mem_spacing);
+
+ /* Handle the second source register (if any) */
+ if (src->multi_regs) {
+ mem_spacing = arm64__reg_size(dl->ops.source.raw);
+
+ if (mem_spacing >= 0)
+ propagate_store_reg_state(state, dloc, dl, src->reg2,
+ dst, mem_spacing);
+ }
+
+ adjust_reg_index_state(state, dloc, dl, dst);
+}
+
+static void update_mov_insn_state(struct type_state *state,
+ struct disasm_line *dl,
+ struct annotated_op_loc *src,
+ struct annotated_op_loc *dst)
+{
+ struct type_state_reg *tsr;
+ u32 insn_offset = dl->al.offset;
+ int sreg = src->reg1;
+ int dreg = dst->reg1;
+
+ if (!has_reg_type(state, dreg))
+ return;
+
+ tsr = &state->regs[dreg];
+ tsr->copied_from = -1;
+
+ if (src->imm) {
+ invalidate_reg_state(tsr);
+ tsr->kind = TSR_KIND_CONST;
+ tsr->imm_value = (s64)src->offset;
+ tsr->offset = 0;
+ tsr->ok = true;
+
+ pr_debug_dtp("mov [%x] imm=%#"PRIx64" -> reg%d\n",
+ insn_offset, tsr->imm_value, dreg);
+ return;
+ }
+
+ if (!has_reg_type(state, sreg) || !state->regs[sreg].ok) {
+ invalidate_reg_state(tsr);
+ return;
+ }
+
+ tsr->type = state->regs[sreg].type;
+ tsr->kind = state->regs[sreg].kind;
+ tsr->imm_value = state->regs[sreg].imm_value;
+ tsr->offset = state->regs[sreg].offset;
+ tsr->lifetime_active = state->regs[sreg].lifetime_active;
+ tsr->lifetime_end = state->regs[sreg].lifetime_end;
+ tsr->ok = state->regs[sreg].ok;
+
+ if (tsr->kind == TSR_KIND_TYPE || tsr->kind == TSR_KIND_POINTER)
+ tsr->copied_from = sreg;
+
+ pr_debug_dtp("mov [%x] reg%d -> reg%d", insn_offset, sreg, dreg);
+ pr_debug_type_name(&tsr->type, tsr->kind);
+}
+
+static void update_add_insn_state(struct type_state *state,
+ struct data_loc_info *dloc,
+ struct disasm_line *dl,
+ struct annotated_op_loc *src,
+ struct annotated_op_loc *dst)
+{
+ struct type_state_reg *tsr;
+ struct type_state_reg src_tsr;
+ u32 insn_offset = dl->al.offset;
+ int sreg = src->reg1;
+ int dreg = dst->reg1;
+ int reg_offset;
+
+ if (!has_reg_type(state, dreg))
+ return;
+
+ tsr = &state->regs[dreg];
+ tsr->copied_from = -1;
+ tsr->lifetime_active = false;
+ tsr->lifetime_end = 0;
+
+retry:
+ if (!has_reg_type(state, sreg) || !state->regs[sreg].ok) {
+ invalidate_reg_state(tsr);
+ return;
+ }
+
+ src_tsr = state->regs[sreg];
+
+ if (arch_get_reg_offset(dloc->arch, src, sreg, state, false, &reg_offset))
+ goto add_non_regoff;
+
+ if (src_tsr.kind == TSR_KIND_CONST || src_tsr.kind == TSR_KIND_GLOBAL_ADDR) {
+ tsr->kind = src_tsr.kind;
+ /* For 'adrp + add' pair: resolve the full global variable address. */
+ tsr->imm_value = src_tsr.imm_value + reg_offset;
+ tsr->offset = 0;
+ tsr->ok = src_tsr.ok;
+
+ pr_debug_dtp("add [%x] %s %#x(reg%d) -> reg%d\n",
+ insn_offset, src_tsr.kind == TSR_KIND_CONST ?
+ "imm" : "global", reg_offset, sreg, dreg);
+ return;
+ }
+
+ if (src_tsr.kind == TSR_KIND_POINTER ||
+ (src_tsr.kind == TSR_KIND_TYPE &&
+ dwarf_tag(&src_tsr.type) == DW_TAG_pointer_type)) {
+ tsr->type = src_tsr.type;
+ tsr->kind = src_tsr.kind;
+ tsr->imm_value = src_tsr.imm_value;
+ tsr->offset = src_tsr.offset + reg_offset;
+ tsr->ok = src_tsr.ok;
+
+ pr_debug_dtp("add [%x] address of %#x(reg%d) -> reg%d",
+ insn_offset, reg_offset, sreg, dreg);
+ pr_debug_type_name(&tsr->type, tsr->kind);
+ return;
+ }
+
+ /*
+ * If src_tsr->kind is TSR_KIND_PERCPU_BASE, treat it as an array.
+ * No special handling is needed here; just fall through to retry
+ * and resolve the type from the second register.
+ */
+
+add_non_regoff:
+ /* Try another register if any */
+ if (src->multi_regs && src->reg1 != src->reg2 && sreg != src->reg2 &&
+ !(src->extend_type || src->shift_type)) {
+ sreg = src->reg2;
+ goto retry;
+ }
+ invalidate_reg_state(tsr);
+}
+
+static void update_adrp_insn_state(struct type_state *state,
+ struct disasm_line *dl,
+ struct annotated_op_loc *dst)
+{
+ struct type_state_reg *tsr;
+ u32 insn_offset = dl->al.offset;
+ int dreg = dst->reg1;
+
+ if (!has_reg_type(state, dreg))
+ return;
+
+ tsr = &state->regs[dreg];
+ tsr->copied_from = -1;
+
+ if (!dl->ops.source.addr) {
+ invalidate_reg_state(tsr);
+ return;
+ }
+
+ tsr->kind = TSR_KIND_GLOBAL_ADDR;
+ /*
+ * Stores a partial page-relative address. The full absolute address
+ * of the global variable will be resolved when a subsequent 'add' or
+ * 'ldr' instruction consumes this register.
+ */
+ tsr->imm_value = dl->ops.source.addr;
+ tsr->offset = 0;
+ tsr->lifetime_active = false;
+ tsr->lifetime_end = 0;
+ tsr->ok = true;
+
+ pr_debug_dtp("adrp [%x] global addr=%#"PRIx64" -> reg%d\n",
+ insn_offset, tsr->imm_value, dreg);
+}
+
+static void update_mrs_insn_state(struct type_state *state,
+ struct data_loc_info *dloc, Dwarf_Die *cu_die,
+ struct disasm_line *dl,
+ struct annotated_op_loc *dst)
+{
+ struct type_state_reg *tsr;
+ Dwarf_Die type_die;
+ u32 insn_offset = dl->al.offset;
+ int dreg = dst->reg1;
+ Dwarf_Die func_die;
+ Dwarf_Attribute attr;
+ u64 ip, pc;
+ struct strbuf sb;
+ char *type_name;
+
+ if (!has_reg_type(state, dreg))
+ return;
+
+ tsr = &state->regs[dreg];
+ tsr->copied_from = -1;
+
+ /* Handle case difference: LLVM (SP_EL0) vs objdump (sp_el0) */
+ if (!dso__kernel(map__dso(dloc->ms->map)) ||
+ strcasecmp(dl->ops.source.raw, "sp_el0")) {
+ invalidate_reg_state(tsr);
+ return;
+ }
+
+ ip = dloc->ms->sym->start + dl->al.offset;
+ pc = map__rip_2objdump(dloc->ms->map, ip);
+
+ /*
+ * Find the inline function 'get_current()' Dwarf_Die and obtain its
+ * return value data type, which should be 'struct task_struct*'.
+ */
+ if (!die_find_inlinefunc(cu_die, pc, &func_die) ||
+ !die_compare_name(&func_die, "get_current") ||
+ !dwarf_attr_integrate(&func_die, DW_AT_type, &attr) ||
+ !dwarf_formref_die(&attr, &type_die)) {
+ invalidate_reg_state(tsr);
+ return;
+ }
+
+ strbuf_init(&sb, 32);
+ die_get_typename_from_type(&type_die, &sb);
+ type_name = strbuf_detach(&sb, NULL);
+
+ if (!type_name || strcmp(type_name, "struct task_struct*")) {
+ invalidate_reg_state(tsr);
+ goto out_free;
+ }
+
+ tsr->type = type_die;
+ tsr->kind = TSR_KIND_TYPE;
+ tsr->offset = 0;
+ tsr->imm_value = 0;
+ tsr->lifetime_active = false;
+ tsr->lifetime_end = 0;
+ tsr->ok = true;
+
+ pr_debug_dtp("mrs [%x] sp_el0 -> reg%d", insn_offset, dreg);
+ pr_debug_type_name(&type_die, tsr->kind);
+
+out_free:
+ free(type_name);
+}
+
+static void update_insn_state_arm64(struct type_state *state,
+ struct data_loc_info *dloc, Dwarf_Die *cu_die,
+ struct disasm_line *dl)
+{
+ struct annotated_insn_loc loc;
+ struct annotated_op_loc *src = &loc.ops[INSN_OP_SOURCE];
+ struct annotated_op_loc *dst = &loc.ops[INSN_OP_TARGET];
+
+ if (is_readonly_insn(dl->ins.name))
+ return;
+
+ if (annotate_get_insn_location(dloc->arch, dl, &loc) < 0)
+ return;
+
+ if (ins__is_call(&dl->ins))
+ update_call_insn_state(state, dloc, cu_die, dl);
+ else if (!strcmp(dl->ins.name, "mrs"))
+ update_mrs_insn_state(state, dloc, cu_die, dl, dst);
+ else if (!strcmp(dl->ins.name, "adrp"))
+ update_adrp_insn_state(state, dl, dst);
+ else if (!strcmp(dl->ins.name, "add"))
+ update_add_insn_state(state, dloc, dl, src, dst);
+ /* Register to register or imm value to register transfers */
+ else if (!strcmp(dl->ins.name, "mov"))
+ update_mov_insn_state(state, dl, src, dst);
+ /* Memory to register transfers */
+ else if (is_standard_load_insn(dl->ins.name))
+ update_load_insn_state(state, dloc, cu_die, dl, src, dst);
+ /* Register to memory transfers */
+ else if (is_standard_store_insn(dl->ins.name))
+ update_store_insn_state(state, dloc, dl, src, dst);
+ else
+ invalidate_unhandled_insn_regs(state, dl, src, dst);
+}
+#endif
+
const struct arch *arch__new_arm64(const struct e_machine_and_e_flags *id,
const char *cpuid __maybe_unused)
{
@@ -467,6 +1238,9 @@ const struct arch *arch__new_arm64(const struct e_machine_and_e_flags *id,
arch->objdump.imm_char = '#';
arch->associate_instruction_ops = arm64__associate_instruction_ops;
arch->extract_op_location = extract_op_location_arm64;
+#ifdef HAVE_LIBDW_SUPPORT
+ arch->update_insn_state = update_insn_state_arm64;
+#endif
/* bl, blr */
err = regcomp(&arm->call_insn, "^blr?$", REG_EXTENDED);
diff --git a/tools/perf/util/annotate-arch/annotate-x86.c b/tools/perf/util/annotate-arch/annotate-x86.c
index 1acf31a2c759..3a6ea7e978e0 100644
--- a/tools/perf/util/annotate-arch/annotate-x86.c
+++ b/tools/perf/util/annotate-arch/annotate-x86.c
@@ -207,15 +207,6 @@ static int x86__cpuid_parse(struct arch *arch, const char *cpuid)
}
#ifdef HAVE_LIBDW_SUPPORT
-static void invalidate_reg_state(struct type_state_reg *reg)
-{
- reg->kind = TSR_KIND_INVALID;
- reg->ok = false;
- reg->lifetime_active = false;
- reg->lifetime_end = 0;
- reg->copied_from = -1;
-}
-
static void update_insn_state_x86(struct type_state *state,
struct data_loc_info *dloc, Dwarf_Die *cu_die,
struct disasm_line *dl)
@@ -545,11 +536,11 @@ static void update_insn_state_x86(struct type_state *state,
if (src->imm) {
tsr->kind = TSR_KIND_CONST;
- tsr->imm_value = src->offset;
+ tsr->imm_value = (s64)src->offset;
tsr->offset = 0;
tsr->ok = true;
- pr_debug_dtp("mov [%x] imm=%#x -> reg%d\n",
+ pr_debug_dtp("mov [%x] imm=%#"PRIx64" -> reg%d\n",
insn_offset, tsr->imm_value, dst->reg1);
return;
}
@@ -600,6 +591,7 @@ retry:
tsr->type = stack->type;
tsr->kind = stack->kind;
tsr->offset = stack->ptr_offset;
+ tsr->imm_value = stack->imm_value;
tsr->ok = true;
} else if (die_get_member_type(&stack->type,
offset - stack->offset,
@@ -607,6 +599,7 @@ retry:
tsr->type = type_die;
tsr->kind = TSR_KIND_TYPE;
tsr->offset = 0;
+ tsr->imm_value = 0;
tsr->ok = true;
} else {
invalidate_reg_state(tsr);
@@ -760,15 +753,24 @@ retry:
}
/* Case 3. register to memory transfers */
if (!src->mem_ref && dst->mem_ref) {
- if (!has_reg_type(state, src->reg1) ||
- !state->regs[src->reg1].ok)
- return;
-
/* Check stack variables with offset */
if (dst->reg1 == fbreg || dst->reg1 == state->stack_reg) {
struct type_state_stack *stack;
int offset = dst->offset - fboff;
+ if (!has_reg_type(state, src->reg1) ||
+ !state->regs[src->reg1].ok) {
+ stack = find_stack_state(state, offset);
+ /*
+ * Preserve compound states when only a member
+ * is overwritten.
+ */
+ if (stack && !stack->compound)
+ delete_stack_state(state, offset);
+
+ return;
+ }
+
tsr = &state->regs[src->reg1];
stack = find_stack_state(state, offset);
@@ -782,10 +784,12 @@ retry:
*/
if (!stack->compound)
set_stack_state(stack, offset, tsr->kind,
- &tsr->type, tsr->offset);
+ &tsr->type, tsr->offset,
+ tsr->imm_value);
} else {
findnew_stack_state(state, offset, tsr->kind,
- &tsr->type, tsr->offset);
+ &tsr->type, tsr->offset,
+ tsr->imm_value);
}
if (dst->reg1 == fbreg) {
diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c
index 19a6ecd67f28..b3de1ca688a2 100644
--- a/tools/perf/util/annotate-data.c
+++ b/tools/perf/util/annotate-data.c
@@ -29,6 +29,7 @@
/* register number of the stack pointer */
#define X86_REG_SP 7
+#define ARM64_REG_SP 31
static void delete_var_types(struct die_var_type *var_types);
@@ -70,6 +71,9 @@ void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind)
case TSR_KIND_CANARY:
pr_info(" stack canary\n");
return;
+ case TSR_KIND_GLOBAL_ADDR:
+ pr_info(" global address\n");
+ return;
case TSR_KIND_TYPE:
default:
break;
@@ -179,6 +183,17 @@ static void init_type_state(struct type_state *state, const struct arch *arch)
state->regs[11].caller_saved = true;
state->ret_reg = 0;
state->stack_reg = X86_REG_SP;
+ } else if (arch__is_arm64(arch)) {
+ /*
+ * Mark x0..x18 as caller-saved. x30 (LR) is intentionally
+ * omitted from the caller_saved set: it is clobbered by
+ * calls, but holds only the return address and is not used
+ * to carry data type information.
+ */
+ for (int i = 0; i < 19; i++)
+ state->regs[i].caller_saved = true;
+ state->ret_reg = 0;
+ state->stack_reg = ARM64_REG_SP;
}
}
@@ -692,7 +707,7 @@ struct type_state_stack *find_stack_state(struct type_state *state,
}
void set_stack_state(struct type_state_stack *stack, int offset, u8 kind,
- Dwarf_Die *type_die, int ptr_offset)
+ Dwarf_Die *type_die, int ptr_offset, u64 imm_value)
{
int tag;
Dwarf_Word size;
@@ -709,8 +724,10 @@ void set_stack_state(struct type_state_stack *stack, int offset, u8 kind,
stack->offset = offset;
stack->ptr_offset = ptr_offset;
stack->kind = kind;
+ stack->imm_value = imm_value;
- if (kind == TSR_KIND_POINTER) {
+ if (kind == TSR_KIND_POINTER || kind == TSR_KIND_CONST ||
+ kind == TSR_KIND_GLOBAL_ADDR) {
stack->compound = false;
return;
}
@@ -731,23 +748,42 @@ void set_stack_state(struct type_state_stack *stack, int offset, u8 kind,
struct type_state_stack *findnew_stack_state(struct type_state *state,
int offset, u8 kind,
Dwarf_Die *type_die,
- int ptr_offset)
+ int ptr_offset, u64 imm_value)
{
struct type_state_stack *stack = find_stack_state(state, offset);
if (stack) {
- set_stack_state(stack, offset, kind, type_die, ptr_offset);
+ set_stack_state(stack, offset, kind, type_die, ptr_offset, imm_value);
return stack;
}
stack = malloc(sizeof(*stack));
if (stack) {
- set_stack_state(stack, offset, kind, type_die, ptr_offset);
+ set_stack_state(stack, offset, kind, type_die, ptr_offset, imm_value);
list_add(&stack->list, &state->stack_vars);
}
return stack;
}
+void delete_stack_state(struct type_state *state, int offset)
+{
+ struct type_state_stack *stack = find_stack_state(state, offset);
+
+ if (stack) {
+ list_del(&stack->list);
+ free(stack);
+ }
+}
+
+void invalidate_reg_state(struct type_state_reg *reg)
+{
+ reg->kind = TSR_KIND_INVALID;
+ reg->ok = false;
+ reg->lifetime_active = false;
+ reg->lifetime_end = 0;
+ reg->copied_from = -1;
+}
+
/* Maintain a cache for quick global variable lookup */
struct global_var_entry {
struct rb_node node;
@@ -1037,7 +1073,7 @@ static void update_var_state(struct type_state *state, struct data_loc_info *dlo
continue;
findnew_stack_state(state, offset, TSR_KIND_TYPE,
- &mem_die, /*ptr_offset=*/0);
+ &mem_die, /*ptr_offset=*/0, /*imm_value=*/0);
if (var->reg == state->stack_reg) {
pr_debug_dtp("var [%"PRIx64"] %#x(reg%d)",
@@ -1220,6 +1256,78 @@ static void setup_stack_canary(struct data_loc_info *dloc)
}
}
+int arch_get_reg_offset(const struct arch *arch, struct annotated_op_loc *op_loc,
+ int breg, struct type_state *state, bool apply_index,
+ int *offset)
+{
+ if (arch__is_arm64(arch)) {
+ int reg2;
+
+ /* Post-indexed addressing: offset is 0 when not applying index */
+ if (op_loc->addr_mode == PERF_AAM_POST_INDEX && !apply_index) {
+ *offset = 0;
+ return 0;
+ }
+
+ /* Single-register addressing: return the stored offset directly */
+ if (!op_loc->multi_regs) {
+ *offset = op_loc->offset;
+ return 0;
+ }
+
+ /*
+ * SIB (Scale-Index-Base) style indexed addressing, e.g.:
+ * Array element load: ldr dst, [base, index, lsl #3]
+ * Array element load: ldr dst, [base, index, uxtw #3]
+ * Array address calc: add dst, base, index lsl #3
+ *
+ * The index reg only computes element offset without modifying
+ * the underlying data type; the dst type is inherited from base.
+ * Therefore, the base reg offset can be treated as 0.
+ */
+ if (op_loc->extend_type || op_loc->shift_type) {
+ /* A memory reference implies breg is a pointer. */
+ if (op_loc->mem_ref) {
+ *offset = 0;
+ return 0;
+ }
+ /*
+ * Otherwise, require breg to be a known pointer to avoid
+ * treating integer arithmetic as indexed address calculation.
+ */
+ if (has_reg_type(state, breg) && state->regs[breg].ok &&
+ (state->regs[breg].kind == TSR_KIND_POINTER ||
+ (state->regs[breg].kind == TSR_KIND_TYPE &&
+ dwarf_tag(&state->regs[breg].type) == DW_TAG_pointer_type))) {
+ *offset = 0;
+ return 0;
+ }
+ return -1;
+ }
+
+ /*
+ * For register-based addressing without shift/extension,
+ * derive the offset from the type of reg2.
+ */
+ reg2 = op_loc->reg1 == breg ? op_loc->reg2 : op_loc->reg1;
+ if (has_reg_type(state, reg2) && state->regs[reg2].ok) {
+ if (state->regs[reg2].kind == TSR_KIND_CONST)
+ *offset = (s64)state->regs[reg2].imm_value;
+ else if (state->regs[reg2].kind == TSR_KIND_PERCPU_BASE)
+ *offset = 0; /* it does not contribute to the offset */
+ else
+ return -1;
+
+ return 0;
+ }
+
+ return -1;
+ }
+
+ *offset = op_loc->offset;
+ return 0;
+}
+
/*
* It's at the target address, check if it has a matching type.
* It returns PERF_TMR_BAIL_OUT when it looks up per-cpu variables which
@@ -1233,18 +1341,25 @@ static enum type_match_result check_matching_type(struct type_state *state,
{
u32 insn_offset = dl->al.offset;
int reg = dloc->op->reg1;
- int offset = dloc->op->offset;
- const char *offset_sign = "";
+ int offset;
+ const char *offset_sign;
bool retry = true;
- if (offset < 0) {
- offset = -offset;
- offset_sign = "-";
+again:
+ if (arch_get_reg_offset(dloc->arch, dloc->op, reg, state, false, &offset)) {
+ /*
+ * Fall back to the instruction's offset. This prevents the
+ * register type of the instruction from being completely lost.
+ * Compared to dropping this type inference entirely, providing
+ * a rough type hint is likely more useful.
+ */
+ offset = dloc->op->offset;
}
-again:
+ offset_sign = offset < 0 ? "-" : "";
+
pr_debug_dtp("chk [%x] reg%d offset=%s%#x ok=%d kind=%d ",
- insn_offset, reg, offset_sign, offset,
+ insn_offset, reg, offset_sign, abs(offset),
state->regs[reg].ok, state->regs[reg].kind);
if (!state->regs[reg].ok)
@@ -1267,7 +1382,7 @@ again:
*/
ptr_type = die_get_pointer_type(&state->regs[reg].type, &ptr_die);
if (!ptr_type) {
- if (dloc->op->offset < 0 && reg != state->stack_reg)
+ if (offset < 0 && reg != state->stack_reg)
goto check_kernel;
return PERF_TMR_NO_POINTER;
@@ -1277,7 +1392,7 @@ again:
if (__die_get_real_type(ptr_type, type_die) == NULL)
return PERF_TMR_NO_POINTER;
- dloc->type_offset = dloc->op->offset + state->regs[reg].offset;
+ dloc->type_offset = offset + state->regs[reg].offset;
if (dwarf_tag(type_die) == DW_TAG_typedef)
die_get_real_type(type_die, &sized_type);
@@ -1301,7 +1416,7 @@ again:
*/
*type_die = state->regs[reg].type;
- dloc->type_offset = dloc->op->offset + state->regs[reg].offset;
+ dloc->type_offset = offset + state->regs[reg].offset;
return check_type_offset(type_die, dloc->type_offset);
}
@@ -1315,7 +1430,7 @@ again:
*/
*type_die = state->regs[reg].type;
- dloc->type_offset = dloc->op->offset;
+ dloc->type_offset = offset;
if (check_type_offset(type_die, dloc->type_offset) != PERF_TMR_OK)
return PERF_TMR_BAIL_OUT;
@@ -1337,11 +1452,31 @@ again:
}
if (state->regs[reg].kind == TSR_KIND_PERCPU_BASE) {
- u64 var_addr = dloc->op->offset;
+ u64 var_addr;
int var_offset;
pr_debug_dtp("percpu var");
+ if (arch__is_arm64(dloc->arch)) {
+ int reg2;
+
+ if (!retry || !dloc->op->multi_regs ||
+ dloc->op->reg1 == dloc->op->reg2 ||
+ dloc->op->extend_type || dloc->op->shift_type)
+ return PERF_TMR_BAIL_OUT;
+
+ reg2 = dloc->op->reg2;
+ if (!has_reg_type(state, reg2) || !state->regs[reg2].ok)
+ return PERF_TMR_NO_TYPE;
+
+ pr_debug_dtp(" : retry\n");
+ retry = false;
+ reg = reg2;
+ goto again;
+ }
+
+ var_addr = (s64) offset;
+
if (dloc->op->multi_regs) {
int reg2 = dloc->op->reg2;
@@ -1364,16 +1499,35 @@ again:
if (state->regs[reg].kind == TSR_KIND_CONST &&
dso__kernel(map__dso(dloc->ms->map))) {
- if (dloc->op->offset < 0 && reg != state->stack_reg && reg != dloc->fbreg)
+ if (offset < 0 && reg != state->stack_reg && reg != dloc->fbreg)
goto check_kernel;
}
+
+ if (state->regs[reg].kind == TSR_KIND_GLOBAL_ADDR) {
+ u64 var_addr = state->regs[reg].imm_value + offset;
+ int var_offset;
+
+ pr_debug_dtp("global addr");
+
+ /*
+ * The register holds the address of a global variable. Try to
+ * find the variable by the address and get its type.
+ */
+ if (get_global_var_type(cu_die, dloc, dloc->ip, var_addr,
+ &var_offset, type_die)) {
+ dloc->type_offset = var_offset;
+ return PERF_TMR_OK;
+ }
+ /* No need to retry global variables */
+ return PERF_TMR_BAIL_OUT;
+ }
check_non_register:
if (reg == dloc->fbreg || reg == state->stack_reg) {
struct type_state_stack *stack;
pr_debug_dtp("%s", reg == dloc->fbreg ? "fbreg" : "stack");
- stack = find_stack_state(state, dloc->type_offset);
+ stack = find_stack_state(state, offset);
if (stack == NULL) {
if (retry) {
pr_debug_dtp(" : retry\n");
@@ -1396,7 +1550,7 @@ check_non_register:
*type_die = stack->type;
/* Update the type offset from the start of slot */
- dloc->type_offset -= stack->offset;
+ dloc->type_offset = offset - stack->offset;
return PERF_TMR_OK;
}
@@ -1414,7 +1568,7 @@ check_non_register:
if (reg != fbreg)
return PERF_TMR_NO_TYPE;
- stack = find_stack_state(state, dloc->type_offset - fboff);
+ stack = find_stack_state(state, offset - fboff);
if (stack == NULL) {
if (retry) {
pr_debug_dtp(" : retry\n");
@@ -1437,7 +1591,7 @@ check_non_register:
*type_die = stack->type;
/* Update the type offset from the start of slot */
- dloc->type_offset -= fboff + stack->offset;
+ dloc->type_offset = offset - fboff - stack->offset;
return PERF_TMR_OK;
}
@@ -1451,7 +1605,7 @@ check_kernel:
arch__is_x86(dloc->arch)) {
pr_debug_dtp("this-cpu var");
- addr = dloc->op->offset;
+ addr = (s64) offset;
if (get_global_var_type(cu_die, dloc, dloc->ip, addr,
&offset, type_die)) {
@@ -1462,8 +1616,8 @@ check_kernel:
}
/* Access to global variable like "-0x7dcf0500(,%rdx,8)" */
- if (dloc->op->offset < 0 && reg != state->stack_reg) {
- addr = (s64) dloc->op->offset;
+ if (offset < 0 && reg != state->stack_reg) {
+ addr = (s64) offset;
if (get_global_var_type(cu_die, dloc, dloc->ip, addr,
&offset, type_die)) {
@@ -1534,7 +1688,8 @@ out:
static int arch_supports_insn_tracking(struct data_loc_info *dloc)
{
- if ((arch__is_x86(dloc->arch)) || (arch__is_powerpc(dloc->arch)))
+ if (arch__is_x86(dloc->arch) || arch__is_powerpc(dloc->arch) ||
+ arch__is_arm64(dloc->arch))
return 1;
return 0;
}
diff --git a/tools/perf/util/annotate-data.h b/tools/perf/util/annotate-data.h
index bbf4fd35c1d7..eee1f8dae3dc 100644
--- a/tools/perf/util/annotate-data.h
+++ b/tools/perf/util/annotate-data.h
@@ -37,6 +37,7 @@ enum type_state_kind {
TSR_KIND_PERCPU_POINTER,
TSR_KIND_POINTER,
TSR_KIND_CANARY,
+ TSR_KIND_GLOBAL_ADDR,
};
/**
@@ -182,7 +183,7 @@ extern struct annotated_data_stat ann_data_stat;
*/
struct type_state_reg {
Dwarf_Die type;
- u32 imm_value;
+ u64 imm_value;
/*
* The offset within the struct that the register points to.
* A value of 0 means the register points to the beginning.
@@ -208,6 +209,7 @@ struct type_state_stack {
int size;
bool compound;
u8 kind;
+ u64 imm_value;
};
/*
@@ -262,18 +264,22 @@ bool has_reg_type(struct type_state *state, int reg);
struct type_state_stack *findnew_stack_state(struct type_state *state,
int offset, u8 kind,
Dwarf_Die *type_die,
- int ptr_offset);
+ int ptr_offset, u64 imm_value);
void set_stack_state(struct type_state_stack *stack, int offset, u8 kind,
- Dwarf_Die *type_die, int ptr_offset);
+ Dwarf_Die *type_die, int ptr_offset, u64 imm_value);
struct type_state_stack *find_stack_state(struct type_state *state,
int offset);
+void delete_stack_state(struct type_state *state, int offset);
+void invalidate_reg_state(struct type_state_reg *reg);
bool get_global_var_type(Dwarf_Die *cu_die, struct data_loc_info *dloc,
u64 ip, u64 var_addr, int *var_offset,
Dwarf_Die *type_die);
bool get_global_var_info(struct data_loc_info *dloc, u64 addr,
const char **var_name, int *var_offset);
void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind);
-
+int arch_get_reg_offset(const struct arch *arch, struct annotated_op_loc *op_loc,
+ int breg, struct type_state *state, bool apply_index,
+ int *offset);
#else /* HAVE_LIBDW_SUPPORT */
static inline struct annotated_data_type *
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index f30f86156a3a..8bb491d389a2 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -123,7 +123,7 @@ static int evlist__regroup(struct evlist *evlist, struct evsel *leader, struct e
return 0;
}
-static bool auxtrace__dont_decode(struct perf_session *session)
+bool auxtrace__dont_decode(struct perf_session *session)
{
return !session->itrace_synth_opts ||
session->itrace_synth_opts->dont_decode;
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
index 5012bead1b92..c29a8444f08e 100644
--- a/tools/perf/util/auxtrace.h
+++ b/tools/perf/util/auxtrace.h
@@ -650,6 +650,7 @@ void auxtrace__free(struct perf_session *session);
bool auxtrace__evsel_is_auxtrace(struct perf_session *session,
struct evsel *evsel);
u64 auxtrace_synth_id_range_start(struct evsel *evsel);
+bool auxtrace__dont_decode(struct perf_session *session);
#define ITRACE_HELP \
" i[period]: synthesize instructions events\n" \
diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
index 26940f1f1b0b..b56cf57a68c8 100644
--- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
+++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
@@ -741,7 +741,7 @@ cs_etm_decoder__new(int decoders, struct cs_etm_decoder_params *d_params,
return decoder;
err_free_decoder:
- cs_etm_decoder__free(decoder);
+ cs_etm_decoder__zfree(&decoder);
return NULL;
}
@@ -793,14 +793,14 @@ int cs_etm_decoder__process_data_block(struct cs_etm_decoder *decoder,
return ret;
}
-void cs_etm_decoder__free(struct cs_etm_decoder *decoder)
+void cs_etm_decoder__zfree(struct cs_etm_decoder **decoder)
{
- if (!decoder)
+ if (!(*decoder))
return;
- ocsd_destroy_dcd_tree(decoder->dcd_tree);
- decoder->dcd_tree = NULL;
- free(decoder);
+ ocsd_destroy_dcd_tree((*decoder)->dcd_tree);
+ (*decoder)->dcd_tree = NULL;
+ zfree(decoder);
}
const char *cs_etm_decoder__get_name(struct cs_etm_decoder *decoder)
diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h
index 12c782fa6db2..2a8214f17958 100644
--- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h
+++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h
@@ -96,7 +96,7 @@ cs_etm_decoder__new(int num_cpu,
struct cs_etm_decoder_params *d_params,
struct cs_etm_trace_params t_params[]);
-void cs_etm_decoder__free(struct cs_etm_decoder *decoder);
+void cs_etm_decoder__zfree(struct cs_etm_decoder **decoder);
int cs_etm_decoder__add_mem_access_cb(struct cs_etm_decoder *decoder,
u64 start, u64 end,
diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
index 2d1ab34f7b6b..bdcf94813129 100644
--- a/tools/perf/util/cs-etm.c
+++ b/tools/perf/util/cs-etm.c
@@ -243,8 +243,12 @@ enum cs_etm_pid_fmt cs_etm__get_pid_fmt(struct cs_etm_queue *etmq)
return etmq->etm->pid_fmt;
}
-static int cs_etm__insert_trace_id_node(struct cs_etm_queue *etmq,
- u8 trace_chan_id, u64 *cpu_metadata)
+/*
+ * Add a traceid_list entry that maps a trace ID to a CPU metadata for this etmq
+ * and update the trace ID in the cpu_metadata.
+ */
+static int cs_etm__map_trace_id(struct cs_etm_queue *etmq, u8 trace_chan_id,
+ u64 *cpu_metadata)
{
/* Get an RB node for this CPU */
struct int_node *inode = intlist__findnew(etmq->traceid_list, trace_chan_id);
@@ -289,8 +293,7 @@ static int cs_etm__insert_trace_id_node(struct cs_etm_queue *etmq,
/* Not one we've seen before, associate the traceID with the metadata pointer */
inode->priv = cpu_metadata;
-
- return 0;
+ return cs_etm__metadata_set_trace_id(trace_chan_id, cpu_metadata);
}
static struct cs_etm_queue *cs_etm__get_queue(struct cs_etm_auxtrace *etm, int cpu)
@@ -309,31 +312,29 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id
{
struct cs_etm_queue *etmq;
- /*
- * If the queue is unformatted then only save one mapping in the
- * queue associated with that CPU so only one decoder is made.
- */
etmq = cs_etm__get_queue(etm, cpu_metadata[CS_ETM_CPU]);
if (!etmq)
return -EINVAL;
+ /*
+ * Ignore HW_IDs for unformatted, a decoder will be made
+ * unconditionally later.
+ */
if (etmq->format == UNFORMATTED)
- return cs_etm__insert_trace_id_node(etmq, trace_chan_id,
- cpu_metadata);
+ return 0;
/*
* Otherwise, version 0 trace IDs are global so save them into every
- * queue.
+ * formatted queue.
*/
for (unsigned int i = 0; i < etm->queues.nr_queues; ++i) {
int ret;
etmq = etm->queues.queue_array[i].priv;
- if (!etmq)
+ if (!etmq || etmq->format == UNFORMATTED)
continue;
- ret = cs_etm__insert_trace_id_node(etmq, trace_chan_id,
- cpu_metadata);
+ ret = cs_etm__map_trace_id(etmq, trace_chan_id, cpu_metadata);
if (ret)
return ret;
}
@@ -344,7 +345,6 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id
static int cs_etm__process_trace_id_v0(struct cs_etm_auxtrace *etm, int cpu,
u64 hw_id)
{
- int err;
u64 *cpu_data;
u8 trace_chan_id = FIELD_GET(CS_AUX_HW_ID_TRACE_ID_MASK, hw_id);
@@ -352,22 +352,13 @@ static int cs_etm__process_trace_id_v0(struct cs_etm_auxtrace *etm, int cpu,
if (cpu_data == NULL)
return -EINVAL;
- err = cs_etm__map_trace_id_v0(etm, trace_chan_id, cpu_data);
- if (err)
- return err;
-
- /*
- * if we are picking up the association from the packet, need to plug
- * the correct trace ID into the metadata for setting up decoders later.
- */
- return cs_etm__metadata_set_trace_id(trace_chan_id, cpu_data);
+ return cs_etm__map_trace_id_v0(etm, trace_chan_id, cpu_data);
}
static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu,
u64 hw_id)
{
struct cs_etm_queue *etmq = cs_etm__get_queue(etm, cpu);
- int ret;
u64 *cpu_data;
u32 sink_id = FIELD_GET(CS_AUX_HW_ID_SINK_ID_MASK, hw_id);
u8 trace_id = FIELD_GET(CS_AUX_HW_ID_TRACE_ID_MASK, hw_id);
@@ -376,6 +367,13 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu,
return -EINVAL;
/*
+ * Ignore HW_IDs for unformatted, a decoder will be made
+ * unconditionally later.
+ */
+ if (etmq->format == UNFORMATTED)
+ return 0;
+
+ /*
* Check sink id hasn't changed in per-cpu mode. In per-thread mode,
* let it pass for now until an actual overlapping trace ID is hit. In
* most cases IDs won't overlap even if the sink changes.
@@ -419,13 +417,41 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu,
if (!cpu_data)
return -EINVAL;
- ret = cs_etm__insert_trace_id_node(etmq, trace_id, cpu_data);
- if (ret)
- return ret;
+ return cs_etm__map_trace_id(etmq, trace_id, cpu_data);
+}
- ret = cs_etm__metadata_set_trace_id(trace_id, cpu_data);
- if (ret)
- return ret;
+static int cs_etm__map_unformatted_trace_ids(struct cs_etm_auxtrace *etm)
+{
+ struct auxtrace_queues *queues = &etm->queues;
+
+ for (unsigned int i = 0; i < queues->nr_queues; i++) {
+ struct auxtrace_queue *queue = &queues->queue_array[i];
+ struct cs_etm_queue *etmq = queue->priv;
+ u64 *cpu_data;
+ int ret;
+
+ if (list_empty(&queue->head) || !etmq || etmq->format != UNFORMATTED)
+ continue;
+
+ if (!intlist__empty(etmq->traceid_list)) {
+ pr_err("CS_ETM: Unformatted queue should't have trace ID\n");
+ return -EINVAL;
+ }
+
+ if (etm->per_thread_decoding)
+ cpu_data = etm->metadata[0];
+ else
+ cpu_data = get_cpu_data(etm, queue->cpu);
+
+ if (!cpu_data) {
+ pr_err("CS_ETM: Unformatted queue missing CPU metadata\n");
+ return -EINVAL;
+ }
+
+ ret = cs_etm__map_trace_id(etmq, 1, cpu_data);
+ if (ret)
+ return ret;
+ }
return 0;
}
@@ -939,7 +965,7 @@ static int cs_etm__flush_events(struct perf_session *session,
struct cs_etm_auxtrace *etm = container_of(session->auxtrace,
struct cs_etm_auxtrace,
auxtrace);
- if (dump_trace)
+ if (dump_trace || auxtrace__dont_decode(session))
return 0;
if (!tool->ordered_events)
@@ -1002,7 +1028,7 @@ static void cs_etm__free_queue(void *priv)
if (!etmq)
return;
- cs_etm_decoder__free(etmq->decoder);
+ cs_etm_decoder__zfree(&etmq->decoder);
cs_etm__free_traceid_queues(etmq);
if (etmq->own_traceid_list) {
@@ -1017,13 +1043,10 @@ static void cs_etm__free_queue(void *priv)
free(etmq);
}
-static void cs_etm__free_events(struct perf_session *session)
+static void cs_etm__free_queues(struct cs_etm_auxtrace *etm)
{
unsigned int i;
- struct cs_etm_auxtrace *aux = container_of(session->auxtrace,
- struct cs_etm_auxtrace,
- auxtrace);
- struct auxtrace_queues *queues = &aux->queues;
+ struct auxtrace_queues *queues = &etm->queues;
for (i = 0; i < queues->nr_queues; i++) {
cs_etm__free_queue(queues->queue_array[i].priv);
@@ -1033,13 +1056,23 @@ static void cs_etm__free_events(struct perf_session *session)
auxtrace_queues__free(queues);
}
+static void cs_etm__free_events(struct perf_session *session)
+{
+ struct cs_etm_auxtrace *aux = container_of(session->auxtrace,
+ struct cs_etm_auxtrace,
+ auxtrace);
+
+ cs_etm__free_queues(aux);
+}
+
static void cs_etm__free(struct perf_session *session)
{
int i;
struct cs_etm_auxtrace *aux = container_of(session->auxtrace,
struct cs_etm_auxtrace,
auxtrace);
- cs_etm__free_events(session);
+
+ cs_etm__free_queues(aux);
session->auxtrace = NULL;
for (i = 0; i < aux->num_cpu; i++)
@@ -2969,7 +3002,7 @@ static int cs_etm__process_event(struct perf_session *session,
struct cs_etm_auxtrace,
auxtrace);
- if (dump_trace)
+ if (dump_trace || auxtrace__dont_decode(session))
return 0;
if (!tool->ordered_events) {
@@ -3476,7 +3509,7 @@ static int cs_etm__create_queue_decoders(struct cs_etm_queue *etmq)
return 0;
out_free_decoder:
- cs_etm_decoder__free(etmq->decoder);
+ cs_etm_decoder__zfree(&etmq->decoder);
out_free:
zfree(&t_params);
return -EINVAL;
@@ -3519,27 +3552,49 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
int err = 0;
int aux_hw_id_found;
int i;
- u64 *ptr = NULL;
+ u64 *ptr = (u64 *) auxtrace_info->priv;
u64 **metadata = NULL;
- /* First the global part */
- ptr = (u64 *) auxtrace_info->priv;
- num_cpu = ptr[CS_PMU_TYPE_CPUS] & 0xffffffff;
+ etm = zalloc(sizeof(*etm));
+ if (!etm)
+ return -ENOMEM;
+
+ session->auxtrace = &etm->auxtrace;
+ etm->auxtrace.free = cs_etm__free;
+ etm->auxtrace.evsel_is_auxtrace = cs_etm__evsel_is_auxtrace;
+ etm->auxtrace.process_event = cs_etm__process_event;
+ etm->auxtrace.process_auxtrace_event = cs_etm__process_auxtrace_event;
+ etm->auxtrace.flush_events = cs_etm__flush_events;
+ etm->auxtrace.free_events = cs_etm__free_events;
+ etm->pmu_type = (unsigned int) ((ptr[CS_PMU_TYPE_CPUS] >> 32) & 0xffffffff);
+
+ /*
+ * Don't go further than the minimum required to identify this event as
+ * auxtrace with cs_etm__evsel_is_auxtrace() so unleader_auxtrace()
+ * works.
+ */
+ if (auxtrace__dont_decode(session))
+ return 0;
/*
* Bound num_cpu by the event size: the global header consumes
* CS_ETM_HEADER_SIZE bytes, and each CPU needs at least one u64
* metadata entry after that.
*/
+ num_cpu = ptr[CS_PMU_TYPE_CPUS] & 0xffffffff;
priv_size = total_size - event_header_size - INFO_HEADER_SIZE -
CS_ETM_HEADER_SIZE;
if (num_cpu <= 0 || priv_size <= 0 ||
- num_cpu > priv_size / (int)sizeof(u64))
- return -EINVAL;
+ num_cpu > priv_size / (int)sizeof(u64)) {
+ err = -EINVAL;
+ goto err_free_etm;
+ }
metadata = zalloc(sizeof(*metadata) * num_cpu);
- if (!metadata)
- return -ENOMEM;
+ if (!metadata) {
+ err = -ENOMEM;
+ goto err_free_etm;
+ }
/* Start parsing after the common part of the header */
i = CS_HEADER_VERSION_MAX;
@@ -3598,13 +3653,6 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
goto err_free_metadata;
}
- etm = zalloc(sizeof(*etm));
-
- if (!etm) {
- err = -ENOMEM;
- goto err_free_metadata;
- }
-
/*
* As all the ETMs run at the same exception level, the system should
* have the same PID format crossing CPUs. So cache the PID format
@@ -3614,7 +3662,7 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
err = auxtrace_queues__init_nr(&etm->queues, max_cpu + 1);
if (err)
- goto err_free_etm;
+ goto err_free_metadata;
for (unsigned int j = 0; j < etm->queues.nr_queues; ++j) {
err = cs_etm__setup_queue(etm, &etm->queues.queue_array[j], j);
@@ -3652,7 +3700,6 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
etm->session = session;
etm->num_cpu = num_cpu;
- etm->pmu_type = (unsigned int) ((ptr[CS_PMU_TYPE_CPUS] >> 32) & 0xffffffff);
etm->snapshot_mode = (ptr[CS_ETM_SNAPSHOT] != 0);
etm->metadata = metadata;
etm->auxtrace_type = auxtrace_info->type;
@@ -3679,14 +3726,6 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
"you can specify the itrace option 'T' for timestamp decoding\n"
"if the Coresight timestamp on the platform is same with the kernel time.\n\n");
- etm->auxtrace.process_event = cs_etm__process_event;
- etm->auxtrace.process_auxtrace_event = cs_etm__process_auxtrace_event;
- etm->auxtrace.flush_events = cs_etm__flush_events;
- etm->auxtrace.free_events = cs_etm__free_events;
- etm->auxtrace.free = cs_etm__free;
- etm->auxtrace.evsel_is_auxtrace = cs_etm__evsel_is_auxtrace;
- session->auxtrace = &etm->auxtrace;
-
cs_etm__setup_timeless_decoding(etm);
etm->tc.time_shift = tc->time_shift;
@@ -3750,6 +3789,11 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
goto err_free_queues;
}
+ /* Create IDs for TRBE sinks so that a decoder is made for each one. */
+ err = cs_etm__map_unformatted_trace_ids(etm);
+ if (err)
+ goto err_free_queues;
+
err = cs_etm__create_decoders(etm);
if (err)
goto err_free_queues;
@@ -3758,14 +3802,14 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
return 0;
err_free_queues:
- auxtrace_queues__free(&etm->queues);
- session->auxtrace = NULL;
-err_free_etm:
- zfree(&etm);
+ cs_etm__free_queues(etm);
err_free_metadata:
/* No need to check @metadata[j], free(NULL) is supported */
for (int j = 0; j < num_cpu; j++)
zfree(&metadata[j]);
zfree(&metadata);
+err_free_etm:
+ session->auxtrace = NULL;
+ zfree(&etm);
return err;
}
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
index 6b737e195ce1..7552c9171d90 100644
--- a/tools/perf/util/debug.h
+++ b/tools/perf/util/debug.h
@@ -33,10 +33,9 @@ extern int debug_type_profile;
})
#define pr_info(fmt, ...) \
eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_debug(fmt, ...) \
- eprintf(1, verbose, pr_fmt(fmt), ##__VA_ARGS__)
#define pr_debugN(n, fmt, ...) \
- eprintf(n, verbose, pr_fmt(fmt), ##__VA_ARGS__)
+ (unlikely(verbose >= (n)) ? eprintf(n, verbose, pr_fmt(fmt), ##__VA_ARGS__) : 0)
+#define pr_debug(fmt, ...) pr_debugN(1, pr_fmt(fmt), ##__VA_ARGS__)
#define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__)
#define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__)
#define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__)
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index 5478c134e7e3..7c289b67b5bd 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -1176,84 +1176,6 @@ int symbol__strerror_disassemble(struct map_symbol *ms, int errnum, char *buf, s
return 0;
}
-static int dso__disassemble_filename(struct dso *dso, char *filename, size_t filename_size)
-{
- char linkname[PATH_MAX];
- char *build_id_filename;
- char *build_id_path = NULL;
- char *pos;
- int len;
-
- if (dso__symtab_type(dso) == DSO_BINARY_TYPE__KALLSYMS &&
- !dso__is_kcore(dso))
- return SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX;
-
- build_id_filename = dso__build_id_filename(dso, NULL, 0, false);
- if (build_id_filename) {
- /*
- * This is a path in perf's own build id cache, not a path on
- * the profiled system, so the symfs layout does not apply.
- */
- path__join(filename, filename_size, symbol_conf.symfs,
- build_id_filename);
- free(build_id_filename);
- } else {
- if (dso__has_build_id(dso))
- return ENOMEM;
- goto fallback;
- }
-
- build_id_path = strdup(filename);
- if (!build_id_path)
- return ENOMEM;
-
- /*
- * old style build-id cache has name of XX/XXXXXXX.. while
- * new style has XX/XXXXXXX../{elf,kallsyms,vdso}.
- * extract the build-id part of dirname in the new style only.
- */
- pos = strrchr(build_id_path, '/');
- if (pos && strlen(pos) < SBUILD_ID_SIZE - 2)
- dirname(build_id_path);
-
- if (dso__is_kcore(dso))
- goto fallback;
-
- len = readlink(build_id_path, linkname, sizeof(linkname) - 1);
- if (len < 0)
- goto fallback;
-
- linkname[len] = '\0';
- if (strstr(linkname, DSO__NAME_KALLSYMS) ||
- access(filename, R_OK)) {
-fallback:
- /*
- * If we don't have build-ids or the build-id file isn't in the
- * cache, or is just a kallsyms file, well, lets hope that this
- * DSO is the same as when 'perf record' ran.
- */
- if (dso__kernel(dso) && dso__long_name(dso)[0] == '/')
- snprintf(filename, filename_size, "%s", dso__long_name(dso));
- else
- __symbol__join_symfs(filename, filename_size, dso__long_name(dso));
-
- mutex_lock(dso__lock(dso));
- if (access(filename, R_OK) && errno == ENOENT && dso__nsinfo(dso)) {
- char *new_name = dso__filename_with_chroot(dso, filename);
- if (new_name) {
- strlcpy(filename, new_name, filename_size);
- free(new_name);
- }
- }
- mutex_unlock(dso__lock(dso));
- } else if (dso__binary_type(dso) == DSO_BINARY_TYPE__NOT_FOUND) {
- dso__set_binary_type(dso, DSO_BINARY_TYPE__BUILD_ID_CACHE);
- }
-
- free(build_id_path);
- return 0;
-}
-
static int symbol__disassemble_raw(char *filename, struct symbol *sym,
struct annotate_args *args)
{
@@ -1566,14 +1488,29 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
struct annotation_options *options = args->options;
struct map *map = args->ms->map;
struct dso *dso = map__dso(map);
- char symfs_filename[PATH_MAX];
+ char *symfs_filename;
bool delete_extract = false;
struct kcore_extract kce;
+ enum dso_binary_type dbt;
bool decomp = false;
- int err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename));
+ int err;
- if (err)
- return err;
+ dbt = dso__find_binary_type(dso);
+
+ if (dbt == DSO_BINARY_TYPE__KALLSYMS)
+ return SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX;
+
+ mutex_lock(dso__lock(dso));
+ symfs_filename = dso__get_filename(dso, "", &decomp, dbt);
+ mutex_unlock(dso__lock(dso));
+
+ if (symfs_filename == NULL) {
+ if (dbt == DSO_BINARY_TYPE__BPF_PROG_INFO ||
+ dbt == DSO_BINARY_TYPE__BPF_IMAGE)
+ symfs_filename = (char *)dso__long_name(dso);
+ else
+ return SYMBOL_ANNOTATE_ERRNO__COULDNT_DETERMINE_FILE_TYPE;
+ }
pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__,
symfs_filename, sym->name, map__unmap_ip(map, sym->start),
@@ -1581,9 +1518,7 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso__long_name(dso), sym, sym->name);
- if (dso__binary_type(dso) == DSO_BINARY_TYPE__NOT_FOUND) {
- return SYMBOL_ANNOTATE_ERRNO__COULDNT_DETERMINE_FILE_TYPE;
- } else if (dso__is_kcore(dso)) {
+ if (dso__is_kcore(dso)) {
kce.addr = map__rip_2objdump(map, sym->start);
kce.kcore_filename = symfs_filename;
kce.len = sym->end - sym->start;
@@ -1591,18 +1526,8 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
if (!kcore_extract__create(&kce)) {
delete_extract = true;
- strlcpy(symfs_filename, kce.extract_filename, sizeof(symfs_filename));
- }
- } else if (dso__needs_decompress(dso)) {
- char tmp[KMOD_DECOMP_LEN];
-
- if (dso__decompress_kmodule_path(dso, symfs_filename, tmp, sizeof(tmp)) < 0)
- return -1;
-
- /* empty pathname means file wasn't actually compressed */
- if (tmp[0] != '\0') {
- decomp = true;
- strcpy(symfs_filename, tmp);
+ dso__put_filename(dso, symfs_filename, decomp);
+ symfs_filename = kce.extract_filename;
}
}
@@ -1660,11 +1585,10 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
pr_debug("Disassembled with %s\n", perf_disassembler__strs[dis]);
}
out_remove_tmp:
- if (decomp)
- unlink(symfs_filename);
-
if (delete_extract)
kcore_extract__delete(&kce);
+ else if (dbt != DSO_BINARY_TYPE__BPF_PROG_INFO && dbt != DSO_BINARY_TYPE__BPF_IMAGE)
+ dso__put_filename(dso, symfs_filename, decomp);
return err;
}
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index d3017c82ffb5..ce98613868e7 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -833,34 +833,149 @@ void dso__data_close(struct dso *dso)
mutex_unlock(dso__data_open_lock());
}
-static void try_to_open_dso(struct dso *dso, struct machine *machine)
- EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock)
+static int dso__find_binary_type_locked(struct dso *dso)
+ EXCLUSIVE_LOCKS_REQUIRED(dso__lock(dso))
{
enum dso_binary_type binary_type_data[] = {
DSO_BINARY_TYPE__BUILD_ID_CACHE,
DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
+ dso__symtab_type(dso),
DSO_BINARY_TYPE__NOT_FOUND,
};
int i = 0;
- struct dso_data *dso_data = dso__data(dso);
+ char *path;
+ bool found = false;
+ bool decomp = false;
+ int saved_errno;
- if (dso_data->fd >= 0)
- return;
+ if (dso__bintype_searched(dso))
+ return dso__binary_type(dso);
+ dso__set_bintype_searched(dso);
- if (dso__binary_type(dso) != DSO_BINARY_TYPE__NOT_FOUND) {
- dso_data->fd = open_dso(dso, machine);
- goto out;
+ switch (dso__binary_type(dso)) {
+ case DSO_BINARY_TYPE__KALLSYMS:
+ case DSO_BINARY_TYPE__GUEST_KALLSYMS:
+ case DSO_BINARY_TYPE__VMLINUX:
+ case DSO_BINARY_TYPE__GUEST_VMLINUX:
+ case DSO_BINARY_TYPE__KCORE:
+ case DSO_BINARY_TYPE__GUEST_KCORE:
+ /* Nothing to do with kernel images */
+ WARN_ON(!dso__kernel(dso));
+ return dso__binary_type(dso);
+ case DSO_BINARY_TYPE__BPF_PROG_INFO:
+ case DSO_BINARY_TYPE__BPF_IMAGE:
+ case DSO_BINARY_TYPE__OOL:
+ case DSO_BINARY_TYPE__JAVA_JIT:
+ /* Same for the special DSOs */
+ return dso__binary_type(dso);
+ case DSO_BINARY_TYPE__GUEST_KMODULE:
+ case DSO_BINARY_TYPE__GUEST_KMODULE_COMP:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_DSO:
+ case DSO_BINARY_TYPE__BUILD_ID_CACHE:
+ /* We don't expect these are set; fall through */
+ case DSO_BINARY_TYPE__NOT_FOUND:
+ /* Let's find it out (for user DSOs or kernel modules) */
+ break;
+ case DSO_BINARY_TYPE__FEDORA_DEBUGINFO:
+ case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO:
+ case DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO:
+ case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO:
+ case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO:
+ case DSO_BINARY_TYPE__BUILDID_DEBUGINFO:
+ case DSO_BINARY_TYPE__GNU_DEBUGDATA:
+ case DSO_BINARY_TYPE__DEBUGLINK:
+ default:
+ /* Unexpected binary types (usually debug only) */
+ break;
}
do {
dso__set_binary_type(dso, binary_type_data[i++]);
- dso_data->fd = open_dso(dso, machine);
- if (dso_data->fd >= 0)
- goto out;
+ errno = 0;
+ path = dso__get_filename(dso, "", &decomp, dso__binary_type(dso));
+ if (path == NULL) {
+ if (errno == EMFILE)
+ goto open_err;
+ continue;
+ }
+
+ errno = 0;
+ found = filename__has_section(path, ".text");
+ if (!found)
+ saved_errno = errno;
+ dso__put_filename(dso, path, decomp);
+
+ if (found)
+ return dso__binary_type(dso);
+ else if (saved_errno == EMFILE)
+ goto open_err;
} while (dso__binary_type(dso) != DSO_BINARY_TYPE__NOT_FOUND);
-out:
+
+ return dso__binary_type(dso);
+
+open_err:
+ /* Too many open files. Allow retry after closing one. */
+ RC_CHK_ACCESS(dso)->bintype_searched = 0;
+ dso__set_binary_type(dso, DSO_BINARY_TYPE__NOT_FOUND);
+ return -1;
+}
+
+#define FIND_MAX_RETRY 3
+static int __dso__find_binary_type(struct dso *dso)
+ EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock)
+{
+ int ret;
+ int retry = 0;
+
+again:
+ mutex_lock(dso__lock(dso));
+ ret = dso__find_binary_type_locked(dso);
+ mutex_unlock(dso__lock(dso));
+
+ if (ret == -1 && retry++ < FIND_MAX_RETRY) {
+ if (dso__data_open_cnt) {
+ close_first_dso();
+ goto again;
+ }
+ }
+
+ if (ret == -1) {
+ mutex_lock(dso__lock(dso));
+ ret = dso__binary_type(dso);
+ dso__set_bintype_searched(dso);
+ mutex_unlock(dso__lock(dso));
+ }
+
+ return ret;
+}
+#undef FIND_MAX_RETRY
+
+enum dso_binary_type dso__find_binary_type(struct dso *dso)
+{
+ int ret;
+
+ mutex_lock(dso__data_open_lock());
+ ret = __dso__find_binary_type(dso);
+ mutex_unlock(dso__data_open_lock());
+
+ return ret;
+}
+
+static void try_to_open_dso(struct dso *dso, struct machine *machine)
+ EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock)
+{
+ struct dso_data *dso_data = dso__data(dso);
+
+ if (dso_data->fd >= 0)
+ return;
+
+ __dso__find_binary_type(dso);
+ dso_data->fd = open_dso(dso, machine);
+
if (dso_data->fd >= 0)
dso_data->status = DSO_DATA_STATUS_OK;
else
@@ -1377,9 +1492,10 @@ uint16_t dso__e_machine_endian(struct dso *dso, struct machine *machine, uint32_
bool *is_big_endian)
{
uint16_t e_machine = EM_NONE;
+ enum dso_binary_type dbt = dso__find_binary_type(dso);
int fd;
- switch (dso__binary_type(dso)) {
+ switch (dbt) {
case DSO_BINARY_TYPE__KALLSYMS:
case DSO_BINARY_TYPE__GUEST_KALLSYMS:
case DSO_BINARY_TYPE__VMLINUX:
@@ -1722,8 +1838,6 @@ struct dso *dso__new_id(const char *name, const struct dso_id *id)
dso->srclines = RB_ROOT_CACHED;
dso->data_types = RB_ROOT;
dso->global_vars = RB_ROOT;
- dso->data.fd = -1;
- dso->data.status = DSO_DATA_STATUS_UNKNOWN;
dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND;
dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND;
dso->dbginfo_type = DSO_BINARY_TYPE__NOT_FOUND;
@@ -2113,6 +2227,7 @@ void dso__find_dbginfo_type(struct dso *dso)
DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,
DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
+ DSO_BINARY_TYPE__DEBUGLINK,
DSO_BINARY_TYPE__NOT_FOUND,
};
const enum dso_binary_type *type;
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 3f08d45e7f53..d5f7382d4e13 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -349,6 +349,7 @@ DECLARE_RC_STRUCT(dso) {
u8 short_name_allocated:1;
u8 long_name_allocated:1;
u8 is_64_bit:1;
+ u8 bintype_searched:1;
u8 debuginfo_searched:1;
bool sorted_by_name;
bool loaded;
@@ -759,7 +760,7 @@ static inline const char *dso__symsrc_filename(const struct dso *dso)
return RC_CHK_ACCESS(dso)->symsrc_filename;
}
-void dso__set_symsrc_filename(struct dso *dso, char *val);
+void dso__set_symsrc_filename(struct dso *dso, char *val) EXCLUSIVE_LOCKS_REQUIRED(dso__lock(dso));
static inline void dso__free_symsrc_filename(struct dso *dso)
{
@@ -957,6 +958,8 @@ void dso__reset_find_symbol_cache(struct dso *dso);
size_t dso__fprintf_symbols_by_name(struct dso *dso, FILE *fp);
size_t dso__fprintf(struct dso *dso, FILE *fp);
+enum dso_binary_type dso__find_binary_type(struct dso *dso);
+
static inline enum dso_binary_type dso__binary_type(const struct dso *dso)
{
return RC_CHK_ACCESS(dso)->binary_type;
@@ -967,6 +970,16 @@ static inline void dso__set_binary_type(struct dso *dso, enum dso_binary_type bt
RC_CHK_ACCESS(dso)->binary_type = bt;
}
+static inline bool dso__bintype_searched(const struct dso *dso)
+{
+ return RC_CHK_ACCESS(dso)->bintype_searched;
+}
+
+static inline void dso__set_bintype_searched(struct dso *dso)
+{
+ RC_CHK_ACCESS(dso)->bintype_searched = 1;
+}
+
static inline bool dso__is_vmlinux(const struct dso *dso)
{
enum dso_binary_type bt = dso__binary_type(dso);
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 9c5e7510f0c0..d060fca8090f 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -3005,6 +3005,19 @@ check:
return false;
}
+static void evsel__close_fds(struct evsel *evsel, int start_cpu_map_idx,
+ int cpu_map_idx, int thread, int nthreads)
+{
+ do {
+ while (--thread >= 0) {
+ if (FD(evsel, cpu_map_idx, thread) >= 0)
+ close(FD(evsel, cpu_map_idx, thread));
+ FD(evsel, cpu_map_idx, thread) = -1;
+ }
+ thread = nthreads;
+ } while (--cpu_map_idx >= start_cpu_map_idx);
+}
+
static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,
struct perf_thread_map *threads,
int start_cpu_map_idx, int end_cpu_map_idx)
@@ -3014,6 +3027,12 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,
enum rlimit_action set_rlimit = NO_CHANGE;
struct perf_cpu cpu;
+ /*
+ * A failed attempt being retried after evsel__fallback() would
+ * otherwise keep group members out of their group in get_group_fd().
+ */
+ evsel->supported = true;
+
if (evsel__is_retire_lat(evsel)) {
err = evsel__tpebs_open(evsel);
goto out;
@@ -3098,7 +3117,11 @@ retry_open:
bpf_counter__install_pe(evsel, idx, fd);
- if (unlikely(test_attr__enabled())) {
+ /*
+ * Support probes aren't what was asked for, leave them
+ * out of the PERF_TEST_ATTR dump.
+ */
+ if (unlikely(test_attr__enabled() && !evsel->support_probe)) {
test_attr__open(&evsel->core.attr, pid, cpu,
fd, group_fd, evsel->open_flags);
}
@@ -3140,8 +3163,14 @@ try_fallback:
if (err == -EMFILE && rlimit__increase_nofile(&set_rlimit))
goto retry_open;
- if (err == -EINVAL && evsel__detect_missing_features(evsel, cpu))
+ if (err == -EINVAL && evsel__detect_missing_features(evsel, cpu)) {
+ /*
+ * The restart opens the events again, so close what this
+ * attempt opened before its entries are overwritten.
+ */
+ evsel__close_fds(evsel, start_cpu_map_idx, idx, thread, nthreads);
goto fallback_missing_features;
+ }
if (evsel__precise_ip_fallback(evsel))
goto retry_open;
@@ -3151,14 +3180,7 @@ out_close:
threads->err_thread = thread;
old_errno = errno;
- do {
- while (--thread >= 0) {
- if (FD(evsel, idx, thread) >= 0)
- close(FD(evsel, idx, thread));
- FD(evsel, idx, thread) = -1;
- }
- thread = nthreads;
- } while (--idx >= start_cpu_map_idx);
+ evsel__close_fds(evsel, start_cpu_map_idx, idx, thread, nthreads);
errno = old_errno;
out:
if (err)
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 174f3414fd3c..53f3641b66e1 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -120,6 +120,7 @@ struct evsel {
u64 db_id;
bool uniquified_name;
bool supported;
+ bool support_probe; /* Opened just to check for support. */
bool needs_swap;
bool disabled;
bool no_aux_samples;
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 443694926f1a..e75a4fcce975 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -409,7 +409,8 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)
u64 prev_period = he->stat.period;
u64 prev_latency = he->stat.latency;
- if (prev_period == 0)
+ if (prev_period == 0 &&
+ (!symbol_conf.cumulate_callchain || he->stat_acc->period == 0))
return true;
he_stat__decay(&he->stat);
@@ -442,7 +443,8 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)
}
}
- return he->stat.period == 0 && he->stat.latency == 0;
+ return he->stat.period == 0 && he->stat.latency == 0 &&
+ (!symbol_conf.cumulate_callchain || he->stat_acc->period == 0);
}
static void hists__delete_entry(struct hists *hists, struct hist_entry *he)
diff --git a/tools/perf/util/libbfd.c b/tools/perf/util/libbfd.c
index a907d7c1ac94..992e2c2a7198 100644
--- a/tools/perf/util/libbfd.c
+++ b/tools/perf/util/libbfd.c
@@ -288,7 +288,7 @@ int libbfd__addr2line(const char *dso_name, u64 addr,
if (node && inline_list__append_dso_a2l(dso, node, sym)) {
inline_node__clear_frames(node);
- ret = 0;
+ ret = -ENOMEM;
goto out;
}
@@ -302,7 +302,7 @@ int libbfd__addr2line(const char *dso_name, u64 addr,
if (node != NULL) {
if (inline_list__append_dso_a2l(dso, node, sym)) {
inline_node__clear_frames(node);
- ret = 0;
+ ret = -ENOMEM;
goto out;
}
}
@@ -312,7 +312,7 @@ int libbfd__addr2line(const char *dso_name, u64 addr,
if (file) {
*file = a2l->filename ? strdup(a2l->filename) : NULL;
if (!*file) {
- /* Leave ret as 0 so that another addr2line is tried. */
+ ret = a2l->filename ? -ENOMEM : 0;
goto out;
}
}
@@ -463,7 +463,7 @@ int dso__load_bfd_symbols(struct dso *dso, const char *debugfile)
symbols__fixup_duplicate(dso__symbols(dso));
dso__set_adjust_symbols(dso, true);
- err = 0;
+ err = dso__has_symbols(dso);
out_free:
free(symbols);
out_close:
diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c
index c4504cceb313..e660623338d5 100644
--- a/tools/perf/util/libdw.c
+++ b/tools/perf/util/libdw.c
@@ -7,6 +7,7 @@
#include "callchain.h"
#include <fcntl.h>
#include <unistd.h>
+#include <string.h>
#include <elfutils/libdwfl.h>
static const Dwfl_Callbacks offline_callbacks = {
@@ -175,10 +176,15 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr,
int lineno = 0;
int ret = 0;
+ if (!dso__has_srcline(dso))
+ return 0;
+
mutex_lock(dso__lock(dso));
dwfl = dso__libdw_dwfl(dso);
- if (!dwfl)
+ if (!dwfl) {
+ ret = -1;
goto out;
+ }
mod = dwfl_addrmodule(dwfl, addr);
if (!mod) {
@@ -192,14 +198,23 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr,
* libdw.
*/
if (!dwfl_module_getdwarf(mod, &bias)) {
- ret = -1;
+ /*
+ * Free the Dwfl session so the mmap'd ELF binary is unmapped
+ * immediately, and mark the DSO as lacking srcline/DWARF info
+ * so neither libdw nor slower fallback backends (addr2line
+ * subprocess, libbfd) are repeatedly invoked on stripped DSOs.
+ */
+ dso__free_libdw(dso);
+ dso__set_has_srcline(dso, false);
+ ret = 0;
goto out;
}
/* Find source line information for the address. */
dwline = dwfl_module_getsrc(mod, addr + bias);
if (!dwline) {
- ret = -1;
+ /* conclusively missing information -> abort fallbacks */
+ ret = 0;
goto out;
}
@@ -228,7 +243,7 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr,
free(*file);
*file = NULL;
}
- ret = 0;
+ ret = -ENOMEM;
goto out;
}
@@ -245,7 +260,7 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr,
*file = NULL;
}
inline_node__clear_frames(node);
- ret = 0;
+ ret = args.err;
goto out;
}
}
diff --git a/tools/perf/util/libdw.h b/tools/perf/util/libdw.h
index b12094737415..c8f319ce35df 100644
--- a/tools/perf/util/libdw.h
+++ b/tools/perf/util/libdw.h
@@ -23,7 +23,8 @@ struct symbol;
* finds the source line information for the given address, and optionally
* resolves inline function call chains.
*
- * Returns 1 on success (found), 0 on failure (not found).
+ * Returns 1 on success (found), 0 when DWARF/line info is conclusively
+ * missing, and < 0 on initialization or uncompiled error.
*/
int libdw__addr2line(u64 addr, char **file,
unsigned int *line_nr, struct dso *dso,
@@ -47,7 +48,7 @@ static inline int libdw__addr2line(u64 addr __maybe_unused, char **file __maybe_
struct inline_node *node __maybe_unused,
struct symbol *sym __maybe_unused)
{
- return 0;
+ return -1;
}
static inline void dso__free_libdw(struct dso *dso __maybe_unused)
diff --git a/tools/perf/util/llvm-c-helpers.cpp b/tools/perf/util/llvm-c-helpers.cpp
index 004081bd12c9..d990166100f0 100644
--- a/tools/perf/util/llvm-c-helpers.cpp
+++ b/tools/perf/util/llvm-c-helpers.cpp
@@ -50,20 +50,20 @@ static LLVMSymbolizer *get_symbolizer()
return instance;
}
-/* Returns 0 on error, 1 on success. */
+/* Returns < 0 on error, 0 when line info is missing, 1 on success. */
static int extract_file_and_line(const DILineInfo &line_info, char **file,
unsigned int *line)
{
- if (file) {
- if (line_info.FileName == "<invalid>") {
- /* Match the convention of libbfd. */
+ if (line_info.FileName == "<invalid>") {
+ if (file)
*file = nullptr;
- } else {
- /* The caller expects to get something it can free(). */
- *file = strdup(line_info.FileName.c_str());
- if (*file == nullptr)
- return 0;
- }
+ return 0;
+ }
+ if (file) {
+ /* The caller expects to get something it can free(). */
+ *file = strdup(line_info.FileName.c_str());
+ if (*file == nullptr)
+ return -ENOMEM;
}
if (line)
*line = line_info.Line;
@@ -86,30 +86,42 @@ int llvm_addr2line(const char *dso_name, u64 addr,
Expected<DIInliningInfo> res_or_err =
symbolizer->symbolizeInlinedCode(dso_name,
sectioned_addr);
- if (!res_or_err)
- return 0;
+ if (!res_or_err) {
+ consumeError(res_or_err.takeError());
+ return -1;
+ }
unsigned num_frames = res_or_err->getNumberOfFrames();
if (num_frames == 0)
return 0;
- if (extract_file_and_line(res_or_err->getFrame(0),
- file, line) == 0)
- return 0;
+ int ret = extract_file_and_line(res_or_err->getFrame(0),
+ file, line);
+ if (ret <= 0)
+ return ret;
*inline_frames = (llvm_a2l_frame *)calloc(
num_frames, sizeof(**inline_frames));
- if (*inline_frames == nullptr)
- return 0;
+ if (*inline_frames == nullptr) {
+ if (file)
+ zfree(file);
+ return -ENOMEM;
+ }
for (unsigned i = 0; i < num_frames; ++i) {
const DILineInfo &src = res_or_err->getFrame(i);
llvm_a2l_frame &dst = (*inline_frames)[i];
- if (src.FileName == "<invalid>")
- /* Match the convention of libbfd. */
- dst.filename = nullptr;
- else
- dst.filename = strdup(src.FileName.c_str());
+ if (src.FileName == "<invalid>") {
+ for (unsigned j = 0; j < i; ++j) {
+ zfree(&(*inline_frames)[j].filename);
+ zfree(&(*inline_frames)[j].funcname);
+ }
+ zfree(inline_frames);
+ if (file)
+ zfree(file);
+ return 0;
+ }
+ dst.filename = strdup(src.FileName.c_str());
dst.funcname = strdup(src.FunctionName.c_str());
dst.line = src.Line;
@@ -120,7 +132,9 @@ int llvm_addr2line(const char *dso_name, u64 addr,
zfree(&(*inline_frames)[j].funcname);
}
zfree(inline_frames);
- return 0;
+ if (file)
+ zfree(file);
+ return -ENOMEM;
}
}
@@ -131,8 +145,10 @@ int llvm_addr2line(const char *dso_name, u64 addr,
Expected<DILineInfo> res_or_err =
symbolizer->symbolizeCode(dso_name, sectioned_addr);
- if (!res_or_err)
- return 0;
+ if (!res_or_err) {
+ consumeError(res_or_err.takeError());
+ return -1;
+ }
return extract_file_and_line(*res_or_err, file, line);
}
}
diff --git a/tools/perf/util/llvm.c b/tools/perf/util/llvm.c
index 722e71aab100..0ecb1a53359e 100644
--- a/tools/perf/util/llvm.c
+++ b/tools/perf/util/llvm.c
@@ -44,8 +44,11 @@ int llvm__addr2line(const char *dso_name __maybe_unused, u64 addr __maybe_unused
int num_frames = llvm_addr2line(dso_name, addr, file, line,
node && unwind_inlines, &inline_frames);
- if (num_frames == 0 || !inline_frames) {
- /* Error, or we didn't want inlines. */
+ if (num_frames <= 0 || !inline_frames) {
+ /*
+ * Error (< 0 -> iterate fallbacks), missing line info (0), or
+ * we resolved lines without wanting inlines (1).
+ */
return num_frames;
}
@@ -60,8 +63,11 @@ int llvm__addr2line(const char *dso_name __maybe_unused, u64 addr __maybe_unused
inline_frames[i].line);
}
if (inline_list__append(inline_sym, srcline, node) != 0) {
+ if (file)
+ zfree(file);
+ inline_node__clear_frames(node);
free_llvm_inline_frames(inline_frames, num_frames);
- return 0;
+ return -1;
}
}
free_llvm_inline_frames(inline_frames, num_frames);
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 1d9d3bf57720..076d167380f2 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -2860,7 +2860,7 @@ static int thread__resolve_callchain_sample(struct thread *thread,
if (chain)
chain_nr = chain->nr;
- if (evsel__has_branch_callstack(evsel)) {
+ if (chain && evsel__has_branch_callstack(evsel)) {
struct perf_env *env = evsel__env(evsel);
err = resolve_lbr_callchain_sample(thread, cursor, sample, parent,
diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c
index 358e70c4f3ed..6dcc4a9304b9 100644
--- a/tools/perf/util/mmap.c
+++ b/tools/perf/util/mmap.c
@@ -330,7 +330,7 @@ int mmap__mmap(struct mmap *map, struct mmap_params *mp, int fd, struct perf_cpu
int perf_mmap__push(struct mmap *md, void *to,
int push(struct mmap *map, void *to, void *buf, size_t size))
{
- u64 head = perf_mmap__read_head(&md->core);
+ u64 head;
unsigned char *data = md->core.base + page_size;
unsigned long size;
void *buf;
@@ -340,6 +340,7 @@ int perf_mmap__push(struct mmap *md, void *to,
if (rc < 0)
return (rc == -EAGAIN) ? 1 : -1;
+ head = md->core.overwrite ? md->core.start : md->core.end;
size = md->core.end - md->core.start;
if ((md->core.start & md->core.mask) + size != (md->core.end & md->core.mask)) {
diff --git a/tools/perf/util/mutex.c b/tools/perf/util/mutex.c
index 7aa1f3f55a7d..df3b7cce3d5a 100644
--- a/tools/perf/util/mutex.c
+++ b/tools/perf/util/mutex.c
@@ -17,37 +17,106 @@ static void check_err(const char *fn, int err)
#define CHECK_ERR(err) check_err(__func__, err)
-static void __mutex_init(struct mutex *mtx, bool pshared, bool recursive)
+#ifdef HAVE_PTHREAD_MUTEX_ROBUST
+/*
+ * A robust mutex whose owner died while holding it comes back as EOWNERDEAD,
+ * with the caller holding it: make it consistent, which takes it over; failing
+ * that, a give_up caller unlocks it, leaving it ENOTRECOVERABLE, not leaked.
+ */
+static int mutex_take_over(struct mutex *mtx, int err, bool give_up)
+{
+ if (err != EOWNERDEAD)
+ return err;
+
+ pr_debug("mutex: taking over one left by a dead owner\n");
+ err = pthread_mutex_consistent(&mtx->lock);
+ if (err && give_up)
+ CHECK_ERR(pthread_mutex_unlock(&mtx->lock));
+
+ return err;
+}
+#else
+#define mutex_take_over(mtx, err, give_up) ((void)(mtx), (void)(give_up), (err))
+#endif
+
+static bool __mutex_init(struct mutex *mtx, bool pshared, bool recursive, bool robust)
{
pthread_mutexattr_t attr;
+ bool ok = false, robust_unavailable = false;
+ int err;
- CHECK_ERR(pthread_mutexattr_init(&attr));
+ err = pthread_mutexattr_init(&attr);
+ if (err)
+ goto out;
#ifndef NDEBUG
/* In normal builds enable error checking, such as recursive usage. */
- CHECK_ERR(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK));
+ err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+ if (err)
+ goto out_destroy;
#endif
- if (recursive)
- CHECK_ERR(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE));
- if (pshared)
- CHECK_ERR(pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED));
- CHECK_ERR(pthread_mutex_init(&mtx->lock, &attr));
+ if (recursive) {
+ err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ if (err)
+ goto out_destroy;
+ }
+ if (pshared) {
+ err = pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+ if (err)
+ goto out_destroy;
+ }
+ if (robust) {
+#ifdef HAVE_PTHREAD_MUTEX_ROBUST
+ err = pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST);
+ if (err)
+ robust_unavailable = true;
+#else
+ robust_unavailable = true;
+#endif
+ if (robust_unavailable)
+ goto out_destroy;
+ }
+
+ err = pthread_mutex_init(&mtx->lock, &attr);
+ if (!err)
+ ok = true;
+ else if (robust && pshared && err == ENOTSUP)
+ /*
+ * glibc refuses a robust process shared mutex with ENOTSUP when
+ * set_robust_list(2) isn't usable, i.e. when the machine provides
+ * no robust mutexes for the process shared case: an unavailable
+ * API, not a bad request.
+ */
+ robust_unavailable = true;
+out_destroy:
CHECK_ERR(pthread_mutexattr_destroy(&attr));
+out:
+ if (robust_unavailable)
+ pr_debug("mutex: robust mutexes aren't available\n");
+ else if (err)
+ CHECK_ERR(err);
+
+ return ok;
}
void mutex_init(struct mutex *mtx)
{
- __mutex_init(mtx, /*pshared=*/false, /*recursive=*/false);
+ __mutex_init(mtx, /*pshared=*/false, /*recursive=*/false, /*robust=*/false);
}
void mutex_init_pshared(struct mutex *mtx)
{
- __mutex_init(mtx, /*pshared=*/true, /*recursive=*/false);
+ __mutex_init(mtx, /*pshared=*/true, /*recursive=*/false, /*robust=*/false);
}
void mutex_init_recursive(struct mutex *mtx)
{
- __mutex_init(mtx, /*pshared=*/false, /*recursive=*/true);
+ __mutex_init(mtx, /*pshared=*/false, /*recursive=*/true, /*robust=*/false);
+}
+
+bool mutex_init_pshared_robust(struct mutex *mtx)
+{
+ return __mutex_init(mtx, /*pshared=*/true, /*recursive=*/false, /*robust=*/true);
}
void mutex_destroy(struct mutex *mtx)
@@ -58,7 +127,8 @@ void mutex_destroy(struct mutex *mtx)
void mutex_lock(struct mutex *mtx)
NO_THREAD_SAFETY_ANALYSIS
{
- CHECK_ERR(pthread_mutex_lock(&mtx->lock));
+ CHECK_ERR(mutex_take_over(mtx, pthread_mutex_lock(&mtx->lock),
+ /*give_up=*/false));
}
void mutex_unlock(struct mutex *mtx)
@@ -69,7 +139,8 @@ void mutex_unlock(struct mutex *mtx)
bool mutex_trylock(struct mutex *mtx)
{
- int ret = pthread_mutex_trylock(&mtx->lock);
+ int ret = mutex_take_over(mtx, pthread_mutex_trylock(&mtx->lock),
+ /*give_up=*/true);
if (ret == 0)
return true; /* Lock acquired. */
@@ -111,7 +182,9 @@ void cond_destroy(struct cond *cnd)
void cond_wait(struct cond *cnd, struct mutex *mtx)
{
- CHECK_ERR(pthread_cond_wait(&cnd->cond, &mtx->lock));
+ CHECK_ERR(mutex_take_over(mtx,
+ pthread_cond_wait(&cnd->cond, &mtx->lock),
+ /*give_up=*/false));
}
void cond_signal(struct cond *cnd)
diff --git a/tools/perf/util/mutex.h b/tools/perf/util/mutex.h
index 555493767588..5de1065ea2b9 100644
--- a/tools/perf/util/mutex.h
+++ b/tools/perf/util/mutex.h
@@ -142,6 +142,12 @@ void mutex_init(struct mutex *mtx);
void mutex_init_pshared(struct mutex *mtx);
/* Initializes a mutex that may be recursively held on the same thread. */
void mutex_init_recursive(struct mutex *mtx);
+/*
+ * As mutex_init_pshared(), plus the robust attribute: a process dying while
+ * holding it doesn't leave it locked, but the takeover isn't reported and the
+ * data it protects may be mid update. Returns false when not available.
+ */
+bool mutex_init_pshared_robust(struct mutex *mtx);
void mutex_destroy(struct mutex *mtx);
void mutex_lock(struct mutex *mtx) EXCLUSIVE_LOCK_FUNCTION(*mtx);
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 2f3317ce17f0..f3ba70d4f76b 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
#include <linux/hw_breakpoint.h>
#include <linux/err.h>
#include <linux/list_sort.h>
#include <linux/zalloc.h>
#include <dirent.h>
-#include <errno.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include "cpumap.h"
diff --git a/tools/perf/util/pfm.c b/tools/perf/util/pfm.c
index f80d6b0df47a..ad9bc481da70 100644
--- a/tools/perf/util/pfm.c
+++ b/tools/perf/util/pfm.c
@@ -141,6 +141,7 @@ static bool is_libpfm_event_supported(const char *name, struct perf_cpu_map *cpu
return false;
evsel->is_libpfm_event = true;
+ evsel->support_probe = true;
ret = evsel__open(evsel, cpus, threads);
if (ret == -EACCES) {
diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c
index 0242243681b6..77428d9e73c4 100644
--- a/tools/perf/util/print-events.c
+++ b/tools/perf/util/print-events.c
@@ -147,6 +147,7 @@ bool is_event_supported(u8 type, u64 config)
evsel = evsel__new(&attr);
if (evsel) {
+ evsel->support_probe = true;
ret = evsel__open(evsel, NULL, tmap) >= 0;
if (!ret) {
diff --git a/tools/perf/util/print_insn.h b/tools/perf/util/print_insn.h
index cefa5c5f246e..3de9438af9cd 100644
--- a/tools/perf/util/print_insn.h
+++ b/tools/perf/util/print_insn.h
@@ -4,6 +4,7 @@
#include <stddef.h>
#include <stdio.h>
+#include <sys/types.h>
#include <linux/types.h>
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 4eb17bdc5fd1..60dbe2dad5eb 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -3824,10 +3824,12 @@ static PyObject *pyrf__parse_events(PyObject *self, PyObject *args)
perf_evlist__set_maps(evlist__core(evlist), cpus, threads);
if (parse_events(evlist, input, &err)) {
parse_events_error__print(&err, input);
+ parse_events_error__exit(&err);
PyErr_SetFromErrno(PyExc_OSError);
evlist__put(evlist);
return NULL;
}
+ parse_events_error__exit(&err);
result = pyrf_evlist__from_evlist(evlist);
evlist__put(evlist);
return result;
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c
index a8d16106f619..23951c35fb66 100644
--- a/tools/perf/util/srcline.c
+++ b/tools/perf/util/srcline.c
@@ -181,9 +181,9 @@ static int addr2line(const char *dso_name, u64 addr, char **file, unsigned int *
break;
case A2L_STYLE_UNKNOWN:
default:
- break;
+ return 0;
}
- if (ret > 0)
+ if (ret >= 0)
return ret;
}
@@ -250,6 +250,10 @@ static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
addr2line(dso_name, addr, /*file=*/NULL, /*line_nr=*/NULL, dso,
/*unwind_inlines=*/true, node, sym);
+ if (!dso__has_srcline(dso) && list_empty(&node->val)) {
+ inline_node__delete(node);
+ return NULL;
+ }
return node;
}
@@ -276,8 +280,8 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
if (dso_name == NULL)
goto out_err;
- if (!addr2line(dso_name, addr, &file, &line, dso,
- unwind_inlines, /*node=*/NULL, sym)) {
+ if (addr2line(dso_name, addr, &file, &line, dso,
+ unwind_inlines, /*node=*/NULL, sym) <= 0) {
free(dso_name);
goto out_err;
}
@@ -302,6 +306,7 @@ out_err:
dso__set_has_srcline(dso, false);
dso__free_a2l(dso);
dso__free_a2l_libbfd(dso);
+ dso__free_libdw(dso);
}
mutex_unlock(dso__lock(dso));
out:
@@ -331,8 +336,8 @@ char *get_srcline_split(struct dso *dso, u64 addr, unsigned *line)
if (dso_name == NULL)
goto out_err;
- if (!addr2line(dso_name, addr, &file, line, dso, /*unwind_inlines=*/true,
- /*node=*/NULL, /*sym=*/NULL)) {
+ if (addr2line(dso_name, addr, &file, line, dso, /*unwind_inlines=*/true,
+ /*node=*/NULL, /*sym=*/NULL) <= 0) {
free(dso_name);
goto out_err;
}
@@ -350,6 +355,7 @@ out_err:
dso__set_has_srcline(dso, false);
dso__free_a2l(dso);
dso__free_a2l_libbfd(dso);
+ dso__free_libdw(dso);
}
mutex_unlock(dso__lock(dso));
@@ -448,6 +454,9 @@ struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr,
char *dso_name;
struct inline_node *node;
+ if (!dso__has_srcline(dso))
+ return NULL;
+
dso_name = srcline_dso_name(dso);
if (dso_name == NULL)
return NULL;
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index e955c3feddcd..1e2716c6af45 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -228,7 +228,8 @@ bool filename__has_section(const char *filename, const char *sec)
if (gelf_getehdr(elf, &ehdr) == NULL)
goto elf_out;
- found = !!elf_section_by_name(elf, &ehdr, &shdr, sec, NULL);
+ found = !!elf_section_by_name(elf, &ehdr, &shdr, sec, NULL) &&
+ shdr.sh_type == SHT_PROGBITS;
elf_out:
elf_end(elf);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 5d98888d068c..b61a574ce9a3 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -967,23 +967,6 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
return count;
}
-static uint16_t machine_or_dso_e_machine(struct machine *machine, struct dso *dso)
-{
- uint16_t e_machine = EM_NONE;
- /* DSO should be most accurate */
- if (dso)
- e_machine = dso__e_machine(dso, machine, /*e_flags=*/NULL);
-
- if (e_machine != EM_NONE)
- return e_machine;
-
- /* Check the global environment next. */
- if (machine && machine->env && machine->env->e_machine != EM_NONE)
- return machine->env->e_machine;
-
- return perf_env__e_machine(machine ? machine->env : NULL, /*e_flags=*/NULL);
-}
-
/*
* Split the symbols into maps, making sure there are no overlaps, i.e. the
* kernel range is broken in several maps, named [kernel].N, as we don't have
@@ -1005,7 +988,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
return -1;
machine = maps__machine(kmaps);
- e_machine = machine_or_dso_e_machine(machine, dso);
+ e_machine = perf_env__e_machine(machine->env, /*e_flags=*/NULL);
while (next) {
char *module;
@@ -1099,6 +1082,13 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
return -1;
dso__set_kernel(ndso, dso__kernel(dso));
+ if (dso__kernel(dso) == DSO_SPACE__KERNEL_GUEST) {
+ dso__set_binary_type(ndso, DSO_BINARY_TYPE__GUEST_KALLSYMS);
+ dso__set_symtab_type(ndso, DSO_BINARY_TYPE__GUEST_KALLSYMS);
+ } else {
+ dso__set_binary_type(ndso, DSO_BINARY_TYPE__KALLSYMS);
+ dso__set_symtab_type(ndso, DSO_BINARY_TYPE__KALLSYMS);
+ }
dso__set_loaded(ndso);
curr_map = map__new2(pos->start, ndso);
@@ -1560,7 +1550,7 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
free(new_node);
}
- if (machine_or_dso_e_machine(machine, dso) == EM_X86_64) {
+ if (perf_env__e_machine(machine->env, /*e_flags=*/NULL) == EM_X86_64) {
u64 addr;
/*
@@ -1848,7 +1838,7 @@ int dso__load(struct dso *dso, struct map *map)
ret = dso__load_guest_kernel_sym(dso, map);
machine = maps__machine(map__kmaps(map));
- if (machine && machine_or_dso_e_machine(machine, dso) == EM_X86_64)
+ if (perf_env__e_machine(machine->env, /*e_flags=*/NULL) == EM_X86_64)
machine__map_x86_64_entry_trampolines(machine, dso);
goto out;
}
@@ -1857,6 +1847,7 @@ int dso__load(struct dso *dso, struct map *map)
if (perfmap) {
ret = dso__load_perf_map(map_path, dso);
+ dso__set_binary_type(dso, DSO_BINARY_TYPE__JAVA_JIT);
dso__set_symtab_type(dso, ret > 0
? DSO_BINARY_TYPE__JAVA_JIT
: DSO_BINARY_TYPE__NOT_FOUND);
@@ -1930,9 +1921,10 @@ int dso__load(struct dso *dso, struct map *map)
if (nsexit)
nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
- if (bfdrc == 0) {
- ret = 0;
- break;
+ if (bfdrc > 0) {
+ dso__set_symtab_type(dso, symtab_type);
+ ret = 1;
+ goto out_destroy;
}
if (!is_reg || sirc < 0)
@@ -1952,19 +1944,6 @@ int dso__load(struct dso *dso, struct map *map)
if (next_slot) {
ss_pos++;
-
- /*
- * The binary type is used to find the file containing
- * the executed instructions, so prefer the types that
- * refer to the actual object over debug-only files such
- * as DSO_BINARY_TYPE__DEBUGLINK.
- */
- if (dso__binary_type(dso) == DSO_BINARY_TYPE__NOT_FOUND ||
- symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE ||
- (symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_DSO &&
- dso__binary_type(dso) != DSO_BINARY_TYPE__BUILD_ID_CACHE))
- dso__set_binary_type(dso, symtab_type);
-
if (syms_ss && runtime_ss)
break;
} else {
@@ -1997,6 +1976,7 @@ int dso__load(struct dso *dso, struct map *map)
ret += nr_plt;
}
+out_destroy:
for (; ss_pos > 0; ss_pos--)
symsrc__destroy(&ss_[ss_pos - 1]);
out_free:
@@ -2079,7 +2059,7 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map)
if (filename != NULL) {
err = dso__load_vmlinux(dso, map, filename, true);
if (err > 0)
- goto out;
+ dso__set_symtab_type(dso, DSO_BINARY_TYPE__BUILD_ID_CACHE);
}
out:
return err;
@@ -2235,8 +2215,10 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map)
filename = __dso__build_id_filename(dso, NULL, 0, false, false);
if (filename != NULL) {
err = dso__load_vmlinux(dso, map, filename, true);
- if (err > 0)
+ if (err > 0) {
+ dso__set_symtab_type(dso, DSO_BINARY_TYPE__BUILD_ID_CACHE);
return err;
+ }
}
if (!symbol_conf.ignore_vmlinux && vmlinux_path != NULL) {