From 13d1ba6b86e806097e8c70777474f76cee182a25 Mon Sep 17 00:00:00 2001 From: "Frank Ch. Eigler" Date: Fri, 10 Apr 2020 20:37:06 -0400 Subject: [PATCH] PR12609 redux: filter out duplicate probes due to partial-inining Ever since gcc 4.7ish, partially-inlined functions can sometimes show up as separate top-level functions named FOO.part.* in the ELF symbol table, but identifed as ordinary function FOO in DWARF. The latter means that stap would probe both the partially inlined part of the function, and the non-inlined second part, for a plain .function() probe. This new code suppresses the probe on the .part. one, based on a symbol name heuristic. Also RHBZ1169184. --- tapsets.cxx | 26 +++++++++++++++++ testsuite/systemtap.base/partial-inline.c | 32 +++++++++++++++++++++ testsuite/systemtap.base/partial-inline.exp | 22 ++++++++++++++ testsuite/systemtap.base/partial-inline.stp | 8 ++++++ 4 files changed, 88 insertions(+) create mode 100644 testsuite/systemtap.base/partial-inline.c create mode 100644 testsuite/systemtap.base/partial-inline.exp create mode 100644 testsuite/systemtap.base/partial-inline.stp diff --git a/tapsets.cxx b/tapsets.cxx index 61e479c85..46ef0ff40 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -2192,6 +2192,10 @@ query_dwarf_inline_instance (Dwarf_Die * die, dwarf_query * q) Dwarf_Addr entrypc; if (q->dw.die_entrypc (die, &entrypc)) { + // PR12609: The tails of partially-inlined functions show up + // in the query_dwarf_func() path, not here. The heads do + // come here, and should be processed here. + inline_instance_info inl; inl.die = *die; inl.name = q->dw.function_name; @@ -2258,6 +2262,7 @@ query_dwarf_func (Dwarf_Die * func, dwarf_query * q) if (q->sess.verbose>2) clog << _F("selected function %s\n", q->dw.function_name.c_str()); + func_info func; q->dw.function_die (&func.die); func.name = q->dw.function_name; @@ -2270,6 +2275,27 @@ query_dwarf_func (Dwarf_Die * func, dwarf_query * q) if (q->dw.function_entrypc (&entrypc)) { func.entrypc = entrypc; + + // PR12609: handle partial-inlined functions. These look + // like normal inlined instances in DWARF (so come through + // here), but in fact are common/tail parts of a normal + // inlined function instance. They do not represent entry + // points, so we filter them out. DWARF/gcc doesn't leave + // any attributes to identify these from there, so we look + // up the ELF symbol name and rely on a heuristic. + GElf_Sym sym; + GElf_Off off = 0; + const char *name = dwfl_module_addrinfo (q->dw.module, entrypc, + &off, &sym, NULL, NULL, NULL); + + if (name != NULL && strstr(name, ".part.") != NULL) + { + if (q->sess.verbose>2) + clog << _F("skipping partially-inlined instance " + "%s at %p\n", name, (void*)entrypc); + return DWARF_CB_OK; + } + q->filtered_functions.push_back (func); } /* else this function is fully inlined, just ignore it */ diff --git a/testsuite/systemtap.base/partial-inline.c b/testsuite/systemtap.base/partial-inline.c new file mode 100644 index 000000000..ef109bf7e --- /dev/null +++ b/testsuite/systemtap.base/partial-inline.c @@ -0,0 +1,32 @@ +extern void abort (void); +volatile int v; +#define V1 v++; v++; v++; v++; v++; v++; v++; v++; v++; v++; +#define V2 V1 V1 V1 V1 V1 V1 V1 V1 V1 V1 +static int +foo (int x) +{ + int a = 2 * x + 4; + int b = 6; + if (x < 30) + { + int c = 8; + int d = 8 * x; + return 6; + } + else + { + int e = 134; + int f = 9 * x; + V2 + return v + 17; + } +} + +int +main (void) +{ + if (foo (v) > 10000) + abort (); + foo (70); + return 0; +} diff --git a/testsuite/systemtap.base/partial-inline.exp b/testsuite/systemtap.base/partial-inline.exp new file mode 100644 index 000000000..771abaa2e --- /dev/null +++ b/testsuite/systemtap.base/partial-inline.exp @@ -0,0 +1,22 @@ +set test "partial-inline" +if {![installtest_p]} { untested $test; return } + +target_compile $srcdir/$subdir/$test.c $test.exe executable "additional_flags=-O2 additional_flags=-g additional_flags=-fasynchronous-unwind-tables" + +spawn stap $srcdir/$subdir/$test.stp -c ./$test.exe +set bad 0 +set good 0 +expect { + -timeout 30 + "foo.part" { incr bad; exp_continue } + "foo" { incr good; exp_continue } + "main" { incr good; exp_continue } + timeout { fail "$test (timeout)" } + eof { } +} +catch {close}; catch {wait} +if {$good > 0 && $bad == 0} { + pass "$test" +} else { + fail "$test ($good,$bad)" +} diff --git a/testsuite/systemtap.base/partial-inline.stp b/testsuite/systemtap.base/partial-inline.stp new file mode 100644 index 000000000..05cea8cfa --- /dev/null +++ b/testsuite/systemtap.base/partial-inline.stp @@ -0,0 +1,8 @@ +#! /usr/bin/env stap + +probe process.function("foo") +{ + println(usymname(uaddr())) /* "main" for inlined parts of foo(), + and we don't want to see + foo.part for partially-inlined body */ +} -- 2.43.5