diff options
Diffstat (limited to 'tools/perf/util')
-rw-r--r-- | tools/perf/util/dwarf-aux.c | 62 | ||||
-rw-r--r-- | tools/perf/util/machine.c | 43 | ||||
-rw-r--r-- | tools/perf/util/machine.h | 2 | ||||
-rw-r--r-- | tools/perf/util/map.c | 116 | ||||
-rw-r--r-- | tools/perf/util/map.h | 7 | ||||
-rw-r--r-- | tools/perf/util/map_groups.h | 21 | ||||
-rw-r--r-- | tools/perf/util/metricgroup.c | 2 | ||||
-rw-r--r-- | tools/perf/util/parse-events.c | 78 | ||||
-rw-r--r-- | tools/perf/util/parse-events.h | 4 | ||||
-rw-r--r-- | tools/perf/util/probe-event.c | 19 | ||||
-rw-r--r-- | tools/perf/util/probe-event.h | 3 | ||||
-rw-r--r-- | tools/perf/util/probe-file.c | 14 | ||||
-rw-r--r-- | tools/perf/util/probe-file.h | 2 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.c | 116 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.h | 1 | ||||
-rw-r--r-- | tools/perf/util/symbol.c | 84 |
16 files changed, 408 insertions, 166 deletions
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 5544bfbd0f6c..aa898014ad12 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -59,6 +59,51 @@ const char *cu_get_comp_dir(Dwarf_Die *cu_die) return dwarf_formstring(&attr); } +/* Unlike dwarf_getsrc_die(), cu_getsrc_die() only returns statement line */ +static Dwarf_Line *cu_getsrc_die(Dwarf_Die *cu_die, Dwarf_Addr addr) +{ + Dwarf_Addr laddr; + Dwarf_Lines *lines; + Dwarf_Line *line; + size_t nlines, l, u, n; + bool flag; + + if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0 || + nlines == 0) + return NULL; + + /* Lines are sorted by address, use binary search */ + l = 0; u = nlines - 1; + while (l < u) { + n = u - (u - l) / 2; + line = dwarf_onesrcline(lines, n); + if (!line || dwarf_lineaddr(line, &laddr) != 0) + return NULL; + if (addr < laddr) + u = n - 1; + else + l = n; + } + /* Going backward to find the lowest line */ + do { + line = dwarf_onesrcline(lines, --l); + if (!line || dwarf_lineaddr(line, &laddr) != 0) + return NULL; + } while (laddr == addr); + l++; + /* Going foward to find the statement line */ + do { + line = dwarf_onesrcline(lines, l++); + if (!line || dwarf_lineaddr(line, &laddr) != 0 || + dwarf_linebeginstatement(line, &flag) != 0) + return NULL; + if (laddr > addr) + return NULL; + } while (!flag); + + return line; +} + /** * cu_find_lineinfo - Get a line number and file name for given address * @cu_die: a CU DIE @@ -72,17 +117,26 @@ int cu_find_lineinfo(Dwarf_Die *cu_die, unsigned long addr, const char **fname, int *lineno) { Dwarf_Line *line; - Dwarf_Addr laddr; + Dwarf_Die die_mem; + Dwarf_Addr faddr; - line = dwarf_getsrc_die(cu_die, (Dwarf_Addr)addr); - if (line && dwarf_lineaddr(line, &laddr) == 0 && - addr == (unsigned long)laddr && dwarf_lineno(line, lineno) == 0) { + if (die_find_realfunc(cu_die, (Dwarf_Addr)addr, &die_mem) + && die_entrypc(&die_mem, &faddr) == 0 && + faddr == addr) { + *fname = dwarf_decl_file(&die_mem); + dwarf_decl_line(&die_mem, lineno); + goto out; + } + + line = cu_getsrc_die(cu_die, (Dwarf_Addr)addr); + if (line && dwarf_lineno(line, lineno) == 0) { *fname = dwarf_linesrc(line, NULL, NULL); if (!*fname) /* line number is useless without filename */ *lineno = 0; } +out: return *lineno ?: -ENOENT; } diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 6a0f5c25ce3e..71ee078d30f4 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -772,45 +772,16 @@ int machine__process_ksymbol(struct machine *machine __maybe_unused, return machine__process_ksymbol_register(machine, event, sample); } -static void dso__adjust_kmod_long_name(struct dso *dso, const char *filename) -{ - const char *dup_filename; - - if (!filename || !dso || !dso->long_name) - return; - if (dso->long_name[0] != '[') - return; - if (!strchr(filename, '/')) - return; - - dup_filename = strdup(filename); - if (!dup_filename) - return; - - dso__set_long_name(dso, dup_filename, true); -} - -struct map *machine__findnew_module_map(struct machine *machine, u64 start, - const char *filename) +static struct map *machine__addnew_module_map(struct machine *machine, u64 start, + const char *filename) { struct map *map = NULL; - struct dso *dso = NULL; struct kmod_path m; + struct dso *dso; if (kmod_path__parse_name(&m, filename)) return NULL; - map = map_groups__find_by_name(&machine->kmaps, m.name); - if (map) { - /* - * If the map's dso is an offline module, give dso__load() - * a chance to find the file path of that module by fixing - * long_name. - */ - dso__adjust_kmod_long_name(map->dso, filename); - goto out; - } - dso = machine__findnew_module_dso(machine, &m, filename); if (dso == NULL) goto out; @@ -1409,7 +1380,7 @@ static int machine__create_module(void *arg, const char *name, u64 start, if (arch__fix_module_text_start(&start, &size, name) < 0) return -1; - map = machine__findnew_module_map(machine, start, name); + map = machine__addnew_module_map(machine, start, name); if (map == NULL) return -1; map->end = start + size; @@ -1584,8 +1555,8 @@ static int machine__process_kernel_mmap_event(struct machine *machine, strlen(machine->mmap_name) - 1) == 0; if (event->mmap.filename[0] == '/' || (!is_kernel_mmap && event->mmap.filename[0] == '[')) { - map = machine__findnew_module_map(machine, event->mmap.start, - event->mmap.filename); + map = machine__addnew_module_map(machine, event->mmap.start, + event->mmap.filename); if (map == NULL) goto out_problem; @@ -2414,7 +2385,7 @@ static int thread__resolve_callchain_sample(struct thread *thread, } check_calls: - if (callchain_param.order != ORDER_CALLEE) { + if (chain && callchain_param.order != ORDER_CALLEE) { err = find_prev_cpumode(chain, thread, cursor, parent, root_al, &cpumode, chain->nr - first_call); if (err) diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 18e13c0ccd6a..1016978f575a 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -221,8 +221,6 @@ struct symbol *machine__find_kernel_symbol_by_name(struct machine *machine, return map_groups__find_symbol_by_name(&machine->kmaps, name, mapp); } -struct map *machine__findnew_module_map(struct machine *machine, u64 start, - const char *filename); int arch__fix_module_text_start(u64 *start, u64 *size, const char *name); int machine__load_kallsyms(struct machine *machine, const char *filename); diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 359846833a00..67e0f81416cb 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -26,7 +26,6 @@ #include "ui/ui.h" static void __maps__insert(struct maps *maps, struct map *map); -static void __maps__insert_name(struct maps *maps, struct map *map); static inline int is_anon_memory(const char *filename, u32 flags) { @@ -566,7 +565,6 @@ u64 map__objdump_2mem(struct map *map, u64 ip) static void maps__init(struct maps *maps) { maps->entries = RB_ROOT; - maps->names = RB_ROOT; init_rwsem(&maps->lock); } @@ -574,30 +572,72 @@ void map_groups__init(struct map_groups *mg, struct machine *machine) { maps__init(&mg->maps); mg->machine = machine; + mg->last_search_by_name = NULL; + mg->nr_maps = 0; + mg->maps_by_name = NULL; refcount_set(&mg->refcnt, 1); } -void map_groups__insert(struct map_groups *mg, struct map *map) +static void __map_groups__free_maps_by_name(struct map_groups *mg) { - maps__insert(&mg->maps, map); + /* + * Free everything to try to do it from the rbtree in the next search + */ + zfree(&mg->maps_by_name); + mg->nr_maps_allocated = 0; } -static void __maps__purge(struct maps *maps) +void map_groups__insert(struct map_groups *mg, struct map *map) { - struct map *pos, *next; + struct maps *maps = &mg->maps; - maps__for_each_entry_safe(maps, pos, next) { - rb_erase_init(&pos->rb_node, &maps->entries); - map__put(pos); + down_write(&maps->lock); + __maps__insert(maps, map); + ++mg->nr_maps; + + /* + * If we already performed some search by name, then we need to add the just + * inserted map and resort. + */ + if (mg->maps_by_name) { + if (mg->nr_maps > mg->nr_maps_allocated) { + int nr_allocate = mg->nr_maps * 2; + struct map **maps_by_name = realloc(mg->maps_by_name, nr_allocate * sizeof(map)); + + if (maps_by_name == NULL) { + __map_groups__free_maps_by_name(mg); + return; + } + + mg->maps_by_name = maps_by_name; + mg->nr_maps_allocated = nr_allocate; + } + mg->maps_by_name[mg->nr_maps - 1] = map; + __map_groups__sort_by_name(mg); } + up_write(&maps->lock); } -static void __maps__purge_names(struct maps *maps) +void map_groups__remove(struct map_groups *mg, struct map *map) +{ + struct maps *maps = &mg->maps; + down_write(&maps->lock); + if (mg->last_search_by_name == map) + mg->last_search_by_name = NULL; + + __maps__remove(maps, map); + --mg->nr_maps; + if (mg->maps_by_name) + __map_groups__free_maps_by_name(mg); + up_write(&maps->lock); +} + +static void __maps__purge(struct maps *maps) { struct map *pos, *next; - maps__for_each_entry_by_name_safe(maps, pos, next) { - rb_erase_init(&pos->rb_node_name, &maps->names); + maps__for_each_entry_safe(maps, pos, next) { + rb_erase_init(&pos->rb_node, &maps->entries); map__put(pos); } } @@ -606,7 +646,6 @@ static void maps__exit(struct maps *maps) { down_write(&maps->lock); __maps__purge(maps); - __maps__purge_names(maps); up_write(&maps->lock); } @@ -745,7 +784,6 @@ size_t map_groups__fprintf(struct map_groups *mg, FILE *fp) static void __map_groups__insert(struct map_groups *mg, struct map *map) { __maps__insert(&mg->maps, map); - __maps__insert_name(&mg->maps, map); } int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, FILE *fp) @@ -902,42 +940,17 @@ static void __maps__insert(struct maps *maps, struct map *map) map__get(map); } -static void __maps__insert_name(struct maps *maps, struct map *map) -{ - struct rb_node **p = &maps->names.rb_node; - struct rb_node *parent = NULL; - struct map *m; - int rc; - - while (*p != NULL) { - parent = *p; - m = rb_entry(parent, struct map, rb_node_name); - rc = strcmp(m->dso->short_name, map->dso->short_name); - if (rc < 0) - p = &(*p)->rb_left; - else - p = &(*p)->rb_right; - } - rb_link_node(&map->rb_node_name, parent, p); - rb_insert_color(&map->rb_node_name, &maps->names); - map__get(map); -} - void maps__insert(struct maps *maps, struct map *map) { down_write(&maps->lock); __maps__insert(maps, map); - __maps__insert_name(maps, map); up_write(&maps->lock); } -static void __maps__remove(struct maps *maps, struct map *map) +void __maps__remove(struct maps *maps, struct map *map) { rb_erase_init(&map->rb_node, &maps->entries); map__put(map); - - rb_erase_init(&map->rb_node_name, &maps->names); - map__put(map); } void maps__remove(struct maps *maps, struct map *map) @@ -994,29 +1007,6 @@ struct map *map__next(struct map *map) return map ? __map__next(map) : NULL; } -struct map *maps__first_by_name(struct maps *maps) -{ - struct rb_node *first = rb_first(&maps->names); - - if (first) - return rb_entry(first, struct map, rb_node_name); - return NULL; -} - -static struct map *__map__next_by_name(struct map *map) -{ - struct rb_node *next = rb_next(&map->rb_node_name); - - if (next) - return rb_entry(next, struct map, rb_node_name); - return NULL; -} - -struct map *map__next_by_name(struct map *map) -{ - return map ? __map__next_by_name(map) : NULL; -} - struct kmap *__map__kmap(struct map *map) { if (!map->dso || !map->dso->kernel) diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 365deb6375ab..0a6c45f85cd9 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -23,13 +23,11 @@ struct map { struct rb_node rb_node; struct list_head node; }; - struct rb_node rb_node_name; u64 start; u64 end; - bool erange_warned; - u32 priv; + bool erange_warned:1; + bool priv:1; u32 prot; - u32 flags; u64 pgoff; u64 reloc; u32 maj, min; /* only valid for MMAP2 record */ @@ -43,6 +41,7 @@ struct map { struct dso *dso; refcount_t refcnt; + u32 flags; }; struct kmap; diff --git a/tools/perf/util/map_groups.h b/tools/perf/util/map_groups.h index 99cb810acc7c..63ed211fe241 100644 --- a/tools/perf/util/map_groups.h +++ b/tools/perf/util/map_groups.h @@ -16,12 +16,12 @@ struct thread; struct maps { struct rb_root entries; - struct rb_root names; struct rw_semaphore lock; }; void maps__insert(struct maps *maps, struct map *map); void maps__remove(struct maps *maps, struct map *map); +void __maps__remove(struct maps *maps, struct map *map); struct map *maps__find(struct maps *maps, u64 addr); struct map *maps__first(struct maps *maps); struct map *map__next(struct map *map); @@ -33,19 +33,15 @@ struct map *map__next(struct map *map); for (map = maps__first(maps), next = map__next(map); map; map = next, next = map__next(map)) struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, struct map **mapp); -struct map *maps__first_by_name(struct maps *maps); -struct map *map__next_by_name(struct map *map); - -#define maps__for_each_entry_by_name(maps, map) \ - for (map = maps__first_by_name(maps); map; map = map__next_by_name(map)) - -#define maps__for_each_entry_by_name_safe(maps, map, next) \ - for (map = maps__first_by_name(maps), next = map__next_by_name(map); map; map = next, next = map__next_by_name(map)) struct map_groups { struct maps maps; struct machine *machine; + struct map *last_search_by_name; + struct map **maps_by_name; refcount_t refcnt; + unsigned int nr_maps; + unsigned int nr_maps_allocated; #ifdef HAVE_LIBUNWIND_SUPPORT void *addr_space; struct unwind_libunwind_ops *unwind_libunwind_ops; @@ -79,10 +75,7 @@ size_t map_groups__fprintf(struct map_groups *mg, FILE *fp); void map_groups__insert(struct map_groups *mg, struct map *map); -static inline void map_groups__remove(struct map_groups *mg, struct map *map) -{ - maps__remove(&mg->maps, map); -} +void map_groups__remove(struct map_groups *mg, struct map *map); static inline struct map *map_groups__find(struct map_groups *mg, u64 addr) { @@ -108,4 +101,6 @@ struct map *map_groups__find_by_name(struct map_groups *mg, const char *name); int map_groups__merge_in(struct map_groups *kmaps, struct map *new_map); +void __map_groups__sort_by_name(struct map_groups *mg); + #endif // __PERF_MAP_GROUPS_H diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index a7c0424dbda3..6a4d350d5cdb 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -523,7 +523,7 @@ int metricgroup__parse_groups(const struct option *opt, if (ret) return ret; pr_debug("adding %s\n", extra_events.buf); - memset(&parse_error, 0, sizeof(struct parse_events_error)); + bzero(&parse_error, sizeof(parse_error)); ret = parse_events(perf_evlist, extra_events.buf, &parse_error); if (ret) { parse_events_print_error(&parse_error, extra_events.buf); diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 6d18ff9bce49..6bae9d6edc12 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -189,12 +189,29 @@ void parse_events__handle_error(struct parse_events_error *err, int idx, free(help); return; } - WARN_ONCE(err->str, "WARNING: multiple event parsing errors\n"); - err->idx = idx; - free(err->str); - err->str = str; - free(err->help); - err->help = help; + switch (err->num_errors) { + case 0: + err->idx = idx; + err->str = str; + err->help = help; + break; + case 1: + err->first_idx = err->idx; + err->idx = idx; + err->first_str = err->str; + err->str = str; + err->first_help = err->help; + err->help = help; + break; + default: + WARN_ONCE(1, "WARNING: multiple event parsing errors\n"); + free(err->str); + err->str = str; + free(err->help); + err->help = help; + break; + } + err->num_errors++; } struct tracepoint_path *tracepoint_id_to_path(u64 config) @@ -1349,7 +1366,7 @@ int parse_events_add_pmu(struct parse_events_state *parse_state, if (asprintf(&err_str, "Cannot find PMU `%s'. Missing kernel support?", name) >= 0) - parse_events__handle_error(err, -1, err_str, NULL); + parse_events__handle_error(err, 0, err_str, NULL); return -EINVAL; } @@ -2007,15 +2024,14 @@ static int get_term_width(void) return ws.ws_col > MAX_WIDTH ? MAX_WIDTH : ws.ws_col; } -void parse_events_print_error(struct parse_events_error *err, - const char *event) +static void __parse_events_print_error(int err_idx, const char *err_str, + const char *err_help, const char *event) { const char *str = "invalid or unsupported event: "; char _buf[MAX_WIDTH]; char *buf = (char *) event; int idx = 0; - - if (err->str) { + if (err_str) { /* -2 for extra '' in the final fprintf */ int width = get_term_width() - 2; int len_event = strlen(event); @@ -2038,8 +2054,8 @@ void parse_events_print_error(struct parse_events_error *err, buf = _buf; /* We're cutting from the beginning. */ - if (err->idx > max_err_idx) - cut = err->idx - max_err_idx; + if (err_idx > max_err_idx) + cut = err_idx - max_err_idx; strncpy(buf, event + cut, max_len); @@ -2052,16 +2068,33 @@ void parse_events_print_error(struct parse_events_error *err, buf[max_len] = 0; } - idx = len_str + err->idx - cut; + idx = len_str + err_idx - cut; } fprintf(stderr, "%s'%s'\n", str, buf); if (idx) { - fprintf(stderr, "%*s\\___ %s\n", idx + 1, "", err->str); - if (err->help) - fprintf(stderr, "\n%s\n", err->help); - zfree(&err->str); - zfree(&err->help); + fprintf(stderr, "%*s\\___ %s\n", idx + 1, "", err_str); + if (err_help) + fprintf(stderr, "\n%s\n", err_help); + } +} + +void parse_events_print_error(struct parse_events_error *err, + const char *event) +{ + if (!err->num_errors) + return; + + __parse_events_print_error(err->idx, err->str, err->help, event); + zfree(&err->str); + zfree(&err->help); + + if (err->num_errors > 1) { + fputs("\nInitial error:\n", stderr); + __parse_events_print_error(err->first_idx, err->first_str, + err->first_help, event); + zfree(&err->first_str); + zfree(&err->first_help); } } @@ -2071,8 +2104,11 @@ int parse_events_option(const struct option *opt, const char *str, int unset __maybe_unused) { struct evlist *evlist = *(struct evlist **)opt->value; - struct parse_events_error err = { .idx = 0, }; - int ret = parse_events(evlist, str, &err); + struct parse_events_error err; + int ret; + + bzero(&err, sizeof(err)); + ret = parse_events(evlist, str, &err); if (ret) { parse_events_print_error(&err, str); diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 5ee8ac93840c..ff367f248fe8 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -110,9 +110,13 @@ struct parse_events_term { }; struct parse_events_error { + int num_errors; /* number of errors encountered */ int idx; /* index in the parsed string */ char *str; /* string to display at the index */ char *help; /* optional help string */ + int first_idx;/* as above, but for the first encountered error */ + char *first_str; + char *first_help; }; struct parse_events_state { diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index e29948b8fcab..52b2d165453a 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -46,7 +46,7 @@ #define PERFPROBE_GROUP "probe" bool probe_event_dry_run; /* Dry run flag */ -struct probe_conf probe_conf; +struct probe_conf probe_conf = { .magic_num = DEFAULT_PROBE_MAGIC_NUM }; #define semantic_error(msg ...) pr_err("Semantic error :" msg) @@ -1679,6 +1679,14 @@ int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) if (ret < 0) goto out; + /* Generate event name if needed */ + if (!pev->event && pev->point.function && pev->point.line + && !pev->point.lazy_line && !pev->point.offset) { + if (asprintf(&pev->event, "%s_L%d", pev->point.function, + pev->point.line) < 0) + return -ENOMEM; + } + /* Copy arguments and ensure return probe has no C argument */ pev->nargs = argc - 1; pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs); @@ -2730,8 +2738,13 @@ static int probe_trace_event__set_name(struct probe_trace_event *tev, if (tev->event == NULL || tev->group == NULL) return -ENOMEM; - /* Add added event name to namelist */ - strlist__add(namelist, event); + /* + * Add new event name to namelist if multiprobe event is NOT + * supported, since we have to use new event name for following + * probes in that case. + */ + if (!multiprobe_event_is_supported()) + strlist__add(namelist, event); return 0; } diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 96a319cd2378..4f0eb3a20c36 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -16,10 +16,13 @@ struct probe_conf { bool no_inlines; bool cache; int max_probes; + unsigned long magic_num; }; extern struct probe_conf probe_conf; extern bool probe_event_dry_run; +#define DEFAULT_PROBE_MAGIC_NUM 0xdeade12d /* u32: 3735937325 */ + struct symbol; /* kprobe-tracer and uprobe-tracer tracing point */ diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c index b659466ea498..5003ba403345 100644 --- a/tools/perf/util/probe-file.c +++ b/tools/perf/util/probe-file.c @@ -1007,6 +1007,8 @@ enum ftrace_readme { FTRACE_README_KRETPROBE_OFFSET, FTRACE_README_UPROBE_REF_CTR, FTRACE_README_USER_ACCESS, + FTRACE_README_MULTIPROBE_EVENT, + FTRACE_README_IMMEDIATE_VALUE, FTRACE_README_END, }; @@ -1020,6 +1022,8 @@ static struct { DEFINE_TYPE(FTRACE_README_KRETPROBE_OFFSET, "*place (kretprobe): *"), DEFINE_TYPE(FTRACE_README_UPROBE_REF_CTR, "*ref_ctr_offset*"), DEFINE_TYPE(FTRACE_README_USER_ACCESS, "*[u]<offset>*"), + DEFINE_TYPE(FTRACE_README_MULTIPROBE_EVENT, "*Create/append/*"), + DEFINE_TYPE(FTRACE_README_IMMEDIATE_VALUE, "*\\imm-value,*"), }; static bool scan_ftrace_readme(enum ftrace_readme type) @@ -1085,3 +1089,13 @@ bool user_access_is_supported(void) { return scan_ftrace_readme(FTRACE_README_USER_ACCESS); } + +bool multiprobe_event_is_supported(void) +{ + return scan_ftrace_readme(FTRACE_README_MULTIPROBE_EVENT); +} + +bool immediate_value_is_supported(void) +{ + return scan_ftrace_readme(FTRACE_README_IMMEDIATE_VALUE); +} diff --git a/tools/perf/util/probe-file.h b/tools/perf/util/probe-file.h index 986c1c94f64f..0dba88c0f5f0 100644 --- a/tools/perf/util/probe-file.h +++ b/tools/perf/util/probe-file.h @@ -71,6 +71,8 @@ bool probe_type_is_available(enum probe_type type); bool kretprobe_offset_is_supported(void); bool uprobe_ref_ctr_is_supported(void); bool user_access_is_supported(void); +bool multiprobe_event_is_supported(void); +bool immediate_value_is_supported(void); #else /* ! HAVE_LIBELF_SUPPORT */ static inline struct probe_cache *probe_cache__new(const char *tgt __maybe_unused, struct nsinfo *nsi __maybe_unused) { diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 9ecea45da4ca..38d6cd22779f 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -177,6 +177,17 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL) goto static_var; + /* Constant value */ + if (dwarf_attr(vr_die, DW_AT_const_value, &attr) && + immediate_value_is_supported()) { + Dwarf_Sword snum; + + dwarf_formsdata(&attr, &snum); + ret = asprintf(&tvar->value, "\\%ld", (long)snum); + + return ret < 0 ? -ENOMEM : 0; + } + /* TODO: handle more than 1 exprs */ if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) return -EINVAL; /* Broken DIE ? */ @@ -525,6 +536,14 @@ next: return 0; } +static void print_var_not_found(const char *varname) +{ + pr_err("Failed to find the location of the '%s' variable at this address.\n" + " Perhaps it has been optimized out.\n" + " Use -V with the --range option to show '%s' location range.\n", + varname, varname); +} + /* Show a variables in kprobe event format */ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) { @@ -536,11 +555,11 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops, &pf->sp_die, pf->machine, pf->tvar); + if (ret == -ENOENT && pf->skip_empty_arg) + /* This can be found in other place. skip it */ + return 0; 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" - " Use -V with the --range option to show '%s' location range.\n", - pf->pvar->var, pf->pvar->var); + print_var_not_found(pf->pvar->var); } else if (ret == -ENOTSUP) pr_err("Sorry, we don't support this variable location yet.\n"); else if (ret == 0 && pf->pvar->field) { @@ -587,6 +606,8 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) /* Search again in global variables */ if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 0, &vr_die)) { + if (pf->skip_empty_arg) + return 0; pr_warning("Failed to find '%s' in this function.\n", pf->pvar->var); ret = -ENOENT; @@ -776,6 +797,39 @@ static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem) return fsp.found ? die_mem : NULL; } +static int verify_representive_line(struct probe_finder *pf, const char *fname, + int lineno, Dwarf_Addr addr) +{ + const char *__fname, *__func = NULL; + Dwarf_Die die_mem; + int __lineno; + + /* Verify line number and address by reverse search */ + if (cu_find_lineinfo(&pf->cu_die, addr, &__fname, &__lineno) < 0) + return 0; + + pr_debug2("Reversed line: %s:%d\n", __fname, __lineno); + if (strcmp(fname, __fname) || lineno == __lineno) + return 0; + + pr_warning("This line is sharing the addrees with other lines.\n"); + + if (pf->pev->point.function) { + /* Find best match function name and lines */ + pf->addr = addr; + if (find_best_scope(pf, &die_mem) + && die_match_name(&die_mem, pf->pev->point.function) + && dwarf_decl_line(&die_mem, &lineno) == 0) { + __func = dwarf_diename(&die_mem); + __lineno -= lineno; + } + } + pr_warning("Please try to probe at %s:%d instead.\n", + __func ? : __fname, __lineno); + + return -ENOENT; +} + static int probe_point_line_walker(const char *fname, int lineno, Dwarf_Addr addr, void *data) { @@ -786,6 +840,9 @@ static int probe_point_line_walker(const char *fname, int lineno, if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) return 0; + if (verify_representive_line(pf, fname, lineno, addr)) + return -ENOENT; + pf->addr = addr; sc_die = find_best_scope(pf, &die_mem); if (!sc_die) { @@ -1337,6 +1394,44 @@ end: return ret; } +static int fill_empty_trace_arg(struct perf_probe_event *pev, + struct probe_trace_event *tevs, int ntevs) +{ + char **valp; + char *type; + int i, j, ret; + + for (i = 0; i < pev->nargs; i++) { + type = NULL; + for (j = 0; j < ntevs; j++) { + if (tevs[j].args[i].value) { + type = tevs[j].args[i].type; + break; + } + } + if (j == ntevs) { + print_var_not_found(pev->args[i].var); + return -ENOENT; + } + for (j = 0; j < ntevs; j++) { + valp = &tevs[j].args[i].value; + if (*valp) + continue; + + ret = asprintf(valp, "\\%lx", probe_conf.magic_num); + if (ret < 0) + return -ENOMEM; + /* Note that type can be NULL */ + if (type) { + tevs[j].args[i].type = strdup(type); + if (!tevs[j].args[i].type) + return -ENOMEM; + } + } + } + return 0; +} + /* Find probe_trace_events specified by perf_probe_event from debuginfo */ int debuginfo__find_trace_events(struct debuginfo *dbg, struct perf_probe_event *pev, @@ -1355,7 +1450,13 @@ int debuginfo__find_trace_events(struct debuginfo *dbg, tf.tevs = *tevs; tf.ntevs = 0; + if (pev->nargs != 0 && immediate_value_is_supported()) + tf.pf.skip_empty_arg = true; + ret = debuginfo__find_probes(dbg, &tf.pf); + if (ret >= 0 && tf.pf.skip_empty_arg) + ret = fill_empty_trace_arg(pev, tf.tevs, tf.ntevs); + if (ret < 0) { for (i = 0; i < tf.ntevs; i++) clear_probe_trace_event(&tf.tevs[i]); @@ -1698,12 +1799,19 @@ static int line_range_walk_cb(const char *fname, int lineno, void *data) { struct line_finder *lf = data; + const char *__fname; + int __lineno; int err; if ((strtailcmp(fname, lf->fname) != 0) || (lf->lno_s > lineno || lf->lno_e < lineno)) return 0; + /* Make sure this line can be reversable */ + if (cu_find_lineinfo(&lf->cu_die, addr, &__fname, &__lineno) > 0 + && (lineno != __lineno || strcmp(fname, __fname))) + return 0; + err = line_range_add_line(fname, lineno, lf->lr); if (err < 0 && err != -EEXIST) return err; diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 670c477bf8cf..11be10080613 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -87,6 +87,7 @@ struct probe_finder { unsigned int machine; /* Target machine arch */ struct perf_probe_arg *pvar; /* Current target variable */ struct probe_trace_arg *tvar; /* Current result variable */ + bool skip_empty_arg; /* Skip non-exist args */ }; struct trace_event_finder { diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 88f4cfbdb69a..db9667aacb88 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1530,7 +1530,7 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod, case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP: /* * kernel modules know their symtab type - it's set when - * creating a module dso in machine__findnew_module_map(). + * creating a module dso in machine__addnew_module_map(). */ return kmod && dso->symtab_type == type; @@ -1760,28 +1760,82 @@ out: return ret; } -struct map *map_groups__find_by_name(struct map_groups *mg, const char *name) +static int map__strcmp(const void *a, const void *b) +{ + const struct map *ma = *(const struct map **)a, *mb = *(const struct map **)b; + return strcmp(ma->dso->short_name, mb->dso->short_name); +} + +static int map__strcmp_name(const void *name, const void *b) +{ + const struct map *map = *(const struct map **)b; + return strcmp(name, map->dso->short_name); +} + +void __map_groups__sort_by_name(struct map_groups *mg) +{ + qsort(mg->maps_by_name, mg->nr_maps, sizeof(struct map *), map__strcmp); +} + +static int map__groups__sort_by_name_from_rbtree(struct map_groups *mg) { - struct maps *maps = &mg->maps; struct map *map; - struct rb_node *node; + struct map **maps_by_name = realloc(mg->maps_by_name, mg->nr_maps * sizeof(map)); + int i = 0; - down_read(&maps->lock); + if (maps_by_name == NULL) + return -1; - for (node = maps->names.rb_node; node; ) { - int rc; + mg->maps_by_name = maps_by_name; + mg->nr_maps_allocated = mg->nr_maps; - map = rb_entry(node, struct map, rb_node_name); + maps__for_each_entry(&mg->maps, map) + maps_by_name[i++] = map; - rc = strcmp(map->dso->short_name, name); - if (rc < 0) - node = node->rb_left; - else if (rc > 0) - node = node->rb_right; - else + __map_groups__sort_by_name(mg); + return 0; +} - goto out_unlock; +static struct map *__map_groups__find_by_name(struct map_groups *mg, const char *name) +{ + struct map **mapp; + + if (mg->maps_by_name == NULL && + map__groups__sort_by_name_from_rbtree(mg)) + return NULL; + + mapp = bsearch(name, mg->maps_by_name, mg->nr_maps, sizeof(*mapp), map__strcmp_name); + if (mapp) + return *mapp; + return NULL; +} + +struct map *map_groups__find_by_name(struct map_groups *mg, const char *name) +{ + struct maps *maps = &mg->maps; + struct map *map; + + down_read(&maps->lock); + + if (mg->last_search_by_name && strcmp(mg->last_search_by_name->dso->short_name, name) == 0) { + map = mg->last_search_by_name; + goto out_unlock; } + /* + * If we have mg->maps_by_name, then the name isn't in the rbtree, + * as mg->maps_by_name mirrors the rbtree when lookups by name are + * made. + */ + map = __map_groups__find_by_name(mg, name); + if (map || mg->maps_by_name != NULL) + goto out_unlock; + + /* Fallback to traversing the rbtree... */ + maps__for_each_entry(maps, map) + if (strcmp(map->dso->short_name, name) == 0) { + mg->last_search_by_name = map; + goto out_unlock; + } map = NULL; |