diff options
| author | Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | 2010-10-21 06:13:41 -0400 |
|---|---|---|
| committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2010-10-21 14:11:44 -0400 |
| commit | 469b9b88488e89114bb3e9ac5ee7906b7b96123f (patch) | |
| tree | 9f002195a8b1167eddbe560ac834e6ba2dedb19f | |
| parent | fb8c5a56c7ddbc2b0d2ee7a8da60fe1355f75141 (diff) | |
perf probe: Add basic module support
Add basic module probe support on perf probe. This introduces "--module
<MODNAME>" option to perf probe for putting probes and showing lines and
variables in the given module.
Currently, this supports only probing on running modules. Supporting off-line
module probing is the next step.
e.g.)
[show lines]
# ./perf probe --module drm -L drm_vblank_info
<drm_vblank_info:0>
0 int drm_vblank_info(struct seq_file *m, void *data)
1 {
struct drm_info_node *node = (struct drm_info_node *) m->private
3 struct drm_device *dev = node->minor->dev;
...
[show vars]
# ./perf probe --module drm -V drm_vblank_info:3
Available variables at drm_vblank_info:3
@<drm_vblank_info+20>
(unknown_type) data
struct drm_info_node* node
struct seq_file* m
[put a probe]
# ./perf probe --module drm drm_vblank_info:3 node m
Add new event:
probe:drm_vblank_info (on drm_vblank_info:3 with node m)
You can now use it on all perf tools, such as:
perf record -e probe:drm_vblank_info -aR sleep 1
[list probes]
# ./perf probe -l
probe:drm_vblank_info (on drm_vblank_info:3@drivers/gpu/drm/drm_info.c with ...
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <20101021101341.3542.71638.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
| -rw-r--r-- | tools/perf/Documentation/perf-probe.txt | 9 | ||||
| -rw-r--r-- | tools/perf/builtin-probe.c | 11 | ||||
| -rw-r--r-- | tools/perf/util/map.h | 10 | ||||
| -rw-r--r-- | tools/perf/util/probe-event.c | 123 | ||||
| -rw-r--r-- | tools/perf/util/probe-event.h | 10 | ||||
| -rw-r--r-- | tools/perf/util/probe-finder.c | 142 | ||||
| -rw-r--r-- | tools/perf/util/probe-finder.h | 3 |
7 files changed, 239 insertions, 69 deletions
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 148c178a35aa..62de1b7f4e76 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt | |||
| @@ -16,9 +16,9 @@ or | |||
| 16 | or | 16 | or |
| 17 | 'perf probe' --list | 17 | 'perf probe' --list |
| 18 | or | 18 | or |
| 19 | 'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]' | 19 | 'perf probe' [options] --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]' |
| 20 | or | 20 | or |
| 21 | 'perf probe' [--externs] --vars='PROBEPOINT' | 21 | 'perf probe' [options] --vars='PROBEPOINT' |
| 22 | 22 | ||
| 23 | DESCRIPTION | 23 | DESCRIPTION |
| 24 | ----------- | 24 | ----------- |
| @@ -33,6 +33,11 @@ OPTIONS | |||
| 33 | --vmlinux=PATH:: | 33 | --vmlinux=PATH:: |
| 34 | Specify vmlinux path which has debuginfo (Dwarf binary). | 34 | Specify vmlinux path which has debuginfo (Dwarf binary). |
| 35 | 35 | ||
| 36 | -m:: | ||
| 37 | --module=MODNAME:: | ||
| 38 | Specify module name in which perf-probe searches probe points | ||
| 39 | or lines. | ||
| 40 | |||
| 36 | -s:: | 41 | -s:: |
| 37 | --source=PATH:: | 42 | --source=PATH:: |
| 38 | Specify path to kernel source. | 43 | Specify path to kernel source. |
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index bdf60cfdf70f..2e000c068cc5 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
| @@ -57,6 +57,7 @@ static struct { | |||
| 57 | struct perf_probe_event events[MAX_PROBES]; | 57 | struct perf_probe_event events[MAX_PROBES]; |
| 58 | struct strlist *dellist; | 58 | struct strlist *dellist; |
| 59 | struct line_range line_range; | 59 | struct line_range line_range; |
| 60 | const char *target_module; | ||
| 60 | int max_probe_points; | 61 | int max_probe_points; |
| 61 | } params; | 62 | } params; |
| 62 | 63 | ||
| @@ -162,8 +163,8 @@ static const char * const probe_usage[] = { | |||
| 162 | "perf probe [<options>] --del '[GROUP:]EVENT' ...", | 163 | "perf probe [<options>] --del '[GROUP:]EVENT' ...", |
| 163 | "perf probe --list", | 164 | "perf probe --list", |
| 164 | #ifdef DWARF_SUPPORT | 165 | #ifdef DWARF_SUPPORT |
| 165 | "perf probe --line 'LINEDESC'", | 166 | "perf probe [<options>] --line 'LINEDESC'", |
| 166 | "perf probe [--externs] --vars 'PROBEPOINT'", | 167 | "perf probe [<options>] --vars 'PROBEPOINT'", |
| 167 | #endif | 168 | #endif |
| 168 | NULL | 169 | NULL |
| 169 | }; | 170 | }; |
| @@ -214,6 +215,8 @@ static const struct option options[] = { | |||
| 214 | "file", "vmlinux pathname"), | 215 | "file", "vmlinux pathname"), |
| 215 | OPT_STRING('s', "source", &symbol_conf.source_prefix, | 216 | OPT_STRING('s', "source", &symbol_conf.source_prefix, |
| 216 | "directory", "path to kernel source"), | 217 | "directory", "path to kernel source"), |
| 218 | OPT_STRING('m', "module", ¶ms.target_module, | ||
| 219 | "modname", "target module name"), | ||
| 217 | #endif | 220 | #endif |
| 218 | OPT__DRY_RUN(&probe_event_dry_run), | 221 | OPT__DRY_RUN(&probe_event_dry_run), |
| 219 | OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, | 222 | OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, |
| @@ -278,7 +281,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
| 278 | usage_with_options(probe_usage, options); | 281 | usage_with_options(probe_usage, options); |
| 279 | } | 282 | } |
| 280 | 283 | ||
| 281 | ret = show_line_range(¶ms.line_range); | 284 | ret = show_line_range(¶ms.line_range, params.target_module); |
| 282 | if (ret < 0) | 285 | if (ret < 0) |
| 283 | pr_err(" Error: Failed to show lines. (%d)\n", ret); | 286 | pr_err(" Error: Failed to show lines. (%d)\n", ret); |
| 284 | return ret; | 287 | return ret; |
| @@ -291,6 +294,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
| 291 | } | 294 | } |
| 292 | ret = show_available_vars(params.events, params.nevents, | 295 | ret = show_available_vars(params.events, params.nevents, |
| 293 | params.max_probe_points, | 296 | params.max_probe_points, |
| 297 | params.target_module, | ||
| 294 | params.show_ext_vars); | 298 | params.show_ext_vars); |
| 295 | if (ret < 0) | 299 | if (ret < 0) |
| 296 | pr_err(" Error: Failed to show vars. (%d)\n", ret); | 300 | pr_err(" Error: Failed to show vars. (%d)\n", ret); |
| @@ -310,6 +314,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
| 310 | if (params.nevents) { | 314 | if (params.nevents) { |
| 311 | ret = add_perf_probe_events(params.events, params.nevents, | 315 | ret = add_perf_probe_events(params.events, params.nevents, |
| 312 | params.max_probe_points, | 316 | params.max_probe_points, |
| 317 | params.target_module, | ||
| 313 | params.force_add); | 318 | params.force_add); |
| 314 | if (ret < 0) { | 319 | if (ret < 0) { |
| 315 | pr_err(" Error: Failed to add events. (%d)\n", ret); | 320 | pr_err(" Error: Failed to add events. (%d)\n", ret); |
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 78575796d5f3..b397c0383728 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
| @@ -215,6 +215,16 @@ struct symbol *map_groups__find_function_by_name(struct map_groups *self, | |||
| 215 | return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter); | 215 | return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter); |
| 216 | } | 216 | } |
| 217 | 217 | ||
| 218 | static inline | ||
| 219 | struct symbol *machine__find_kernel_function_by_name(struct machine *self, | ||
| 220 | const char *name, | ||
| 221 | struct map **mapp, | ||
| 222 | symbol_filter_t filter) | ||
| 223 | { | ||
| 224 | return map_groups__find_function_by_name(&self->kmaps, name, mapp, | ||
| 225 | filter); | ||
| 226 | } | ||
| 227 | |||
| 218 | int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, | 228 | int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, |
| 219 | int verbose, FILE *fp); | 229 | int verbose, FILE *fp); |
| 220 | 230 | ||
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 82b0976e2053..3b6a5297bf16 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
| @@ -74,10 +74,9 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) | |||
| 74 | static char *synthesize_perf_probe_point(struct perf_probe_point *pp); | 74 | static char *synthesize_perf_probe_point(struct perf_probe_point *pp); |
| 75 | static struct machine machine; | 75 | static struct machine machine; |
| 76 | 76 | ||
| 77 | /* Initialize symbol maps and path of vmlinux */ | 77 | /* Initialize symbol maps and path of vmlinux/modules */ |
| 78 | static int init_vmlinux(void) | 78 | static int init_vmlinux(void) |
| 79 | { | 79 | { |
| 80 | struct dso *kernel; | ||
| 81 | int ret; | 80 | int ret; |
| 82 | 81 | ||
| 83 | symbol_conf.sort_by_name = true; | 82 | symbol_conf.sort_by_name = true; |
| @@ -91,33 +90,61 @@ static int init_vmlinux(void) | |||
| 91 | goto out; | 90 | goto out; |
| 92 | } | 91 | } |
| 93 | 92 | ||
| 94 | ret = machine__init(&machine, "/", 0); | 93 | ret = machine__init(&machine, "", HOST_KERNEL_ID); |
| 95 | if (ret < 0) | 94 | if (ret < 0) |
| 96 | goto out; | 95 | goto out; |
| 97 | 96 | ||
| 98 | kernel = dso__new_kernel(symbol_conf.vmlinux_name); | 97 | if (machine__create_kernel_maps(&machine) < 0) { |
| 99 | if (kernel == NULL) | 98 | pr_debug("machine__create_kernel_maps "); |
| 100 | die("Failed to create kernel dso."); | 99 | goto out; |
| 101 | 100 | } | |
| 102 | ret = __machine__create_kernel_maps(&machine, kernel); | ||
| 103 | if (ret < 0) | ||
| 104 | pr_debug("Failed to create kernel maps.\n"); | ||
| 105 | |||
| 106 | out: | 101 | out: |
| 107 | if (ret < 0) | 102 | if (ret < 0) |
| 108 | pr_warning("Failed to init vmlinux path.\n"); | 103 | pr_warning("Failed to init vmlinux path.\n"); |
| 109 | return ret; | 104 | return ret; |
| 110 | } | 105 | } |
| 111 | 106 | ||
| 107 | static struct symbol *__find_kernel_function_by_name(const char *name, | ||
| 108 | struct map **mapp) | ||
| 109 | { | ||
| 110 | return machine__find_kernel_function_by_name(&machine, name, mapp, | ||
| 111 | NULL); | ||
| 112 | } | ||
| 113 | |||
| 114 | const char *kernel_get_module_path(const char *module) | ||
| 115 | { | ||
| 116 | struct dso *dso; | ||
| 117 | |||
| 118 | if (module) { | ||
| 119 | list_for_each_entry(dso, &machine.kernel_dsos, node) { | ||
| 120 | if (strncmp(dso->short_name + 1, module, | ||
| 121 | dso->short_name_len - 2) == 0) | ||
| 122 | goto found; | ||
| 123 | } | ||
| 124 | pr_debug("Failed to find module %s.\n", module); | ||
| 125 | return NULL; | ||
| 126 | } else { | ||
| 127 | dso = machine.vmlinux_maps[MAP__FUNCTION]->dso; | ||
| 128 | if (dso__load_vmlinux_path(dso, | ||
| 129 | machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) { | ||
| 130 | pr_debug("Failed to load kernel map.\n"); | ||
| 131 | return NULL; | ||
| 132 | } | ||
| 133 | } | ||
| 134 | found: | ||
| 135 | return dso->long_name; | ||
| 136 | } | ||
| 137 | |||
| 112 | #ifdef DWARF_SUPPORT | 138 | #ifdef DWARF_SUPPORT |
| 113 | static int open_vmlinux(void) | 139 | static int open_vmlinux(const char *module) |
| 114 | { | 140 | { |
| 115 | if (map__load(machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) { | 141 | const char *path = kernel_get_module_path(module); |
| 116 | pr_debug("Failed to load kernel map.\n"); | 142 | if (!path) { |
| 117 | return -EINVAL; | 143 | pr_err("Failed to find path of %s module", module ?: "kernel"); |
| 144 | return -ENOENT; | ||
| 118 | } | 145 | } |
| 119 | pr_debug("Try to open %s\n", machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name); | 146 | pr_debug("Try to open %s\n", path); |
| 120 | return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY); | 147 | return open(path, O_RDONLY); |
| 121 | } | 148 | } |
| 122 | 149 | ||
| 123 | /* | 150 | /* |
| @@ -125,20 +152,19 @@ static int open_vmlinux(void) | |||
| 125 | * Currently only handles kprobes. | 152 | * Currently only handles kprobes. |
| 126 | */ | 153 | */ |
| 127 | static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | 154 | static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, |
| 128 | struct perf_probe_point *pp) | 155 | struct perf_probe_point *pp) |
| 129 | { | 156 | { |
| 130 | struct symbol *sym; | 157 | struct symbol *sym; |
| 131 | int fd, ret = -ENOENT; | 158 | struct map *map; |
| 159 | u64 addr; | ||
| 160 | int ret = -ENOENT; | ||
| 132 | 161 | ||
| 133 | sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION], | 162 | sym = __find_kernel_function_by_name(tp->symbol, &map); |
| 134 | tp->symbol, NULL); | ||
| 135 | if (sym) { | 163 | if (sym) { |
| 136 | fd = open_vmlinux(); | 164 | addr = map->unmap_ip(map, sym->start + tp->offset); |
| 137 | if (fd >= 0) { | 165 | pr_debug("try to find %s+%ld@%llx\n", tp->symbol, |
| 138 | ret = find_perf_probe_point(fd, | 166 | tp->offset, addr); |
| 139 | sym->start + tp->offset, pp); | 167 | ret = find_perf_probe_point((unsigned long)addr, pp); |
| 140 | close(fd); | ||
| 141 | } | ||
| 142 | } | 168 | } |
| 143 | if (ret <= 0) { | 169 | if (ret <= 0) { |
| 144 | pr_debug("Failed to find corresponding probes from " | 170 | pr_debug("Failed to find corresponding probes from " |
| @@ -156,12 +182,12 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | |||
| 156 | /* Try to find perf_probe_event with debuginfo */ | 182 | /* Try to find perf_probe_event with debuginfo */ |
| 157 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | 183 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, |
| 158 | struct probe_trace_event **tevs, | 184 | struct probe_trace_event **tevs, |
| 159 | int max_tevs) | 185 | int max_tevs, const char *module) |
| 160 | { | 186 | { |
| 161 | bool need_dwarf = perf_probe_event_need_dwarf(pev); | 187 | bool need_dwarf = perf_probe_event_need_dwarf(pev); |
| 162 | int fd, ntevs; | 188 | int fd, ntevs; |
| 163 | 189 | ||
| 164 | fd = open_vmlinux(); | 190 | fd = open_vmlinux(module); |
| 165 | if (fd < 0) { | 191 | if (fd < 0) { |
| 166 | if (need_dwarf) { | 192 | if (need_dwarf) { |
| 167 | pr_warning("Failed to open debuginfo file.\n"); | 193 | pr_warning("Failed to open debuginfo file.\n"); |
| @@ -300,7 +326,7 @@ error: | |||
| 300 | * Show line-range always requires debuginfo to find source file and | 326 | * Show line-range always requires debuginfo to find source file and |
| 301 | * line number. | 327 | * line number. |
| 302 | */ | 328 | */ |
| 303 | int show_line_range(struct line_range *lr) | 329 | int show_line_range(struct line_range *lr, const char *module) |
| 304 | { | 330 | { |
| 305 | int l = 1; | 331 | int l = 1; |
| 306 | struct line_node *ln; | 332 | struct line_node *ln; |
| @@ -313,7 +339,7 @@ int show_line_range(struct line_range *lr) | |||
| 313 | if (ret < 0) | 339 | if (ret < 0) |
| 314 | return ret; | 340 | return ret; |
| 315 | 341 | ||
| 316 | fd = open_vmlinux(); | 342 | fd = open_vmlinux(module); |
| 317 | if (fd < 0) { | 343 | if (fd < 0) { |
| 318 | pr_warning("Failed to open debuginfo file.\n"); | 344 | pr_warning("Failed to open debuginfo file.\n"); |
| 319 | return fd; | 345 | return fd; |
| @@ -421,7 +447,7 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev, | |||
| 421 | 447 | ||
| 422 | /* Show available variables on given probe point */ | 448 | /* Show available variables on given probe point */ |
| 423 | int show_available_vars(struct perf_probe_event *pevs, int npevs, | 449 | int show_available_vars(struct perf_probe_event *pevs, int npevs, |
| 424 | int max_vls, bool externs) | 450 | int max_vls, const char *module, bool externs) |
| 425 | { | 451 | { |
| 426 | int i, fd, ret = 0; | 452 | int i, fd, ret = 0; |
| 427 | 453 | ||
| @@ -429,7 +455,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, | |||
| 429 | if (ret < 0) | 455 | if (ret < 0) |
| 430 | return ret; | 456 | return ret; |
| 431 | 457 | ||
| 432 | fd = open_vmlinux(); | 458 | fd = open_vmlinux(module); |
| 433 | if (fd < 0) { | 459 | if (fd < 0) { |
| 434 | pr_warning("Failed to open debuginfo file.\n"); | 460 | pr_warning("Failed to open debuginfo file.\n"); |
| 435 | return fd; | 461 | return fd; |
| @@ -447,8 +473,15 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, | |||
| 447 | #else /* !DWARF_SUPPORT */ | 473 | #else /* !DWARF_SUPPORT */ |
| 448 | 474 | ||
| 449 | static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | 475 | static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, |
| 450 | struct perf_probe_point *pp) | 476 | struct perf_probe_point *pp) |
| 451 | { | 477 | { |
| 478 | struct symbol *sym; | ||
| 479 | |||
| 480 | sym = __find_kernel_function_by_name(tp->symbol, NULL); | ||
| 481 | if (!sym) { | ||
| 482 | pr_err("Failed to find symbol %s in kernel.\n", tp->symbol); | ||
| 483 | return -ENOENT; | ||
| 484 | } | ||
| 452 | pp->function = strdup(tp->symbol); | 485 | pp->function = strdup(tp->symbol); |
| 453 | if (pp->function == NULL) | 486 | if (pp->function == NULL) |
| 454 | return -ENOMEM; | 487 | return -ENOMEM; |
| @@ -460,7 +493,7 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | |||
| 460 | 493 | ||
| 461 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | 494 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, |
| 462 | struct probe_trace_event **tevs __unused, | 495 | struct probe_trace_event **tevs __unused, |
| 463 | int max_tevs __unused) | 496 | int max_tevs __unused, const char *mod __unused) |
| 464 | { | 497 | { |
| 465 | if (perf_probe_event_need_dwarf(pev)) { | 498 | if (perf_probe_event_need_dwarf(pev)) { |
| 466 | pr_warning("Debuginfo-analysis is not supported.\n"); | 499 | pr_warning("Debuginfo-analysis is not supported.\n"); |
| @@ -469,14 +502,15 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
| 469 | return 0; | 502 | return 0; |
| 470 | } | 503 | } |
| 471 | 504 | ||
| 472 | int show_line_range(struct line_range *lr __unused) | 505 | int show_line_range(struct line_range *lr __unused, const char *module __unused) |
| 473 | { | 506 | { |
| 474 | pr_warning("Debuginfo-analysis is not supported.\n"); | 507 | pr_warning("Debuginfo-analysis is not supported.\n"); |
| 475 | return -ENOSYS; | 508 | return -ENOSYS; |
| 476 | } | 509 | } |
| 477 | 510 | ||
| 478 | int show_available_vars(struct perf_probe_event *pevs __unused, | 511 | int show_available_vars(struct perf_probe_event *pevs __unused, |
| 479 | int npevs __unused, int max_probe_points __unused) | 512 | int npevs __unused, int max_vls __unused, |
| 513 | const char *module __unused, bool externs __unused) | ||
| 480 | { | 514 | { |
| 481 | pr_warning("Debuginfo-analysis is not supported.\n"); | 515 | pr_warning("Debuginfo-analysis is not supported.\n"); |
| 482 | return -ENOSYS; | 516 | return -ENOSYS; |
| @@ -1159,7 +1193,7 @@ error: | |||
| 1159 | } | 1193 | } |
| 1160 | 1194 | ||
| 1161 | static int convert_to_perf_probe_event(struct probe_trace_event *tev, | 1195 | static int convert_to_perf_probe_event(struct probe_trace_event *tev, |
| 1162 | struct perf_probe_event *pev) | 1196 | struct perf_probe_event *pev) |
| 1163 | { | 1197 | { |
| 1164 | char buf[64] = ""; | 1198 | char buf[64] = ""; |
| 1165 | int i, ret; | 1199 | int i, ret; |
| @@ -1588,14 +1622,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, | |||
| 1588 | 1622 | ||
| 1589 | static int convert_to_probe_trace_events(struct perf_probe_event *pev, | 1623 | static int convert_to_probe_trace_events(struct perf_probe_event *pev, |
| 1590 | struct probe_trace_event **tevs, | 1624 | struct probe_trace_event **tevs, |
| 1591 | int max_tevs) | 1625 | int max_tevs, const char *module) |
| 1592 | { | 1626 | { |
| 1593 | struct symbol *sym; | 1627 | struct symbol *sym; |
| 1594 | int ret = 0, i; | 1628 | int ret = 0, i; |
| 1595 | struct probe_trace_event *tev; | 1629 | struct probe_trace_event *tev; |
| 1596 | 1630 | ||
| 1597 | /* Convert perf_probe_event with debuginfo */ | 1631 | /* Convert perf_probe_event with debuginfo */ |
| 1598 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs); | 1632 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module); |
| 1599 | if (ret != 0) | 1633 | if (ret != 0) |
| 1600 | return ret; | 1634 | return ret; |
| 1601 | 1635 | ||
| @@ -1644,8 +1678,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, | |||
| 1644 | } | 1678 | } |
| 1645 | 1679 | ||
| 1646 | /* Currently just checking function name from symbol map */ | 1680 | /* Currently just checking function name from symbol map */ |
| 1647 | sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION], | 1681 | sym = __find_kernel_function_by_name(tev->point.symbol, NULL); |
| 1648 | tev->point.symbol, NULL); | ||
| 1649 | if (!sym) { | 1682 | if (!sym) { |
| 1650 | pr_warning("Kernel symbol \'%s\' not found.\n", | 1683 | pr_warning("Kernel symbol \'%s\' not found.\n", |
| 1651 | tev->point.symbol); | 1684 | tev->point.symbol); |
| @@ -1668,7 +1701,7 @@ struct __event_package { | |||
| 1668 | }; | 1701 | }; |
| 1669 | 1702 | ||
| 1670 | int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | 1703 | int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, |
| 1671 | int max_tevs, bool force_add) | 1704 | int max_tevs, const char *module, bool force_add) |
| 1672 | { | 1705 | { |
| 1673 | int i, j, ret; | 1706 | int i, j, ret; |
| 1674 | struct __event_package *pkgs; | 1707 | struct __event_package *pkgs; |
| @@ -1689,7 +1722,9 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | |||
| 1689 | pkgs[i].pev = &pevs[i]; | 1722 | pkgs[i].pev = &pevs[i]; |
| 1690 | /* Convert with or without debuginfo */ | 1723 | /* Convert with or without debuginfo */ |
| 1691 | ret = convert_to_probe_trace_events(pkgs[i].pev, | 1724 | ret = convert_to_probe_trace_events(pkgs[i].pev, |
| 1692 | &pkgs[i].tevs, max_tevs); | 1725 | &pkgs[i].tevs, |
| 1726 | max_tevs, | ||
| 1727 | module); | ||
| 1693 | if (ret < 0) | 1728 | if (ret < 0) |
| 1694 | goto end; | 1729 | goto end; |
| 1695 | pkgs[i].ntevs = ret; | 1730 | pkgs[i].ntevs = ret; |
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index c74b1fd2c1fa..5accbedfea37 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
| @@ -115,14 +115,18 @@ extern void clear_perf_probe_event(struct perf_probe_event *pev); | |||
| 115 | /* Command string to line-range */ | 115 | /* Command string to line-range */ |
| 116 | extern int parse_line_range_desc(const char *cmd, struct line_range *lr); | 116 | extern int parse_line_range_desc(const char *cmd, struct line_range *lr); |
| 117 | 117 | ||
| 118 | /* Internal use: Return kernel/module path */ | ||
| 119 | extern const char *kernel_get_module_path(const char *module); | ||
| 118 | 120 | ||
| 119 | extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | 121 | extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, |
| 120 | int max_probe_points, bool force_add); | 122 | int max_probe_points, const char *module, |
| 123 | bool force_add); | ||
| 121 | extern int del_perf_probe_events(struct strlist *dellist); | 124 | extern int del_perf_probe_events(struct strlist *dellist); |
| 122 | extern int show_perf_probe_events(void); | 125 | extern int show_perf_probe_events(void); |
| 123 | extern int show_line_range(struct line_range *lr); | 126 | extern int show_line_range(struct line_range *lr, const char *module); |
| 124 | extern int show_available_vars(struct perf_probe_event *pevs, int npevs, | 127 | extern int show_available_vars(struct perf_probe_event *pevs, int npevs, |
| 125 | int max_probe_points, bool externs); | 128 | int max_probe_points, const char *module, |
| 129 | bool externs); | ||
| 126 | 130 | ||
| 127 | 131 | ||
| 128 | /* Maximum index number of event-name postfix */ | 132 | /* Maximum index number of event-name postfix */ |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index a274fd0c143e..c20bd52833aa 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
| @@ -116,6 +116,101 @@ static void line_list__free(struct list_head *head) | |||
| 116 | } | 116 | } |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | /* Dwarf FL wrappers */ | ||
| 120 | |||
| 121 | static int __linux_kernel_find_elf(Dwfl_Module *mod, | ||
| 122 | void **userdata, | ||
| 123 | const char *module_name, | ||
| 124 | Dwarf_Addr base, | ||
| 125 | char **file_name, Elf **elfp) | ||
| 126 | { | ||
| 127 | int fd; | ||
| 128 | const char *path = kernel_get_module_path(module_name); | ||
| 129 | |||
| 130 | if (path) { | ||
| 131 | fd = open(path, O_RDONLY); | ||
| 132 | if (fd >= 0) { | ||
| 133 | *file_name = strdup(path); | ||
| 134 | return fd; | ||
| 135 | } | ||
| 136 | } | ||
| 137 | /* If failed, try to call standard method */ | ||
| 138 | return dwfl_linux_kernel_find_elf(mod, userdata, module_name, base, | ||
| 139 | file_name, elfp); | ||
| 140 | } | ||
| 141 | |||
| 142 | static char *debuginfo_path; /* Currently dummy */ | ||
| 143 | |||
| 144 | static const Dwfl_Callbacks offline_callbacks = { | ||
| 145 | .find_debuginfo = dwfl_standard_find_debuginfo, | ||
| 146 | .debuginfo_path = &debuginfo_path, | ||
| 147 | |||
| 148 | .section_address = dwfl_offline_section_address, | ||
| 149 | |||
| 150 | /* We use this table for core files too. */ | ||
| 151 | .find_elf = dwfl_build_id_find_elf, | ||
| 152 | }; | ||
| 153 | |||
| 154 | static const Dwfl_Callbacks kernel_callbacks = { | ||
| 155 | .find_debuginfo = dwfl_standard_find_debuginfo, | ||
| 156 | .debuginfo_path = &debuginfo_path, | ||
| 157 | |||
| 158 | .find_elf = __linux_kernel_find_elf, | ||
| 159 | .section_address = dwfl_linux_kernel_module_section_address, | ||
| 160 | }; | ||
| 161 | |||
| 162 | /* Get a Dwarf from offline image */ | ||
| 163 | static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias) | ||
| 164 | { | ||
| 165 | Dwfl_Module *mod; | ||
| 166 | Dwarf *dbg = NULL; | ||
| 167 | |||
| 168 | if (!dwflp) | ||
| 169 | return NULL; | ||
| 170 | |||
| 171 | *dwflp = dwfl_begin(&offline_callbacks); | ||
| 172 | if (!*dwflp) | ||
| 173 | return NULL; | ||
| 174 | |||
| 175 | mod = dwfl_report_offline(*dwflp, "", "", fd); | ||
| 176 | if (!mod) | ||
| 177 | goto error; | ||
| 178 | |||
| 179 | dbg = dwfl_module_getdwarf(mod, bias); | ||
| 180 | if (!dbg) { | ||
| 181 | error: | ||
| 182 | dwfl_end(*dwflp); | ||
| 183 | *dwflp = NULL; | ||
| 184 | } | ||
| 185 | return dbg; | ||
| 186 | } | ||
| 187 | |||
| 188 | /* Get a Dwarf from live kernel image */ | ||
| 189 | static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp, | ||
| 190 | Dwarf_Addr *bias) | ||
| 191 | { | ||
| 192 | Dwarf *dbg; | ||
| 193 | |||
| 194 | if (!dwflp) | ||
| 195 | return NULL; | ||
| 196 | |||
| 197 | *dwflp = dwfl_begin(&kernel_callbacks); | ||
| 198 | if (!*dwflp) | ||
| 199 | return NULL; | ||
| 200 | |||
| 201 | /* Load the kernel dwarves: Don't care the result here */ | ||
| 202 | dwfl_linux_kernel_report_kernel(*dwflp); | ||
| 203 | dwfl_linux_kernel_report_modules(*dwflp); | ||
| 204 | |||
| 205 | dbg = dwfl_addrdwarf(*dwflp, addr, bias); | ||
| 206 | /* Here, check whether we could get a real dwarf */ | ||
| 207 | if (!dbg) { | ||
| 208 | dwfl_end(*dwflp); | ||
| 209 | *dwflp = NULL; | ||
| 210 | } | ||
| 211 | return dbg; | ||
| 212 | } | ||
| 213 | |||
| 119 | /* Dwarf wrappers */ | 214 | /* Dwarf wrappers */ |
| 120 | 215 | ||
| 121 | /* Find the realpath of the target file. */ | 216 | /* Find the realpath of the target file. */ |
| @@ -1177,10 +1272,12 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
| 1177 | Dwarf_Off off, noff; | 1272 | Dwarf_Off off, noff; |
| 1178 | size_t cuhl; | 1273 | size_t cuhl; |
| 1179 | Dwarf_Die *diep; | 1274 | Dwarf_Die *diep; |
| 1180 | Dwarf *dbg; | 1275 | Dwarf *dbg = NULL; |
| 1276 | Dwfl *dwfl; | ||
| 1277 | Dwarf_Addr bias; /* Currently ignored */ | ||
| 1181 | int ret = 0; | 1278 | int ret = 0; |
| 1182 | 1279 | ||
| 1183 | dbg = dwarf_begin(fd, DWARF_C_READ); | 1280 | dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias); |
| 1184 | if (!dbg) { | 1281 | if (!dbg) { |
| 1185 | pr_warning("No dwarf info found in the vmlinux - " | 1282 | pr_warning("No dwarf info found in the vmlinux - " |
| 1186 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | 1283 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); |
| @@ -1221,7 +1318,8 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
| 1221 | off = noff; | 1318 | off = noff; |
| 1222 | } | 1319 | } |
| 1223 | line_list__free(&pf->lcache); | 1320 | line_list__free(&pf->lcache); |
| 1224 | dwarf_end(dbg); | 1321 | if (dwfl) |
| 1322 | dwfl_end(dwfl); | ||
| 1225 | 1323 | ||
| 1226 | return ret; | 1324 | return ret; |
| 1227 | } | 1325 | } |
| @@ -1412,23 +1510,31 @@ int find_available_vars_at(int fd, struct perf_probe_event *pev, | |||
| 1412 | } | 1510 | } |
| 1413 | 1511 | ||
| 1414 | /* Reverse search */ | 1512 | /* Reverse search */ |
| 1415 | int find_perf_probe_point(int fd, unsigned long addr, | 1513 | int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt) |
| 1416 | struct perf_probe_point *ppt) | ||
| 1417 | { | 1514 | { |
| 1418 | Dwarf_Die cudie, spdie, indie; | 1515 | Dwarf_Die cudie, spdie, indie; |
| 1419 | Dwarf *dbg; | 1516 | Dwarf *dbg = NULL; |
| 1517 | Dwfl *dwfl = NULL; | ||
| 1420 | Dwarf_Line *line; | 1518 | Dwarf_Line *line; |
| 1421 | Dwarf_Addr laddr, eaddr; | 1519 | Dwarf_Addr laddr, eaddr, bias = 0; |
| 1422 | const char *tmp; | 1520 | const char *tmp; |
| 1423 | int lineno, ret = 0; | 1521 | int lineno, ret = 0; |
| 1424 | bool found = false; | 1522 | bool found = false; |
| 1425 | 1523 | ||
| 1426 | dbg = dwarf_begin(fd, DWARF_C_READ); | 1524 | /* Open the live linux kernel */ |
| 1427 | if (!dbg) | 1525 | dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias); |
| 1428 | return -EBADF; | 1526 | if (!dbg) { |
| 1527 | pr_warning("No dwarf info found in the vmlinux - " | ||
| 1528 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | ||
| 1529 | ret = -EINVAL; | ||
| 1530 | goto end; | ||
| 1531 | } | ||
| 1429 | 1532 | ||
| 1533 | /* Adjust address with bias */ | ||
| 1534 | addr += bias; | ||
| 1430 | /* Find cu die */ | 1535 | /* Find cu die */ |
| 1431 | if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) { | 1536 | if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) { |
| 1537 | pr_warning("No CU DIE is found at %lx\n", addr); | ||
| 1432 | ret = -EINVAL; | 1538 | ret = -EINVAL; |
| 1433 | goto end; | 1539 | goto end; |
| 1434 | } | 1540 | } |
| @@ -1491,7 +1597,8 @@ found: | |||
| 1491 | } | 1597 | } |
| 1492 | 1598 | ||
| 1493 | end: | 1599 | end: |
| 1494 | dwarf_end(dbg); | 1600 | if (dwfl) |
| 1601 | dwfl_end(dwfl); | ||
| 1495 | if (ret >= 0) | 1602 | if (ret >= 0) |
| 1496 | ret = found ? 1 : 0; | 1603 | ret = found ? 1 : 0; |
| 1497 | return ret; | 1604 | return ret; |
| @@ -1624,6 +1731,8 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | |||
| 1624 | struct line_finder *lf = param->data; | 1731 | struct line_finder *lf = param->data; |
| 1625 | struct line_range *lr = lf->lr; | 1732 | struct line_range *lr = lf->lr; |
| 1626 | 1733 | ||
| 1734 | pr_debug("find (%lx) %s\n", dwarf_dieoffset(sp_die), | ||
| 1735 | dwarf_diename(sp_die)); | ||
| 1627 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && | 1736 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && |
| 1628 | die_compare_name(sp_die, lr->function)) { | 1737 | die_compare_name(sp_die, lr->function)) { |
| 1629 | lf->fname = dwarf_decl_file(sp_die); | 1738 | lf->fname = dwarf_decl_file(sp_die); |
| @@ -1667,10 +1776,12 @@ int find_line_range(int fd, struct line_range *lr) | |||
| 1667 | Dwarf_Off off = 0, noff; | 1776 | Dwarf_Off off = 0, noff; |
| 1668 | size_t cuhl; | 1777 | size_t cuhl; |
| 1669 | Dwarf_Die *diep; | 1778 | Dwarf_Die *diep; |
| 1670 | Dwarf *dbg; | 1779 | Dwarf *dbg = NULL; |
| 1780 | Dwfl *dwfl; | ||
| 1781 | Dwarf_Addr bias; /* Currently ignored */ | ||
| 1671 | const char *comp_dir; | 1782 | const char *comp_dir; |
| 1672 | 1783 | ||
| 1673 | dbg = dwarf_begin(fd, DWARF_C_READ); | 1784 | dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias); |
| 1674 | if (!dbg) { | 1785 | if (!dbg) { |
| 1675 | pr_warning("No dwarf info found in the vmlinux - " | 1786 | pr_warning("No dwarf info found in the vmlinux - " |
| 1676 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | 1787 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); |
| @@ -1716,8 +1827,7 @@ int find_line_range(int fd, struct line_range *lr) | |||
| 1716 | } | 1827 | } |
| 1717 | 1828 | ||
| 1718 | pr_debug("path: %s\n", lr->path); | 1829 | pr_debug("path: %s\n", lr->path); |
| 1719 | dwarf_end(dbg); | 1830 | dwfl_end(dwfl); |
| 1720 | |||
| 1721 | return (ret < 0) ? ret : lf.found; | 1831 | return (ret < 0) ? ret : lf.found; |
| 1722 | } | 1832 | } |
| 1723 | 1833 | ||
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 516912a04011..bba69d455699 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
| @@ -22,7 +22,7 @@ extern int find_probe_trace_events(int fd, struct perf_probe_event *pev, | |||
| 22 | int max_tevs); | 22 | int max_tevs); |
| 23 | 23 | ||
| 24 | /* Find a perf_probe_point from debuginfo */ | 24 | /* Find a perf_probe_point from debuginfo */ |
| 25 | extern int find_perf_probe_point(int fd, unsigned long addr, | 25 | extern int find_perf_probe_point(unsigned long addr, |
| 26 | struct perf_probe_point *ppt); | 26 | struct perf_probe_point *ppt); |
| 27 | 27 | ||
| 28 | /* Find a line range */ | 28 | /* Find a line range */ |
| @@ -35,6 +35,7 @@ extern int find_available_vars_at(int fd, struct perf_probe_event *pev, | |||
| 35 | 35 | ||
| 36 | #include <dwarf.h> | 36 | #include <dwarf.h> |
| 37 | #include <libdw.h> | 37 | #include <libdw.h> |
| 38 | #include <libdwfl.h> | ||
| 38 | #include <version.h> | 39 | #include <version.h> |
| 39 | 40 | ||
| 40 | struct probe_finder { | 41 | struct probe_finder { |
