diff options
author | Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | 2015-09-30 12:41:33 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2015-09-30 17:34:35 -0400 |
commit | 9b239a12bc872f212cefbaee4d028c838abe6ff3 (patch) | |
tree | 483e19ee3b88364bbb7ac4d08b2a7c53cc1f4c3e | |
parent | 9135949ddd9d0d8d73a2f441912508d25a4a82a2 (diff) |
perf probe: Show correct source lines of probes on kmodules
Perf probe always failed to find appropriate line numbers because of
failing to find .text start address offset from debuginfo.
e.g.
----
# ./perf probe -m pcspkr pcspkr_event:5
Added new events:
probe:pcspkr_event (on pcspkr_event:5 in pcspkr)
probe:pcspkr_event_1 (on pcspkr_event:5 in pcspkr)
You can now use it in all perf tools, such as:
perf record -e probe:pcspkr_event_1 -aR sleep 1
# ./perf probe -l
Failed to find debug information for address ffffffffa031f006
Failed to find debug information for address ffffffffa031f016
probe:pcspkr_event (on pcspkr_event+6 in pcspkr)
probe:pcspkr_event_1 (on pcspkr_event+22 in pcspkr)
----
This fixes the above issue as below.
1. Get the relative address of the symbol in .text by using
map->start.
2. Adjust the address by adding the offset of .text section
in the kernel module binary.
With this fix, perf probe -l shows lines correctly.
----
# ./perf probe -l
probe:pcspkr_event (on pcspkr_event:5@drivers/input/misc/pcspkr.c in pcspkr)
probe:pcspkr_event_1 (on pcspkr_event:5@drivers/input/misc/pcspkr.c in pcspkr)
----
Reported-by: Arnaldo Carvalho de Melo <acme@kernel.org>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/r/20150930164132.3733.24643.stgit@localhost.localdomain
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/util/probe-event.c | 35 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.c | 42 |
2 files changed, 65 insertions, 12 deletions
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 5d68f68797a9..65be284823d5 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -137,7 +137,8 @@ static struct ref_reloc_sym *kernel_get_ref_reloc_sym(void) | |||
137 | return kmap->ref_reloc_sym; | 137 | return kmap->ref_reloc_sym; |
138 | } | 138 | } |
139 | 139 | ||
140 | static u64 kernel_get_symbol_address_by_name(const char *name, bool reloc) | 140 | static int kernel_get_symbol_address_by_name(const char *name, u64 *addr, |
141 | bool reloc, bool reladdr) | ||
141 | { | 142 | { |
142 | struct ref_reloc_sym *reloc_sym; | 143 | struct ref_reloc_sym *reloc_sym; |
143 | struct symbol *sym; | 144 | struct symbol *sym; |
@@ -146,12 +147,14 @@ static u64 kernel_get_symbol_address_by_name(const char *name, bool reloc) | |||
146 | /* ref_reloc_sym is just a label. Need a special fix*/ | 147 | /* ref_reloc_sym is just a label. Need a special fix*/ |
147 | reloc_sym = kernel_get_ref_reloc_sym(); | 148 | reloc_sym = kernel_get_ref_reloc_sym(); |
148 | if (reloc_sym && strcmp(name, reloc_sym->name) == 0) | 149 | if (reloc_sym && strcmp(name, reloc_sym->name) == 0) |
149 | return (reloc) ? reloc_sym->addr : reloc_sym->unrelocated_addr; | 150 | *addr = (reloc) ? reloc_sym->addr : reloc_sym->unrelocated_addr; |
150 | else { | 151 | else { |
151 | sym = __find_kernel_function_by_name(name, &map); | 152 | sym = __find_kernel_function_by_name(name, &map); |
152 | if (sym) | 153 | if (!sym) |
153 | return map->unmap_ip(map, sym->start) - | 154 | return -ENOENT; |
154 | ((reloc) ? 0 : map->reloc); | 155 | *addr = map->unmap_ip(map, sym->start) - |
156 | ((reloc) ? 0 : map->reloc) - | ||
157 | ((reladdr) ? map->start : 0); | ||
155 | } | 158 | } |
156 | return 0; | 159 | return 0; |
157 | } | 160 | } |
@@ -245,12 +248,14 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs) | |||
245 | static bool kprobe_blacklist__listed(unsigned long address); | 248 | static bool kprobe_blacklist__listed(unsigned long address); |
246 | static bool kprobe_warn_out_range(const char *symbol, unsigned long address) | 249 | static bool kprobe_warn_out_range(const char *symbol, unsigned long address) |
247 | { | 250 | { |
248 | u64 etext_addr; | 251 | u64 etext_addr = 0; |
252 | int ret; | ||
249 | 253 | ||
250 | /* Get the address of _etext for checking non-probable text symbol */ | 254 | /* Get the address of _etext for checking non-probable text symbol */ |
251 | etext_addr = kernel_get_symbol_address_by_name("_etext", false); | 255 | ret = kernel_get_symbol_address_by_name("_etext", &etext_addr, |
256 | false, false); | ||
252 | 257 | ||
253 | if (etext_addr != 0 && etext_addr < address) | 258 | if (ret == 0 && etext_addr < address) |
254 | pr_warning("%s is out of .text, skip it.\n", symbol); | 259 | pr_warning("%s is out of .text, skip it.\n", symbol); |
255 | else if (kprobe_blacklist__listed(address)) | 260 | else if (kprobe_blacklist__listed(address)) |
256 | pr_warning("%s is blacklisted function, skip it.\n", symbol); | 261 | pr_warning("%s is blacklisted function, skip it.\n", symbol); |
@@ -517,8 +522,10 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp, | |||
517 | goto error; | 522 | goto error; |
518 | addr += stext; | 523 | addr += stext; |
519 | } else if (tp->symbol) { | 524 | } else if (tp->symbol) { |
520 | addr = kernel_get_symbol_address_by_name(tp->symbol, false); | 525 | /* If the module is given, this returns relative address */ |
521 | if (addr == 0) | 526 | ret = kernel_get_symbol_address_by_name(tp->symbol, &addr, |
527 | false, !!tp->module); | ||
528 | if (ret != 0) | ||
522 | goto error; | 529 | goto error; |
523 | addr += tp->offset; | 530 | addr += tp->offset; |
524 | } | 531 | } |
@@ -1884,8 +1891,12 @@ static int find_perf_probe_point_from_map(struct probe_trace_point *tp, | |||
1884 | goto out; | 1891 | goto out; |
1885 | sym = map__find_symbol(map, addr, NULL); | 1892 | sym = map__find_symbol(map, addr, NULL); |
1886 | } else { | 1893 | } else { |
1887 | if (tp->symbol) | 1894 | if (tp->symbol && !addr) { |
1888 | addr = kernel_get_symbol_address_by_name(tp->symbol, true); | 1895 | ret = kernel_get_symbol_address_by_name(tp->symbol, |
1896 | &addr, true, false); | ||
1897 | if (ret < 0) | ||
1898 | goto out; | ||
1899 | } | ||
1889 | if (addr) { | 1900 | if (addr) { |
1890 | addr += tp->offset; | 1901 | addr += tp->offset; |
1891 | sym = __find_kernel_function(addr, &map); | 1902 | sym = __find_kernel_function(addr, &map); |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 35f905f4f34c..f0708ffd5e07 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -1402,6 +1402,41 @@ int debuginfo__find_available_vars_at(struct debuginfo *dbg, | |||
1402 | return (ret < 0) ? ret : af.nvls; | 1402 | return (ret < 0) ? ret : af.nvls; |
1403 | } | 1403 | } |
1404 | 1404 | ||
1405 | /* For the kernel module, we need a special code to get a DIE */ | ||
1406 | static int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs) | ||
1407 | { | ||
1408 | int n, i; | ||
1409 | Elf32_Word shndx; | ||
1410 | Elf_Scn *scn; | ||
1411 | Elf *elf; | ||
1412 | GElf_Shdr mem, *shdr; | ||
1413 | const char *p; | ||
1414 | |||
1415 | elf = dwfl_module_getelf(dbg->mod, &dbg->bias); | ||
1416 | if (!elf) | ||
1417 | return -EINVAL; | ||
1418 | |||
1419 | /* Get the number of relocations */ | ||
1420 | n = dwfl_module_relocations(dbg->mod); | ||
1421 | if (n < 0) | ||
1422 | return -ENOENT; | ||
1423 | /* Search the relocation related .text section */ | ||
1424 | for (i = 0; i < n; i++) { | ||
1425 | p = dwfl_module_relocation_info(dbg->mod, i, &shndx); | ||
1426 | if (strcmp(p, ".text") == 0) { | ||
1427 | /* OK, get the section header */ | ||
1428 | scn = elf_getscn(elf, shndx); | ||
1429 | if (!scn) | ||
1430 | return -ENOENT; | ||
1431 | shdr = gelf_getshdr(scn, &mem); | ||
1432 | if (!shdr) | ||
1433 | return -ENOENT; | ||
1434 | *offs = shdr->sh_addr; | ||
1435 | } | ||
1436 | } | ||
1437 | return 0; | ||
1438 | } | ||
1439 | |||
1405 | /* Reverse search */ | 1440 | /* Reverse search */ |
1406 | int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr, | 1441 | int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr, |
1407 | struct perf_probe_point *ppt) | 1442 | struct perf_probe_point *ppt) |
@@ -1410,9 +1445,16 @@ int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr, | |||
1410 | Dwarf_Addr _addr = 0, baseaddr = 0; | 1445 | Dwarf_Addr _addr = 0, baseaddr = 0; |
1411 | const char *fname = NULL, *func = NULL, *basefunc = NULL, *tmp; | 1446 | const char *fname = NULL, *func = NULL, *basefunc = NULL, *tmp; |
1412 | int baseline = 0, lineno = 0, ret = 0; | 1447 | int baseline = 0, lineno = 0, ret = 0; |
1448 | bool reloc = false; | ||
1413 | 1449 | ||
1450 | retry: | ||
1414 | /* Find cu die */ | 1451 | /* Find cu die */ |
1415 | if (!dwarf_addrdie(dbg->dbg, (Dwarf_Addr)addr, &cudie)) { | 1452 | if (!dwarf_addrdie(dbg->dbg, (Dwarf_Addr)addr, &cudie)) { |
1453 | if (!reloc && debuginfo__get_text_offset(dbg, &baseaddr) == 0) { | ||
1454 | addr += baseaddr; | ||
1455 | reloc = true; | ||
1456 | goto retry; | ||
1457 | } | ||
1416 | pr_warning("Failed to find debug information for address %lx\n", | 1458 | pr_warning("Failed to find debug information for address %lx\n", |
1417 | addr); | 1459 | addr); |
1418 | ret = -EINVAL; | 1460 | ret = -EINVAL; |