Summary: | STAP_PROBES don't work in c++ constructors/destructors | ||
---|---|---|---|
Product: | systemtap | Reporter: | Mark Wielaard <mark> |
Component: | runtime | Assignee: | Unassigned <systemtap> |
Status: | RESOLVED FIXED | ||
Severity: | normal | ||
Priority: | P2 | ||
Version: | unspecified | ||
Target Milestone: | --- | ||
Host: | Target: | ||
Build: | Last reconfirmed: | ||
Bug Depends on: | |||
Bug Blocks: | 10461 | ||
Attachments: | class testcase |
Description
Mark Wielaard
2009-08-12 14:58:03 UTC
Created attachment 4128 [details]
class testcase
Testcase that shows the issue when compiled with:
$ g++ -I includes/sys -o test cxxclass.cxx
/tmp/ccqveEm3.o:(.data._ZZN10ProbeClassC1ERiPKcE8labelval[ProbeClass::ProbeClass(int&,
char const*)::labelval]+0x0): undefined reference to `.L21'
/tmp/ccqveEm3.o:(.data._ZZN10ProbeClassD1EvE8labelval[ProbeClass::~ProbeClass()::labelval]+0x0):
undefined reference to `.L22'
collect2: ld returned 1 exit status
(In reply to comment #1) > Created an attachment (id=4128) > class testcase > > Testcase that shows the issue when compiled with: > $ g++ -I includes/sys -o test cxxclass.cxx > /tmp/ccqveEm3.o:(.data._ZZN10ProbeClassC1ERiPKcE8labelval[ProbeClass::ProbeClass(int&, > char const*)::labelval]+0x0): undefined reference to `.L21' > /tmp/ccqveEm3.o:(.data._ZZN10ProbeClassD1EvE8labelval[ProbeClass::~ProbeClass()::labelval]+0x0): > undefined reference to `.L22' > collect2: ld returned 1 exit status Note that this testcase seems to show a somewhat different issue from what was described in comment #1. The issue in the above is the reference to the computed goto for the label in a constructor/destructor. We have the computed goto reference assigned to a static variable initializer to prevent inlining of the function that the probe is on (since we can only reference and store one label address in the .probes section through the preprocessor). Interestingly just removing the computed goto reference seems to make g++ do the right thing, even with optimizations. We get multiple duplicated probe name addresses in the probes section: #define STAP_UNINLINE_LABEL(label) \ - __extension__ static volatile long labelval __attribute__ ((unused)) = (long) &&label + // __extension__ static volatile long labelval __attribute__ ((unused)) = (long) &&label $ stap -vvvvvv -L 'process("./test").mark("*")' 2>&1 | grep ^saw saw .probes cons@0x40063b saw .probes meth@0x40067b saw .probes dest@0x4006b4 saw .probes cons@0x4006e8 saw .probes meth@0x400728 saw .probes dest@0x400766 saw .probes dest@0x4007a2 saw .probes dest@0x4007e4 stap doesn't know how to deal with that though, and currently only creates one statement probe per name seen. stap -vve 'probe process("./test").mark("cons") { printf("cons hit @0x%x\n", uaddr()); }' -c ./test [...] probe main@/home/mark/src/systemtap/cxxclass.cxx.test.cxx:53 process=/usr/local/src/systemtap/test.orig reloc=.absolute section=.text pc=0x40063b [...] cons call: 64 meth call: 64 24 dest call: 42 cons call2: 24 meth call2: 24 40 dest call2: 42 cons hit @0x40063b Also trying to reference the local args of the mark doesn't work: semantic error: unable to find local 'arg1' near pc 0x40063b in main(cxxclass.cxx.test.cxx) (alternatives: argc argv): identifier '$arg1' at <input>:1:94 source: probe process("./test.orig").mark("cons") { printf("cons hit @0x%x name=%s i=%d\n", uaddr(), $arg1, $arg2); } As can be seen from the alternatives suggested only the variables of the function into which the probe was inlined are visible. (In reply to comment #2) > As can be seen from the alternatives suggested only the variables of the > function into which the probe was inlined are visible. This is because they aren't available as dwarf locations. Note that the values are there. We even mark them as comments in the assembly: # 14 "cxxclass.cxx.test.cxx" 1 2: nop /* %rax %edx */ # 0 "" 2 So we can actually access them, if we would know the registers they are stored in: $ stap -e 'probe process("./test").mark("cons") { printf("cons hit @0x%x name=%s i=%d\n", uaddr(), user_string_quoted(u_register("rax")), register("edx")); }' -c ./test cons call: 64 meth call: 64 24 dest call: 42 cons call2: 24 meth call2: 24 40 dest call2: 42 cons hit @0x40063f name="call" i=64 (In reply to comment #3) > (In reply to comment #2) > > As can be seen from the alternatives suggested only the variables of the > > function into which the probe was inlined are visible. > > This is because they aren't available as dwarf locations. But the are... $ ./loc2c-test -e ./test.orig 0x40063f cxxclass.cxx (0x11): 0x400620 (/home/mark/src/test/cxxclass.cxx:54) .. 0x4007db (/home/mark/src/test/cxxclass.cxx:28) stdin [ 8ac] _IO_FILE* stdout [ 8b9] _IO_FILE* ProbeClass (0x1d): 0x400625 (/home/mark/src/test/cxxclass.cxx:14) .. 0x400656 (/home/mark/src/test/cxxclass.cxx:20) n [ 6c5] char const* v [ 6ca] <unknown 0x10> const this [ 6cf] ProbeClass* const <unnamed> (0xb): 0x400625 (/home/mark/src/test/cxxclass.cxx:14) .. 0x400640 (/home/mark/src/test/cxxclass.cxx:15) arg1 [ 6e5] char const* volatile arg2 [ 6ed] int volatile But loc2c also has trouble retrieving them: $ ./loc2c-test -e ./test.orig 0x40063b arg1 loc2c-test: required DW_AT_frame_base attribute not supplied $ ./loc2c-test -e ./test.orig 0x40063b arg2 loc2c-test: required DW_AT_frame_base attribute not supplied (In reply to comment #4) > But loc2c also has trouble retrieving them: > > $ ./loc2c-test -e ./test.orig 0x40063b arg1 > loc2c-test: required DW_AT_frame_base attribute not supplied > $ ./loc2c-test -e ./test.orig 0x40063b arg2 > loc2c-test: required DW_AT_frame_base attribute not supplied gdb can retrieve them: $ gdb ./test [...] (gdb) break *0x40063b Breakpoint 1 at 0x40063b: file cxxclass.cxx, line 14. (gdb) run Starting program: /usr/local/src/systemtap/test.orig Breakpoint 1, 0x000000000040063b in ProbeClass (n=<value optimized out>, v=<value optimized out>, this=<value optimized out>) at cxxclass.cxx.test.cxx:14 14 STAP_PROBE2(_test_, cons, name, ref); (gdb) print arg1 $1 = 0x4008d8 "call" (gdb) print arg2 $2 = 64 The problem is accessing the correct outer CFA of the inlined_subroutine in loc2c-test.c and dwflpp.cxx. See case DW_TAG_inlined_subroutine: /* XXX */ dwflp.cxx find_variable_and_frame_base also has another issue finding the actual variable at the pc location because off this snippet: // if pc and scope_die are disjoint then we need dwarf_getscopes_die for (sidx = 0; sidx < nscopes; sidx++) if (scopes[sidx].addr == scope_die->addr) break; if (sidx == nscopes) nscopes = dwarf_getscopes_die (scope_die, &scopes); I'll open another bug as soon as I have a simpler testcase for "not finding/resolving inlined local variable definitions". Dodji pointed me at gcc bug http://gcc.gnu.org/bugzilla/show_bug.cgi?id=37801 "DWARF output for inlined functions doesn't always use DW_TAG_inlined_subroutine" (now fixed, but not in any released gcc yet) that might explain some oddities. There are two more gcc bugs associated with this issue now. The following contains a small example of the issue with using labelrefs in C++ class constructors: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41090 "Using static label reference in c++ class constructor produces wrong code" And while working on a partial workaround putting everything in their own section through variable attributes I came across a section conflict when doing that from a c and a c++ context: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41091 "Using section attribute in c and c++ function causes section type conflict" And another gcc bug for another try at a workaround... http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41097 "Inlined variable debug location disappears when ptr type" (In reply to comment #8) > And another gcc bug for another try at a workaround... > http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41097 > "Inlined variable debug location disappears when ptr type" This isn't really relevant, since it isn't how STAP_PROBES synthesizes the local args (it does use the volatile typedef typeof trick already mentioned in the gcc bug discussion). (In reply to comment #5) > (In reply to comment #4) > I'll open another bug as soon as I have a simpler testcase for "not > finding/resolving inlined local variable definitions". Which is PR10533 "inlined vars are not always found". Although this doesn't fix the general comdat section issue, it does fix the issue at hand: commit 08b22cd52aad1023c91341a1fd9bdb821cf4c90f Author: Mark Wielaard <mjw@redhat.com> Date: Tue Aug 18 23:03:00 2009 +0200 PR10512 STAP_PROBES don't work in c++ constructors/destructors testcase. * testsuite/systemtap.base/cxxclass.exp: New file. * testsuite/systemtap.base/cxxclass.stp: Likewise. * testsuite/systemtap.base/cxxclass.cxx: Likewise. commit a80a54cb804d0df40e064a72bd8c6bf7c1258e02 Author: Mark Wielaard <mjw@redhat.com> Date: Tue Aug 18 22:08:51 2009 +0200 PR10512 Referencing computed goto labels in c++ constructors does work. PR10533 inlined vars are not always found was fixed which means we no longer need to prevent inlining of the STAP_PROBE macros. * includes/sys/sdt.h (STAP_UNINLINE_LABEL): Removed. (STAP_PROBE[1-9]_): Remove label: and STAP_UNINLINE_LABEL. When we find other c++ related issues we should open a new bug. |