Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751102AbbD3LnM (ORCPT ); Thu, 30 Apr 2015 07:43:12 -0400 Received: from e28smtp02.in.ibm.com ([122.248.162.2]:49026 "EHLO e28smtp02.in.ibm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750729AbbD3LnJ (ORCPT ); Thu, 30 Apr 2015 07:43:09 -0400 From: "Naveen N. Rao" To: acme@kernel.org, masami.hiramatsu.pt@hitachi.com Cc: linux-kernel@vger.kernel.org Subject: [PATCH] perf probe: Ignore tail calls to probed functions Date: Thu, 30 Apr 2015 17:12:31 +0530 Message-Id: <1430394151-15928-1-git-send-email-naveen.n.rao@linux.vnet.ibm.com> X-Mailer: git-send-email 2.3.5 X-TM-AS-MML: disable X-Content-Scanned: Fidelis XPS MAILER x-cbid: 15043011-0005-0000-0000-0000050A2FC0 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 5157 Lines: 147 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. Signed-off-by: Naveen N. Rao --- I'm seeing this with RHEL7 kernels on ppc64. The specific example in this case is do_fork: <1><82e7e1>: Abbrev Number: 118 (DW_TAG_subprogram) <82e7e2> DW_AT_external : 1 <82e7e2> DW_AT_name : (indirect string, offset: 0x3dfe0): do_fork <82e7e6> DW_AT_decl_file : 7 <82e7e7> DW_AT_decl_line : 1583 <82e7e9> DW_AT_prototyped : 1 <82e7e9> DW_AT_type : <0x8110eb> <82e7ed> DW_AT_inline : 1 (inlined) <82e7ee> DW_AT_sibling : <0x82e878> <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> <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> - Naveen tools/perf/util/dwarf-aux.c | 37 +++++++++++++++++++++++++++++++++++++ tools/perf/util/dwarf-aux.h | 4 ++++ tools/perf/util/probe-finder.c | 12 +++++++++--- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index c34e024..851a76f 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -417,6 +417,43 @@ struct __addr_die_search_param { Dwarf_Die *die_mem; }; +static int __die_search_func_tail_cb(Dwarf_Die *fn_die, void *data) +{ + struct __addr_die_search_param *ad = data; + Dwarf_Addr addr = 0; + + if (dwarf_tag(fn_die) == DW_TAG_subprogram && + !dwarf_highpc(fn_die, &addr) && + addr == ad->addr) { + memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); + return DWARF_CB_ABORT; + } + return DWARF_CB_OK; +} + +/** + * die_find_tailfunc - Search for a non-inlined function with tail call at + * given address + * @cu_die: a CU DIE which including @addr + * @addr: target address + * @die_mem: a buffer for result DIE + * + * Search for a non-inlined function DIE with tail call at @addr. Stores the + * DIE to @die_mem and returns it if found. Returns NULL if failed. + */ +Dwarf_Die *die_find_tailfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, + Dwarf_Die *die_mem) +{ + struct __addr_die_search_param ad; + ad.addr = addr; + ad.die_mem = die_mem; + /* dwarf_getscopes can't find subprogram. */ + if (!dwarf_getfuncs(cu_die, __die_search_func_tail_cb, &ad, 0)) + return NULL; + else + return die_mem; +} + /* die_find callback for non-inlined function search */ static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) { diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index af7dbcd..c9278ed 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h @@ -82,6 +82,10 @@ extern Dwarf_Die *die_find_child(Dwarf_Die *rt_die, extern Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, Dwarf_Die *die_mem); +/* Search a non-inlined function with tail call at given address */ +Dwarf_Die *die_find_tailfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, + Dwarf_Die *die_mem); + /* Search the top inlined function including given address */ extern Dwarf_Die *die_find_top_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, Dwarf_Die *die_mem); diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index b5bf9d5..4cb461e 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -660,9 +660,15 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) /* If not a real subprogram, find a real one */ if (!die_is_func_def(sc_die)) { if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { - pr_warning("Failed to find probe point in any " - "functions.\n"); - return -ENOENT; + if (die_find_tailfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { + pr_warning("Ignoring tail call from %s\n", + dwarf_diename(&pf->sp_die)); + return 0; + } else { + pr_warning("Failed to find probe point in any " + "functions.\n"); + return -ENOENT; + } } } else memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die)); -- 2.3.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/