diff options
Diffstat (limited to 'tools')
33 files changed, 698 insertions, 121 deletions
diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt index 68c89190e79c..cb081ac59fd1 100644 --- a/tools/perf/Documentation/perf-config.txt +++ b/tools/perf/Documentation/perf-config.txt @@ -110,6 +110,14 @@ Given a $HOME/.perfconfig like this: order = caller sort-key = function + [report] + # Defaults + sort-order = comm,dso,symbol + percent-limit = 0 + queue-size = 0 + children = true + group = true + Variables ~~~~~~~~~ diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index f37d123d5dac..e6c9902c6d82 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -21,6 +21,8 @@ or 'perf probe' [options] --vars='PROBEPOINT' or 'perf probe' [options] --funcs +or +'perf probe' [options] --definition='PROBE' [...] DESCRIPTION ----------- @@ -34,6 +36,8 @@ OPTIONS -k:: --vmlinux=PATH:: Specify vmlinux path which has debuginfo (Dwarf binary). + Only when using this with --definition, you can give an offline + vmlinux file. -m:: --module=MODNAME|PATH:: @@ -96,6 +100,11 @@ OPTIONS can also list functions in a user space executable / shared library. This also can accept a FILTER rule argument. +-D:: +--definition=:: + Show trace-event definition converted from given probe-event instead + of write it into tracing/[k,u]probe_events. + --filter=FILTER:: (Only for --vars and --funcs) Set filter. FILTER is a combination of glob pattern, see FILTER PATTERN for detail. diff --git a/tools/perf/Documentation/perfconfig.example b/tools/perf/Documentation/perfconfig.example index 1d8d5bc4cd2d..2b477c1d1efe 100644 --- a/tools/perf/Documentation/perfconfig.example +++ b/tools/perf/Documentation/perfconfig.example @@ -27,3 +27,12 @@ use_offset = true jump_arrows = true show_nr_jumps = false + +[report] + + # Defaults + sort-order = comm,dso,symbol + percent-limit = 0 + queue-size = 0 + children = true + group = true diff --git a/tools/perf/arch/arm/include/dwarf-regs-table.h b/tools/perf/arch/arm/include/dwarf-regs-table.h new file mode 100644 index 000000000000..f298d034c37b --- /dev/null +++ b/tools/perf/arch/arm/include/dwarf-regs-table.h @@ -0,0 +1,9 @@ +#ifdef DEFINE_DWARF_REGSTR_TABLE +/* This is included in perf/util/dwarf-regs.c */ + +static const char * const arm_regstr_tbl[] = { + "%r0", "%r1", "%r2", "%r3", "%r4", + "%r5", "%r6", "%r7", "%r8", "%r9", "%r10", + "%fp", "%ip", "%sp", "%lr", "%pc", +}; +#endif diff --git a/tools/perf/arch/arm64/include/dwarf-regs-table.h b/tools/perf/arch/arm64/include/dwarf-regs-table.h new file mode 100644 index 000000000000..26759363f921 --- /dev/null +++ b/tools/perf/arch/arm64/include/dwarf-regs-table.h @@ -0,0 +1,13 @@ +#ifdef DEFINE_DWARF_REGSTR_TABLE +/* This is included in perf/util/dwarf-regs.c */ + +static const char * const aarch64_regstr_tbl[] = { + "%r0", "%r1", "%r2", "%r3", "%r4", + "%r5", "%r6", "%r7", "%r8", "%r9", + "%r10", "%r11", "%r12", "%r13", "%r14", + "%r15", "%r16", "%r17", "%r18", "%r19", + "%r20", "%r21", "%r22", "%r23", "%r24", + "%r25", "%r26", "%r27", "%r28", "%r29", + "%lr", "%sp", +}; +#endif diff --git a/tools/perf/arch/powerpc/include/dwarf-regs-table.h b/tools/perf/arch/powerpc/include/dwarf-regs-table.h new file mode 100644 index 000000000000..db4730f5585c --- /dev/null +++ b/tools/perf/arch/powerpc/include/dwarf-regs-table.h @@ -0,0 +1,27 @@ +#ifdef DEFINE_DWARF_REGSTR_TABLE +/* This is included in perf/util/dwarf-regs.c */ + +/* + * Reference: + * http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html + * http://refspecs.linux-foundation.org/elf/elfspec_ppc.pdf + */ +#define REG_DWARFNUM_NAME(reg, idx) [idx] = "%" #reg + +static const char * const powerpc_regstr_tbl[] = { + "%gpr0", "%gpr1", "%gpr2", "%gpr3", "%gpr4", + "%gpr5", "%gpr6", "%gpr7", "%gpr8", "%gpr9", + "%gpr10", "%gpr11", "%gpr12", "%gpr13", "%gpr14", + "%gpr15", "%gpr16", "%gpr17", "%gpr18", "%gpr19", + "%gpr20", "%gpr21", "%gpr22", "%gpr23", "%gpr24", + "%gpr25", "%gpr26", "%gpr27", "%gpr28", "%gpr29", + "%gpr30", "%gpr31", + REG_DWARFNUM_NAME(msr, 66), + REG_DWARFNUM_NAME(ctr, 109), + REG_DWARFNUM_NAME(link, 108), + REG_DWARFNUM_NAME(xer, 101), + REG_DWARFNUM_NAME(dar, 119), + REG_DWARFNUM_NAME(dsisr, 118), +}; + +#endif diff --git a/tools/perf/arch/s390/include/dwarf-regs-table.h b/tools/perf/arch/s390/include/dwarf-regs-table.h new file mode 100644 index 000000000000..9da74a933bd6 --- /dev/null +++ b/tools/perf/arch/s390/include/dwarf-regs-table.h @@ -0,0 +1,8 @@ +#ifdef DEFINE_DWARF_REGSTR_TABLE +/* This is included in perf/util/dwarf-regs.c */ + +static const char * const s390_regstr_tbl[] = { + "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", + "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", +}; +#endif diff --git a/tools/perf/arch/sh/include/dwarf-regs-table.h b/tools/perf/arch/sh/include/dwarf-regs-table.h new file mode 100644 index 000000000000..3a2deaf3dab4 --- /dev/null +++ b/tools/perf/arch/sh/include/dwarf-regs-table.h @@ -0,0 +1,25 @@ +#ifdef DEFINE_DWARF_REGSTR_TABLE +/* This is included in perf/util/dwarf-regs.c */ + +const char * const sh_regstr_tbl[] = { + "r0", + "r1", + "r2", + "r3", + "r4", + "r5", + "r6", + "r7", + "r8", + "r9", + "r10", + "r11", + "r12", + "r13", + "r14", + "r15", + "pc", + "pr", +}; + +#endif diff --git a/tools/perf/arch/sparc/include/dwarf-regs-table.h b/tools/perf/arch/sparc/include/dwarf-regs-table.h new file mode 100644 index 000000000000..12c07619002c --- /dev/null +++ b/tools/perf/arch/sparc/include/dwarf-regs-table.h @@ -0,0 +1,18 @@ +#ifdef DEFINE_DWARF_REGSTR_TABLE +/* This is included in perf/util/dwarf-regs.c */ + +static const char * const sparc_regstr_tbl[] = { + "%g0", "%g1", "%g2", "%g3", "%g4", "%g5", "%g6", "%g7", + "%o0", "%o1", "%o2", "%o3", "%o4", "%o5", "%sp", "%o7", + "%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7", + "%i0", "%i1", "%i2", "%i3", "%i4", "%i5", "%fp", "%i7", + "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7", + "%f8", "%f9", "%f10", "%f11", "%f12", "%f13", "%f14", "%f15", + "%f16", "%f17", "%f18", "%f19", "%f20", "%f21", "%f22", "%f23", + "%f24", "%f25", "%f26", "%f27", "%f28", "%f29", "%f30", "%f31", + "%f32", "%f33", "%f34", "%f35", "%f36", "%f37", "%f38", "%f39", + "%f40", "%f41", "%f42", "%f43", "%f44", "%f45", "%f46", "%f47", + "%f48", "%f49", "%f50", "%f51", "%f52", "%f53", "%f54", "%f55", + "%f56", "%f57", "%f58", "%f59", "%f60", "%f61", "%f62", "%f63", +}; +#endif diff --git a/tools/perf/arch/x86/include/dwarf-regs-table.h b/tools/perf/arch/x86/include/dwarf-regs-table.h new file mode 100644 index 000000000000..39ac7cbb525b --- /dev/null +++ b/tools/perf/arch/x86/include/dwarf-regs-table.h @@ -0,0 +1,14 @@ +#ifdef DEFINE_DWARF_REGSTR_TABLE +/* This is included in perf/util/dwarf-regs.c */ + +static const char * const x86_32_regstr_tbl[] = { + "%ax", "%cx", "%dx", "%bx", "$stack",/* Stack address instead of %sp */ + "%bp", "%si", "%di", +}; + +static const char * const x86_64_regstr_tbl[] = { + "%ax", "dx", "%cx", "%bx", "%si", "%di", + "%bp", "%sp", "%r8", "%r9", "%r10", "%r11", + "%r12", "%r13", "%r14", "%r15", +}; +#endif diff --git a/tools/perf/arch/xtensa/include/dwarf-regs-table.h b/tools/perf/arch/xtensa/include/dwarf-regs-table.h new file mode 100644 index 000000000000..aa0444a33fe6 --- /dev/null +++ b/tools/perf/arch/xtensa/include/dwarf-regs-table.h @@ -0,0 +1,8 @@ +#ifdef DEFINE_DWARF_REGSTR_TABLE +/* This is included in perf/util/dwarf-regs.c */ + +static const char * const xtensa_regstr_tbl[] = { + "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", + "a8", "a9", "a10", "a11", "a12", "a13", "a14", "a15", +}; +#endif diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 9c1034d81b4f..f07b23011b22 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -204,8 +204,6 @@ static int __cmd_annotate(struct perf_annotate *ann) struct perf_evsel *pos; u64 total_nr_samples; - machines__set_symbol_filter(&session->machines, symbol__annotate_init); - if (ann->cpu_list) { ret = perf_session__cpu_bitmap(session, ann->cpu_list, ann->cpu_bitmap); @@ -367,7 +365,10 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) if (annotate.session == NULL) return -1; - symbol_conf.priv_size = sizeof(struct annotation); + ret = symbol__annotation_init(); + if (ret < 0) + goto out_delete; + symbol_conf.try_vmlinux_path = true; ret = symbol__init(&annotate.session->header.env); diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index ee5b42173ba3..f87996b0cb29 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -326,6 +326,11 @@ static int perf_add_probe_events(struct perf_probe_event *pevs, int npevs) if (ret < 0) goto out_cleanup; + if (params.command == 'D') { /* it shows definition */ + ret = show_probe_trace_events(pevs, npevs); + goto out_cleanup; + } + ret = apply_perf_probe_events(pevs, npevs); if (ret < 0) goto out_cleanup; @@ -454,6 +459,14 @@ out: return ret; } +#ifdef HAVE_DWARF_SUPPORT +#define PROBEDEF_STR \ + "[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT [[NAME=]ARG ...]" +#else +#define PROBEDEF_STR "[EVENT=]FUNC[+OFF|%return] [[NAME=]ARG ...]" +#endif + + static int __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) { @@ -479,13 +492,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) opt_set_filter_with_command, DEFAULT_LIST_FILTER), OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.", opt_set_filter_with_command), - OPT_CALLBACK('a', "add", NULL, -#ifdef HAVE_DWARF_SUPPORT - "[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT" - " [[NAME=]ARG ...]", -#else - "[EVENT=]FUNC[+OFF|%return] [[NAME=]ARG ...]", -#endif + OPT_CALLBACK('a', "add", NULL, PROBEDEF_STR, "probe point definition, where\n" "\t\tGROUP:\tGroup name (optional)\n" "\t\tEVENT:\tEvent name\n" @@ -503,6 +510,9 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) "\t\tARG:\tProbe argument (kprobe-tracer argument format.)\n", #endif opt_add_probe_event), + OPT_CALLBACK('D', "definition", NULL, PROBEDEF_STR, + "Show trace event definition of given traceevent for k/uprobe_events.", + opt_add_probe_event), OPT_BOOLEAN('f', "force", &probe_conf.force_add, "forcibly add events" " with existing name"), OPT_CALLBACK('L', "line", NULL, @@ -548,6 +558,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) set_option_flag(options, 'a', "add", PARSE_OPT_EXCLUSIVE); set_option_flag(options, 'd', "del", PARSE_OPT_EXCLUSIVE); + set_option_flag(options, 'D', "definition", PARSE_OPT_EXCLUSIVE); set_option_flag(options, 'l', "list", PARSE_OPT_EXCLUSIVE); #ifdef HAVE_DWARF_SUPPORT set_option_flag(options, 'L', "line", PARSE_OPT_EXCLUSIVE); @@ -600,6 +611,14 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) */ symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); + /* + * Except for --list, --del and --add, other command doesn't depend + * nor change running kernel. So if user gives offline vmlinux, + * ignore its buildid. + */ + if (!strchr("lda", params.command) && symbol_conf.vmlinux_name) + symbol_conf.ignore_vmlinux_buildid = true; + switch (params.command) { case 'l': if (params.uprobes) { @@ -643,7 +662,9 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) return ret; } break; + case 'D': case 'a': + /* Ensure the last given target is used */ if (params.target && !params.target_used) { pr_err(" Error: -x/-m must follow the probe definitions.\n"); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index b9e046baa5fc..1a07c4cdf6ed 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -984,9 +984,9 @@ repeat: * implementation. */ if (ui__has_annotation()) { - symbol_conf.priv_size = sizeof(struct annotation); - machines__set_symbol_filter(&session->machines, - symbol__annotate_init); + ret = symbol__annotation_init(); + if (ret < 0) + goto error; /* * For searching by name on the "Browse map details". * providing it only in verbose mode not to bloat too diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index a3223aa22213..e0919006fcba 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -680,7 +680,7 @@ static int symbol_filter(struct map *map, struct symbol *sym) return 1; if (symbol__is_idle(sym)) - sym->ignore = true; + sym->idle = 1; return 0; } @@ -783,7 +783,7 @@ static void perf_event__process_sample(struct perf_tool *tool, } } - if (al.sym == NULL || !al.sym->ignore) { + if (al.sym == NULL || !al.sym->idle) { struct hists *hists = evsel__hists(evsel); struct hist_entry_iter iter = { .evsel = evsel, @@ -1324,7 +1324,9 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) if (symbol_conf.cumulate_callchain && !callchain_param.order_set) callchain_param.order = ORDER_CALLER; - symbol_conf.priv_size = sizeof(struct annotation); + status = symbol__annotation_init(); + if (status < 0) + goto out_delete_evlist; symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); if (symbol__init(NULL) < 0) diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c index e63abab7d5a1..77513bf99d1b 100644 --- a/tools/perf/tests/vmlinux-kallsyms.c +++ b/tools/perf/tests/vmlinux-kallsyms.c @@ -28,6 +28,7 @@ int test__vmlinux_matches_kallsyms(int subtest __maybe_unused) enum map_type type = MAP__FUNCTION; struct maps *maps = &vmlinux.kmaps.maps[type]; u64 mem_start, mem_end; + bool header_printed; /* * Step 1: @@ -143,7 +144,7 @@ next_pair: */ s64 skew = mem_end - UM(pair->end); if (llabs(skew) >= page_size) - pr_debug("%#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n", + pr_debug("WARN: %#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n", mem_start, sym->name, mem_end, UM(pair->end)); @@ -154,22 +155,23 @@ next_pair: * kallsyms. */ continue; - } else { pair = machine__find_kernel_symbol_by_name(&kallsyms, type, sym->name, NULL, NULL); if (pair) { if (UM(pair->start) == mem_start) goto next_pair; - pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n", + pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n", mem_start, sym->name, pair->name); } else { - pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n", + pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n", mem_start, sym->name, first_pair->name); } + + continue; } } else - pr_debug("%#" PRIx64 ": %s not on kallsyms\n", + pr_debug("ERR : %#" PRIx64 ": %s not on kallsyms\n", mem_start, sym->name); err = -1; @@ -178,7 +180,7 @@ next_pair: if (!verbose) goto out; - pr_info("Maps only in vmlinux:\n"); + header_printed = false; for (map = maps__first(maps); map; map = map__next(map)) { struct map * @@ -192,13 +194,18 @@ next_pair: (map->dso->kernel ? map->dso->short_name : map->dso->name)); - if (pair) + if (pair) { pair->priv = 1; - else + } else { + if (!header_printed) { + pr_info("WARN: Maps only in vmlinux:\n"); + header_printed = true; + } map__fprintf(map, stderr); + } } - pr_info("Maps in vmlinux with a different name in kallsyms:\n"); + header_printed = false; for (map = maps__first(maps); map; map = map__next(map)) { struct map *pair; @@ -211,24 +218,33 @@ next_pair: continue; if (pair->start == mem_start) { - pair->priv = 1; - pr_info(" %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as", + if (!header_printed) { + pr_info("WARN: Maps in vmlinux with a different name in kallsyms:\n"); + header_printed = true; + } + + pr_info("WARN: %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as", map->start, map->end, map->pgoff, map->dso->name); if (mem_end != pair->end) - pr_info(":\n*%" PRIx64 "-%" PRIx64 " %" PRIx64, + pr_info(":\nWARN: *%" PRIx64 "-%" PRIx64 " %" PRIx64, pair->start, pair->end, pair->pgoff); pr_info(" %s\n", pair->dso->name); pair->priv = 1; } } - pr_info("Maps only in kallsyms:\n"); + header_printed = false; maps = &kallsyms.kmaps.maps[type]; for (map = maps__first(maps); map; map = map__next(map)) { - if (!map->priv) + if (!map->priv) { + if (!header_printed) { + pr_info("WARN: Maps only in kallsyms:\n"); + header_printed = true; + } map__fprintf(map, stderr); + } } out: machine__exit(&kallsyms); diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 91c5f6e1af59..f1a6d17c5a37 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -98,6 +98,7 @@ endif libperf-$(CONFIG_DWARF) += probe-finder.o libperf-$(CONFIG_DWARF) += dwarf-aux.o +libperf-$(CONFIG_DWARF) += dwarf-regs.o libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 25a9259a6a6e..1b59e3129216 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -491,13 +491,6 @@ static struct ins *ins__find(const char *name) return bsearch(name, instructions, nmemb, sizeof(struct ins), ins__key_cmp); } -int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym) -{ - struct annotation *notes = symbol__annotation(sym); - pthread_mutex_init(¬es->lock, NULL); - return 0; -} - int symbol__alloc_hist(struct symbol *sym) { struct annotation *notes = symbol__annotation(sym); diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index f67ccb027561..e96f4daed9b9 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -177,7 +177,6 @@ enum symbol_disassemble_errno { int symbol__strerror_disassemble(struct symbol *sym, struct map *map, int errnum, char *buf, size_t buflen); -int symbol__annotate_init(struct map *map, struct symbol *sym); int symbol__annotate_printf(struct symbol *sym, struct map *map, struct perf_evsel *evsel, bool full_paths, int min_pcnt, int max_lines, int context); diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index a347b19c961a..faec899435f2 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -1085,3 +1085,182 @@ int die_get_var_range(Dwarf_Die *sp_die __maybe_unused, return -ENOTSUP; } #endif + +/* + * die_has_loclist - Check if DW_AT_location of @vr_die is a location list + * @vr_die: a variable DIE + */ +static bool die_has_loclist(Dwarf_Die *vr_die) +{ + Dwarf_Attribute loc; + int tag = dwarf_tag(vr_die); + + if (tag != DW_TAG_formal_parameter && + tag != DW_TAG_variable) + return false; + + return (dwarf_attr_integrate(vr_die, DW_AT_location, &loc) && + dwarf_whatform(&loc) == DW_FORM_sec_offset); +} + +/* + * die_is_optimized_target - Check if target program is compiled with + * optimization + * @cu_die: a CU DIE + * + * For any object in given CU whose DW_AT_location is a location list, + * target program is compiled with optimization. This is applicable to + * clang as well. + */ +bool die_is_optimized_target(Dwarf_Die *cu_die) +{ + Dwarf_Die tmp_die; + + if (die_has_loclist(cu_die)) + return true; + + if (!dwarf_child(cu_die, &tmp_die) && + die_is_optimized_target(&tmp_die)) + return true; + + if (!dwarf_siblingof(cu_die, &tmp_die) && + die_is_optimized_target(&tmp_die)) + return true; + + return false; +} + +/* + * die_search_idx - Search index of given line address + * @lines: Line records of single CU + * @nr_lines: Number of @lines + * @addr: address we are looking for + * @idx: index to be set by this function (return value) + * + * Search for @addr by looping over every lines of CU. If address + * matches, set index of that line in @idx. Note that single source + * line can have multiple line records. i.e. single source line can + * have multiple index. + */ +static bool die_search_idx(Dwarf_Lines *lines, unsigned long nr_lines, + Dwarf_Addr addr, unsigned long *idx) +{ + unsigned long i; + Dwarf_Addr tmp; + + for (i = 0; i < nr_lines; i++) { + if (dwarf_lineaddr(dwarf_onesrcline(lines, i), &tmp)) + return false; + + if (tmp == addr) { + *idx = i; + return true; + } + } + return false; +} + +/* + * die_get_postprologue_addr - Search next address after function prologue + * @entrypc_idx: entrypc index + * @lines: Line records of single CU + * @nr_lines: Number of @lines + * @hignpc: high PC address of function + * @postprologue_addr: Next address after function prologue (return value) + * + * Look for prologue-end marker. If there is no explicit marker, return + * address of next line record or next source line. + */ +static bool die_get_postprologue_addr(unsigned long entrypc_idx, + Dwarf_Lines *lines, + unsigned long nr_lines, + Dwarf_Addr highpc, + Dwarf_Addr *postprologue_addr) +{ + unsigned long i; + int entrypc_lno, lno; + Dwarf_Line *line; + Dwarf_Addr addr; + bool p_end; + + /* entrypc_lno is actual source line number */ + line = dwarf_onesrcline(lines, entrypc_idx); + if (dwarf_lineno(line, &entrypc_lno)) + return false; + + for (i = entrypc_idx; i < nr_lines; i++) { + line = dwarf_onesrcline(lines, i); + + if (dwarf_lineaddr(line, &addr) || + dwarf_lineno(line, &lno) || + dwarf_lineprologueend(line, &p_end)) + return false; + + /* highpc is exclusive. [entrypc,highpc) */ + if (addr >= highpc) + break; + + /* clang supports prologue-end marker */ + if (p_end) + break; + + /* Actual next line in source */ + if (lno != entrypc_lno) + break; + + /* + * Single source line can have multiple line records. + * For Example, + * void foo() { printf("hello\n"); } + * contains two line records. One points to declaration and + * other points to printf() line. Variable 'lno' won't get + * incremented in this case but 'i' will. + */ + if (i != entrypc_idx) + break; + } + + dwarf_lineaddr(line, postprologue_addr); + if (*postprologue_addr >= highpc) + dwarf_lineaddr(dwarf_onesrcline(lines, i - 1), + postprologue_addr); + + return true; +} + +/* + * die_skip_prologue - Use next address after prologue as probe location + * @sp_die: a subprogram DIE + * @cu_die: a CU DIE + * @entrypc: entrypc of the function + * + * Function prologue prepares stack and registers before executing function + * logic. When target program is compiled without optimization, function + * parameter information is only valid after prologue. When we probe entrypc + * of the function, and try to record function parameter, it contains + * garbage value. + */ +void die_skip_prologue(Dwarf_Die *sp_die, Dwarf_Die *cu_die, + Dwarf_Addr *entrypc) +{ + size_t nr_lines = 0; + unsigned long entrypc_idx = 0; + Dwarf_Lines *lines = NULL; + Dwarf_Addr postprologue_addr; + Dwarf_Addr highpc; + + if (dwarf_highpc(sp_die, &highpc)) + return; + + if (dwarf_getsrclines(cu_die, &lines, &nr_lines)) + return; + + if (!die_search_idx(lines, nr_lines, *entrypc, &entrypc_idx)) + return; + + if (!die_get_postprologue_addr(entrypc_idx, lines, nr_lines, + highpc, &postprologue_addr)) + return; + + *entrypc = postprologue_addr; +} diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index dc0ce1adb075..8b6d2f83af02 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h @@ -125,4 +125,12 @@ int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf); /* Get the name and type of given variable DIE, stored as "type\tname" */ int die_get_varname(Dwarf_Die *vr_die, struct strbuf *buf); int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf); + +/* Check if target program is compiled with optimization */ +bool die_is_optimized_target(Dwarf_Die *cu_die); + +/* Use next address after prologue as probe location */ +void die_skip_prologue(Dwarf_Die *sp_die, Dwarf_Die *cu_die, + Dwarf_Addr *entrypc); + #endif diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c new file mode 100644 index 000000000000..62bc4a86a970 --- /dev/null +++ b/tools/perf/util/dwarf-regs.c @@ -0,0 +1,59 @@ +/* + * dwarf-regs.c : Mapping of DWARF debug register numbers into register names. + * + * Written by: Masami Hiramatsu <mhiramat@kernel.org> + */ + +#include <util.h> +#include <debug.h> +#include <dwarf-regs.h> +#include <elf.h> + +#ifndef EM_AARCH64 +#define EM_AARCH64 183 /* ARM 64 bit */ +#endif + +/* Define const char * {arch}_register_tbl[] */ +#define DEFINE_DWARF_REGSTR_TABLE +#include "../arch/x86/include/dwarf-regs-table.h" +#include "../arch/arm/include/dwarf-regs-table.h" +#include "../arch/arm64/include/dwarf-regs-table.h" +#include "../arch/sh/include/dwarf-regs-table.h" +#include "../arch/powerpc/include/dwarf-regs-table.h" +#include "../arch/s390/include/dwarf-regs-table.h" +#include "../arch/sparc/include/dwarf-regs-table.h" +#include "../arch/xtensa/include/dwarf-regs-table.h" + +#define __get_dwarf_regstr(tbl, n) (((n) < ARRAY_SIZE(tbl)) ? (tbl)[(n)] : NULL) + +/* Return architecture dependent register string (for kprobe-tracer) */ +const char *get_dwarf_regstr(unsigned int n, unsigned int machine) +{ + switch (machine) { + case EM_NONE: /* Generic arch - use host arch */ + return get_arch_regstr(n); + case EM_386: + return __get_dwarf_regstr(x86_32_regstr_tbl, n); + case EM_X86_64: + return __get_dwarf_regstr(x86_64_regstr_tbl, n); + case EM_ARM: + return __get_dwarf_regstr(arm_regstr_tbl, n); + case EM_AARCH64: + return __get_dwarf_regstr(aarch64_regstr_tbl, n); + case EM_SH: + return __get_dwarf_regstr(sh_regstr_tbl, n); + case EM_S390: + return __get_dwarf_regstr(s390_regstr_tbl, n); + case EM_PPC: + case EM_PPC64: + return __get_dwarf_regstr(powerpc_regstr_tbl, n); + case EM_SPARC: + case EM_SPARCV9: + return __get_dwarf_regstr(sparc_regstr_tbl, n); + case EM_XTENSA: + return __get_dwarf_regstr(xtensa_regstr_tbl, n); + default: + pr_err("ELF MACHINE %x is not supported.\n", machine); + } + return NULL; +} diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c index 3674e77ad640..9111e0666950 100644 --- a/tools/perf/util/evsel_fprintf.c +++ b/tools/perf/util/evsel_fprintf.c @@ -122,7 +122,7 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, if (!node) break; - if (node->sym && node->sym->ignore) + if (node->sym && node->sym->idle) goto next; printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); @@ -181,7 +181,7 @@ int sample__fprintf_sym(struct perf_sample *sample, struct addr_location *al, if (cursor != NULL) { printed += sample__fprintf_callchain(sample, left_alignment, print_opts, cursor, fp); - } else if (!(al->sym && al->sym->ignore)) { + } else if (!(al->sym && al->sym->idle)) { printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); if (print_ip) diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h index 07c644ed64c4..43bfd8da7919 100644 --- a/tools/perf/util/include/dwarf-regs.h +++ b/tools/perf/util/include/dwarf-regs.h @@ -3,6 +3,12 @@ #ifdef HAVE_DWARF_SUPPORT const char *get_arch_regstr(unsigned int n); +/* + * get_dwarf_regstr - Returns ftrace register string from DWARF regnum + * n: DWARF register number + * machine: ELF machine signature (EM_*) + */ +const char *get_dwarf_regstr(unsigned int n, unsigned int machine); #endif #ifdef HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET diff --git a/tools/perf/util/lzma.c b/tools/perf/util/lzma.c index 95a1acb61245..9ddea5cecd94 100644 --- a/tools/perf/util/lzma.c +++ b/tools/perf/util/lzma.c @@ -29,6 +29,7 @@ int lzma_decompress_to_file(const char *input, int output_fd) lzma_action action = LZMA_RUN; lzma_stream strm = LZMA_STREAM_INIT; lzma_ret ret; + int err = -1; u8 buf_in[BUFSIZE]; u8 buf_out[BUFSIZE]; @@ -45,7 +46,7 @@ int lzma_decompress_to_file(const char *input, int output_fd) if (ret != LZMA_OK) { pr_err("lzma: lzma_stream_decoder failed %s (%d)\n", lzma_strerror(ret), ret); - return -1; + goto err_fclose; } strm.next_in = NULL; @@ -60,7 +61,7 @@ int lzma_decompress_to_file(const char *input, int output_fd) if (ferror(infile)) { pr_err("lzma: read error: %s\n", strerror(errno)); - return -1; + goto err_fclose; } if (feof(infile)) @@ -74,7 +75,7 @@ int lzma_decompress_to_file(const char *input, int output_fd) if (writen(output_fd, buf_out, write_size) != write_size) { pr_err("lzma: write error: %s\n", strerror(errno)); - return -1; + goto err_fclose; } strm.next_out = buf_out; @@ -83,13 +84,15 @@ int lzma_decompress_to_file(const char *input, int output_fd) if (ret != LZMA_OK) { if (ret == LZMA_STREAM_END) - return 0; + break; pr_err("lzma: failed %s\n", lzma_strerror(ret)); - return -1; + goto err_fclose; } } + err = 0; +err_fclose: fclose(infile); - return 0; + return err; } diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 28733962cd80..a543e9ca581c 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -674,6 +674,10 @@ post_process_kernel_probe_trace_events(struct probe_trace_event *tevs, char *tmp; int i, skipped = 0; + /* Skip post process if the target is an offline kernel */ + if (symbol_conf.ignore_vmlinux_buildid) + return 0; + reloc_sym = kernel_get_ref_reloc_sym(); if (!reloc_sym) { pr_warning("Relocated base symbol is not found!\n"); @@ -1614,19 +1618,27 @@ out: return ret; } +/* Returns true if *any* ARG is either C variable, $params or $vars. */ +bool perf_probe_with_var(struct perf_probe_event *pev) +{ + int i = 0; + + for (i = 0; i < pev->nargs; i++) + if (is_c_varname(pev->args[i].var) || + !strcmp(pev->args[i].var, PROBE_ARG_PARAMS) || + !strcmp(pev->args[i].var, PROBE_ARG_VARS)) + return true; + return false; +} + /* Return true if this perf_probe_event requires debuginfo */ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) { - int i; - if (pev->point.file || pev->point.line || pev->point.lazy_line) return true; - for (i = 0; i < pev->nargs; i++) - if (is_c_varname(pev->args[i].var) || - !strcmp(pev->args[i].var, "$params") || - !strcmp(pev->args[i].var, "$vars")) - return true; + if (perf_probe_with_var(pev)) + return true; return false; } @@ -3207,6 +3219,52 @@ int convert_perf_probe_events(struct perf_probe_event *pevs, int npevs) return 0; } +static int show_probe_trace_event(struct probe_trace_event *tev) +{ + char *buf = synthesize_probe_trace_command(tev); + + if (!buf) { + pr_debug("Failed to synthesize probe trace event.\n"); + return -EINVAL; + } + + /* Showing definition always go stdout */ + printf("%s\n", buf); + free(buf); + + return 0; +} + +int show_probe_trace_events(struct perf_probe_event *pevs, int npevs) +{ + struct strlist *namelist = strlist__new(NULL, NULL); + struct probe_trace_event *tev; + struct perf_probe_event *pev; + int i, j, ret = 0; + + if (!namelist) + return -ENOMEM; + + for (j = 0; j < npevs && !ret; j++) { + pev = &pevs[j]; + for (i = 0; i < pev->ntevs && !ret; i++) { + tev = &pev->tevs[i]; + /* Skip if the symbol is out of .text or blacklisted */ + if (!tev->point.symbol && !pev->uprobes) + continue; + + /* Set new name for tev (and update namelist) */ + ret = probe_trace_event__set_name(tev, pev, + namelist, true); + if (!ret) + ret = show_probe_trace_event(tev); + } + } + strlist__delete(namelist); + + return ret; +} + int apply_perf_probe_events(struct perf_probe_event *pevs, int npevs) { int i, ret = 0; @@ -3289,24 +3347,10 @@ out: return ret; } -/* TODO: don't use a global variable for filter ... */ -static struct strfilter *available_func_filter; - -/* - * If a symbol corresponds to a function with global binding and - * matches filter return 0. For all others return 1. - */ -static int filter_available_functions(struct map *map __maybe_unused, - struct symbol *sym) -{ - if (strfilter__compare(available_func_filter, sym->name)) - return 0; - return 1; -} - int show_available_funcs(const char *target, struct strfilter *_filter, bool user) { + struct rb_node *nd; struct map *map; int ret; @@ -3324,9 +3368,7 @@ int show_available_funcs(const char *target, struct strfilter *_filter, return -EINVAL; } - /* Load symbols with given filter */ - available_func_filter = _filter; - ret = map__load(map, filter_available_functions); + ret = map__load(map, NULL); if (ret) { if (ret == -2) { char *str = strfilter__string(_filter); @@ -3343,7 +3385,14 @@ int show_available_funcs(const char *target, struct strfilter *_filter, /* Show all (filtered) symbols */ setup_pager(); - dso__fprintf_symbols_by_name(map->dso, map->type, stdout); + + for (nd = rb_first(&map->dso->symbol_names[map->type]); nd; nd = rb_next(nd)) { + struct symbol_name_rb_node *pos = rb_entry(nd, struct symbol_name_rb_node, rb_node); + + if (strfilter__compare(_filter, pos->sym.name)) + printf("%s\n", pos->sym.name); + } + end: if (user) { map__put(map); diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index f4f45db77c1c..8091d15113f7 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -128,6 +128,8 @@ char *synthesize_perf_probe_point(struct perf_probe_point *pp); int perf_probe_event__copy(struct perf_probe_event *dst, struct perf_probe_event *src); +bool perf_probe_with_var(struct perf_probe_event *pev); + /* Check the perf_probe_event needs debuginfo */ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); @@ -147,6 +149,7 @@ int line_range__init(struct line_range *lr); int add_perf_probe_events(struct perf_probe_event *pevs, int npevs); int convert_perf_probe_events(struct perf_probe_event *pevs, int npevs); int apply_perf_probe_events(struct perf_probe_event *pevs, int npevs); +int show_probe_trace_events(struct perf_probe_event *pevs, int npevs); void cleanup_perf_probe_events(struct perf_probe_event *pevs, int npevs); int del_perf_probe_events(struct strfilter *filter); diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c index 697ef66bff91..6f931e442f14 100644 --- a/tools/perf/util/probe-file.c +++ b/tools/perf/util/probe-file.c @@ -73,11 +73,10 @@ static void print_both_open_warning(int kerr, int uerr) static int open_probe_events(const char *trace_file, bool readwrite) { char buf[PATH_MAX]; - const char *tracing_dir = ""; int ret; - ret = e_snprintf(buf, PATH_MAX, "%s/%s%s", - tracing_path, tracing_dir, trace_file); + ret = e_snprintf(buf, PATH_MAX, "%s/%s", + tracing_path, trace_file); if (ret >= 0) { pr_debug("Opening %s write=%d\n", buf, readwrite); if (readwrite && !probe_event_dry_run) diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index ac4740f8ee3a..8daca4fc1f8d 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -171,6 +171,7 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs) */ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, Dwarf_Op *fb_ops, Dwarf_Die *sp_die, + unsigned int machine, struct probe_trace_arg *tvar) { Dwarf_Attribute attr; @@ -266,7 +267,7 @@ static_var: if (!tvar) return ret2; - regs = get_arch_regstr(regn); + regs = get_dwarf_regstr(regn, machine); if (!regs) { /* This should be a bug in DWARF or this tool */ pr_warning("Mapping for the register number %u " @@ -543,7 +544,7 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) dwarf_diename(vr_die)); ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops, - &pf->sp_die, pf->tvar); + &pf->sp_die, pf->machine, pf->tvar); if (ret == -ENOENT || ret == -EINVAL) { pr_err("Failed to find the location of the '%s' variable at this address.\n" " Perhaps it has been optimized out.\n" @@ -906,6 +907,38 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) return die_walk_lines(sp_die, probe_point_lazy_walker, pf); } +static void skip_prologue(Dwarf_Die *sp_die, struct probe_finder *pf) +{ + struct perf_probe_point *pp = &pf->pev->point; + + /* Not uprobe? */ + if (!pf->pev->uprobes) + return; + + /* Compiled with optimization? */ + if (die_is_optimized_target(&pf->cu_die)) + return; + + /* Don't know entrypc? */ + if (!pf->addr) + return; + + /* Only FUNC and FUNC@SRC are eligible. */ + if (!pp->function || pp->line || pp->retprobe || pp->lazy_line || + pp->offset || pp->abs_address) + return; + + /* Not interested in func parameter? */ + if (!perf_probe_with_var(pf->pev)) + return; + + pr_info("Target program is compiled without optimization. Skipping prologue.\n" + "Probe on address 0x%" PRIx64 " to force probing at the function entry.\n\n", + pf->addr); + + die_skip_prologue(sp_die, &pf->cu_die, &pf->addr); +} + static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) { struct probe_finder *pf = data; @@ -968,6 +1001,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) if (pp->lazy_line) param->retval = find_probe_point_lazy(sp_die, pf); else { + skip_prologue(sp_die, pf); pf->addr += pp->offset; /* TODO: Check the address in this function */ param->retval = call_probe_finder(sp_die, pf); @@ -1106,11 +1140,8 @@ static int debuginfo__find_probes(struct debuginfo *dbg, struct probe_finder *pf) { int ret = 0; - -#if _ELFUTILS_PREREQ(0, 142) Elf *elf; GElf_Ehdr ehdr; - GElf_Shdr shdr; if (pf->cfi_eh || pf->cfi_dbg) return debuginfo__find_probe_location(dbg, pf); @@ -1123,11 +1154,18 @@ static int debuginfo__find_probes(struct debuginfo *dbg, if (gelf_getehdr(elf, &ehdr) == NULL) return -EINVAL; - if (elf_section_by_name(elf, &ehdr, &shdr, ".eh_frame", NULL) && - shdr.sh_type == SHT_PROGBITS) - pf->cfi_eh = dwarf_getcfi_elf(elf); + pf->machine = ehdr.e_machine; + +#if _ELFUTILS_PREREQ(0, 142) + do { + GElf_Shdr shdr; + + if (elf_section_by_name(elf, &ehdr, &shdr, ".eh_frame", NULL) && + shdr.sh_type == SHT_PROGBITS) + pf->cfi_eh = dwarf_getcfi_elf(elf); - pf->cfi_dbg = dwarf_getcfi(dbg->dbg); + pf->cfi_dbg = dwarf_getcfi(dbg->dbg); + } while (0); #endif ret = debuginfo__find_probe_location(dbg, pf); @@ -1155,7 +1193,7 @@ static int copy_variables_cb(Dwarf_Die *die_mem, void *data) (tag == DW_TAG_variable && vf->vars)) { if (convert_variable_location(die_mem, vf->pf->addr, vf->pf->fb_ops, &pf->sp_die, - NULL) == 0) { + pf->machine, NULL) == 0) { vf->args[vf->nargs].var = (char *)dwarf_diename(die_mem); if (vf->args[vf->nargs].var == NULL) { vf->ret = -ENOMEM; @@ -1318,7 +1356,7 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data) tag == DW_TAG_variable) { ret = convert_variable_location(die_mem, af->pf.addr, af->pf.fb_ops, &af->pf.sp_die, - NULL); + af->pf.machine, NULL); if (ret == 0 || ret == -ERANGE) { int ret2; bool externs = !af->child; diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 51137fccb9c8..f1d8558f498e 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -80,6 +80,7 @@ struct probe_finder { Dwarf_CFI *cfi_dbg; #endif Dwarf_Op *fb_ops; /* Frame base attribute */ + unsigned int machine; /* Target machine arch */ struct perf_probe_arg *pvar; /* Current target variable */ struct probe_trace_arg *tvar; /* Current result variable */ }; diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index a811c13a74d6..295d3147a803 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -206,6 +206,37 @@ Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, return NULL; } +static bool want_demangle(bool is_kernel_sym) +{ + return is_kernel_sym ? symbol_conf.demangle_kernel : symbol_conf.demangle; +} + +static char *demangle_sym(struct dso *dso, int kmodule, const char *elf_name) +{ + int demangle_flags = verbose ? (DMGL_PARAMS | DMGL_ANSI) : DMGL_NO_OPTS; + char *demangled = NULL; + + /* + * We need to figure out if the object was created from C++ sources + * DWARF DW_compile_unit has this, but we don't always have access + * to it... + */ + if (!want_demangle(dso->kernel || kmodule)) + return demangled; + + demangled = bfd_demangle(NULL, elf_name, demangle_flags); + if (demangled == NULL) + demangled = java_demangle_sym(elf_name, JAVA_DEMANGLE_NORET); + else if (rust_is_mangled(demangled)) + /* + * Input to Rust demangling is the BFD-demangled + * name which it Rust-demangles in place. + */ + rust_demangle_sym(demangled); + + return demangled; +} + #define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \ for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \ idx < nr_entries; \ @@ -301,11 +332,19 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss, struct map * elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_rel_entries) { + const char *elf_name = NULL; + char *demangled = NULL; symidx = GELF_R_SYM(pos->r_info); plt_offset += shdr_plt.sh_entsize; gelf_getsym(syms, symidx, &sym); + + elf_name = elf_sym__name(&sym, symstrs); + demangled = demangle_sym(dso, 0, elf_name); + if (demangled != NULL) + elf_name = demangled; snprintf(sympltname, sizeof(sympltname), - "%s@plt", elf_sym__name(&sym, symstrs)); + "%s@plt", elf_name); + free(demangled); f = symbol__new(plt_offset, shdr_plt.sh_entsize, STB_GLOBAL, sympltname); @@ -323,11 +362,19 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss, struct map * GElf_Rel pos_mem, *pos; elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_rel_entries) { + const char *elf_name = NULL; + char *demangled = NULL; symidx = GELF_R_SYM(pos->r_info); plt_offset += shdr_plt.sh_entsize; gelf_getsym(syms, symidx, &sym); + + elf_name = elf_sym__name(&sym, symstrs); + demangled = demangle_sym(dso, 0, elf_name); + if (demangled != NULL) + elf_name = demangled; snprintf(sympltname, sizeof(sympltname), - "%s@plt", elf_sym__name(&sym, symstrs)); + "%s@plt", elf_name); + free(demangled); f = symbol__new(plt_offset, shdr_plt.sh_entsize, STB_GLOBAL, sympltname); @@ -685,7 +732,7 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, } /* Always reject images with a mismatched build-id: */ - if (dso->has_build_id) { + if (dso->has_build_id && !symbol_conf.ignore_vmlinux_buildid) { u8 build_id[BUILD_ID_SIZE]; if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0) { @@ -775,11 +822,6 @@ static u64 ref_reloc(struct kmap *kmap) return 0; } -static bool want_demangle(bool is_kernel_sym) -{ - return is_kernel_sym ? symbol_conf.demangle_kernel : symbol_conf.demangle; -} - void __weak arch__sym_update(struct symbol *s __maybe_unused, GElf_Sym *sym __maybe_unused) { } @@ -1070,29 +1112,10 @@ int dso__load_sym(struct dso *dso, struct map *map, sym.st_value -= shdr.sh_addr - shdr.sh_offset; } new_symbol: - /* - * We need to figure out if the object was created from C++ sources - * DWARF DW_compile_unit has this, but we don't always have access - * to it... - */ - if (want_demangle(dso->kernel || kmodule)) { - int demangle_flags = DMGL_NO_OPTS; - if (verbose) - demangle_flags = DMGL_PARAMS | DMGL_ANSI; - - demangled = bfd_demangle(NULL, elf_name, demangle_flags); - if (demangled == NULL) - demangled = java_demangle_sym(elf_name, JAVA_DEMANGLE_NORET); - else if (rust_is_mangled(demangled)) - /* - * Input to Rust demangling is the BFD-demangled - * name which it Rust-demangles in place. - */ - rust_demangle_sym(demangled); + demangled = demangle_sym(dso, kmodule, elf_name); + if (demangled != NULL) + elf_name = demangled; - if (demangled != NULL) - elf_name = demangled; - } f = symbol__new(sym.st_value, sym.st_size, GELF_ST_BIND(sym.st_info), elf_name); free(demangled); @@ -1113,9 +1136,8 @@ new_symbol: * For misannotated, zeroed, ASM function sizes. */ if (nr > 0) { - if (!symbol_conf.allow_aliases) - symbols__fixup_duplicate(&dso->symbols[map->type]); symbols__fixup_end(&dso->symbols[map->type]); + symbols__fixup_duplicate(&dso->symbols[map->type]); if (kmap) { /* * We need to fixup this here too because we create new diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 37e8d20ae03e..98cd50384c32 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -9,6 +9,7 @@ #include <fcntl.h> #include <unistd.h> #include <inttypes.h> +#include "annotate.h" #include "build-id.h" #include "util.h" #include "debug.h" @@ -152,6 +153,9 @@ void symbols__fixup_duplicate(struct rb_root *symbols) struct rb_node *nd; struct symbol *curr, *next; + if (symbol_conf.allow_aliases) + return; + nd = rb_first(symbols); while (nd) { @@ -235,8 +239,13 @@ struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name) if (sym == NULL) return NULL; - if (symbol_conf.priv_size) + if (symbol_conf.priv_size) { + if (symbol_conf.init_annotation) { + struct annotation *notes = (void *)sym; + pthread_mutex_init(¬es->lock, NULL); + } sym = ((void *)sym) + symbol_conf.priv_size; + } sym->start = start; sym->end = len ? start + len : start; @@ -1234,8 +1243,8 @@ int __dso__load_kallsyms(struct dso *dso, const char *filename, if (kallsyms__delta(map, filename, &delta)) return -1; - symbols__fixup_duplicate(&dso->symbols[map->type]); symbols__fixup_end(&dso->symbols[map->type]); + symbols__fixup_duplicate(&dso->symbols[map->type]); if (dso->kernel == DSO_TYPE_GUEST_KERNEL) dso->symtab_type = DSO_BINARY_TYPE__GUEST_KALLSYMS; @@ -1948,6 +1957,23 @@ static bool symbol__read_kptr_restrict(void) return value; } +int symbol__annotation_init(void) +{ + if (symbol_conf.initialized) { + pr_err("Annotation needs to be init before symbol__init()\n"); + return -1; + } + + if (symbol_conf.init_annotation) { + pr_warning("Annotation being initialized multiple times\n"); + return 0; + } + + symbol_conf.priv_size += sizeof(struct annotation); + symbol_conf.init_annotation = true; + return 0; +} + int symbol__init(struct perf_env *env) { const char *symfs; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 699f7cbcfe72..e54ee7c78ca3 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -57,7 +57,7 @@ struct symbol { u64 end; u16 namelen; u8 binding; - bool ignore; + u8 idle:1; u8 arch_sym; char name[0]; }; @@ -88,6 +88,7 @@ struct symbol_conf { unsigned short priv_size; unsigned short nr_events; bool try_vmlinux_path, + init_annotation, force, ignore_vmlinux, ignore_vmlinux_buildid, @@ -277,6 +278,8 @@ struct perf_env; int symbol__init(struct perf_env *env); void symbol__exit(void); void symbol__elf_init(void); +int symbol__annotation_init(void); + struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name); size_t __symbol__fprintf_symname_offs(const struct symbol *sym, const struct addr_location *al, |