From 45633a169571e81835ec53fe0c089dd20f3195f1 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 29 Sep 2015 16:53:12 +0200 Subject: [PATCH 01/16] tools: Fix shadowed declaration in err.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The error variable breaks build on CentOS 6.7, due to collision with global error symbol: CC util/evlist.o cc1: warnings being treated as errors In file included from util/evlist.c:28: tools/include/linux/err.h: In function ‘ERR_PTR’: tools/include/linux/err.h:34: error: declaration of ‘error’ shadows a global declaration util/util.h:135: error: shadowed declaration is here Using 'error_' name instead to fix it. Signed-off-by: Jiri Olsa Link: http://lkml.kernel.org/n/tip-i9mdgdbrgauy3fe76s9rd125@git.kernel.org Reported-by: Vinson Lee [ Use 'error_' instead of 'err' to, visually, not diverge too much from include/linux/err.h ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/include/linux/err.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/include/linux/err.h b/tools/include/linux/err.h index c9ada48f5156..bdc3dd8131d4 100644 --- a/tools/include/linux/err.h +++ b/tools/include/linux/err.h @@ -31,9 +31,9 @@ #define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO) -static inline void * __must_check ERR_PTR(long error) +static inline void * __must_check ERR_PTR(long error_) { - return (void *) error; + return (void *) error_; } static inline long __must_check PTR_ERR(__force const void *ptr) From 272ed29a91aea4397c05157dea7210dd7e81557a Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 29 Sep 2015 17:05:31 +0200 Subject: [PATCH 02/16] perf tools: Fix shadowed declaration in parse-events.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The error variable breaks build on CentOS 6.7, due to a collision with a global error symbol: CC util/parse-events.o cc1: warnings being treated as errors util/parse-events.c:419: error: declaration of ‘error’ shadows a global declaration util/util.h:135: error: shadowed declaration is here util/parse-events.c: In function ‘add_tracepoint_multi_event’: ... Using different argument names instead to fix it. Reported-by: Vinson Lee Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: He Kuang Cc: H. Peter Anvin Cc: Jiri Olsa Cc: linux-tip-commits@vger.kernel.org Cc: Matt Fleming Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Raphael Beamonte Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/20150929150531.GI27383@krava.redhat.com [ Fix one more case, at line 770 ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/parse-events.c | 40 +++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 5ffb356cbcc6..c1c64fb647aa 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -389,7 +389,7 @@ int parse_events_add_cache(struct list_head *list, int *idx, return add_event(list, idx, &attr, name, NULL); } -static void tracepoint_error(struct parse_events_error *error, int err, +static void tracepoint_error(struct parse_events_error *e, int err, char *sys, char *name) { char help[BUFSIZ]; @@ -402,30 +402,30 @@ static void tracepoint_error(struct parse_events_error *error, int err, switch (err) { case EACCES: - error->str = strdup("can't access trace events"); + e->str = strdup("can't access trace events"); break; case ENOENT: - error->str = strdup("unknown tracepoint"); + e->str = strdup("unknown tracepoint"); break; default: - error->str = strdup("failed to add tracepoint"); + e->str = strdup("failed to add tracepoint"); break; } tracing_path__strerror_open_tp(err, help, sizeof(help), sys, name); - error->help = strdup(help); + e->help = strdup(help); } static int add_tracepoint(struct list_head *list, int *idx, char *sys_name, char *evt_name, - struct parse_events_error *error __maybe_unused, + struct parse_events_error *err, struct list_head *head_config) { struct perf_evsel *evsel; evsel = perf_evsel__newtp_idx(sys_name, evt_name, (*idx)++); if (IS_ERR(evsel)) { - tracepoint_error(error, PTR_ERR(evsel), sys_name, evt_name); + tracepoint_error(err, PTR_ERR(evsel), sys_name, evt_name); return PTR_ERR(evsel); } @@ -443,7 +443,7 @@ static int add_tracepoint(struct list_head *list, int *idx, static int add_tracepoint_multi_event(struct list_head *list, int *idx, char *sys_name, char *evt_name, - struct parse_events_error *error, + struct parse_events_error *err, struct list_head *head_config) { char evt_path[MAXPATHLEN]; @@ -454,7 +454,7 @@ static int add_tracepoint_multi_event(struct list_head *list, int *idx, snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name); evt_dir = opendir(evt_path); if (!evt_dir) { - tracepoint_error(error, errno, sys_name, evt_name); + tracepoint_error(err, errno, sys_name, evt_name); return -1; } @@ -469,7 +469,7 @@ static int add_tracepoint_multi_event(struct list_head *list, int *idx, continue; ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name, - error, head_config); + err, head_config); } closedir(evt_dir); @@ -478,19 +478,19 @@ static int add_tracepoint_multi_event(struct list_head *list, int *idx, static int add_tracepoint_event(struct list_head *list, int *idx, char *sys_name, char *evt_name, - struct parse_events_error *error, + struct parse_events_error *err, struct list_head *head_config) { return strpbrk(evt_name, "*?") ? add_tracepoint_multi_event(list, idx, sys_name, evt_name, - error, head_config) : + err, head_config) : add_tracepoint(list, idx, sys_name, evt_name, - error, head_config); + err, head_config); } static int add_tracepoint_multi_sys(struct list_head *list, int *idx, char *sys_name, char *evt_name, - struct parse_events_error *error, + struct parse_events_error *err, struct list_head *head_config) { struct dirent *events_ent; @@ -499,7 +499,7 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx, events_dir = opendir(tracing_events_path); if (!events_dir) { - tracepoint_error(error, errno, sys_name, evt_name); + tracepoint_error(err, errno, sys_name, evt_name); return -1; } @@ -515,7 +515,7 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx, continue; ret = add_tracepoint_event(list, idx, events_ent->d_name, - evt_name, error, head_config); + evt_name, err, head_config); } closedir(events_dir); @@ -767,23 +767,23 @@ do { \ int parse_events_add_tracepoint(struct list_head *list, int *idx, char *sys, char *event, - struct parse_events_error *error, + struct parse_events_error *err, struct list_head *head_config) { if (head_config) { struct perf_event_attr attr; - if (config_attr(&attr, head_config, error, + if (config_attr(&attr, head_config, err, config_term_tracepoint)) return -EINVAL; } if (strpbrk(sys, "*?")) return add_tracepoint_multi_sys(list, idx, sys, event, - error, head_config); + err, head_config); else return add_tracepoint_event(list, idx, sys, event, - error, head_config); + err, head_config); } int parse_events_add_numeric(struct parse_events_evlist *data, From b7f9ff5654d53fa47e0b5ff20f834f3e7a611846 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 29 Sep 2015 17:34:46 -0300 Subject: [PATCH 03/16] perf maps: Introduce maps__find_symbol_by_name() Out of map_groups__find_symbol_by_name(), so that we can turn this later one first into a call to maps__find_symbol_by_name(MAP__FUNCTION) + MAP__VARIABLE, and then to just one call, we'll merge MAP__FUNCTION with MAP__VARIABLE maps, to simplify the code. Cc: Adrian Hunter Cc: Borislav Petkov Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Stephane Eranian Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-pvkar0jacqn92g148u9sqttt@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/map.c | 19 +++++++++++++------ tools/perf/util/map.h | 2 ++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index b1c475d9b240..622c2c9a1e65 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -553,13 +553,9 @@ struct symbol *map_groups__find_symbol(struct map_groups *mg, return NULL; } -struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, - enum map_type type, - const char *name, - struct map **mapp, - symbol_filter_t filter) +struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, + struct map **mapp, symbol_filter_t filter) { - struct maps *maps = &mg->maps[type]; struct symbol *sym; struct rb_node *nd; @@ -583,6 +579,17 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, return sym; } +struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, + enum map_type type, + const char *name, + struct map **mapp, + symbol_filter_t filter) +{ + struct symbol *sym = maps__find_symbol_by_name(&mg->maps[type], name, mapp, filter); + + return sym; +} + int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter) { if (ams->addr < ams->map->start || ams->addr >= ams->map->end) { diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 57829e89b78b..7309d64ce39e 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -190,6 +190,8 @@ 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); +struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, + struct map **mapp, symbol_filter_t filter); void map_groups__init(struct map_groups *mg, struct machine *machine); void map_groups__exit(struct map_groups *mg); int map_groups__clone(struct map_groups *mg, From 40862a7b793945c7080d1566ca3dc6249f3c6354 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 29 Sep 2015 11:52:37 +0300 Subject: [PATCH 04/16] perf report: Amend documentation about max_stack and synthesized callchains The --max_stack option was added as an optimization to reduce processing time, so people specifying --max-stack might get a increased processing time if combined with synthesized callchains, but otherwise no real harm. A warning about setting both --max_stack and the synthesized callchains max depth seems like overkill. Amend the documentation. Reported-by: Arnaldo Carvalho de Melo Signed-off-by: Adrian Hunter Cc: Jiri Olsa Link: http://lkml.kernel.org/r/560A5155.4060105@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-report.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index b941d5e07e28..ce499035e6d8 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -205,6 +205,8 @@ OPTIONS beyond the specified depth will be ignored. This is a trade-off between information loss and faster processing especially for workloads that can have a very long callchain stack. + Note that when using the --itrace option the synthesized callchain size + will override this value if the synthesized callchain size is bigger. Default: 127 From eb56db54326f910348defbee2803ec0675a664b2 Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Thu, 24 Sep 2015 17:53:49 -0400 Subject: [PATCH 05/16] perf tools: Fix build break on powerpc due to sample_reg_masks The perf_regs.c file does not get built on Powerpc as CONFIG_PERF_REGS is false. So the weak definition for 'sample_regs_masks' doesn't get picked up. Adding perf_regs.o to util/Build unconditionally, exposes a redefinition error for 'perf_reg_value()' function (due to the static inline version in util/perf_regs.h). So use #ifdef HAVE_PERF_REGS_SUPPORT' around that function. Signed-off-by: Sukadev Bhattiprolu Acked-by: Jiri Olsa Cc: Dominik Dingel Cc: Naveen N. Rao Cc: Stephane Eranian Cc: linuxppc-dev@ozlabs.org Link: http://lkml.kernel.org/r/20150930182836.GA27858@us.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/Build | 2 +- tools/perf/util/perf_regs.c | 2 ++ tools/perf/util/perf_regs.h | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 4bc7a9ab45b1..9217119c4108 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -18,6 +18,7 @@ libperf-y += levenshtein.o libperf-y += llvm-utils.o libperf-y += parse-options.o libperf-y += parse-events.o +libperf-y += perf_regs.o libperf-y += path.o libperf-y += rbtree.o libperf-y += bitmap.o @@ -104,7 +105,6 @@ libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o libperf-y += scripting-engines/ -libperf-$(CONFIG_PERF_REGS) += perf_regs.o libperf-$(CONFIG_ZLIB) += zlib.o libperf-$(CONFIG_LZMA) += lzma.o diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c index 885e8ac83997..6b8eb13e14e4 100644 --- a/tools/perf/util/perf_regs.c +++ b/tools/perf/util/perf_regs.c @@ -6,6 +6,7 @@ const struct sample_reg __weak sample_reg_masks[] = { SMPL_REG_END }; +#ifdef HAVE_PERF_REGS_SUPPORT int perf_reg_value(u64 *valp, struct regs_dump *regs, int id) { int i, idx = 0; @@ -29,3 +30,4 @@ int perf_reg_value(u64 *valp, struct regs_dump *regs, int id) *valp = regs->cache_regs[id]; return 0; } +#endif diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h index 2984dcc54d67..679d6e493962 100644 --- a/tools/perf/util/perf_regs.h +++ b/tools/perf/util/perf_regs.h @@ -2,6 +2,7 @@ #define __PERF_REGS_H #include +#include struct regs_dump; From 77e65977495cd6f6fcfacd8c16bdd9c8c18a1d72 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 30 Sep 2015 11:08:58 -0300 Subject: [PATCH 06/16] perf machine: Use machine__kernel_map() thoroughly In places where we were using its open coded equivalent. Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-khkdugcdoqy3tkszm3jdxgbe@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-kmem.c | 2 +- tools/perf/builtin-report.c | 2 +- tools/perf/tests/code-reading.c | 2 +- tools/perf/util/event.c | 7 +++---- tools/perf/util/machine.c | 23 ++++++++++++----------- tools/perf/util/map.c | 2 +- tools/perf/util/probe-event.c | 7 ++++--- 7 files changed, 23 insertions(+), 22 deletions(-) diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 23b1faaaa4cc..b122c4e0818e 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -329,7 +329,7 @@ static int build_alloc_func_list(void) return -EINVAL; } - kernel_map = machine->vmlinux_maps[MAP__FUNCTION]; + kernel_map = machine__kernel_map(machine, MAP__FUNCTION) if (map__load(kernel_map, NULL) < 0) { pr_err("cannot load kernel map\n"); return -ENOENT; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 37c9f5125887..7962605b3576 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -387,7 +387,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, static void report__warn_kptr_restrict(const struct report *rep) { - struct map *kernel_map = rep->session->machines.host.vmlinux_maps[MAP__FUNCTION]; + struct map *kernel_map = machine__kernel_map(&rep->session->machines.host, MAP__FUNCTION); struct kmap *kernel_kmap = kernel_map ? map__kmap(kernel_map) : NULL; if (kernel_map == NULL || diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 2d21183bd661..47017a8aa685 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -473,7 +473,7 @@ static int do_test_code_reading(bool try_kcore) symbol_conf.kallsyms_name = "/proc/kallsyms"; /* Load kernel map */ - map = machine->vmlinux_maps[MAP__FUNCTION]; + map = machine__kernel_map(machine, MAP__FUNCTION); ret = map__load(map, NULL); if (ret < 0) { pr_debug("map__load failed\n"); diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index b1bb348ec3b6..281ab7f87446 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -649,12 +649,12 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, size_t size; const char *mmap_name; char name_buff[PATH_MAX]; - struct map *map; + struct map *map = machine__kernel_map(machine, MAP__FUNCTION); struct kmap *kmap; int err; union perf_event *event; - if (machine->vmlinux_maps[0] == NULL) + if (map == NULL) return -1; /* @@ -680,7 +680,6 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL; } - map = machine->vmlinux_maps[MAP__FUNCTION]; kmap = map__kmap(map); size = snprintf(event->mmap.filename, sizeof(event->mmap.filename), "%s%s", mmap_name, kmap->ref_reloc_sym->name) + 1; @@ -1008,7 +1007,7 @@ int perf_event__preprocess_sample(const union perf_event *event, * it now. */ if (cpumode == PERF_RECORD_MISC_KERNEL && - machine->vmlinux_maps[MAP__FUNCTION] == NULL) + machine__kernel_map(machine, MAP__FUNCTION) == NULL) machine__create_kernel_maps(machine); thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, al); diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index fd1efeafb343..d71b7dcf4579 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -625,7 +625,7 @@ size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp) { int i; size_t printed = 0; - struct dso *kdso = machine->vmlinux_maps[MAP__FUNCTION]->dso; + struct dso *kdso = machine__kernel_map(machine, MAP__FUNCTION)->dso; if (kdso->has_build_id) { char filename[PATH_MAX]; @@ -741,6 +741,7 @@ int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) for (type = 0; type < MAP__NR_TYPES; ++type) { struct kmap *kmap; + struct map *map; machine->vmlinux_maps[type] = map__new2(start, kernel, type); if (machine->vmlinux_maps[type] == NULL) @@ -749,13 +750,13 @@ int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) machine->vmlinux_maps[type]->map_ip = machine->vmlinux_maps[type]->unmap_ip = identity__map_ip; - kmap = map__kmap(machine->vmlinux_maps[type]); + map = machine__kernel_map(machine, type); + kmap = map__kmap(map); if (!kmap) return -1; kmap->kmaps = &machine->kmaps; - map_groups__insert(&machine->kmaps, - machine->vmlinux_maps[type]); + map_groups__insert(&machine->kmaps, map); } return 0; @@ -767,13 +768,13 @@ void machine__destroy_kernel_maps(struct machine *machine) for (type = 0; type < MAP__NR_TYPES; ++type) { struct kmap *kmap; + struct map *map = machine__kernel_map(machine, type); - if (machine->vmlinux_maps[type] == NULL) + if (map == NULL) continue; - kmap = map__kmap(machine->vmlinux_maps[type]); - map_groups__remove(&machine->kmaps, - machine->vmlinux_maps[type]); + kmap = map__kmap(map); + map_groups__remove(&machine->kmaps, map); if (kmap && kmap->ref_reloc_sym) { /* * ref_reloc_sym is shared among all maps, so free just @@ -867,7 +868,7 @@ int machines__create_kernel_maps(struct machines *machines, pid_t pid) int machine__load_kallsyms(struct machine *machine, const char *filename, enum map_type type, symbol_filter_t filter) { - struct map *map = machine->vmlinux_maps[type]; + struct map *map = machine__kernel_map(machine, MAP__FUNCTION); int ret = dso__load_kallsyms(map->dso, filename, map, filter); if (ret > 0) { @@ -886,7 +887,7 @@ int machine__load_kallsyms(struct machine *machine, const char *filename, int machine__load_vmlinux_path(struct machine *machine, enum map_type type, symbol_filter_t filter) { - struct map *map = machine->vmlinux_maps[type]; + struct map *map = machine__kernel_map(machine, MAP__FUNCTION); int ret = dso__load_vmlinux_path(map->dso, map, filter); if (ret > 0) @@ -1244,7 +1245,7 @@ static int machine__process_kernel_mmap_event(struct machine *machine, /* * preload dso of guest kernel and modules */ - dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION], + dso__load(kernel, machine__kernel_map(machine, MAP__FUNCTION), NULL); } } diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 622c2c9a1e65..2a9d859d30b9 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -235,7 +235,7 @@ struct map *map__new2(u64 start, struct dso *dso, enum map_type type) */ bool __map__is_kernel(const struct map *map) { - return map->groups->machine->vmlinux_maps[map->type] == map; + return machine__kernel_map(map->groups->machine, map->type) == map; } static void map__exit(struct map *map) diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 7fb0533ab18c..a0b3f3c886f0 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -126,11 +126,12 @@ static struct ref_reloc_sym *kernel_get_ref_reloc_sym(void) { /* kmap->ref_reloc_sym should be set if host_machine is initialized */ struct kmap *kmap; + struct map *map = machine__kernel_map(host_machine, MAP__FUNCTION); - if (map__load(host_machine->vmlinux_maps[MAP__FUNCTION], NULL) < 0) + if (map__load(map, NULL) < 0) return NULL; - kmap = map__kmap(host_machine->vmlinux_maps[MAP__FUNCTION]); + kmap = map__kmap(map); if (!kmap) return NULL; return kmap->ref_reloc_sym; @@ -281,7 +282,7 @@ static int kernel_get_module_dso(const char *module, struct dso **pdso) return -ENOENT; } - map = host_machine->vmlinux_maps[MAP__FUNCTION]; + map = machine__kernel_map(host_machine, MAP__FUNCTION); dso = map->dso; vmlinux_name = symbol_conf.vmlinux_name; From a5e813c68649366aaa3f785772b00ea6ccad7b8d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 30 Sep 2015 11:54:04 -0300 Subject: [PATCH 07/16] perf machine: Add method for common kernel_map(FUNCTION) operation And it is also a step in the direction of killing the separation of data and text maps in map_groups. Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-rrds86kb3wx5wk8v38v56gw8@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-kmem.c | 2 +- tools/perf/builtin-report.c | 2 +- tools/perf/tests/code-reading.c | 2 +- tools/perf/tests/vmlinux-kallsyms.c | 4 ++-- tools/perf/util/event.c | 4 ++-- tools/perf/util/intel-pt.c | 2 +- tools/perf/util/machine.c | 15 +++++++-------- tools/perf/util/machine.h | 8 +++++++- tools/perf/util/map.c | 2 +- tools/perf/util/probe-event.c | 4 ++-- 10 files changed, 25 insertions(+), 20 deletions(-) diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index b122c4e0818e..93ce665f976f 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -329,7 +329,7 @@ static int build_alloc_func_list(void) return -EINVAL; } - kernel_map = machine__kernel_map(machine, MAP__FUNCTION) + kernel_map = machine__kernel_map(machine); if (map__load(kernel_map, NULL) < 0) { pr_err("cannot load kernel map\n"); return -ENOENT; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 7962605b3576..b5623639f67d 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -387,7 +387,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, static void report__warn_kptr_restrict(const struct report *rep) { - struct map *kernel_map = machine__kernel_map(&rep->session->machines.host, MAP__FUNCTION); + struct map *kernel_map = machine__kernel_map(&rep->session->machines.host); struct kmap *kernel_kmap = kernel_map ? map__kmap(kernel_map) : NULL; if (kernel_map == NULL || diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 47017a8aa685..49b1959dda41 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -473,7 +473,7 @@ static int do_test_code_reading(bool try_kcore) symbol_conf.kallsyms_name = "/proc/kallsyms"; /* Load kernel map */ - map = machine__kernel_map(machine, MAP__FUNCTION); + map = machine__kernel_map(machine); ret = map__load(map, NULL); if (ret < 0) { pr_debug("map__load failed\n"); diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c index b34c5fc829ae..d677e018e504 100644 --- a/tools/perf/tests/vmlinux-kallsyms.c +++ b/tools/perf/tests/vmlinux-kallsyms.c @@ -68,7 +68,7 @@ int test__vmlinux_matches_kallsyms(void) * to see if the running kernel was relocated by checking if it has the * same value in the vmlinux file we load. */ - kallsyms_map = machine__kernel_map(&kallsyms, type); + kallsyms_map = machine__kernel_map(&kallsyms); /* * Step 5: @@ -80,7 +80,7 @@ int test__vmlinux_matches_kallsyms(void) goto out; } - vmlinux_map = machine__kernel_map(&vmlinux, type); + vmlinux_map = machine__kernel_map(&vmlinux); /* * Step 6: diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 281ab7f87446..cb98b5af9e17 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -649,7 +649,7 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, size_t size; const char *mmap_name; char name_buff[PATH_MAX]; - struct map *map = machine__kernel_map(machine, MAP__FUNCTION); + struct map *map = machine__kernel_map(machine); struct kmap *kmap; int err; union perf_event *event; @@ -1007,7 +1007,7 @@ int perf_event__preprocess_sample(const union perf_event *event, * it now. */ if (cpumode == PERF_RECORD_MISC_KERNEL && - machine__kernel_map(machine, MAP__FUNCTION) == NULL) + machine__kernel_map(machine) == NULL) machine__create_kernel_maps(machine); thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, al); diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index 03ff072b5993..97f963a3dcb9 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -1268,7 +1268,7 @@ static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip) if (ptss_ip) *ptss_ip = 0; - map = machine__kernel_map(machine, MAP__FUNCTION); + map = machine__kernel_map(machine); if (!map) return 0; diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index d71b7dcf4579..76fe167c359e 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -625,7 +625,7 @@ size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp) { int i; size_t printed = 0; - struct dso *kdso = machine__kernel_map(machine, MAP__FUNCTION)->dso; + struct dso *kdso = machine__kernel_map(machine)->dso; if (kdso->has_build_id) { char filename[PATH_MAX]; @@ -750,7 +750,7 @@ int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) machine->vmlinux_maps[type]->map_ip = machine->vmlinux_maps[type]->unmap_ip = identity__map_ip; - map = machine__kernel_map(machine, type); + map = __machine__kernel_map(machine, type); kmap = map__kmap(map); if (!kmap) return -1; @@ -768,7 +768,7 @@ void machine__destroy_kernel_maps(struct machine *machine) for (type = 0; type < MAP__NR_TYPES; ++type) { struct kmap *kmap; - struct map *map = machine__kernel_map(machine, type); + struct map *map = __machine__kernel_map(machine, type); if (map == NULL) continue; @@ -868,7 +868,7 @@ int machines__create_kernel_maps(struct machines *machines, pid_t pid) int machine__load_kallsyms(struct machine *machine, const char *filename, enum map_type type, symbol_filter_t filter) { - struct map *map = machine__kernel_map(machine, MAP__FUNCTION); + struct map *map = machine__kernel_map(machine); int ret = dso__load_kallsyms(map->dso, filename, map, filter); if (ret > 0) { @@ -887,7 +887,7 @@ int machine__load_kallsyms(struct machine *machine, const char *filename, int machine__load_vmlinux_path(struct machine *machine, enum map_type type, symbol_filter_t filter) { - struct map *map = machine__kernel_map(machine, MAP__FUNCTION); + struct map *map = machine__kernel_map(machine); int ret = dso__load_vmlinux_path(map->dso, map, filter); if (ret > 0) @@ -1245,8 +1245,7 @@ static int machine__process_kernel_mmap_event(struct machine *machine, /* * preload dso of guest kernel and modules */ - dso__load(kernel, machine__kernel_map(machine, MAP__FUNCTION), - NULL); + dso__load(kernel, machine__kernel_map(machine), NULL); } } return 0; @@ -1998,7 +1997,7 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid, int machine__get_kernel_start(struct machine *machine) { - struct map *map = machine__kernel_map(machine, MAP__FUNCTION); + struct map *map = machine__kernel_map(machine); int err = 0; /* diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 9dfc4281f940..2c2b443df5ba 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -48,11 +48,17 @@ struct machine { }; static inline -struct map *machine__kernel_map(struct machine *machine, enum map_type type) +struct map *__machine__kernel_map(struct machine *machine, enum map_type type) { return machine->vmlinux_maps[type]; } +static inline +struct map *machine__kernel_map(struct machine *machine) +{ + return __machine__kernel_map(machine, MAP__FUNCTION); +} + int machine__get_kernel_start(struct machine *machine); static inline u64 machine__kernel_start(struct machine *machine) diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 2a9d859d30b9..4e38c396a897 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -235,7 +235,7 @@ struct map *map__new2(u64 start, struct dso *dso, enum map_type type) */ bool __map__is_kernel(const struct map *map) { - return machine__kernel_map(map->groups->machine, map->type) == map; + return __machine__kernel_map(map->groups->machine, map->type) == map; } static void map__exit(struct map *map) diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index a0b3f3c886f0..8b9a6807dab4 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -126,7 +126,7 @@ static struct ref_reloc_sym *kernel_get_ref_reloc_sym(void) { /* kmap->ref_reloc_sym should be set if host_machine is initialized */ struct kmap *kmap; - struct map *map = machine__kernel_map(host_machine, MAP__FUNCTION); + struct map *map = machine__kernel_map(host_machine); if (map__load(map, NULL) < 0) return NULL; @@ -282,7 +282,7 @@ static int kernel_get_module_dso(const char *module, struct dso **pdso) return -ENOENT; } - map = machine__kernel_map(host_machine, MAP__FUNCTION); + map = machine__kernel_map(host_machine); dso = map->dso; vmlinux_name = symbol_conf.vmlinux_name; From 8e947f1e84fd1588f66e5f2ea69c80647de72cd4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 30 Sep 2015 12:42:07 -0300 Subject: [PATCH 08/16] tools lib symbol: Rename kallsyms2elf_type to kallsyms2elf_binding It is about binding, not type, we have just a letter in kallsyms that should map both for the ELF type (STT_FUNC, etc) and to the ELF symbol binding (STB_WEAK, STB_GLOBAL, etc), so rename it now before introducing kallsyms2_elf_type() Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-uu5vj343ms1q2wm55690on6v@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/symbol/kallsyms.h | 2 +- tools/perf/util/symbol.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/lib/symbol/kallsyms.h b/tools/lib/symbol/kallsyms.h index 6084f5e18b3c..bb86c5b76ee8 100644 --- a/tools/lib/symbol/kallsyms.h +++ b/tools/lib/symbol/kallsyms.h @@ -9,7 +9,7 @@ #define KSYM_NAME_LEN 256 #endif -static inline u8 kallsyms2elf_type(char type) +static inline u8 kallsyms2elf_binding(char type) { if (type == 'W') return STB_WEAK; diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 1f97ffb158a6..bcda43bee4d4 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -624,7 +624,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name, * symbols, setting length to 0, and rely on * symbols__fixup_end() to fix it up. */ - sym = symbol__new(start, 0, kallsyms2elf_type(type), name); + sym = symbol__new(start, 0, kallsyms2elf_binding(type), name); if (sym == NULL) return -ENOMEM; /* From f845086a8ed49e657a750f87f85966d75c8a9645 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 30 Sep 2015 13:02:08 -0300 Subject: [PATCH 09/16] tools lib symbol: Introduce kallsyms2elf_type Map 't', 'T' (text, local, global), 'w' and 'W' (weak text, local, global) as STT_FUNC, and the rest as STT_OBJECT Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-sbwcixulpc5v1xuxn3xvm0nn@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/symbol/kallsyms.c | 6 ++++++ tools/lib/symbol/kallsyms.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/tools/lib/symbol/kallsyms.c b/tools/lib/symbol/kallsyms.c index 18bc271a4bbc..5e431077fcd6 100644 --- a/tools/lib/symbol/kallsyms.c +++ b/tools/lib/symbol/kallsyms.c @@ -2,6 +2,12 @@ #include #include +u8 kallsyms2elf_type(char type) +{ + type = tolower(type); + return (type == 't' || type == 'w') ? STT_FUNC : STT_OBJECT; +} + int kallsyms__parse(const char *filename, void *arg, int (*process_symbol)(void *arg, const char *name, char type, u64 start)) diff --git a/tools/lib/symbol/kallsyms.h b/tools/lib/symbol/kallsyms.h index bb86c5b76ee8..4071316a766e 100644 --- a/tools/lib/symbol/kallsyms.h +++ b/tools/lib/symbol/kallsyms.h @@ -17,6 +17,8 @@ static inline u8 kallsyms2elf_binding(char type) return isupper(type) ? STB_GLOBAL : STB_LOCAL; } +u8 kallsyms2elf_type(char type); + int kallsyms__parse(const char *filename, void *arg, int (*process_symbol)(void *arg, const char *name, char type, u64 start)); From 663b1151f2d674e2004c015cbe9995749033c49a Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 1 Oct 2015 01:41:30 +0900 Subject: [PATCH 10/16] perf probe: Fix to remove dot suffix from second or latter events Fix to remove dot suffix (e.g. .const, .isra) from the second or latter events which has suffix numbers. Since the previous commit 35a23ff928b0 ("perf probe: Cut off the gcc optimization postfixes from function name") didn't care about the suffix numbered events, therefore we'll have an error when we add additional events on the same dot suffix functions. e.g. ---- # ./perf probe -f -a get_sigframe.isra.2.constprop.3 \ -a get_sigframe.isra.2.constprop.3 Failed to write event: Invalid argument Error: Failed to add events. ---- This fixes above issue as below: ---- # ./perf probe -f -a get_sigframe.isra.2.constprop.3 \ -a get_sigframe.isra.2.constprop.3 Added new events: probe:get_sigframe (on get_sigframe.isra.2.constprop.3) probe:get_sigframe_1 (on get_sigframe.isra.2.constprop.3) You can now use it in all perf tools, such as: perf record -e probe:get_sigframe_1 -aR sleep 1 ---- Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/r/20150930164130.3733.26573.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 8b9a6807dab4..5d68f68797a9 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -2289,36 +2289,41 @@ static int get_new_event_name(char *buf, size_t len, const char *base, struct strlist *namelist, bool allow_suffix) { int i, ret; - char *p; + char *p, *nbase; if (*base == '.') base++; + nbase = strdup(base); + if (!nbase) + return -ENOMEM; - /* Try no suffix */ - ret = e_snprintf(buf, len, "%s", base); + /* Cut off the dot suffixes (e.g. .const, .isra)*/ + p = strchr(nbase, '.'); + if (p && p != nbase) + *p = '\0'; + + /* Try no suffix number */ + ret = e_snprintf(buf, len, "%s", nbase); if (ret < 0) { pr_debug("snprintf() failed: %d\n", ret); - return ret; + goto out; } - /* Cut off the postfixes (e.g. .const, .isra)*/ - p = strchr(buf, '.'); - if (p && p != buf) - *p = '\0'; if (!strlist__has_entry(namelist, buf)) - return 0; + goto out; if (!allow_suffix) { pr_warning("Error: event \"%s\" already exists. " - "(Use -f to force duplicates.)\n", base); - return -EEXIST; + "(Use -f to force duplicates.)\n", buf); + ret = -EEXIST; + goto out; } /* Try to add suffix */ for (i = 1; i < MAX_EVENT_INDEX; i++) { - ret = e_snprintf(buf, len, "%s_%d", base, i); + ret = e_snprintf(buf, len, "%s_%d", nbase, i); if (ret < 0) { pr_debug("snprintf() failed: %d\n", ret); - return ret; + goto out; } if (!strlist__has_entry(namelist, buf)) break; @@ -2328,6 +2333,8 @@ static int get_new_event_name(char *buf, size_t len, const char *base, ret = -ERANGE; } +out: + free(nbase); return ret; } From 9135949ddd9d0d8d73a2f441912508d25a4a82a2 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 1 Oct 2015 01:41:28 +0900 Subject: [PATCH 11/16] perf probe: Begin and end libdwfl report session correctly Fix a trival bug about libdwfl usage of the report session, it should explicitly begin and end a report session around dwfl_report_offline(). Signed-off-by: Masami Hiramatsu Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/r/20150930164128.3733.59876.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-finder.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 29c43c0680a8..35f905f4f34c 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -70,6 +70,7 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, if (!dbg->dwfl) goto error; + dwfl_report_begin(dbg->dwfl); dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); if (!dbg->mod) goto error; @@ -78,6 +79,8 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, if (!dbg->dbg) goto error; + dwfl_report_end(dbg->dwfl, NULL, NULL); + return 0; error: if (dbg->dwfl) From 9b239a12bc872f212cefbaee4d028c838abe6ff3 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 1 Oct 2015 01:41:33 +0900 Subject: [PATCH 12/16] perf probe: Show correct source lines of probes on kmodules Perf probe always failed to find appropriate line numbers because of failing to find .text start address offset from debuginfo. e.g. ---- # ./perf probe -m pcspkr pcspkr_event:5 Added new events: probe:pcspkr_event (on pcspkr_event:5 in pcspkr) probe:pcspkr_event_1 (on pcspkr_event:5 in pcspkr) You can now use it in all perf tools, such as: perf record -e probe:pcspkr_event_1 -aR sleep 1 # ./perf probe -l Failed to find debug information for address ffffffffa031f006 Failed to find debug information for address ffffffffa031f016 probe:pcspkr_event (on pcspkr_event+6 in pcspkr) probe:pcspkr_event_1 (on pcspkr_event+22 in pcspkr) ---- This fixes the above issue as below. 1. Get the relative address of the symbol in .text by using map->start. 2. Adjust the address by adding the offset of .text section in the kernel module binary. With this fix, perf probe -l shows lines correctly. ---- # ./perf probe -l probe:pcspkr_event (on pcspkr_event:5@drivers/input/misc/pcspkr.c in pcspkr) probe:pcspkr_event_1 (on pcspkr_event:5@drivers/input/misc/pcspkr.c in pcspkr) ---- Reported-by: Arnaldo Carvalho de Melo Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/r/20150930164132.3733.24643.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 35 ++++++++++++++++++---------- tools/perf/util/probe-finder.c | 42 ++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 5d68f68797a9..65be284823d5 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -137,7 +137,8 @@ static struct ref_reloc_sym *kernel_get_ref_reloc_sym(void) return kmap->ref_reloc_sym; } -static u64 kernel_get_symbol_address_by_name(const char *name, bool reloc) +static int kernel_get_symbol_address_by_name(const char *name, u64 *addr, + bool reloc, bool reladdr) { struct ref_reloc_sym *reloc_sym; struct symbol *sym; @@ -146,12 +147,14 @@ static u64 kernel_get_symbol_address_by_name(const char *name, bool reloc) /* ref_reloc_sym is just a label. Need a special fix*/ reloc_sym = kernel_get_ref_reloc_sym(); if (reloc_sym && strcmp(name, reloc_sym->name) == 0) - return (reloc) ? reloc_sym->addr : reloc_sym->unrelocated_addr; + *addr = (reloc) ? reloc_sym->addr : reloc_sym->unrelocated_addr; else { sym = __find_kernel_function_by_name(name, &map); - if (sym) - return map->unmap_ip(map, sym->start) - - ((reloc) ? 0 : map->reloc); + if (!sym) + return -ENOENT; + *addr = map->unmap_ip(map, sym->start) - + ((reloc) ? 0 : map->reloc) - + ((reladdr) ? map->start : 0); } return 0; } @@ -245,12 +248,14 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs) static bool kprobe_blacklist__listed(unsigned long address); static bool kprobe_warn_out_range(const char *symbol, unsigned long address) { - u64 etext_addr; + u64 etext_addr = 0; + int ret; /* Get the address of _etext for checking non-probable text symbol */ - etext_addr = kernel_get_symbol_address_by_name("_etext", false); + ret = kernel_get_symbol_address_by_name("_etext", &etext_addr, + false, false); - if (etext_addr != 0 && etext_addr < address) + if (ret == 0 && etext_addr < address) pr_warning("%s is out of .text, skip it.\n", symbol); else if (kprobe_blacklist__listed(address)) pr_warning("%s is blacklisted function, skip it.\n", symbol); @@ -517,8 +522,10 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp, goto error; addr += stext; } else if (tp->symbol) { - addr = kernel_get_symbol_address_by_name(tp->symbol, false); - if (addr == 0) + /* If the module is given, this returns relative address */ + ret = kernel_get_symbol_address_by_name(tp->symbol, &addr, + false, !!tp->module); + if (ret != 0) goto error; addr += tp->offset; } @@ -1884,8 +1891,12 @@ static int find_perf_probe_point_from_map(struct probe_trace_point *tp, goto out; sym = map__find_symbol(map, addr, NULL); } else { - if (tp->symbol) - addr = kernel_get_symbol_address_by_name(tp->symbol, true); + if (tp->symbol && !addr) { + ret = kernel_get_symbol_address_by_name(tp->symbol, + &addr, true, false); + if (ret < 0) + goto out; + } if (addr) { addr += tp->offset; sym = __find_kernel_function(addr, &map); diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 35f905f4f34c..f0708ffd5e07 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -1402,6 +1402,41 @@ int debuginfo__find_available_vars_at(struct debuginfo *dbg, return (ret < 0) ? ret : af.nvls; } +/* For the kernel module, we need a special code to get a DIE */ +static int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs) +{ + int n, i; + Elf32_Word shndx; + Elf_Scn *scn; + Elf *elf; + GElf_Shdr mem, *shdr; + const char *p; + + elf = dwfl_module_getelf(dbg->mod, &dbg->bias); + if (!elf) + return -EINVAL; + + /* Get the number of relocations */ + n = dwfl_module_relocations(dbg->mod); + if (n < 0) + return -ENOENT; + /* Search the relocation related .text section */ + for (i = 0; i < n; i++) { + p = dwfl_module_relocation_info(dbg->mod, i, &shndx); + if (strcmp(p, ".text") == 0) { + /* OK, get the section header */ + scn = elf_getscn(elf, shndx); + if (!scn) + return -ENOENT; + shdr = gelf_getshdr(scn, &mem); + if (!shdr) + return -ENOENT; + *offs = shdr->sh_addr; + } + } + return 0; +} + /* Reverse search */ int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr, struct perf_probe_point *ppt) @@ -1410,9 +1445,16 @@ int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr, Dwarf_Addr _addr = 0, baseaddr = 0; const char *fname = NULL, *func = NULL, *basefunc = NULL, *tmp; int baseline = 0, lineno = 0, ret = 0; + bool reloc = false; +retry: /* Find cu die */ if (!dwarf_addrdie(dbg->dbg, (Dwarf_Addr)addr, &cudie)) { + if (!reloc && debuginfo__get_text_offset(dbg, &baseaddr) == 0) { + addr += baseaddr; + reloc = true; + goto retry; + } pr_warning("Failed to find debug information for address %lx\n", addr); ret = -EINVAL; From 20f49859c785183d5296670a10dace454131274b Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 1 Oct 2015 01:41:35 +0900 Subject: [PATCH 13/16] perf probe: Fix a segfault bug in debuginfo_cache perf probe --list will get a segfault if the first kprobe event is on a module and the second or latter one is on the kernel. e.g. ---- # ./perf probe -q -m pcspkr pcspkr_event # ./perf probe -q vfs_read # ./perf probe -l Segmentation fault (core dumped) ---- This is because the debuginfo_cache fails to handle NULL module name, which causes segfault on strcmp. (Note that strcmp("something", NULL) always causes segfault) To fix this debuginfo_cache__open always translates the NULL module name to "kernel" (this is correct, because NULL module name means opening the debuginfo for the kernel) ---- # ./perf probe -l probe:pcspkr_event (on pcspkr_event@drivers/input/misc/pcspkr.c in pcspkr) probe:vfs_read (on vfs_read@ksrc/linux-3/fs/read_write.c) ---- Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/r/20150930164135.3733.23993.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 65be284823d5..3010abc071ff 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -441,19 +441,22 @@ static char *debuginfo_cache_path; static struct debuginfo *debuginfo_cache__open(const char *module, bool silent) { - if ((debuginfo_cache_path && !strcmp(debuginfo_cache_path, module)) || - (!debuginfo_cache_path && !module && debuginfo_cache)) + const char *path = module; + + /* If the module is NULL, it should be the kernel. */ + if (!module) + path = "kernel"; + + if (debuginfo_cache_path && !strcmp(debuginfo_cache_path, path)) goto out; /* Copy module path */ free(debuginfo_cache_path); - if (module) { - debuginfo_cache_path = strdup(module); - if (!debuginfo_cache_path) { - debuginfo__delete(debuginfo_cache); - debuginfo_cache = NULL; - goto out; - } + debuginfo_cache_path = strdup(path); + if (!debuginfo_cache_path) { + debuginfo__delete(debuginfo_cache); + debuginfo_cache = NULL; + goto out; } debuginfo_cache = open_debuginfo(module, silent); From 6cca13bdf5a5d561a855259689874e0c7266eec3 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 1 Oct 2015 01:41:37 +0900 Subject: [PATCH 14/16] perf probe: Improve error message when %return is on inlined function perf probe shows more precisely message when it finds given %return target function is inlined. Without this fix: ---- # ./perf probe -V getname_flags%return Return probe must be on the head of a real function. Debuginfo analysis failed. Error: Failed to show vars. ---- With this fix: ---- # ./perf probe -V getname_flags%return Failed to find "getname_flags%return", because getname_flags is an inlined function and has no return point. Debuginfo analysis failed. Error: Failed to show vars. ---- Suggested-by: Arnaldo Carvalho de Melo Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/r/20150930164137.3733.55055.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-finder.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index f0708ffd5e07..bd8f03de5e40 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -594,6 +594,7 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) /* Convert subprogram DIE to trace point */ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, Dwarf_Addr paddr, bool retprobe, + const char *function, struct probe_trace_point *tp) { Dwarf_Addr eaddr, highaddr; @@ -637,8 +638,10 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, /* Return probe must be on the head of a subprogram */ if (retprobe) { if (eaddr != paddr) { - pr_warning("Return probe must be on the head of" - " a real function.\n"); + pr_warning("Failed to find \"%s%%return\",\n" + " because %s is an inlined function and" + " has no return point.\n", function, + function); return -EINVAL; } tp->retprobe = true; @@ -1178,6 +1181,7 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) { struct trace_event_finder *tf = container_of(pf, struct trace_event_finder, pf); + struct perf_probe_point *pp = &pf->pev->point; struct probe_trace_event *tev; struct perf_probe_arg *args; int ret, i; @@ -1192,7 +1196,7 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) /* Trace point should be converted from subprogram DIE */ ret = convert_to_trace_point(&pf->sp_die, tf->mod, pf->addr, - pf->pev->point.retprobe, &tev->point); + pp->retprobe, pp->function, &tev->point); if (ret < 0) return ret; @@ -1322,6 +1326,7 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) { struct available_var_finder *af = container_of(pf, struct available_var_finder, pf); + struct perf_probe_point *pp = &pf->pev->point; struct variable_list *vl; Dwarf_Die die_mem; int ret; @@ -1335,7 +1340,7 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) /* Trace point should be converted from subprogram DIE */ ret = convert_to_trace_point(&pf->sp_die, af->mod, pf->addr, - pf->pev->point.retprobe, &vl->point); + pp->retprobe, pp->function, &vl->point); if (ret < 0) return ret; From dfc431cbdc3a3c0556f1cd462d724d107cc15a9e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 30 Sep 2015 17:13:26 -0300 Subject: [PATCH 15/16] perf list: Remove blank lines, headers when piping output So that one can, for instance, use it with wc -l: # perf list *:*write* | wc -l 60 Or to look for the "bio" tracepoints, without 'perf list' headers: # perf list *:*bio* | head block:block_bio_backmerge [Tracepoint event] block:block_bio_bounce [Tracepoint event] block:block_bio_complete [Tracepoint event] block:block_bio_frontmerge [Tracepoint event] block:block_bio_queue [Tracepoint event] block:block_bio_remap [Tracepoint event] # Cc: Adrian Hunter Cc: Borislav Petkov Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Stephane Eranian Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-ts7sc0x8u4io4cifzkup4j44@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-list.c | 2 +- tools/perf/util/parse-events.c | 13 +++++++------ tools/perf/util/pmu.c | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index af5bd0514108..602414040344 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c @@ -36,7 +36,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) setup_pager(); - if (!raw_dump) + if (!raw_dump && pager_in_use()) printf("\nList of pre-defined events (to be used in -e):\n\n"); if (argc == 0) { diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index c1c64fb647aa..5d0cfce2599b 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -1534,7 +1534,7 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob, printf(" %-50s [%s]\n", evt_list[evt_i++], event_type_descriptors[PERF_TYPE_TRACEPOINT]); } - if (evt_num) + if (evt_num && pager_in_use()) printf("\n"); out_free: @@ -1690,7 +1690,7 @@ int print_hwcache_events(const char *event_glob, bool name_only) printf(" %-50s [%s]\n", evt_list[evt_i++], event_type_descriptors[PERF_TYPE_HW_CACHE]); } - if (evt_num) + if (evt_num && pager_in_use()) printf("\n"); out_free: @@ -1763,7 +1763,7 @@ void print_symbol_events(const char *event_glob, unsigned type, } printf(" %-50s [%s]\n", evt_list[evt_i++], event_type_descriptors[type]); } - if (evt_num) + if (evt_num && pager_in_use()) printf("\n"); out_free: @@ -1804,13 +1804,14 @@ void print_events(const char *event_glob, bool name_only) printf(" %-50s [%s]\n", "cpu/t1=v1[,t2=v2,t3 ...]/modifier", event_type_descriptors[PERF_TYPE_RAW]); - printf(" (see 'man perf-list' on how to encode it)\n"); - printf("\n"); + if (pager_in_use()) + printf(" (see 'man perf-list' on how to encode it)\n\n"); printf(" %-50s [%s]\n", "mem:[/len][:access]", event_type_descriptors[PERF_TYPE_BREAKPOINT]); - printf("\n"); + if (pager_in_use()) + printf("\n"); } print_tracepoint_events(NULL, NULL, name_only); diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index ac42c97be9e4..41a356ba3cfe 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -1026,7 +1026,7 @@ void print_pmu_events(const char *event_glob, bool name_only) printf(" %-50s [Kernel PMU event]\n", aliases[j]); printed++; } - if (printed) + if (printed && pager_in_use()) printf("\n"); out_free: for (j = 0; j < len; j++) From 7f8d1ade1b19f684ed3a7c4fb1dc5d347127b438 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 30 Sep 2015 17:49:49 -0300 Subject: [PATCH 16/16] perf tools: By default use the most precise "cycles" hw counter available If the user doesn't specify any event, try the most precise "cycles" available, i.e. start by "cycles:ppp" and go on removing "p" till it works. E.g. $ perf record usleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.017 MB perf.data (11 samples) ] $ perf evlist cycles:pp $ perf evlist -v cycles:pp: size: 112, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD, disabled: 1, inherit: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, precise_ip: 2, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1 $ grep 'model name' /proc/cpuinfo | head -1 model name : Intel(R) Core(TM) i7-3667U CPU @ 2.00GHz $ When 'cycles' appears explicitely is specified this will not be tried, i.e. the user has full control of the level of precision to be used: $ perf record -e cycles usleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.016 MB perf.data (9 samples) ] $ perf evlist cycles $ perf evlist -v cycles: size: 112, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD, disabled: 1, inherit: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1 $ Cc: Adrian Hunter Cc: Borislav Petkov Cc: Chandler Carruth Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Stephane Eranian Cc: Wang Nan Link: https://www.youtube.com/watch?v=nXaxk27zwlk Link: http://lkml.kernel.org/n/tip-b1ywebmt22pi78vjxau01wth@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 89546228b8ed..e7e195d867ea 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -205,6 +205,20 @@ void perf_evlist__set_leader(struct perf_evlist *evlist) } } +static void perf_event_attr__set_max_precise_ip(struct perf_event_attr *attr) +{ + attr->precise_ip = 3; + + while (attr->precise_ip != 0) { + int fd = sys_perf_event_open(attr, 0, -1, -1, 0); + if (fd != -1) { + close(fd); + break; + } + --attr->precise_ip; + } +} + int perf_evlist__add_default(struct perf_evlist *evlist) { struct perf_event_attr attr = { @@ -215,13 +229,15 @@ int perf_evlist__add_default(struct perf_evlist *evlist) event_attr_init(&attr); + perf_event_attr__set_max_precise_ip(&attr); + evsel = perf_evsel__new(&attr); if (evsel == NULL) goto error; - /* use strdup() because free(evsel) assumes name is allocated */ - evsel->name = strdup("cycles"); - if (!evsel->name) + /* use asprintf() because free(evsel) assumes name is allocated */ + if (asprintf(&evsel->name, "cycles%.*s", + attr.precise_ip ? attr.precise_ip + 1 : 0, ":ppp") < 0) goto error_free; perf_evlist__add(evlist, evsel);