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 /tools | |
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>
Diffstat (limited to 'tools')
-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 { |