bombela / backward-cpp

A beautiful stack trace pretty printer for C++
MIT License
3.73k stars 471 forks source link

"Incomplete" stack-trace #216

Open aytey opened 3 years ago

aytey commented 3 years ago

tl;dr: backward-cpp gives incomplete stack-traces (while gdb gives complete ones); I'm trying to understand if this is solvable.

Problem

I'm experiencing an issue where backward-cpp gives me a stack-trace like this:

Stack trace (most recent call last):
#1    Object "", at 0, in
#0    Object "/lib64/libc-2.33.so", at 0x7fd373d1e52f, in

while gdb gives me a stack-trace like this:

backtrace:
#0  0x0000000000000000 in ?? ()
#1  0x0000555555558813 in cls::mthd (this=0x7fffffffdabf) at ./unit.cpp:13
#2  0x000055555555882e in do_it () at ./unit.cpp:18
#3  0x0000555555558949 in main () at main.cpp:44

Notice: gdb has do_it and cls::mthd, while backward-cpp does not.

This is an example I have found "in the wild", but some of my stack-traces are correct (I'm saying this because I believe I am using backward-cpp correctly; e.g., using the correct defines/link flags).

Question

Is it possible to get backwards-cpp to give better (/"more correct") stack-traces here?

Example code

When I found this issue, I used https://github.com/marxin/cvise to automatically construct my "failing test-case" (if the code looks weird, maybe it is, but it is derived from valid code that displays the same behaviour; I'm actually surprised that this code links, given there's no definition of d::e).

unit.cpp

struct b {
  virtual void c() {}
};
struct d : b {
  virtual int e(int, int);
} * f;
struct cls {
  int mthd();
};
int cls::mthd() {
  b g;
  f = (d *)&g;
  f->e(1, 1);
  return 1;
}
void do_it() {
  cls a;
  a.mthd();
}

main.cpp

#include <unit.cpp>

#define BACKWARD_HAS_BACKTRACE 0
#define BACKWARD_HAS_BACKTRACE_SYMBOL 0
#define BACKWARD_HAS_BFD 0
#define BACKWARD_HAS_DW 1
#define BACKWARD_HAS_DWARF 0
#define BACKWARD_HAS_LIBUNWIND 0
#define BACKWARD_HAS_UNWIND 1
#include <backward.hpp>

#ifdef USE_HANDLERS
void handler(int sig) {
  using namespace backward;
  StackTrace st;
  st.load_here(64);
  st.skip_n_firsts(3);
  Printer p;
  p.print(st);

  exit(1);
}

void setup_handlers(void) {
  signal(SIGSEGV, handler);
  signal(SIGABRT, handler);
  signal(SIGBUS, handler);
  signal(SIGILL, handler);
  signal(SIGFPE, handler);
}
#else
namespace backward {
backward::SignalHandling sh;
}
#endif

int main(void) {
#ifdef USE_HANDLERS
  setup_handlers();
#endif
  do_it();
  return 0;
}

Compilation

use_handlers=1
if [ $use_handlers -eq 1 ]; then
    defines="-DUSE_HANDLERS"
else
    defines=""
fi
g++ -g $defines -Wall -Wextra -Wpedantic -Werror -std=c++11 -I. main.cpp -ldw -ldl -lunwind -o main

Output

When using "manual" signal handlers (i.e., p.print(st);)

stderr via backward-cpp

Stack trace (most recent call last):
#1    Object "", at 0, in
#0    Object "/lib64/libc-2.33.so", at 0x7fd373d1e52f, in

gdb

backtrace:
#0  0x0000000000000000 in ?? ()
#1  0x0000555555558813 in cls::mthd (this=0x7fffffffdabf) at ./unit.cpp:13
#2  0x000055555555882e in do_it () at ./unit.cpp:18
#3  0x0000555555558949 in main () at main.cpp:44

When using "automatic" signal handlers (i.e., backward::SignalHandling sh)

stderr via backward-cpp

Stack trace (most recent call last):
#5    Object "", at 0, in
#4    Object "/lib64/libc-2.33.so", at 0x7f98f98f352f, in
#3    Source "./backward.hpp", line 4256, in sig_handler [0x559943bca0c5]
       4253: #endif
       4254:   static void
       4255:   sig_handler(int signo, siginfo_t *info, void *_ctx) {
      >4256:     handleSignal(signo, info, _ctx);
       4257:
       4258:     // try to forward the signal.
       4259:     raise(info->si_signo);
#2    Source "./backward.hpp", line 4233, in handleSignal [0x559943bca005]
       4230:       st.load_from(error_addr, 32, reinterpret_cast<void *>(uctx),
       4231:                    info->si_addr);
       4232:     } else {
      >4233:       st.load_here(32, reinterpret_cast<void *>(uctx), info->si_addr);
       4234:     }
       4235:
       4236:     Printer printer;
#1    Source "./backward.hpp", line 862, in load_here [0x559943bc738a]
        859:       return 0;
        860:     }
        861:     _stacktrace.resize(depth);
      > 862:     size_t trace_cnt = details::unwind(callback(*this), depth);
        863:     _stacktrace.resize(trace_cnt);
        864:     skip_n_firsts(0);
        865:     return size();
#0    Source "./backward.hpp", line 844, in unwind<backward::StackTraceImpl<backward::system_tag::linux_tag>::callback> [0x559943bca590]
        842: template <typename F> size_t unwind(F f, size_t depth) {
        843:   Unwinder<F> unwinder;
      > 844:   return unwinder(f, depth);
        845: }
        846:
        847: } // namespace details
Segmentation fault (Address not mapped to object [(nil)])

gdb

backtrace:
#0  0x0000000000000000 in ?? ()
#1  0x0000555555558883 in cls::mthd (this=0x7fffffffdabf) at ./unit.cpp:13
#2  0x000055555555889e in do_it () at ./unit.cpp:18
#3  0x00005555555588aa in main () at main.cpp:44

System specifics

atg@vapvdatg01:/tmp> uname -a
Linux vapvdatg01 5.11.11-1-default #1 SMP Tue Mar 30 17:57:52 UTC 2021 (dbc4a02) x86_64 x86_64 x86_64 GNU/Linux

atg@vapvdatg01:/tmp> lsb-release -a
LSB Version:    n/a
Distributor ID: openSUSE
Description:    openSUSE Tumbleweed
Release:        20210408
Codename:       n/a

atg@vapvdatg01:/tmp> /lib64/libc.so.6 --version
GNU C Library (GNU libc) release release version 2.33 (git 9826b03b74).
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Configured for x86_64-suse-linux.
Compiled by GNU CC version 10.2.1 20210303 [revision 85977f624a34eac309f9d77a58164553dfc82975].
libc ABIs: UNIQUE IFUNC ABSOLUTE
For bug reporting instructions, please see:
<http://bugs.opensuse.org>.

atg@vapvdatg01:/tmp> gcc --version
gcc (SUSE Linux) 10.2.1 20210401 [revision 892024d4af83b258801ff7484bf28f0cf1a1a999]
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Library versions

aytey commented 3 years ago

Okay, so changing:

#define BACKWARD_HAS_LIBUNWIND 0
#define BACKWARD_HAS_UNWIND 1

to

#define BACKWARD_HAS_LIBUNWIND 1
#define BACKWARD_HAS_UNWIND 0

gives me better traces:

Stack trace (most recent call last):
#3    Source "../sysdeps/x86_64/start.S", line 120, in _start
#2    Object "/lib64/libc-2.33.so", at 0x7fea802d8b24, in __libc_start_main
#1    Source "./main.cpp", line 44, in main
         41: #ifdef USE_HANDLERS
         42:   setup_handlers();
         43: #endif
      >  44:   do_it();
         45:   return 0;
         46: }
#0    Source "./unit.cpp", line 13, in mthd
         10: int cls::mthd() {
         11:   b g;
         12:   f = (d *)&g;
      >  13:   f->e(1, 1);
         14:   return 1;
         15: }
         16: void do_it() {

Interestingly, I took the defines from CMakeFiles/test_stacktrace.dir/flags.make, which uses BACKWARD_HAS_UNWIND over BACKWARD_HAS_LIBUNWIND.

bombela commented 3 years ago

gdb might be looking for the debug symbols in the various .debug directories.

https://sourceware.org/gdb/current/onlinedocs/gdb/Separate-Debug-Files.html