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/perf/util/probe-event.c | |
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/perf/util/probe-event.c')
-rw-r--r-- | tools/perf/util/probe-event.c | 123 |
1 files changed, 79 insertions, 44 deletions
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; |