diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index dad79ede4e0ae0..89fdac243324bc 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -145,7 +145,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/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 diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index b3e6ed10f40c6f..f3cf5039ec08c1 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -177,7 +177,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) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index dcf288a4fb9a9a..99cdf884553618 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -3212,18 +3212,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); @@ -3231,6 +3230,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; } diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index b1f57401ff2394..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; } @@ -790,14 +800,12 @@ static int perf_sample__fprintf_start(struct perf_script *script, printed += fprintf(fp, "VCPU:%03d ", sample->vcpu); 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/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/probe-event.c b/tools/perf/util/probe-event.c index 1a5b7fa459b232..f43a70e36c471d 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -945,6 +945,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; } } diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index f171360b0ef4db..a9a379e312e7c8 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -256,11 +256,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]; } @@ -376,10 +388,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); 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 diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index fe5e6991ae4b49..431369fff8290d 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -305,6 +305,27 @@ const char *thread__comm_str(struct thread *thread) return str; } +static const char *__thread__exec_comm_str(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; +} + static int __thread__comm_len(struct thread *thread, const char *comm) { if (!comm) @@ -398,7 +419,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 (thread__comm_set(parent)) { - const char *comm = thread__comm_str(parent); + const char *comm = thread__exec_comm_str(parent); int err; if (!comm) return -ENOMEM; diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index e79225a0ea46b7..3593fcea597f7e 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -102,6 +102,7 @@ int thread__comm_len(struct thread *thread); struct comm *thread__comm(struct thread *thread); struct comm *thread__exec_comm(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);