Commit 469b9b88488e89114bb3e9ac5ee7906b7b96123f
Committed by
Arnaldo Carvalho de Melo
1 parent
fb8c5a56c7
Exists in
master
and in
7 other branches
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>
Showing 7 changed files with 239 additions and 69 deletions Side-by-side Diff
tools/perf/Documentation/perf-probe.txt
... | ... | @@ -16,9 +16,9 @@ |
16 | 16 | or |
17 | 17 | 'perf probe' --list |
18 | 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 | 20 | or |
21 | -'perf probe' [--externs] --vars='PROBEPOINT' | |
21 | +'perf probe' [options] --vars='PROBEPOINT' | |
22 | 22 | |
23 | 23 | DESCRIPTION |
24 | 24 | ----------- |
... | ... | @@ -32,6 +32,11 @@ |
32 | 32 | -k:: |
33 | 33 | --vmlinux=PATH:: |
34 | 34 | Specify vmlinux path which has debuginfo (Dwarf binary). |
35 | + | |
36 | +-m:: | |
37 | +--module=MODNAME:: | |
38 | + Specify module name in which perf-probe searches probe points | |
39 | + or lines. | |
35 | 40 | |
36 | 41 | -s:: |
37 | 42 | --source=PATH:: |
tools/perf/builtin-probe.c
... | ... | @@ -57,6 +57,7 @@ |
57 | 57 | struct perf_probe_event events[MAX_PROBES]; |
58 | 58 | struct strlist *dellist; |
59 | 59 | struct line_range line_range; |
60 | + const char *target_module; | |
60 | 61 | int max_probe_points; |
61 | 62 | } params; |
62 | 63 | |
... | ... | @@ -162,8 +163,8 @@ |
162 | 163 | "perf probe [<options>] --del '[GROUP:]EVENT' ...", |
163 | 164 | "perf probe --list", |
164 | 165 | #ifdef DWARF_SUPPORT |
165 | - "perf probe --line 'LINEDESC'", | |
166 | - "perf probe [--externs] --vars 'PROBEPOINT'", | |
166 | + "perf probe [<options>] --line 'LINEDESC'", | |
167 | + "perf probe [<options>] --vars 'PROBEPOINT'", | |
167 | 168 | #endif |
168 | 169 | NULL |
169 | 170 | }; |
... | ... | @@ -214,6 +215,8 @@ |
214 | 215 | "file", "vmlinux pathname"), |
215 | 216 | OPT_STRING('s', "source", &symbol_conf.source_prefix, |
216 | 217 | "directory", "path to kernel source"), |
218 | + OPT_STRING('m', "module", ¶ms.target_module, | |
219 | + "modname", "target module name"), | |
217 | 220 | #endif |
218 | 221 | OPT__DRY_RUN(&probe_event_dry_run), |
219 | 222 | OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, |
... | ... | @@ -278,7 +281,7 @@ |
278 | 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 | 285 | if (ret < 0) |
283 | 286 | pr_err(" Error: Failed to show lines. (%d)\n", ret); |
284 | 287 | return ret; |
... | ... | @@ -291,6 +294,7 @@ |
291 | 294 | } |
292 | 295 | ret = show_available_vars(params.events, params.nevents, |
293 | 296 | params.max_probe_points, |
297 | + params.target_module, | |
294 | 298 | params.show_ext_vars); |
295 | 299 | if (ret < 0) |
296 | 300 | pr_err(" Error: Failed to show vars. (%d)\n", ret); |
... | ... | @@ -310,6 +314,7 @@ |
310 | 314 | if (params.nevents) { |
311 | 315 | ret = add_perf_probe_events(params.events, params.nevents, |
312 | 316 | params.max_probe_points, |
317 | + params.target_module, | |
313 | 318 | params.force_add); |
314 | 319 | if (ret < 0) { |
315 | 320 | pr_err(" Error: Failed to add events. (%d)\n", ret); |
tools/perf/util/map.h
... | ... | @@ -215,6 +215,16 @@ |
215 | 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 | 228 | int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, |
219 | 229 | int verbose, FILE *fp); |
220 | 230 |
tools/perf/util/probe-event.c
... | ... | @@ -74,10 +74,9 @@ |
74 | 74 | static char *synthesize_perf_probe_point(struct perf_probe_point *pp); |
75 | 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 | 78 | static int init_vmlinux(void) |
79 | 79 | { |
80 | - struct dso *kernel; | |
81 | 80 | int ret; |
82 | 81 | |
83 | 82 | symbol_conf.sort_by_name = true; |
84 | 83 | |
85 | 84 | |
86 | 85 | |
87 | 86 | |
88 | 87 | |
... | ... | @@ -91,33 +90,61 @@ |
91 | 90 | goto out; |
92 | 91 | } |
93 | 92 | |
94 | - ret = machine__init(&machine, "/", 0); | |
93 | + ret = machine__init(&machine, "", HOST_KERNEL_ID); | |
95 | 94 | if (ret < 0) |
96 | 95 | goto out; |
97 | 96 | |
98 | - kernel = dso__new_kernel(symbol_conf.vmlinux_name); | |
99 | - if (kernel == NULL) | |
100 | - die("Failed to create kernel dso."); | |
101 | - | |
102 | - ret = __machine__create_kernel_maps(&machine, kernel); | |
103 | - if (ret < 0) | |
104 | - pr_debug("Failed to create kernel maps.\n"); | |
105 | - | |
97 | + if (machine__create_kernel_maps(&machine) < 0) { | |
98 | + pr_debug("machine__create_kernel_maps "); | |
99 | + goto out; | |
100 | + } | |
106 | 101 | out: |
107 | 102 | if (ret < 0) |
108 | 103 | pr_warning("Failed to init vmlinux path.\n"); |
109 | 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 | 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) { | |
116 | - pr_debug("Failed to load kernel map.\n"); | |
117 | - return -EINVAL; | |
141 | + const char *path = kernel_get_module_path(module); | |
142 | + if (!path) { | |
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); | |
120 | - return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY); | |
146 | + pr_debug("Try to open %s\n", path); | |
147 | + return open(path, O_RDONLY); | |
121 | 148 | } |
122 | 149 | |
123 | 150 | /* |
124 | 151 | |
125 | 152 | |
126 | 153 | |
... | ... | @@ -125,20 +152,19 @@ |
125 | 152 | * Currently only handles kprobes. |
126 | 153 | */ |
127 | 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 | 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], | |
134 | - tp->symbol, NULL); | |
162 | + sym = __find_kernel_function_by_name(tp->symbol, &map); | |
135 | 163 | if (sym) { |
136 | - fd = open_vmlinux(); | |
137 | - if (fd >= 0) { | |
138 | - ret = find_perf_probe_point(fd, | |
139 | - sym->start + tp->offset, pp); | |
140 | - close(fd); | |
141 | - } | |
164 | + addr = map->unmap_ip(map, sym->start + tp->offset); | |
165 | + pr_debug("try to find %s+%ld@%llx\n", tp->symbol, | |
166 | + tp->offset, addr); | |
167 | + ret = find_perf_probe_point((unsigned long)addr, pp); | |
142 | 168 | } |
143 | 169 | if (ret <= 0) { |
144 | 170 | pr_debug("Failed to find corresponding probes from " |
145 | 171 | |
... | ... | @@ -156,12 +182,12 @@ |
156 | 182 | /* Try to find perf_probe_event with debuginfo */ |
157 | 183 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, |
158 | 184 | struct probe_trace_event **tevs, |
159 | - int max_tevs) | |
185 | + int max_tevs, const char *module) | |
160 | 186 | { |
161 | 187 | bool need_dwarf = perf_probe_event_need_dwarf(pev); |
162 | 188 | int fd, ntevs; |
163 | 189 | |
164 | - fd = open_vmlinux(); | |
190 | + fd = open_vmlinux(module); | |
165 | 191 | if (fd < 0) { |
166 | 192 | if (need_dwarf) { |
167 | 193 | pr_warning("Failed to open debuginfo file.\n"); |
... | ... | @@ -300,7 +326,7 @@ |
300 | 326 | * Show line-range always requires debuginfo to find source file and |
301 | 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 | 331 | int l = 1; |
306 | 332 | struct line_node *ln; |
... | ... | @@ -313,7 +339,7 @@ |
313 | 339 | if (ret < 0) |
314 | 340 | return ret; |
315 | 341 | |
316 | - fd = open_vmlinux(); | |
342 | + fd = open_vmlinux(module); | |
317 | 343 | if (fd < 0) { |
318 | 344 | pr_warning("Failed to open debuginfo file.\n"); |
319 | 345 | return fd; |
... | ... | @@ -421,7 +447,7 @@ |
421 | 447 | |
422 | 448 | /* Show available variables on given probe point */ |
423 | 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 | 452 | int i, fd, ret = 0; |
427 | 453 | |
... | ... | @@ -429,7 +455,7 @@ |
429 | 455 | if (ret < 0) |
430 | 456 | return ret; |
431 | 457 | |
432 | - fd = open_vmlinux(); | |
458 | + fd = open_vmlinux(module); | |
433 | 459 | if (fd < 0) { |
434 | 460 | pr_warning("Failed to open debuginfo file.\n"); |
435 | 461 | return fd; |
436 | 462 | |
... | ... | @@ -447,8 +473,15 @@ |
447 | 473 | #else /* !DWARF_SUPPORT */ |
448 | 474 | |
449 | 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 | 485 | pp->function = strdup(tp->symbol); |
453 | 486 | if (pp->function == NULL) |
454 | 487 | return -ENOMEM; |
... | ... | @@ -460,7 +493,7 @@ |
460 | 493 | |
461 | 494 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, |
462 | 495 | struct probe_trace_event **tevs __unused, |
463 | - int max_tevs __unused) | |
496 | + int max_tevs __unused, const char *mod __unused) | |
464 | 497 | { |
465 | 498 | if (perf_probe_event_need_dwarf(pev)) { |
466 | 499 | pr_warning("Debuginfo-analysis is not supported.\n"); |
467 | 500 | |
... | ... | @@ -469,14 +502,15 @@ |
469 | 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 | 507 | pr_warning("Debuginfo-analysis is not supported.\n"); |
475 | 508 | return -ENOSYS; |
476 | 509 | } |
477 | 510 | |
478 | 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 | 515 | pr_warning("Debuginfo-analysis is not supported.\n"); |
482 | 516 | return -ENOSYS; |
... | ... | @@ -1159,7 +1193,7 @@ |
1159 | 1193 | } |
1160 | 1194 | |
1161 | 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 | 1198 | char buf[64] = ""; |
1165 | 1199 | int i, ret; |
1166 | 1200 | |
... | ... | @@ -1588,14 +1622,14 @@ |
1588 | 1622 | |
1589 | 1623 | static int convert_to_probe_trace_events(struct perf_probe_event *pev, |
1590 | 1624 | struct probe_trace_event **tevs, |
1591 | - int max_tevs) | |
1625 | + int max_tevs, const char *module) | |
1592 | 1626 | { |
1593 | 1627 | struct symbol *sym; |
1594 | 1628 | int ret = 0, i; |
1595 | 1629 | struct probe_trace_event *tev; |
1596 | 1630 | |
1597 | 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 | 1633 | if (ret != 0) |
1600 | 1634 | return ret; |
1601 | 1635 | |
... | ... | @@ -1644,8 +1678,7 @@ |
1644 | 1678 | } |
1645 | 1679 | |
1646 | 1680 | /* Currently just checking function name from symbol map */ |
1647 | - sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION], | |
1648 | - tev->point.symbol, NULL); | |
1681 | + sym = __find_kernel_function_by_name(tev->point.symbol, NULL); | |
1649 | 1682 | if (!sym) { |
1650 | 1683 | pr_warning("Kernel symbol \'%s\' not found.\n", |
1651 | 1684 | tev->point.symbol); |
... | ... | @@ -1668,7 +1701,7 @@ |
1668 | 1701 | }; |
1669 | 1702 | |
1670 | 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 | 1706 | int i, j, ret; |
1674 | 1707 | struct __event_package *pkgs; |
... | ... | @@ -1689,7 +1722,9 @@ |
1689 | 1722 | pkgs[i].pev = &pevs[i]; |
1690 | 1723 | /* Convert with or without debuginfo */ |
1691 | 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 | 1728 | if (ret < 0) |
1694 | 1729 | goto end; |
1695 | 1730 | pkgs[i].ntevs = ret; |
tools/perf/util/probe-event.h
... | ... | @@ -115,14 +115,18 @@ |
115 | 115 | /* Command string to line-range */ |
116 | 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 | 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 | 124 | extern int del_perf_probe_events(struct strlist *dellist); |
122 | 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 | 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 | 132 | /* Maximum index number of event-name postfix */ |
tools/perf/util/probe-finder.c
... | ... | @@ -116,6 +116,101 @@ |
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 | 214 | /* Dwarf wrappers */ |
120 | 215 | |
121 | 216 | /* Find the realpath of the target file. */ |
122 | 217 | |
... | ... | @@ -1177,10 +1272,12 @@ |
1177 | 1272 | Dwarf_Off off, noff; |
1178 | 1273 | size_t cuhl; |
1179 | 1274 | Dwarf_Die *diep; |
1180 | - Dwarf *dbg; | |
1275 | + Dwarf *dbg = NULL; | |
1276 | + Dwfl *dwfl; | |
1277 | + Dwarf_Addr bias; /* Currently ignored */ | |
1181 | 1278 | int ret = 0; |
1182 | 1279 | |
1183 | - dbg = dwarf_begin(fd, DWARF_C_READ); | |
1280 | + dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias); | |
1184 | 1281 | if (!dbg) { |
1185 | 1282 | pr_warning("No dwarf info found in the vmlinux - " |
1186 | 1283 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); |
... | ... | @@ -1221,7 +1318,8 @@ |
1221 | 1318 | off = noff; |
1222 | 1319 | } |
1223 | 1320 | line_list__free(&pf->lcache); |
1224 | - dwarf_end(dbg); | |
1321 | + if (dwfl) | |
1322 | + dwfl_end(dwfl); | |
1225 | 1323 | |
1226 | 1324 | return ret; |
1227 | 1325 | } |
1228 | 1326 | |
1229 | 1327 | |
1230 | 1328 | |
1231 | 1329 | |
1232 | 1330 | |
... | ... | @@ -1412,23 +1510,31 @@ |
1412 | 1510 | } |
1413 | 1511 | |
1414 | 1512 | /* Reverse search */ |
1415 | -int find_perf_probe_point(int fd, unsigned long addr, | |
1416 | - struct perf_probe_point *ppt) | |
1513 | +int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt) | |
1417 | 1514 | { |
1418 | 1515 | Dwarf_Die cudie, spdie, indie; |
1419 | - Dwarf *dbg; | |
1516 | + Dwarf *dbg = NULL; | |
1517 | + Dwfl *dwfl = NULL; | |
1420 | 1518 | Dwarf_Line *line; |
1421 | - Dwarf_Addr laddr, eaddr; | |
1519 | + Dwarf_Addr laddr, eaddr, bias = 0; | |
1422 | 1520 | const char *tmp; |
1423 | 1521 | int lineno, ret = 0; |
1424 | 1522 | bool found = false; |
1425 | 1523 | |
1426 | - dbg = dwarf_begin(fd, DWARF_C_READ); | |
1427 | - if (!dbg) | |
1428 | - return -EBADF; | |
1524 | + /* Open the live linux kernel */ | |
1525 | + dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias); | |
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 | 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 | 1538 | ret = -EINVAL; |
1433 | 1539 | goto end; |
1434 | 1540 | } |
... | ... | @@ -1491,7 +1597,8 @@ |
1491 | 1597 | } |
1492 | 1598 | |
1493 | 1599 | end: |
1494 | - dwarf_end(dbg); | |
1600 | + if (dwfl) | |
1601 | + dwfl_end(dwfl); | |
1495 | 1602 | if (ret >= 0) |
1496 | 1603 | ret = found ? 1 : 0; |
1497 | 1604 | return ret; |
... | ... | @@ -1624,6 +1731,8 @@ |
1624 | 1731 | struct line_finder *lf = param->data; |
1625 | 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 | 1736 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && |
1628 | 1737 | die_compare_name(sp_die, lr->function)) { |
1629 | 1738 | lf->fname = dwarf_decl_file(sp_die); |
1630 | 1739 | |
... | ... | @@ -1667,10 +1776,12 @@ |
1667 | 1776 | Dwarf_Off off = 0, noff; |
1668 | 1777 | size_t cuhl; |
1669 | 1778 | Dwarf_Die *diep; |
1670 | - Dwarf *dbg; | |
1779 | + Dwarf *dbg = NULL; | |
1780 | + Dwfl *dwfl; | |
1781 | + Dwarf_Addr bias; /* Currently ignored */ | |
1671 | 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 | 1785 | if (!dbg) { |
1675 | 1786 | pr_warning("No dwarf info found in the vmlinux - " |
1676 | 1787 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); |
... | ... | @@ -1716,8 +1827,7 @@ |
1716 | 1827 | } |
1717 | 1828 | |
1718 | 1829 | pr_debug("path: %s\n", lr->path); |
1719 | - dwarf_end(dbg); | |
1720 | - | |
1830 | + dwfl_end(dwfl); | |
1721 | 1831 | return (ret < 0) ? ret : lf.found; |
1722 | 1832 | } |
tools/perf/util/probe-finder.h
... | ... | @@ -22,7 +22,7 @@ |
22 | 22 | int max_tevs); |
23 | 23 | |
24 | 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 | 26 | struct perf_probe_point *ppt); |
27 | 27 | |
28 | 28 | /* Find a line range */ |
... | ... | @@ -35,6 +35,7 @@ |
35 | 35 | |
36 | 36 | #include <dwarf.h> |
37 | 37 | #include <libdw.h> |
38 | +#include <libdwfl.h> | |
38 | 39 | #include <version.h> |
39 | 40 | |
40 | 41 | struct probe_finder { |