ldc
ldc copied to clipboard
Debug symbols not shown in stacktrace on Linux/x64
An assertion failure, e.g. forced by the following main4.d application:
int fun(int x){ assert(x<0); return x+x;}
void main(){int x=fun(10);}
results in a stack trace without symbols:
[email protected](3): Assertion failure
----------------
./main4() [0x401ddc]
./main4() [0x401e02]
./main4() [0x40f194]
./main4() [0x40f069]
./main4() [0x40f105]
./main4() [0x40f069]
./main4() [0x40efc8]
./main4() [0x401f28]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7f66b960edc5]
Application is compiled with -g.
This bug was uncovered by the fix for issue #115.
A quick note: the output ./main4() [0x401ddc]... is what returned by a call to backtrace_symbols() in DefaultTraceInfo.opApply() in core.runtime. The symbols are in the executable main4, but for some reason backtrace_symbols doesn't extract them, except for the one in /lib64/libc.so.6. It would help to know how backtrace_symbols finds a symbol given an address.
I found the reason for the problem. It's the missing -rdynamic command line option when linking the executable.
charlie@fusion:~$ /usr/bin/gcc app.o -o app -rdynamic -L/opt/ldc/lib -lphobos2-ldc-debug -ldruntime-ldc-debug -lrt -Wl,--gc-sections -ldl -lpthread -lm -m64
charlie@fusion:~$ ./app
[email protected](2): Assertion failure
----------------
./app(int app.fun(int)+0x2c) [0x4c6a9c]
./app(_Dmain+0x12) [0x4c6ac2]
./app(_D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ6runAllMFZ9__lambda1MFZv+0x24) [0x568164]
./app(void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).tryExec(scope void delegate())+0x29) [0x568039]
./app(void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll()+0x45) [0x5680d5]
./app(void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).tryExec(scope void delegate())+0x29) [0x568039]
./app(_d_run_main+0x448) [0x567f98]
./app(main+0x28) [0x4c6be8]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7f28046ffec5]
I'm not sure what's the best way to fix this. Should -rdynamic always be in the command line on Linux targets, or only when -g is also present?
Please, review the fix in PR https://github.com/ldc-developers/ldc/pull/864
I moved the commit that resolved this issue to a separate branch in my fork. I do not want to make a PR against master branch because the change breaks a regression test on Linux, which is caused by a bug in the front-end. The front-end bug appears to be fixed on merge-2.067 branch. If David @klickverbot and Kai @redstar are ok with that, I'll make a PR against merge-2.067 branch and the issue can be closed. Please, advise.
backtrace_symbols() apparently relies on the dynamic symbols table and so requires -L-export-dynamic (implicit for DMD) for all function names to end up in there, which may increase the binary size considerably. IIRC, we agreed not to export them by default.
More recent backtrace code uses DWARF lineinfo data to display file/line infos too (restricted to frames in the executable itself, no other DSOs), so -g in the meantime provides some infos in case -L-export-dynamic is missing.
Trying to derive the function names from the DWARF debuginfos would be a nice-to-have, as well as using all DSOs' debuginfos, not just the executable's.
Debian 4.9.65-3+deb9u2 (2018-01-04), 1.10.0-beta1.
ldmd2 ./test.d -g -debug
/test │·
object.Exception@./test.d(5): 123 │·
---------------- │·
??:? [0x555cef7ca56e] │·
??:? [0x555cef7cce7a] │·
??:? [0x555cef7bc50d] │·
test.d:5 [0x555cef7b733d] │·
??:? [0x555cef7bc18f] │·
??:? [0x555cef7bc085] │·
__entrypoint.d:8 [0x555cef7b7424] │·
??:? __libc_start_main [0x7fc491f1c2e0] │·
??:? [0x555cef7b7189]
Debian 10 4.15.17-1 (2018-04-19) has the same result
Everything as expected - use -link-defaultlib-debug to get file/line infos for druntime in case you're wondering about that.
I think it should show symbal name, here is what it print with -link-defaultlib-debug
ldmd2 ./test.d -g -debug -link-defaultlib-debug │················································
object.Exception@./test.d(5): 123 │················································
---------------- │················································
runtime.d:787 [0x56259025a878] │················································
runtime.d:1056 [0x56259025a1b8] │················································
dmain2.d:296 [0x56259023f7c0] │················································
deh.d:24 [0x56259025f34e] │················································
dwarfeh.d:321 [0x562590240352] │················································
test.d:5 [0x5625902356bd] │················································
dmain2.d:516 [0x56259023fe7c] │················································
dmain2.d:477 [0x56259023fc59] │················································
dmain2.d:516 [0x56259023fd7a] │················································
dmain2.d:477 [0x56259023fc59] │················································
dmain2.d:536 [0x56259023fb90] │················································
__entrypoint.d:8 [0x5625902357a4] │················································
??:? __libc_start_main [0x7ff8cc58a2e0] │················································
??:? [0x562590235509]
the data for symbal format like this: ./app(+0x14cdc) [0x55902354dcec]
I think it should show symbal name
As stated above, use -L-export-dynamic for that.
And of course -g for file and lineno from the user program:
james@ubuntu18:~$ ldc2 -link-defaultlib-debug -L-export-dynamic test.d
james@ubuntu18:~$ ./test
[email protected](3): Assertion failure
----------------
exception.d:429 onAssertError [0x562bcbe15f0b]
exception.d:595 _d_assert [0x562bcbe16484]
??:? void test.f() [0x562bcbe15713]
??:? _Dmain [0x562bcbe15728]
james@ubuntu18:~$ ldc2 -g -link-defaultlib-debug -L-export-dynamic test.d
james@ubuntu18:~$ ./test
[email protected](3): Assertion failure
----------------
exception.d:429 onAssertError [0x55e293219f0b]
exception.d:595 _d_assert [0x55e29321a484]
test.d:3 void test.f() [0x55e293219713]
test.d:8 _Dmain [0x55e293219728]
which may increase the binary size considerably. IIRC, we agreed not to export them by default.
Is this still verboten? The mangled names were changed since this decision so they are not so huge (to use backreferences).
Just ran into this recently, and it's a bit tough to have all my dub projects now do:
lflags "-export-dynamic" platform="ldc"
The problem isn't the size of the mangled names, but that -L--export-dynamic defeats linker stripping via --gc-sections - even if a symbol is unused in the binary, it is still exported (if it has public visibility) and not stripped. With LDC v1.28, a Phobos hello-world on Linux x64 (linked against static druntime & Phobos) is 1.2 MB; with -L--export-dynamic, it explodes to 6.7 MB.
OK, so is there a better way to do this?
Trying to derive the function names from the DWARF debuginfos
Is that a viable alternative? I'm faced with a project that I used to compile with dmd on x86, but my system is now ARM, so I have to use ldc, and this just jumped out immediately as a big difference.
It would presumably take quite an effort to get there [in druntime exclusively]. But the druntime backtrace code is currently limited to DWARF in the executable only (no shared .so libs), so there's vast room for improvements in that druntime department anyway.
[Note that if you just want to unconditionally add -L--export-dynamic, you can do that once in etc/ldc2.conf.]