This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: why no stacktrace?


> > All these languages, i.e., its compilers, (even Fortran) provided a stack
> > trace when a program crashed. Not so gcc/g++. Why not?
> 
> I think this question has not been answered, yet (instead, the
> question: What can I do instead? has been answered).
> 
> I believe the reason is that it is not possible, atleast without
> sacrificing a good deal of performance, and without a major
> re-engineering.
> 
> To provide a reliable back-trace, you must have a reliable stack
> unwinder. The C ABI of most architectures does not have this built-in,
> so gcc would have to emulate that function. For example, it could have
> and alternative back-trace stack, and put a pointer to a string onto
> that stack every time a function is entered, and pop it when the
> function returns. Of course, that convention would not be honored by
> the non-gcc-compiled functions (e.g. the C library), and it would add
> noticable overhead.
> 
I am not a compiler specialist, but isn't the return address on the stack
anyway?

> This is much easier achieved in the source code:
> 
> int foo()
> {
>   TraceEnter();
>   do_something();
>   TraceReturn(4);
> }
> 
I don't see how you would have access to the line number where foo() was called,
unless you add TraceCall() before each call which would be nearly impossible
in case of overloaded operators.

> If you know a clever way to achieve all your desired functionality
> with little overhead, please let us know.
> 
well, this is how I do it (with limited portability):

class stack { // simplified layout of call stack
  long           fudge[PREV_OFF]; // PREV_OFF is platform specific
  stack*         prev;       // pointer to stack frame of caller
  unsigned long  ret;        // return address
  static stack*  main_frame; // stack frame of main()
public:
  inline void   set_main_frame() { main_frame = this; }
  inline stack* up()             { return prev; }
  void trace();                // traverse stack up to main() and print 
  unsigned long pc(int level); // return program counter some levels up
};

stack* stack::main_frame = 0;

inline static void flush_registers() {
  #if ARCHITECTURE == SPARC
  asm ("ta 3"); // flush register windows
  #endif
}

unsigned long stack::pc(int level) {
/*------
  returns programm counter at calling point (level == 0) or 
  <level> levels up in the calling sequence
*/
#if defined(__GNUC__)
  void* bra = 0;
  switch (level) {
    case 1: bra = __builtin_return_address(1); break;
    case 2: bra = __builtin_return_address(2); break;
    case 3: bra = __builtin_return_address(3); break;
    case 4: bra = __builtin_return_address(4); break;
    default: ;
  }
  return (unsigned long) bra - 1;
#else // Sun Solaris CC
  flush_registers();
  stack* tp = this;
  #if (SYSTEM == SOLARIS1 || SYSTEM == SOLARIS2)
    level--;
  #endif
  for (int i = 0; i < level; i++) {
    if (tp == main_frame) break;
    tp = tp->prev;
  }
  return tp->ret - 1;
#endif
}

void stack::trace() {
  flush_registers();
#if (SYSTEM == SOLARIS1 || SYSTEM == SOLARIS2)
  stack* start = this;
#else
  stack* start = prev;
#endif
  for (stack* tp = start; tp != main_frame; tp = tp->prev) {
#if (SYSTEM == SOLARIS1 || SYSTEM == SOLARIS2)
    if (tp->prev == main_frame) break;
#endif
    // print_line uses addr2line to translate the program counter
    // (its 1st parameter) ti function name and line number
    assertions::print_line(tp->ret,1);
  }
}

This works for Solaris gcc and CC and for Linux gcc; overhead is zero.

	-ulrich
----------------------------------------------------------------------------
Ulrich Lauther          ph: +49 89 636 48834 fx: ... 636 42284
Siemens ZT SE 4         Internet: Ulrich.Lauther@mchp.siemens.de

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]