Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf probe ppc64le: Fix probe location when using DWARF

Powerpc has Global Entry Point and Local Entry Point for functions. LEP
catches call from both the GEP and the LEP. Symbol table of ELF contains
GEP and Offset from which we can calculate LEP, but debuginfo does not
have LEP info.

Currently, perf prioritize symbol table over dwarf to probe on LEP for
ppc64le. But when user tries to probe with function parameter, we fall
back to using dwarf(i.e. GEP) and when function called via LEP, probe
will never hit.

For example:

$ objdump -d vmlinux
...
do_sys_open():
c0000000002eb4a0: e8 00 4c 3c addis r2,r12,232
c0000000002eb4a4: 60 00 42 38 addi r2,r2,96
c0000000002eb4a8: a6 02 08 7c mflr r0
c0000000002eb4ac: d0 ff 41 fb std r26,-48(r1)

$ sudo ./perf probe do_sys_open
$ sudo cat /sys/kernel/debug/tracing/kprobe_events
p:probe/do_sys_open _text+3060904

$ sudo ./perf probe 'do_sys_open filename:string'
$ sudo cat /sys/kernel/debug/tracing/kprobe_events
p:probe/do_sys_open _text+3060896 filename_string=+0(%gpr4):string

For second case, perf probed on GEP. So when function will be called via
LEP, probe won't hit.

$ sudo ./perf record -a -e probe:do_sys_open ls
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.195 MB perf.data ]

To resolve this issue, let's not prioritize symbol table, let perf
decide what it wants to use. Perf is already converting GEP to LEP when
it uses symbol table. When perf uses debuginfo, let it find LEP offset
form symbol table. This way we fall back to probe on LEP for all cases.

After patch:

$ sudo ./perf probe 'do_sys_open filename:string'
$ sudo cat /sys/kernel/debug/tracing/kprobe_events
p:probe/do_sys_open _text+3060904 filename_string=+0(%gpr4):string

$ sudo ./perf record -a -e probe:do_sys_open ls
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.197 MB perf.data (11 samples) ]

Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com>
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/r/1470723805-5081-2-git-send-email-ravi.bangoria@linux.vnet.ibm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ravi Bangoria and committed by
Arnaldo Carvalho de Melo
99e608b5 d820456d

+49 -21
+23 -4
tools/perf/arch/powerpc/util/sym-handling.c
··· 54 54 #endif 55 55 56 56 #if defined(_CALL_ELF) && _CALL_ELF == 2 57 - bool arch__prefers_symtab(void) 58 - { 59 - return true; 60 - } 61 57 62 58 #ifdef HAVE_LIBELF_SUPPORT 63 59 void arch__sym_update(struct symbol *s, GElf_Sym *sym) ··· 96 100 tev->point.offset += lep_offset; 97 101 } 98 102 } 103 + 104 + void arch__post_process_probe_trace_events(struct perf_probe_event *pev, 105 + int ntevs) 106 + { 107 + struct probe_trace_event *tev; 108 + struct map *map; 109 + struct symbol *sym = NULL; 110 + struct rb_node *tmp; 111 + int i = 0; 112 + 113 + map = get_target_map(pev->target, pev->uprobes); 114 + if (!map || map__load(map, NULL) < 0) 115 + return; 116 + 117 + for (i = 0; i < ntevs; i++) { 118 + tev = &pev->tevs[i]; 119 + map__for_each_symbol(map, sym, tmp) { 120 + if (map->unmap_ip(map, sym->start) == tev->point.address) 121 + arch__fix_tev_from_maps(pev, tev, map, sym); 122 + } 123 + } 124 + } 125 + 99 126 #endif
+21 -16
tools/perf/util/probe-event.c
··· 180 180 return NULL; 181 181 } 182 182 183 - static struct map *get_target_map(const char *target, bool user) 183 + struct map *get_target_map(const char *target, bool user) 184 184 { 185 185 /* Init maps of given executable or kernel */ 186 186 if (user) ··· 705 705 return skipped; 706 706 } 707 707 708 + void __weak 709 + arch__post_process_probe_trace_events(struct perf_probe_event *pev __maybe_unused, 710 + int ntevs __maybe_unused) 711 + { 712 + } 713 + 708 714 /* Post processing the probe events */ 709 - static int post_process_probe_trace_events(struct probe_trace_event *tevs, 715 + static int post_process_probe_trace_events(struct perf_probe_event *pev, 716 + struct probe_trace_event *tevs, 710 717 int ntevs, const char *module, 711 718 bool uprobe) 712 719 { 720 + int ret; 721 + 713 722 if (uprobe) 714 - return add_exec_to_probe_trace_events(tevs, ntevs, module); 715 - 716 - if (module) 723 + ret = add_exec_to_probe_trace_events(tevs, ntevs, module); 724 + else if (module) 717 725 /* Currently ref_reloc_sym based probe is not for drivers */ 718 - return add_module_to_probe_trace_events(tevs, ntevs, module); 726 + ret = add_module_to_probe_trace_events(tevs, ntevs, module); 727 + else 728 + ret = post_process_kernel_probe_trace_events(tevs, ntevs); 719 729 720 - return post_process_kernel_probe_trace_events(tevs, ntevs); 730 + if (ret >= 0) 731 + arch__post_process_probe_trace_events(pev, ntevs); 732 + 733 + return ret; 721 734 } 722 735 723 736 /* Try to find perf_probe_event with debuginfo */ ··· 771 758 772 759 if (ntevs > 0) { /* Succeeded to find trace events */ 773 760 pr_debug("Found %d probe_trace_events.\n", ntevs); 774 - ret = post_process_probe_trace_events(*tevs, ntevs, 761 + ret = post_process_probe_trace_events(pev, *tevs, ntevs, 775 762 pev->target, pev->uprobes); 776 763 if (ret < 0 || ret == ntevs) { 777 764 clear_probe_trace_events(*tevs, ntevs); ··· 2958 2945 return err; 2959 2946 } 2960 2947 2961 - bool __weak arch__prefers_symtab(void) { return false; } 2962 - 2963 2948 /* Concatinate two arrays */ 2964 2949 static void *memcat(void *a, size_t sz_a, void *b, size_t sz_b) 2965 2950 { ··· 3177 3166 ret = find_probe_trace_events_from_cache(pev, tevs); 3178 3167 if (ret > 0 || pev->sdt) /* SDT can be found only in the cache */ 3179 3168 return ret == 0 ? -ENOENT : ret; /* Found in probe cache */ 3180 - 3181 - if (arch__prefers_symtab() && !perf_probe_event_need_dwarf(pev)) { 3182 - ret = find_probe_trace_events_from_map(pev, tevs); 3183 - if (ret > 0) 3184 - return ret; /* Found in symbol table */ 3185 - } 3186 3169 3187 3170 /* Convert perf_probe_event with debuginfo */ 3188 3171 ret = try_to_find_probe_trace_events(pev, tevs);
+5 -1
tools/perf/util/probe-event.h
··· 158 158 int show_available_vars(struct perf_probe_event *pevs, int npevs, 159 159 struct strfilter *filter); 160 160 int show_available_funcs(const char *module, struct strfilter *filter, bool user); 161 - bool arch__prefers_symtab(void); 162 161 void arch__fix_tev_from_maps(struct perf_probe_event *pev, 163 162 struct probe_trace_event *tev, struct map *map, 164 163 struct symbol *sym); ··· 171 172 172 173 int copy_to_probe_trace_arg(struct probe_trace_arg *tvar, 173 174 struct perf_probe_arg *pvar); 175 + 176 + struct map *get_target_map(const char *target, bool user); 177 + 178 + void arch__post_process_probe_trace_events(struct perf_probe_event *pev, 179 + int ntevs); 174 180 175 181 #endif /*_PROBE_EVENT_H */