This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Re: why no stacktrace?
- To: "Martin v. Loewis" <martin at loewis dot home dot cs dot tu-berlin dot de>
- Subject: Re: why no stacktrace?
- From: Ulrich Lauther <ulrich dot lauther at mchp dot siemens dot de>
- Date: Wed, 5 Jan 2000 10:29:17 +0100 (MET)
- CC: gcc at gcc dot gnu dot org
- Reply-To: Ulrich dot Lauther at mchp dot siemens dot de
> > 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