Walking the stack yourself is pretty quick - most of the slowness in backtrace()
is from looking up symbol names. On x86, you can do the following:
inline uint32_t get_ebp(void)
{
__asm__ __volatile__("mov %%ebp, %%eax");
}
int get_stack_depth(void)
{
uint32_t ebp = get_ebp();
int stack_depth = 0;
while(ebp != 0)
{
ebp = *(uint32_t *)ebp;
stack_depth++;
}
return stack_depth;
}
This will walk the chain of ebp
pointers. Keep in mind that this is extremely non-portable. Also note that this will not count any functions which have been inlined or tail-call optimized (of course, backtrace()
has the same problem).
Another important issue is the termination condition -- once you backtrace up to main()
, there often aren't guarantees about what you'll find in the stack. So, if libc doesn't put a null frame pointer, you'll very likely segfault. You can get the termination value by looking at it at the very beginning of main()
.