Stack Backtracing Inside Your Program

How to use a backtrace to follow the execution path and find out what went wrong and where.

If you usually work with non-trivial C
sources, you may have wondered which execution path (that is, which
sequence of function calls) brought you to a certain point in your
program. Also, it would be even more useful if you could have that
piece of information whenever your beautiful, bug-free program
suddenly crashes, and you have no debugger at hand. What is needed
is a stack backtrace and, thanks to a little
known feature of the GNU C library, obtaining it is a fairly easy
task.

Stack Frames and Backtraces

Before diving into the article, let's briefly go over how
function calls and parameters pass work in C. In order to prepare
for the function call, parameters are pushed on the stack in
reverse order. Afterwards, the caller's return address also is
pushed on the stack and the function is called. Finally, the called
function's entry code creates some more space on the stack for
storage of automatic variables. This layout commonly is called a
stack frame for that particular instance of the function call. When
more function calls are nested, the whole procedure is repeated,
causing the stack to keep growing downwards and building a chain of
stack frames (see Figure 1). Thus, at any given point in a program
it theoretically is possible to backtrace the sequence of stack
frames to the originating calling point, up to the main() function
(to be exact, up to the libc function, which calls main() when the
process starts up).

Figure 1. Nested Function Calls

Stack Backtracing from within GDB

Getting the stack backtrace with GDB (or an equivalent
graphical front end) for a program that crashed while running is
straightforward: you simply issue the bt command, which returns the
list of functions called up to the point of the crash. As this is a
standard practice, we do not provide any more details here; have a
look at the GDB info page if you need specifics (info gdb
stack gets you there).

Stack Backtracing Using libc

If for some reason you're not running inside a debugger, two
options are available for tracing what the program is doing. The
first method is to disseminate it with print and log messages in
order to pinpoint the execution path. In a complex program, this
option can become cumbersome and tedious even if, with the help of
some GCC-specific macros, it can be simplified a bit. Consider, for
example, a debug macro such as

You can propagate this macro quickly throughout your program
by cutting and pasting it. When you do not need it anymore, switch
it off simply by defining it to no-op.

A nicer way to get a stack backtrace, however, is to use some
of the specific support functions provided by glibc. The key one is
backtrace(), which navigates the stack frames from the calling
point to the beginning of the program and provides an array of
return addresses. You then can map each address to the body of a
particular function in your code by having a look at the object
file with the nm command. Or, you can do it a simpler way--use
backtrace_symbols(). This function transforms a list of return
addresses, as returned by backtrace(), into a list of strings, each
containing the function name offset within the function and the
return address. The list of strings is allocated from your heap
space (as if you called malloc()), so you should free() it as soon
as you are done with it.

If you prefer to avoid dynamic memory allocation during the
backtrace--reasonable, as the backtrace is likely to happen under
faulty conditions--you can resort to backtrace_symbols_fd(). This
prints the strings directly to the given file descriptor and does
not allocate new memory for strings storage. It is a safer choice
in those cases where memory heap potentially is corrupted.

In order to convert an address to a function name, the last
two functions rely on symbol information to be available inside the
program itself. To enable this feature, compile your program with
the -rdynamic option (see man dlopen for more details).

Listing 1 demonstrates how to use these functions. The test()
function calls either func_low() or func_high(), both of which call
show_stackframe() to print out the execution path. The program is
compiled with

I'm trying to get this method to work on ARM running embedded linux (running glibc-2.3.2... I am trying to get the PC/EIP from typecasting the 3rd argument of the signal handler as ucontext_t and finding the appropriate arch-dependent register. However, in ARM none of the registers seem to contain anything that looks like the program counter (not even R15, which should actually be the PC). So, I'm unable to find out where the exact crash happened. Any thoughts/ideas?

I appriciate the great work that people have put up on this page.I am having a serious problem with getting the call stack information on ARM. I have tried same kind of code, which works fine for linux on x386 with -rdynamic option of gcc but fails on ARM. It would be great if you could help me on this.

Are there any specific options for compiling this on ARM,if YES then what is that option?.

Is there any easy way to display the rest of the local frame? I'd love to be able to get at the parameters passed. I could just dump the memory added to the stack before the address, but I don't know how far to go.

You can access the stack in C, but it is still true that some platform-specific knowledge is needed to make sense of it. Just take the address of an argument or automatic variable and work from there:
void func( int arg )
{
void *stackframe = &arg;
...
}

I'd appreciate if you could detail the stack argument access a little better. I'm written a wxCrashPrint component for the wxWidgets (wxWindows) framework (here) and would like to show arguments as well.

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.