[PATCH perf/core 2/3] perf-probe: Fix to probe on gcc generated symbols for offline kernel

From: Masami Hiramatsu
Date: Tue Jan 03 2017 - 23:25:25 EST


Fix perf-probe to show probe definition on gcc generated symbols
for offline kernel (including cross-arch kernel image).

Gcc sometimes optimizes functions and generate new symbols with
suffixes such as ".constprop.N" or ".isra.N" etc. Since those
symbol names are not recorded in DWARF, we have to find correct
generated symbols from offline ELF binary to probe on it (kallsyms
doesn't correct it).
For online kernel or uprobes we don't need it because those are
rebased on _text, or a section relative address.

E.g. Without this;
-----
$ perf probe -k build-arm/vmlinux -F __slab_alloc*
__slab_alloc.constprop.9
$ perf probe -k build-arm/vmlinux -D __slab_alloc
p:probe/__slab_alloc __slab_alloc+0
-----
If you put above definition on target machine, it should fail
because there is no __slab_alloc in kallsyms.

With this fix, perf probe shows correct probe definition on
__slab_alloc.constprop.9.
-----
$ perf probe -k build-arm/vmlinux -D __slab_alloc
p:probe/__slab_alloc __slab_alloc.constprop.9+0
-----

Signed-off-by: Masami Hiramatsu <mhiramat@xxxxxxxxxx>
---
tools/perf/util/probe-event.c | 48 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 47 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 542e647..4a57c8a 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -610,6 +610,51 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp,
return ret ? : -ENOENT;
}

+/*
+ * Rename DWARF symbols to ELF symbols -- gcc sometimes optimizes functions
+ * and generate new symbols with suffixes such as .constprop.N or .isra.N
+ * etc. Since those symbols are not recorded in DWARF, we have to find
+ * correct generated symbols from offline ELF binary.
+ * For online kernel or uprobes we don't need this because those are
+ * rebased on _text, or already a section relative address.
+ */
+static int
+post_process_offline_probe_trace_events(struct probe_trace_event *tevs,
+ int ntevs, const char *pathname)
+{
+ struct symbol *sym;
+ struct map *map;
+ unsigned long stext = 0;
+ u64 addr;
+ int i;
+
+ /* Prepare a map for offline binary */
+ map = dso__new_map(pathname);
+ if (!map || get_text_start_address(pathname, &stext) < 0) {
+ pr_warning("Failed to get ELF symbols for %s\n", pathname);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ntevs; i++) {
+ addr = tevs[i].point.address + tevs[i].point.offset - stext;
+ sym = map__find_symbol(map, addr);
+ if (!sym)
+ continue;
+ if (!strcmp(sym->name, tevs[i].point.symbol))
+ continue;
+ /* If we have no realname, use symbol for it */
+ if (!tevs[i].point.realname)
+ tevs[i].point.realname = tevs[i].point.symbol;
+ else
+ free(tevs[i].point.symbol);
+ tevs[i].point.symbol = strdup(sym->name);
+ tevs[i].point.offset = addr - sym->start;
+ }
+ map__put(map);
+
+ return 0;
+}
+
static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
int ntevs, const char *exec)
{
@@ -671,7 +716,8 @@ post_process_kernel_probe_trace_events(struct probe_trace_event *tevs,

/* Skip post process if the target is an offline kernel */
if (symbol_conf.ignore_vmlinux_buildid)
- return 0;
+ return post_process_offline_probe_trace_events(tevs, ntevs,
+ symbol_conf.vmlinux_name);

reloc_sym = kernel_get_ref_reloc_sym();
if (!reloc_sym) {