From 35d307ea9c3a0b3bc830dc537d006547601a657a Mon Sep 17 00:00:00 2001 From: Daniel Shaulov Date: Tue, 5 May 2020 17:06:22 +0300 Subject: [PATCH 01/11] perf probe: Add support for DW_OP_call_frame_cfa vars Add support for probes on variables with DW_OP_call_frame_cfa as the dwarf operation in the debug info. Some compilers (specifically Golang compiler) output DW_OP_call_frame_cfa instead of DW_OP_fbreg for variables on the stack. If DW_OP_call_frame_cfa is the only expression than it is the same as DW_OP_fbreg with an offset of zero. In the case of the Golang compiler, DW_OP_call_frame_cfa may be followed by DW_OP_consts, with a number and than DW_OP_plus. This trio is the same as DW_OP_fbreg with the number from DW_OP_consts as the offset. With this change, probing on functions in Golang with variables works. Signed-off-by: Daniel Shaulov --- tools/perf/util/probe-finder.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 1b118c9c86a699..238edb60be9693 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -248,11 +248,23 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, } /* If this is based on frame buffer, set the offset */ - if (op->atom == DW_OP_fbreg) { + if (op->atom == DW_OP_fbreg || op->atom == DW_OP_call_frame_cfa) { if (fb_ops == NULL) return -ENOTSUP; ref = true; - offs = op->number; + if (op->atom == DW_OP_fbreg) { + offs = op->number; + } else if (nops == 3) { + /* + * In the case of DW_OP_call_frame_cfa, we either have + * an offset of 0 or we have two more expressions that + * add a const + */ + if ((op + 1)->atom != DW_OP_consts || + (op + 2)->atom != DW_OP_plus) + return -ENOTSUP; + offs = (op + 1)->number; + } op = &fb_ops[0]; } From 3bfa53da1b731ca7973e49115cd697869b810cc5 Mon Sep 17 00:00:00 2001 From: Daniel Shaulov Date: Tue, 5 May 2020 17:09:31 +0300 Subject: [PATCH 02/11] perf: Change libdw libraries to support elfutils>=0.178 Up untill now, static compilation of perf was still trying to dynamically load the backends for libebl. Starting with version 0.178 of elfutils, libebl no longer needs to dynamicall load backends. This means that perf compiled statically with elfutils>=0.178 is trully static. In elfutils 0.178, libebl is also no longer a standalone library, it is included as part of libdw, so there is no longer a need for -lebl. Also - some of the backends required -lpthread, and since they are now included in the static build, we also need to add -lpthread. This change does not support older versions of elfutils. Signed-off-by: Daniel Shaulov --- tools/build/feature/Makefile | 2 +- tools/perf/Makefile.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index 3e55edb3ea5499..eb7933f993c47e 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -137,7 +137,7 @@ $(OUTPUT)test-libopencsd.bin: DWARFLIBS := -ldw ifeq ($(findstring -static,${LDFLAGS}),-static) -DWARFLIBS += -lelf -lebl -lz -llzma -lbz2 +DWARFLIBS += -lelf -lz -llzma -lbz2 -lpthread endif $(OUTPUT)test-dwarf.bin: diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index d8e59d31399a56..c2bb40f41543dc 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -156,7 +156,7 @@ ifdef LIBDW_DIR endif DWARFLIBS := -ldw ifeq ($(findstring -static,${LDFLAGS}),-static) - DWARFLIBS += -lelf -lebl -ldl -lz -llzma -lbz2 + DWARFLIBS += -lelf -ldl -lz -llzma -lbz2 -lpthread endif FEATURE_CHECK_CFLAGS-libdw-dwarf-unwind := $(LIBDW_CFLAGS) FEATURE_CHECK_LDFLAGS-libdw-dwarf-unwind := $(LIBDW_LDFLAGS) $(DWARFLIBS) From 2c7eac4062d4f02441b28452aab3d82a8e8bc67b Mon Sep 17 00:00:00 2001 From: Daniel Shaulov Date: Tue, 5 May 2020 18:06:48 +0300 Subject: [PATCH 03/11] perf probe: Make perf-probe use symbols when debuginfo is incomplete When a binary has debug symbols, but they are incomplete, some files compiled with debug info and some didn't, perf-probe will look for the debuginfo, and when it fails to find it will refuse to add the probe. If the debug info is removed entirely perf-probe gracefully degrades to using symbols. This commit adds the fallback to symbols for cases where dwarf exists but fails The issue was found with a nodejs binary which ships with debug info only for the '.c' files but no for '.cc' (c++) files, and trying to probe a c++ function. Signed-off-by: Daniel Shaulov --- tools/perf/util/probe-event.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index a9cff3a50ddf53..56b5ccb4e62ba6 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -926,6 +926,11 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, */ clear_perf_probe_point(&pev->point); memcpy(&pev->point, &tmp, sizeof(tmp)); + } else { + if (need_dwarf) + return -ENOENT; + pr_debug("Failed to use debug info. Try to use symbols.\n"); + return 0; } } From 9be7a5ece05656233b9a91b49dd9ab6b1efc173b Mon Sep 17 00:00:00 2001 From: Daniel Shaulov Date: Wed, 15 Jul 2020 20:40:07 +0300 Subject: [PATCH 04/11] perf probe: Add uint8* as a supported string type When trying to cast a probe variable to string, the type is checked to be "(unsigned) char *". In Golang debug info, the strings are ultimately "uint8 *", so the cast fails. This commit adds the type to the list of types that can be casted to string, and also prints the type when it is found to not match one the types, for easier debugging of future issues. Signed-off-by: Daniel Shaulov --- tools/perf/util/probe-finder.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 238edb60be9693..81f7ce3cec1d51 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -380,10 +380,11 @@ static int convert_variable_type(Dwarf_Die *vr_die, (*ref_ptr)->user_access = user_access; } if (!die_compare_name(&type, "char") && - !die_compare_name(&type, "unsigned char")) { + !die_compare_name(&type, "unsigned char") && + !die_compare_name(&type, "uint8")) { pr_warning("Failed to cast into string: " - "%s is not (unsigned) char *.\n", - dwarf_diename(vr_die)); + "%s is not (unsigned) char * or uint8 *, it is %s *.\n", + dwarf_diename(vr_die), dwarf_diename(&type)); return -EINVAL; } tvar->type = strdup(cast); From ba2776008fb9133a922e9dffb84f6697057569d7 Mon Sep 17 00:00:00 2001 From: Daniel Shaulov Date: Mon, 20 Jul 2020 16:12:50 +0300 Subject: [PATCH 05/11] perf: Add Dockerfile Add a Dockerfile that can be used to build a static perf The image is based on ubunut:16.04 to have a libc that supports older kernel versions. To build a static perf using the Dockerfile you can run the following (at the repo root): docker build -t buildbox - < tools/perf/Dockerfile docker run -v $PWD:/linux -w /linux/tools/perf buildbox sh -c "make clean all LDFLAGS=-static" Signed-off-by: Daniel Shaulov --- tools/perf/Dockerfile | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 tools/perf/Dockerfile diff --git a/tools/perf/Dockerfile b/tools/perf/Dockerfile new file mode 100644 index 00000000000000..9feb0e9577040f --- /dev/null +++ b/tools/perf/Dockerfile @@ -0,0 +1,39 @@ +FROM ubuntu:xenial-20200619 + +WORKDIR /code +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y \ + build-essential \ + git \ + curl \ + autoconf \ + asciidoc \ + libssl-dev \ + zlib1g-dev \ + libaudit-dev \ + binutils-dev \ + libiberty-dev \ + libcap-dev \ + libdwarf-dev \ + liblzma-dev \ + libnuma-dev \ + libbabeltrace-ctf-dev \ + systemtap-sdt-dev \ + libslang2-dev \ + libzstd-dev \ + libbz2-dev \ + flex \ + bison \ + ; + +# Installing a new version of elfutils and libunwind +RUN curl -L ftp://sourceware.org/pub/elfutils/0.179/elfutils-0.179.tar.bz2 -o /tmp/elfutils-0.179.tar.bz2 +RUN cd /tmp && tar -xf elfutils-0.179.tar.bz2 +RUN cd /tmp/elfutils-0.179 && ./configure --disable-debuginfod --prefix=/usr && make && make install + +RUN curl -L http://download.savannah.nongnu.org/releases/libunwind/libunwind-1.4.0.tar.gz -o /tmp/libunwind-1.4.0.tar.gz +RUN cd /tmp && tar -xf libunwind-1.4.0.tar.gz +RUN cd /tmp/libunwind-1.4.0 && ./configure --prefix=/usr && make install + +CMD bash From 5ad1bebfc3ed2d1255f11986c627d77a15912710 Mon Sep 17 00:00:00 2001 From: Yonatan Goldschmidt Date: Tue, 15 Dec 2020 04:11:02 +0200 Subject: [PATCH 06/11] perf inject: Keep previous cmdline Force "perf inject" to write the cmdline it has read from the input file, instead of writing its own cmdline. "perf inject" cmdline is hardly useful, "perf record"s is what we care about. Do note it's not exactly complete - I did not change write_cmdline() so that one still writes readlink("/proc/self/exe") of "perf inject" as the argv[0], and we drop the argv[0] we've read from the file. Unless you use different "perf" binaries for "record" and "inject", this doesn't matter. Signed-off-by: Yonatan Goldschmidt --- tools/perf/builtin-inject.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index ddccc0eb739076..97ec309316bb4a 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -767,6 +767,8 @@ static int __cmd_inject(struct perf_inject *inject) return ret; if (!data_out->is_pipe) { + const char **tmp; + if (inject->build_ids) perf_header__set_feat(&session->header, HEADER_BUILD_ID); @@ -791,7 +793,14 @@ static int __cmd_inject(struct perf_inject *inject) } session->header.data_offset = output_data_offset; session->header.data_size = inject->bytes_written; + tmp = perf_env.cmdline_argv; + // cmdline read & write code is not symmetric; the cmdline we read includes the path of executed + // perf binary as argv[0]. when we hit write_cmdlline, we'll once again write our exe path. + // so skip first arg, to remove this redundancy. + perf_env.nr_cmdline = session->header.env.nr_cmdline - 1; + perf_env.cmdline_argv = session->header.env.cmdline_argv + 1; perf_session__write_header(session, session->evlist, fd, true); + perf_env.cmdline_argv = tmp; // later code free()s it, so to avoid the double free... } return ret; From 40a7823cf90a7e69ce8af88d224dfdd7e371de2d Mon Sep 17 00:00:00 2001 From: Daniel Shaulov Date: Wed, 10 Mar 2021 11:46:12 +0200 Subject: [PATCH 07/11] perf: Use exec_comm instead of comm when printing Process names are much clearer as the first line in a flamegraph If we ever want to upstream this, it needs to be a separate option --- tools/perf/builtin-script.c | 8 +++----- tools/perf/util/thread.c | 21 +++++++++++++++++++++ tools/perf/util/thread.h | 1 + 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 5915f19cee5500..c79a6026558d21 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -732,14 +732,12 @@ static int perf_sample__fprintf_start(struct perf_script *script, char tstr[128]; if (PRINT_FIELD(COMM)) { - const char *comm = thread ? thread__comm_str(thread) : ":-1"; - if (latency_format) - printed += fprintf(fp, "%8.8s ", comm); + printed += fprintf(fp, "%8.8s ", thread__exec_comm_str(thread)); else if (PRINT_FIELD(IP) && evsel__has_callchain(evsel) && symbol_conf.use_callchain) - printed += fprintf(fp, "%s ", comm); + printed += fprintf(fp, "%s ", thread__exec_comm_str(thread)); else - printed += fprintf(fp, "%16s ", comm); + printed += fprintf(fp, "%16s ", thread__exec_comm_str(thread)); } if (PRINT_FIELD(PID) && PRINT_FIELD(TID)) diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 665e5c0618ed3d..a3cc8ebb04a8e6 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -310,6 +310,27 @@ const char *thread__comm_str(struct thread *thread) return str; } +static const char *__thread__exec_comm_str(const struct thread *thread) +{ + const struct comm *comm = thread__exec_comm(thread); + + if (!comm) + return NULL; + + return comm__str(comm); +} + +const char *thread__exec_comm_str(struct thread *thread) +{ + const char *str; + + down_read(&thread->comm_lock); + str = __thread__exec_comm_str(thread); + up_read(&thread->comm_lock); + + return str; +} + /* CHECKME: it should probably better return the max comm len from its comm list */ int thread__comm_len(struct thread *thread) { diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index b066fb30d203da..47849fe5391701 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -103,6 +103,7 @@ int thread__comm_len(struct thread *thread); struct comm *thread__comm(const struct thread *thread); struct comm *thread__exec_comm(const struct thread *thread); const char *thread__comm_str(struct thread *thread); +const char *thread__exec_comm_str(struct thread *thread); int thread__insert_map(struct thread *thread, struct map *map); int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp, bool do_maps_clone); size_t thread__fprintf(struct thread *thread, FILE *fp); From 37e33e68d0a02f31f46e089d4a33d9877ed79768 Mon Sep 17 00:00:00 2001 From: marcin-ol Date: Wed, 21 Sep 2022 01:20:37 +0200 Subject: [PATCH 08/11] Take exec comm str to set child thread's comm when handling thread__fork event (#2) --- tools/perf/util/thread.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index a3cc8ebb04a8e6..ac8a1e0fcebf8c 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -412,7 +412,7 @@ static int thread__clone_maps(struct thread *thread, struct thread *parent, bool int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp, bool do_maps_clone) { if (parent->comm_set) { - const char *comm = thread__comm_str(parent); + const char *comm = thread__exec_comm_str(parent); int err; if (!comm) return -ENOMEM; From 9843a45177e1bd06de1a16805d90a36a26798a7a Mon Sep 17 00:00:00 2001 From: Ilay Rosenberg Date: Tue, 18 Oct 2022 13:00:53 +0300 Subject: [PATCH 09/11] Revert "perf inject: Keep previous cmdline" This reverts commit 5ad1bebfc3ed2d1255f11986c627d77a15912710. --- tools/perf/builtin-inject.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 97ec309316bb4a..ddccc0eb739076 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -767,8 +767,6 @@ static int __cmd_inject(struct perf_inject *inject) return ret; if (!data_out->is_pipe) { - const char **tmp; - if (inject->build_ids) perf_header__set_feat(&session->header, HEADER_BUILD_ID); @@ -793,14 +791,7 @@ static int __cmd_inject(struct perf_inject *inject) } session->header.data_offset = output_data_offset; session->header.data_size = inject->bytes_written; - tmp = perf_env.cmdline_argv; - // cmdline read & write code is not symmetric; the cmdline we read includes the path of executed - // perf binary as argv[0]. when we hit write_cmdlline, we'll once again write our exe path. - // so skip first arg, to remove this redundancy. - perf_env.nr_cmdline = session->header.env.nr_cmdline - 1; - perf_env.cmdline_argv = session->header.env.cmdline_argv + 1; perf_session__write_header(session, session->evlist, fd, true); - perf_env.cmdline_argv = tmp; // later code free()s it, so to avoid the double free... } return ret; From aa689e9b55c7b5ba8d399bc2560d36ef98436150 Mon Sep 17 00:00:00 2001 From: Yonatan Goldschmidt Date: Tue, 25 Apr 2023 14:06:12 +0300 Subject: [PATCH 10/11] perf record: Allow using switch-output=time together with signal The use case is, I'd like to switch upon signal but still protect with a timeout. --- tools/perf/builtin-record.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 0f711f88894cf9..1ea417c869250e 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -3007,18 +3007,17 @@ static int switch_output_setup(struct record *rec) return 0; } - if (!strcmp(s->str, "signal")) { + // strstr, not strcmp, to allow it to be used with size/time. + if (strstr(s->str, "signal")) { do_signal: s->signal = true; pr_debug("switch-output with SIGUSR2 signal\n"); - goto enabled; } val = parse_tag_value(s->str, tags_size); if (val != (unsigned long) -1) { s->size = val; pr_debug("switch-output with %s size threshold\n", s->str); - goto enabled; } val = parse_tag_value(s->str, tags_time); @@ -3026,6 +3025,9 @@ static int switch_output_setup(struct record *rec) s->time = val; pr_debug("switch-output with %s time threshold (%lu seconds)\n", s->str, s->time); + } + + if (s->size || s->time || s->signal) { goto enabled; } From 9909d736d8b8927d79003dfa9732050a08c11221 Mon Sep 17 00:00:00 2001 From: slicklash Date: Tue, 22 Oct 2024 16:14:01 +0200 Subject: [PATCH 11/11] perf: Add -F +symline option (#3) --- tools/perf/builtin-script.c | 10 ++++++ tools/perf/util/evsel_fprintf.c | 5 ++- tools/perf/util/evsel_fprintf.h | 1 + tools/perf/util/symbol.h | 6 ++++ tools/perf/util/symbol_fprintf.c | 54 ++++++++++++++++++++++++++------ 5 files changed, 65 insertions(+), 11 deletions(-) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index a04187582d5241..8d2fe1fd31e96d 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -134,6 +134,7 @@ enum perf_output_field { PERF_OUTPUT_CGROUP = 1ULL << 39, PERF_OUTPUT_RETIRE_LAT = 1ULL << 40, PERF_OUTPUT_DSOFF = 1ULL << 41, + PERF_OUTPUT_SYMLINE = 1ULL << 42, }; struct perf_script { @@ -178,6 +179,7 @@ struct output_option { {.str = "dsoff", .field = PERF_OUTPUT_DSOFF}, {.str = "addr", .field = PERF_OUTPUT_ADDR}, {.str = "symoff", .field = PERF_OUTPUT_SYMOFFSET}, + {.str = "symline", .field = PERF_OUTPUT_SYMLINE}, {.str = "srcline", .field = PERF_OUTPUT_SRCLINE}, {.str = "period", .field = PERF_OUTPUT_PERIOD}, {.str = "iregs", .field = PERF_OUTPUT_IREGS}, @@ -497,6 +499,11 @@ static int evsel__check_attr(struct evsel *evsel, struct perf_session *session) "selected.\n"); return -EINVAL; } + if (PRINT_FIELD(SYMLINE) && !PRINT_FIELD(SYM)) { + pr_err("Display of line offsets requested but symbol is not" + "selected.\n"); + return -EINVAL; + } if (PRINT_FIELD(DSO) && !(evsel->core.attr.sample_type & (PERF_SAMPLE_IP|PERF_SAMPLE_ADDR))) { pr_err("Display of DSO requested but no address to convert.\n"); @@ -582,6 +589,9 @@ static void set_print_ip_opts(struct perf_event_attr *attr) if (PRINT_FIELD(SYMOFFSET)) output[type].print_ip_opts |= EVSEL__PRINT_SYMOFFSET; + if (PRINT_FIELD(SYMLINE)) + output[type].print_ip_opts |= EVSEL__PRINT_SYMLINE; + if (PRINT_FIELD(SRCLINE)) output[type].print_ip_opts |= EVSEL__PRINT_SRCLINE; } diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c index 8719b3cb564661..464ace5b532033 100644 --- a/tools/perf/util/evsel_fprintf.c +++ b/tools/perf/util/evsel_fprintf.c @@ -119,6 +119,7 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, int print_dso = print_opts & EVSEL__PRINT_DSO; int print_dsoff = print_opts & EVSEL__PRINT_DSOFF; int print_symoffset = print_opts & EVSEL__PRINT_SYMOFFSET; + int print_symline = print_opts & EVSEL__PRINT_SYMLINE; int print_oneline = print_opts & EVSEL__PRINT_ONELINE; int print_srcline = print_opts & EVSEL__PRINT_SRCLINE; int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR; @@ -167,7 +168,9 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, node_al.addr = addr; node_al.map = map__get(map); - if (print_symoffset) { + if (print_symline) { + printed += symbol__fprintf_symline_offs(sym, &node_al, fp); + } else if (print_symoffset) { printed += __symbol__fprintf_symname_offs(sym, &node_al, print_unknown_as_addr, true, fp); diff --git a/tools/perf/util/evsel_fprintf.h b/tools/perf/util/evsel_fprintf.h index c8a9fac2f2ddc0..9db9c46ff9dff7 100644 --- a/tools/perf/util/evsel_fprintf.h +++ b/tools/perf/util/evsel_fprintf.h @@ -27,6 +27,7 @@ int evsel__fprintf(struct evsel *evsel, struct perf_attr_details *details, FILE #define EVSEL__PRINT_CALLCHAIN_ARROW (1<<7) #define EVSEL__PRINT_SKIP_IGNORED (1<<8) #define EVSEL__PRINT_DSOFF (1<<9) +#define EVSEL__PRINT_SYMLINE (1<<10) struct addr_location; struct perf_event_attr; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index af87c46b3f89e5..ff0834328fa46c 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -156,6 +156,10 @@ void symbol__elf_init(void); int symbol__annotation_init(void); struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char *name); +size_t __symbol__fprintf_sym_offs(const struct symbol *sym, + const struct addr_location *al, + bool unknown_as_addr, bool print_offsets, + bool print_line, FILE *fp); size_t __symbol__fprintf_symname_offs(const struct symbol *sym, const struct addr_location *al, bool unknown_as_addr, @@ -171,6 +175,8 @@ bool symbol__restricted_filename(const char *filename, const char *restricted_filename); int symbol__config_symfs(const struct option *opt __maybe_unused, const char *dir, int unset __maybe_unused); +size_t symbol__fprintf_symline_offs(const struct symbol *sym, + const struct addr_location *al, FILE *fp); struct symsrc; diff --git a/tools/perf/util/symbol_fprintf.c b/tools/perf/util/symbol_fprintf.c index 088f4abf230fe7..e2165c898bd214 100644 --- a/tools/perf/util/symbol_fprintf.c +++ b/tools/perf/util/symbol_fprintf.c @@ -6,6 +6,7 @@ #include "dso.h" #include "map.h" #include "symbol.h" +#include "srcline.h" size_t symbol__fprintf(struct symbol *sym, FILE *fp) { @@ -16,22 +17,40 @@ size_t symbol__fprintf(struct symbol *sym, FILE *fp) sym->name); } -size_t __symbol__fprintf_symname_offs(const struct symbol *sym, - const struct addr_location *al, - bool unknown_as_addr, - bool print_offsets, FILE *fp) +size_t __symbol__fprintf_sym_offs(const struct symbol *sym, + const struct addr_location *al, + bool unknown_as_addr, bool print_offsets, + bool print_line, FILE *fp) { unsigned long offset; size_t length; if (sym) { length = fprintf(fp, "%s", sym->name); - if (al && print_offsets) { - if (al->addr < sym->end) - offset = al->addr - sym->start; - else - offset = al->addr - map__start(al->map) - sym->start; - length += fprintf(fp, "+0x%lx", offset); + if (al && (print_offsets || print_line)) { + if (print_line) { + int ret = 0; + char *srcline = map__srcline(al->map, al->addr, NULL); + if (srcline != SRCLINE_UNKNOWN) { + ret = fprintf(fp, "+%s", srcline); + if (ret > 0) + length += (size_t)ret; + } + zfree_srcline(&srcline); + srcline = map__srcline(al->map, sym->start, NULL); + if (srcline != SRCLINE_UNKNOWN) { + ret = fprintf(fp, "+%s", srcline); + if (ret > 0) + length += (size_t)ret; + } + zfree_srcline(&srcline); + } else { + if (al->addr < sym->end) + offset = al->addr - sym->start; + else + offset = al->addr - map__start(al->map) - sym->start; + length += fprintf(fp, "+0x%lx", offset); + } } return length; } else if (al && unknown_as_addr) @@ -40,6 +59,15 @@ size_t __symbol__fprintf_symname_offs(const struct symbol *sym, return fprintf(fp, "[unknown]"); } +size_t __symbol__fprintf_symname_offs(const struct symbol *sym, + const struct addr_location *al, + bool unknown_as_addr, bool print_offsets, + FILE *fp) +{ + return __symbol__fprintf_sym_offs(sym, al, unknown_as_addr, + print_offsets, false, fp); +} + size_t symbol__fprintf_symname_offs(const struct symbol *sym, const struct addr_location *al, FILE *fp) @@ -71,3 +99,9 @@ size_t dso__fprintf_symbols_by_name(struct dso *dso, } return ret; } + +size_t symbol__fprintf_symline_offs(const struct symbol *sym, + const struct addr_location *al, FILE *fp) +{ + return __symbol__fprintf_sym_offs(sym, al, false, false, true, fp); +} \ No newline at end of file