diff options
author | Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | 2014-02-06 00:32:23 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2014-02-18 07:34:51 -0500 |
commit | 5a6f63145491f905de1c5c6c46c5cd62c004d0d1 (patch) | |
tree | 4f65f5278921ac1be6e95cd03d15f29a921c4f86 | |
parent | 8f33f7deac485a61f38aa690b85489322a4d958e (diff) |
perf probe: Show source-level or symbol-level info for uprobes
Show source-level or symbol-level information for uprobe events.
Without this change;
# ./perf probe -l
probe_perf:dso__load_vmlinux (on 0x000000000006d110 in /kbuild/ksrc/linux-3/tools/perf/perf)
With this change;
# ./perf probe -l
probe_perf:dso__load_vmlinux (on dso__load_vmlinux@util/symbol.c in /kbuild/ksrc/linux-3/tools/perf/perf)
Changes from v2:
- Update according to previous patches.
Changes from v1:
- Rewrite the code based on new series.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: "David A. Long" <dave.long@linaro.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: yrl.pp-manager.tt@hitachi.com
Link: http://lkml.kernel.org/r/20140206053223.29635.51280.stgit@kbuild-fedora.yrl.intra.hitachi.co.jp
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/util/probe-event.c | 227 |
1 files changed, 144 insertions, 83 deletions
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index f86820c39ea4..3c35b7af2adb 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -249,34 +249,6 @@ out: | |||
249 | return ret; | 249 | return ret; |
250 | } | 250 | } |
251 | 251 | ||
252 | static int convert_to_perf_probe_point(struct probe_trace_point *tp, | ||
253 | struct perf_probe_point *pp) | ||
254 | { | ||
255 | struct symbol *sym; | ||
256 | struct map *map; | ||
257 | u64 addr = kernel_get_symbol_address_by_name(tp->symbol, true); | ||
258 | |||
259 | if (addr) { | ||
260 | addr += tp->offset; | ||
261 | sym = __find_kernel_function(addr, &map); | ||
262 | if (!sym) | ||
263 | goto failed; | ||
264 | pp->function = strdup(sym->name); | ||
265 | pp->offset = addr - map->unmap_ip(map, sym->start); | ||
266 | } else { | ||
267 | failed: | ||
268 | pp->function = strdup(tp->symbol); | ||
269 | pp->offset = tp->offset; | ||
270 | } | ||
271 | |||
272 | if (pp->function == NULL) | ||
273 | return -ENOMEM; | ||
274 | |||
275 | pp->retprobe = tp->retprobe; | ||
276 | |||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | #ifdef HAVE_DWARF_SUPPORT | 252 | #ifdef HAVE_DWARF_SUPPORT |
281 | /* Open new debuginfo of given module */ | 253 | /* Open new debuginfo of given module */ |
282 | static struct debuginfo *open_debuginfo(const char *module) | 254 | static struct debuginfo *open_debuginfo(const char *module) |
@@ -298,44 +270,6 @@ static struct debuginfo *open_debuginfo(const char *module) | |||
298 | return debuginfo__new(path); | 270 | return debuginfo__new(path); |
299 | } | 271 | } |
300 | 272 | ||
301 | /* | ||
302 | * Convert trace point to probe point with debuginfo | ||
303 | * Currently only handles kprobes. | ||
304 | */ | ||
305 | static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | ||
306 | struct perf_probe_point *pp) | ||
307 | { | ||
308 | u64 addr = 0; | ||
309 | int ret = -ENOENT; | ||
310 | struct debuginfo *dinfo; | ||
311 | |||
312 | addr = kernel_get_symbol_address_by_name(tp->symbol, false); | ||
313 | if (addr) { | ||
314 | addr += tp->offset; | ||
315 | pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol, | ||
316 | tp->offset, addr); | ||
317 | |||
318 | dinfo = open_debuginfo(tp->module); | ||
319 | if (dinfo) { | ||
320 | ret = debuginfo__find_probe_point(dinfo, | ||
321 | (unsigned long)addr, pp); | ||
322 | debuginfo__delete(dinfo); | ||
323 | } else { | ||
324 | pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n", | ||
325 | addr); | ||
326 | ret = -ENOENT; | ||
327 | } | ||
328 | } | ||
329 | if (ret <= 0) { | ||
330 | pr_debug("Failed to find corresponding probes from " | ||
331 | "debuginfo. Use kprobe event information.\n"); | ||
332 | return convert_to_perf_probe_point(tp, pp); | ||
333 | } | ||
334 | pp->retprobe = tp->retprobe; | ||
335 | |||
336 | return 0; | ||
337 | } | ||
338 | |||
339 | static int get_text_start_address(const char *exec, unsigned long *address) | 273 | static int get_text_start_address(const char *exec, unsigned long *address) |
340 | { | 274 | { |
341 | Elf *elf; | 275 | Elf *elf; |
@@ -364,6 +298,57 @@ out: | |||
364 | return ret; | 298 | return ret; |
365 | } | 299 | } |
366 | 300 | ||
301 | /* | ||
302 | * Convert trace point to probe point with debuginfo | ||
303 | */ | ||
304 | static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp, | ||
305 | struct perf_probe_point *pp, | ||
306 | bool is_kprobe) | ||
307 | { | ||
308 | struct debuginfo *dinfo = NULL; | ||
309 | unsigned long stext = 0; | ||
310 | u64 addr = tp->address; | ||
311 | int ret = -ENOENT; | ||
312 | |||
313 | /* convert the address to dwarf address */ | ||
314 | if (!is_kprobe) { | ||
315 | if (!addr) { | ||
316 | ret = -EINVAL; | ||
317 | goto error; | ||
318 | } | ||
319 | ret = get_text_start_address(tp->module, &stext); | ||
320 | if (ret < 0) | ||
321 | goto error; | ||
322 | addr += stext; | ||
323 | } else { | ||
324 | addr = kernel_get_symbol_address_by_name(tp->symbol, false); | ||
325 | if (addr == 0) | ||
326 | goto error; | ||
327 | addr += tp->offset; | ||
328 | } | ||
329 | |||
330 | pr_debug("try to find information at %" PRIx64 " in %s\n", addr, | ||
331 | tp->module ? : "kernel"); | ||
332 | |||
333 | dinfo = open_debuginfo(tp->module); | ||
334 | if (dinfo) { | ||
335 | ret = debuginfo__find_probe_point(dinfo, | ||
336 | (unsigned long)addr, pp); | ||
337 | debuginfo__delete(dinfo); | ||
338 | } else { | ||
339 | pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n", addr); | ||
340 | ret = -ENOENT; | ||
341 | } | ||
342 | |||
343 | if (ret > 0) { | ||
344 | pp->retprobe = tp->retprobe; | ||
345 | return 0; | ||
346 | } | ||
347 | error: | ||
348 | pr_debug("Failed to find corresponding probes from debuginfo.\n"); | ||
349 | return ret ? : -ENOENT; | ||
350 | } | ||
351 | |||
367 | static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs, | 352 | static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs, |
368 | int ntevs, const char *exec) | 353 | int ntevs, const char *exec) |
369 | { | 354 | { |
@@ -815,10 +800,12 @@ out: | |||
815 | 800 | ||
816 | #else /* !HAVE_DWARF_SUPPORT */ | 801 | #else /* !HAVE_DWARF_SUPPORT */ |
817 | 802 | ||
818 | static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | 803 | static int |
819 | struct perf_probe_point *pp) | 804 | find_perf_probe_point_from_dwarf(struct probe_trace_point *tp __maybe_unused, |
805 | struct perf_probe_point *pp __maybe_unused, | ||
806 | bool is_kprobe __maybe_unused) | ||
820 | { | 807 | { |
821 | return convert_to_perf_probe_point(tp, pp); | 808 | return -ENOSYS; |
822 | } | 809 | } |
823 | 810 | ||
824 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | 811 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, |
@@ -1343,16 +1330,21 @@ static int parse_probe_trace_command(const char *cmd, | |||
1343 | } else | 1330 | } else |
1344 | p = argv[1]; | 1331 | p = argv[1]; |
1345 | fmt1_str = strtok_r(p, "+", &fmt); | 1332 | fmt1_str = strtok_r(p, "+", &fmt); |
1346 | tp->symbol = strdup(fmt1_str); | 1333 | if (fmt1_str[0] == '0') /* only the address started with 0x */ |
1347 | if (tp->symbol == NULL) { | 1334 | tp->address = strtoul(fmt1_str, NULL, 0); |
1348 | ret = -ENOMEM; | 1335 | else { |
1349 | goto out; | 1336 | /* Only the symbol-based probe has offset */ |
1337 | tp->symbol = strdup(fmt1_str); | ||
1338 | if (tp->symbol == NULL) { | ||
1339 | ret = -ENOMEM; | ||
1340 | goto out; | ||
1341 | } | ||
1342 | fmt2_str = strtok_r(NULL, "", &fmt); | ||
1343 | if (fmt2_str == NULL) | ||
1344 | tp->offset = 0; | ||
1345 | else | ||
1346 | tp->offset = strtoul(fmt2_str, NULL, 10); | ||
1350 | } | 1347 | } |
1351 | fmt2_str = strtok_r(NULL, "", &fmt); | ||
1352 | if (fmt2_str == NULL) | ||
1353 | tp->offset = 0; | ||
1354 | else | ||
1355 | tp->offset = strtoul(fmt2_str, NULL, 10); | ||
1356 | 1348 | ||
1357 | tev->nargs = argc - 2; | 1349 | tev->nargs = argc - 2; |
1358 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); | 1350 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); |
@@ -1623,6 +1615,79 @@ error: | |||
1623 | return NULL; | 1615 | return NULL; |
1624 | } | 1616 | } |
1625 | 1617 | ||
1618 | static int find_perf_probe_point_from_map(struct probe_trace_point *tp, | ||
1619 | struct perf_probe_point *pp, | ||
1620 | bool is_kprobe) | ||
1621 | { | ||
1622 | struct symbol *sym = NULL; | ||
1623 | struct map *map; | ||
1624 | u64 addr; | ||
1625 | int ret = -ENOENT; | ||
1626 | |||
1627 | if (!is_kprobe) { | ||
1628 | map = dso__new_map(tp->module); | ||
1629 | if (!map) | ||
1630 | goto out; | ||
1631 | addr = tp->address; | ||
1632 | sym = map__find_symbol(map, addr, NULL); | ||
1633 | } else { | ||
1634 | addr = kernel_get_symbol_address_by_name(tp->symbol, true); | ||
1635 | if (addr) { | ||
1636 | addr += tp->offset; | ||
1637 | sym = __find_kernel_function(addr, &map); | ||
1638 | } | ||
1639 | } | ||
1640 | if (!sym) | ||
1641 | goto out; | ||
1642 | |||
1643 | pp->retprobe = tp->retprobe; | ||
1644 | pp->offset = addr - map->unmap_ip(map, sym->start); | ||
1645 | pp->function = strdup(sym->name); | ||
1646 | ret = pp->function ? 0 : -ENOMEM; | ||
1647 | |||
1648 | out: | ||
1649 | if (map && !is_kprobe) { | ||
1650 | dso__delete(map->dso); | ||
1651 | map__delete(map); | ||
1652 | } | ||
1653 | |||
1654 | return ret; | ||
1655 | } | ||
1656 | |||
1657 | static int convert_to_perf_probe_point(struct probe_trace_point *tp, | ||
1658 | struct perf_probe_point *pp, | ||
1659 | bool is_kprobe) | ||
1660 | { | ||
1661 | char buf[128]; | ||
1662 | int ret; | ||
1663 | |||
1664 | ret = find_perf_probe_point_from_dwarf(tp, pp, is_kprobe); | ||
1665 | if (!ret) | ||
1666 | return 0; | ||
1667 | ret = find_perf_probe_point_from_map(tp, pp, is_kprobe); | ||
1668 | if (!ret) | ||
1669 | return 0; | ||
1670 | |||
1671 | pr_debug("Failed to find probe point from both of dwarf and map.\n"); | ||
1672 | |||
1673 | if (tp->symbol) { | ||
1674 | pp->function = strdup(tp->symbol); | ||
1675 | pp->offset = tp->offset; | ||
1676 | } else if (!tp->module && !is_kprobe) { | ||
1677 | ret = e_snprintf(buf, 128, "0x%" PRIx64, (u64)tp->address); | ||
1678 | if (ret < 0) | ||
1679 | return ret; | ||
1680 | pp->function = strdup(buf); | ||
1681 | pp->offset = 0; | ||
1682 | } | ||
1683 | if (pp->function == NULL) | ||
1684 | return -ENOMEM; | ||
1685 | |||
1686 | pp->retprobe = tp->retprobe; | ||
1687 | |||
1688 | return 0; | ||
1689 | } | ||
1690 | |||
1626 | static int convert_to_perf_probe_event(struct probe_trace_event *tev, | 1691 | static int convert_to_perf_probe_event(struct probe_trace_event *tev, |
1627 | struct perf_probe_event *pev, bool is_kprobe) | 1692 | struct perf_probe_event *pev, bool is_kprobe) |
1628 | { | 1693 | { |
@@ -1636,11 +1701,7 @@ static int convert_to_perf_probe_event(struct probe_trace_event *tev, | |||
1636 | return -ENOMEM; | 1701 | return -ENOMEM; |
1637 | 1702 | ||
1638 | /* Convert trace_point to probe_point */ | 1703 | /* Convert trace_point to probe_point */ |
1639 | if (is_kprobe) | 1704 | ret = convert_to_perf_probe_point(&tev->point, &pev->point, is_kprobe); |
1640 | ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point); | ||
1641 | else | ||
1642 | ret = convert_to_perf_probe_point(&tev->point, &pev->point); | ||
1643 | |||
1644 | if (ret < 0) | 1705 | if (ret < 0) |
1645 | return ret; | 1706 | return ret; |
1646 | 1707 | ||