This is an archive of the discontinued LLVM Phabricator instance.

[libunwind] Support for leaf function unwinding.
ClosedPublic

Authored by danielkiss on Jul 10 2020, 9:57 AM.

Details

Summary

Unwinding leaf function is useful in cases when the backtrace finds a
leaf function for example when it caused a signal.
This patch also add the support for the DW_CFA_undefined because it marks
the end of the frames.

Ryan Prichard provided code for the tests.

Diff Detail

Event Timeline

danielkiss planned changes to this revision.Jul 10 2020, 9:57 AM
danielkiss created this revision.

This is a WIP patch, just shows my current thinking.

danielkiss retitled this revision from [WIP][libunwind] Support for leaf function unwinding. to [libunwind] Support for leaf function unwinding..
danielkiss edited the summary of this revision. (Show Details)
danielkiss added a reviewer: rprichard.
danielkiss added reviewers: Restricted Project, compnerd, jgorbe.
danielkiss added inline comments.Aug 11 2020, 4:35 AM
libunwind/test/lit.site.cfg.in
48 ↗(On Diff #284452)

I'll reword this...

danielkiss added a reviewer: mstorsjo.

The change itself looks good to me, but the comment in lit.site.cfg.in does look confusing to me as well, so it'd be great to have it reworded.

libunwind/test/lit.site.cfg.in
48 ↗(On Diff #284452)

I don't think this comment was reworded yet?

danielkiss marked an inline comment as done.
danielkiss added inline comments.
libunwind/test/lit.site.cfg.in
48 ↗(On Diff #284452)

Thanks for the catch. patch is updated.

This revision is now accepted and ready to land.Sep 15 2020, 3:49 AM
This revision was automatically updated to reflect the committed changes.
danielkiss marked an inline comment as done.
Herald added projects: Restricted Project, Restricted Project. · View Herald TranscriptSep 16 2020, 2:53 PM
bjope added a subscriber: bjope.Sep 17 2020, 5:45 AM

Having some problems after this patch, when building the runtimes for i386, on a 64-bit RHEL7 server, and then running the added libunwind test cases on the same host.
No expert here, and maybe that isn't a valid scenario, but building/running tests like that has worked pretty good before.

If anyone got an idea what we are doing wrong, or if this is worth a PR (what info is needed then?), etc, please let me know.

Here is a trace showing the failure:

********************
FAIL: libunwind :: signal_unwind.pass.cpp (9 of 9)
******************** TEST 'libunwind :: signal_unwind.pass.cpp' FAILED ********************
Script:
--
: 'COMPILED WITH';  /repo/llvm-project/llvm/build-all-builtins/./bin/clang++ /repo/llvm-project/libunwind/test/signal_unwind.pass.cpp -v --target=i386-unknown-linux-gnu -DLIBUNWIND_NO_TIMER -funwind-tables -include /repo/llvm-project/llvm/build-all-builtins/runtimes/runtimes-i386-unknown-linux-gnu-bins/libcxx/__config_site -I/repo/llvm-project/libunwind/include -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -I/repo/llvm-project/libunwind/test/../../libcxx/test/support -D_LIBCPP_DISABLE_AVAILABILITY -funwind-tables -Werror -Wall -Wextra -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -Wshadow -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-c++11-extensions -Wno-user-defined-literals -Wno-noexcept-type -Wno-atomic-alignment -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -std=c++2a -fcoroutines-ts -Werror=thread-safety -Wuser-defined-warnings  -Wl,--export-dynamic -L/repo/llvm-project/llvm/build-all-builtins/./lib/i386-unknown-linux-gnu/c++ -Wl,-rpath,/repo/llvm-project/llvm/build-all-builtins/./lib/i386-unknown-linux-gnu/c++ -nodefaultlibs -lm -lpthread -lc -lunwind -ldl -lgcc -o /repo/llvm-project/llvm/build-all-builtins/runtimes/runtimes-i386-unknown-linux-gnu-bins/libunwind/test/Output/signal_unwind.pass.cpp.dir/t.tmp.exe
: 'EXECUTED AS';   /repo/llvm-project/libunwind/test/../../libcxx/utils/run.py --execdir /repo/llvm-project/llvm/build-all-builtins/runtimes/runtimes-i386-unknown-linux-gnu-bins/libunwind/test/Output/signal_unwind.pass.cpp.dir --codesign_identity "" --env  --  /repo/llvm-project/llvm/build-all-builtins/runtimes/runtimes-i386-unknown-linux-gnu-bins/libunwind/test/Output/signal_unwind.pass.cpp.dir/t.tmp.exe
--
Exit Code: 255

Command Output (stderr):
--
clang version 12.0.0
Target: i386-unknown-linux-gnu
Thread model: posix
InstalledDir: /repo/llvm-project/llvm/build-all-builtins/./bin
Found candidate GCC installation: /usr/lib/gcc/i686-redhat-linux/4.8.2
Found candidate GCC installation: /usr/lib/gcc/i686-redhat-linux/4.8.5
Found candidate GCC installation: /usr/lib/gcc/x86_64-redhat-linux/4.8.2
Found candidate GCC installation: /usr/lib/gcc/x86_64-redhat-linux/4.8.5
Selected GCC installation: /usr/lib/gcc/x86_64-redhat-linux/4.8.5
Candidate multilib: .;@m64
Candidate multilib: 32;@m32
Selected multilib: 32;@m32
 "/repo/llvm-project/llvm/build-all-builtins/bin/clang-12" -cc1 -triple i386-unknown-linux-gnu -emit-obj -mrelax-all --mrelax-relocations -disable-free -main-file-name signal_unwind.pass.cpp -mrelocation-model static -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu pentium4 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -v -resource-dir /repo/llvm-project/llvm/build-all-builtins/lib/clang/12.0.0 -include /repo/llvm-project/llvm/build-all-builtins/runtimes/runtimes-i386-unknown-linux-gnu-bins/libcxx/__config_site -D LIBUNWIND_NO_TIMER -I /repo/llvm-project/libunwind/include -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -D __STDC_CONSTANT_MACROS -I /repo/llvm-project/libunwind/test/../../libcxx/test/support -D _LIBCPP_DISABLE_AVAILABILITY -D _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../include/c++/4.8.5 -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../include/c++/4.8.5/x86_64-redhat-linux/32 -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../include/c++/4.8.5/backward -internal-isystem /usr/local/include -internal-isystem /repo/llvm-project/llvm/build-all-builtins/lib/clang/12.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Werror -Wall -Wextra -Wshadow -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-c++11-extensions -Wno-user-defined-literals -Wno-noexcept-type -Wno-atomic-alignment -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Werror=thread-safety -Wuser-defined-warnings -std=c++2a -fdeprecated-macro -fdebug-compilation-dir /repo/llvm-project/llvm/build-all-builtins/runtimes/runtimes-i386-unknown-linux-gnu-bins/libunwind/test -ferror-limit 19 -fcoroutines-ts -fgnuc-version=4.2.1 -fno-implicit-modules -fcxx-exceptions -fexceptions -faddrsig -o /tmp/signal_unwind-58cfdb.o -x c++ /repo/llvm-project/libunwind/test/signal_unwind.pass.cpp
clang -cc1 version 12.0.0 based upon LLVM 12.0.0git default target x86_64-unknown-linux-gnu
ignoring nonexistent directory "/include"
#include "..." search starts here:
#include <...> search starts here:
 /repo/llvm-project/libunwind/include
 /repo/llvm-project/libunwind/test/../../libcxx/test/support
 /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../include/c++/4.8.5
 /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../include/c++/4.8.5/x86_64-redhat-linux/32
 /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../include/c++/4.8.5/backward
 /usr/local/include
 /repo/llvm-project/llvm/build-all-builtins/lib/clang/12.0.0/include
 /usr/include
End of search list.
 "/bin/ld" --hash-style=gnu --eh-frame-hdr -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o /repo/llvm-project/llvm/build-all-builtins/runtimes/runtimes-i386-unknown-linux-gnu-bins/libunwind/test/Output/signal_unwind.pass.cpp.dir/t.tmp.exe /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/32/crtbegin.o -L/repo/llvm-project/llvm/build-all-builtins/./lib/i386-unknown-linux-gnu/c++ -L/repo/llvm-project/llvm/build-all-builtins/bin/../lib/i386-unknown-linux-gnu/c++ -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/32 -L/repo/llvm-project/llvm/build-all-builtins/bin/../lib/i386-unknown-linux-gnu -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../.. -L/repo/llvm-project/llvm/build-all-builtins/bin/../lib -L/lib -L/usr/lib /tmp/signal_unwind-58cfdb.o --export-dynamic -rpath /repo/llvm-project/llvm/build-all-builtins/./lib/i386-unknown-linux-gnu/c++ -lm -lpthread -lc -lunwind -ldl -lgcc /usr/lib/gcc/x86_64-redhat-linux/4.8.5/32/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../crtn.o

--

********************
********************
Failed Tests (2):
  libunwind :: signal_unwind.pass.cpp
  libunwind :: unwind_leaffunction.pass.cpp

Here is a trace from executing the signal_unwind.pass test with some LIBUNWIND_PRINT_ macros defines:

libunwind: __unw_init_local(cursor=0xffa582c0, context=0xffa58280)
parseInstructions(instructions=0xf750848c)
DW_CFA_def_cfa(reg=4, offset=4)
DW_CFA_offset(reg=8, offset=-4)
DW_CFA_nop
DW_CFA_nop
parseInstructions(instructions=0xf750a5bc)
DW_CFA_advance_loc: new offset=1
DW_CFA_def_cfa_offset(8)
DW_CFA_advance_loc: new offset=2
DW_CFA_def_cfa_offset(12)
DW_CFA_advance_loc: new offset=3
DW_CFA_def_cfa_offset(16)
DW_CFA_advance_loc: new offset=4
DW_CFA_def_cfa_offset(20)
DW_CFA_advance_loc: new offset=10
DW_CFA_def_cfa_offset(784)
DW_CFA_offset(reg=6, offset=-20)
DW_CFA_offset(reg=7, offset=-16)
DW_CFA_offset(reg=3, offset=-12)
DW_CFA_offset(reg=5, offset=-8)
DW_CFA_advance_loc: new offset=15
DW_CFA_def_cfa_offset(788)
DW_CFA_advance_loc: new offset=16
DW_CFA_def_cfa_offset(784)
DW_CFA_advance_loc: new offset=25
DW_CFA_def_cfa_offset(796)
DW_CFA_advance_loc: new offset=30
DW_CFA_def_cfa_offset(800)
DW_CFA_advance_loc: new offset=38
libunwind: _Unwind_Backtrace(callback=0x8048840)
libunwind: __unw_step(cursor=0xffa582c0)
parseInstructions(instructions=0xf750848c)
DW_CFA_def_cfa(reg=4, offset=4)
DW_CFA_offset(reg=8, offset=-4)
DW_CFA_nop
DW_CFA_nop
parseInstructions(instructions=0xf750a5bc)
DW_CFA_advance_loc: new offset=1
DW_CFA_def_cfa_offset(8)
DW_CFA_advance_loc: new offset=2
DW_CFA_def_cfa_offset(12)
DW_CFA_advance_loc: new offset=3
DW_CFA_def_cfa_offset(16)
DW_CFA_advance_loc: new offset=4
DW_CFA_def_cfa_offset(20)
DW_CFA_advance_loc: new offset=10
DW_CFA_def_cfa_offset(784)
DW_CFA_offset(reg=6, offset=-20)
DW_CFA_offset(reg=7, offset=-16)
DW_CFA_offset(reg=3, offset=-12)
DW_CFA_offset(reg=5, offset=-8)
DW_CFA_advance_loc: new offset=15
DW_CFA_def_cfa_offset(788)
DW_CFA_advance_loc: new offset=16
DW_CFA_def_cfa_offset(784)
DW_CFA_advance_loc: new offset=25
DW_CFA_def_cfa_offset(796)
DW_CFA_advance_loc: new offset=30
DW_CFA_def_cfa_offset(800)
DW_CFA_advance_loc: new offset=38
parseInstructions(instructions=0x8048b34)
DW_CFA_def_cfa(reg=4, offset=4)
DW_CFA_offset(reg=8, offset=-4)
DW_CFA_nop
DW_CFA_nop
parseInstructions(instructions=0x8048b98)
DW_CFA_advance_loc: new offset=1
DW_CFA_def_cfa_offset(8)
DW_CFA_offset(reg=5, offset=-8)
DW_CFA_advance_loc: new offset=3
DW_CFA_def_cfa_register(5)
DW_CFA_nop
DW_CFA_nop
DW_CFA_nop
libunwind: __unw_get_proc_name(cursor=0xffa582c0, &buf=0xffa5833c, bufLen=512)
libunwind: __unw_get_proc_info(cursor=0xffa582c0, &info=0xffa58258)
libunwind:  _backtrace: start_ip=0x8048910, func=_Z14signal_handleri, lsda=0x0, context=0xffa582c0
libunwind: __unw_get_reg(cursor=0xffa582c0, regNum=-1, &value=0xffa581e0)
libunwind: _Unwind_GetIP(context=0xffa582c0) => 0x8048937
libunwind: __unw_step(cursor=0xffa582c0)
parseInstructions(instructions=0x8048b34)
DW_CFA_def_cfa(reg=4, offset=4)
DW_CFA_offset(reg=8, offset=-4)
DW_CFA_nop
DW_CFA_nop
parseInstructions(instructions=0x8048b98)
DW_CFA_advance_loc: new offset=1
DW_CFA_def_cfa_offset(8)
DW_CFA_offset(reg=5, offset=-8)
DW_CFA_advance_loc: new offset=3
DW_CFA_def_cfa_register(5)
DW_CFA_nop
DW_CFA_nop
DW_CFA_nop
libunwind:  _backtrace: ended because cursor reached bottom of stack, returning 5

So it stops directly after the top frame.
If I look at the backtrace in gdb I get the full backtrace all the way to main, so I guess it has to be libunwind that fails to unwind and not the debug info in the binary that is wrong (or is gdb doing this some other way?).

bjope added a subscriber: uabelho.Sep 17 2020, 5:46 AM

We're having the same issue on our buildbot:
http://lab.llvm.org:8011/builders/llvm-clang-win-x-aarch64/builds/3013

We're cross-compiling libunwind for AArch64 Linux.

Could you please fix it or revert the patch? @danielkiss

I think these libunwind tests are relying on the signal trampoline having "good enough" unwind info, but AFAIK, that's not the case with the signal trampoline (__kernel_rt_sigreturn) in the AArch64 Linux vDSO. There was a commit recently adjusting .cfi_startproc to let the unwinder find the vDSO's FDE, but then this was quickly reverted. The FDE change allowed libgcc to find the FDE, and that broke glibc pthread cancellation somehow, so the FDE was then removed:

Sometimes the trampoline comes from libc rather than the kernel, though. I know that Bionic only uses the trampoline on AArch64, but I don't know about other libcs.

Apparently, libgcc and gdb, on the other hand, can handle a signal trampoline by examining the instruction(s) that the target PC points at and matching it against the instruction(s) that a signal trampoline is expected to have. They then do an ad hoc unwind of the frame. Apparently libgcc will segfault if the PC to lookup points at non-readable memory.

I think libgcc may have this ad hoc signal frame handling for x86 as well, so if the x86 glibc/vdso trampolines lack FDEs, then that could explain this test's failure on x86.

Aside: this test is assuming Unix -- do we need to disable it for e.g. Windows?

I suspect this change should be temporarily reverted.

Sorry for the trouble for now the patch is reverted.
I you agre I will resubmit it with ; REQUIRES: x86_64-linux and then figure out the rest of the targets later.

Thanks,
Daniel

bjope added a comment.Sep 18 2020, 4:17 AM

Sorry for the trouble for now the patch is reverted.
I you agre I will resubmit it with ; REQUIRES: x86_64-linux and then figure out the rest of the targets later.

Thanks,
Daniel

Thanks for the revert.
Your proposal for a way forward is fine with me. (Assuming that it's just the new test cases that fail for some targets. But that would be true even without the patch so it is not that something get worse for those targets when applying the actual code changes.)

ldionne added inline comments.
libunwind/test/lit.site.cfg.in
49 ↗(On Diff #292356)

Adding this flag seems to break testing on OS X, where the linker doesn't understand --export-dynamic.

danielkiss added inline comments.Oct 26 2020, 5:40 PM
libunwind/test/lit.site.cfg.in
49 ↗(On Diff #292356)

Fixing it in D90202