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

perf probe: Ignore tail calls to probed functions

perf probe currently errors out if there are any tail calls to probed
functions:

[root@rhel71be]# perf probe do_fork
Failed to find probe point in any functions.
Error: Failed to add events.

Fix this by teaching perf to ignore tail calls.

Without patch:

[root@rhel71be perf]# ./perf probe -v do_fork
probe-definition(0): do_fork symbol:do_fork file:(null) line:0 offset:0
return:0 lazy:(null)
0 arguments
Looking at the vmlinux_path (7 entries long)
symsrc__init: build id mismatch for /boot/vmlinux.
Using /usr/lib/debug/lib/modules/3.10.0-201.el7.ppc64/vmlinux for symbols
Open Debuginfo file:
/usr/lib/debug/lib/modules/3.10.0-201.el7.ppc64/vmlinux
Try to find probe point from debuginfo.
found inline addr: 0xc0000000000bb9b0
Probe point found: do_fork+0
found inline addr: 0xc0000000000bbe20
Probe point found: kernel_thread+48
found inline addr: 0xc0000000000bbe5c
Probe point found: sys_fork+28
found inline addr: 0xc0000000000bbfac
Probe point found: sys_vfork+44
found inline addr: 0xc0000000000bc27c
Failed to find probe point in any functions.
An error occurred in debuginfo analysis (-2).
Error: Failed to add events. Reason: No such file or directory (Code: -2)

With patch:

[root@rhel71be perf]# ./perf probe -v do_fork
probe-definition(0): do_fork symbol:do_fork file:(null) line:0 offset:0
return:0 lazy:(null)
0 arguments
Looking at the vmlinux_path (7 entries long)
symsrc__init: build id mismatch for /boot/vmlinux.
Using /usr/lib/debug/lib/modules/3.10.0-201.el7.ppc64/vmlinux for symbols
Open Debuginfo file:
/usr/lib/debug/lib/modules/3.10.0-201.el7.ppc64/vmlinux
Try to find probe point from debuginfo.
found inline addr: 0xc0000000000bb9b0
Probe point found: do_fork+0
found inline addr: 0xc0000000000bbe20
Probe point found: kernel_thread+48
found inline addr: 0xc0000000000bbe5c
Probe point found: sys_fork+28
found inline addr: 0xc0000000000bbfac
Probe point found: sys_vfork+44
found inline addr: 0xc0000000000bc27c
Ignoring tail call from SyS_clone
Found 4 probe_trace_events.
Opening /sys/kernel/debug/tracing/kprobe_events write=1
No kprobe blacklist support, ignored
Added new events:
Writing event: p:probe/do_fork _text+768432
Failed to write event: Invalid argument
Error: Failed to add events. Reason: Invalid argument (Code: -22)

[Ignore the error about failure to write event - this kernel is missing
a patch to resolve _text properly]

The reason to ignore tail calls is that the address does not belong to
any function frame. In the example above, the address in SyS_clone is
0xc0000000000bc27c, but looking at the debug-info:

<1><830081>: Abbrev Number: 133 (DW_TAG_subprogram)
<830083> DW_AT_external : 1
<830083> DW_AT_name : (indirect string, offset: 0x3cea3): SyS_clone
<830087> DW_AT_decl_file : 7
<830088> DW_AT_decl_line : 1689
<83008a> DW_AT_prototyped : 1
<83008a> DW_AT_type : <0x8110eb>
<83008e> DW_AT_low_pc : 0xc0000000000bc270
<830096> DW_AT_high_pc : 0xc
<83009e> DW_AT_frame_base : 1 byte block: 9c (DW_OP_call_frame_cfa)
<8300a0> DW_AT_GNU_all_call_sites: 1
<8300a0> DW_AT_sibling : <0x830178>
<snip>
<3><830147>: Abbrev Number: 125 (DW_TAG_GNU_call_site)
<830148> DW_AT_low_pc : 0xc0000000000bc27c
<830150> DW_AT_GNU_tail_call: 1
<830150> DW_AT_abstract_origin: <0x82e7e1>

The frame ends at 0xc0000000000bc27c. I suppose this is why this
particular call is a "tail" call. FWIW, systemtap seems to ignore these
as well and requires users to explicitly place probes at these call
sites if necessary. I print out the caller so that users know.

Signed-off-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Link: http://lkml.kernel.org/r/1430394151-15928-1-git-send-email-naveen.n.rao@linux.vnet.ibm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Naveen N. Rao and committed by
Arnaldo Carvalho de Melo
d4c537e6 8b00f469

+50 -3
+37
tools/perf/util/dwarf-aux.c
··· 433 433 Dwarf_Die *die_mem; 434 434 }; 435 435 436 + static int __die_search_func_tail_cb(Dwarf_Die *fn_die, void *data) 437 + { 438 + struct __addr_die_search_param *ad = data; 439 + Dwarf_Addr addr = 0; 440 + 441 + if (dwarf_tag(fn_die) == DW_TAG_subprogram && 442 + !dwarf_highpc(fn_die, &addr) && 443 + addr == ad->addr) { 444 + memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); 445 + return DWARF_CB_ABORT; 446 + } 447 + return DWARF_CB_OK; 448 + } 449 + 450 + /** 451 + * die_find_tailfunc - Search for a non-inlined function with tail call at 452 + * given address 453 + * @cu_die: a CU DIE which including @addr 454 + * @addr: target address 455 + * @die_mem: a buffer for result DIE 456 + * 457 + * Search for a non-inlined function DIE with tail call at @addr. Stores the 458 + * DIE to @die_mem and returns it if found. Returns NULL if failed. 459 + */ 460 + Dwarf_Die *die_find_tailfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, 461 + Dwarf_Die *die_mem) 462 + { 463 + struct __addr_die_search_param ad; 464 + ad.addr = addr; 465 + ad.die_mem = die_mem; 466 + /* dwarf_getscopes can't find subprogram. */ 467 + if (!dwarf_getfuncs(cu_die, __die_search_func_tail_cb, &ad, 0)) 468 + return NULL; 469 + else 470 + return die_mem; 471 + } 472 + 436 473 /* die_find callback for non-inlined function search */ 437 474 static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) 438 475 {
+4
tools/perf/util/dwarf-aux.h
··· 85 85 extern Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, 86 86 Dwarf_Die *die_mem); 87 87 88 + /* Search a non-inlined function with tail call at given address */ 89 + Dwarf_Die *die_find_tailfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, 90 + Dwarf_Die *die_mem); 91 + 88 92 /* Search the top inlined function including given address */ 89 93 extern Dwarf_Die *die_find_top_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, 90 94 Dwarf_Die *die_mem);
+9 -3
tools/perf/util/probe-finder.c
··· 674 674 /* If not a real subprogram, find a real one */ 675 675 if (!die_is_func_def(sc_die)) { 676 676 if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { 677 - pr_warning("Failed to find probe point in any " 678 - "functions.\n"); 679 - return -ENOENT; 677 + if (die_find_tailfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { 678 + pr_warning("Ignoring tail call from %s\n", 679 + dwarf_diename(&pf->sp_die)); 680 + return 0; 681 + } else { 682 + pr_warning("Failed to find probe point in any " 683 + "functions.\n"); 684 + return -ENOENT; 685 + } 680 686 } 681 687 } else 682 688 memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die));