for connected embedded systems
![]() |
![]() |
![]() |
Backtraces
Overview
The libbacktrace library gives you a way to programmatically backtrace a running process from within itself. You can use backtracing for debugging, as well as for diagnostics or logging. Most of the time, you should use gdb for debugging.
The backtrace library lets you:
- backtrace the calling thread
- backtrace a thread within the same process
- backtrace a thread in another process
- backtrace C code
- backtrace C++ code
The backtrace library is compatible with QNX Neutrino 6.3.0 SP2 or later.
Note the following:
- Backtracing is a best effort, and may at times be inaccurate due to the nature of backtracing (e.g. optimized code can confuse the backtracer).
- You can't currently backtrace a thread on a remote node (i.e. over Qnet).
- Backtracing a corrupt stack could cause a fatal SIGSEGV because libbacktrace doesn't trap SIGSEGV.
The following notes apply only to PowerPC targets:
- Backtrace's second entry on PowerPC (PPC) isn't guaranteed to be accurate because of how the PPC handles the lr (link register).
- In the specific case of backtracing BT_SELF, the second entry of a backtrace will be accurate.
![]() |
The backtrace library is an unsupported feature, due to its fragility.
The functionality it offers is available via gdb in a safer
and more stable way; gdb uses debugging information to unwind,
while backtrace uses guessing — it decodes
instructions/stack/registers to figure out the return
address, and this is prone to errors.
GDB uses such guessing only as a fallback when symbols aren't available.
Due to multiple gcc versions that can be used, and in each of them probable different optimizations, backtrace accuracy is directly affected; it is almost certain the backtrace won't work in many cases. |
API
The libbacktrace library defines the following data types and variables:
- bt_accessor_t
- An opaque structure that holds the identity of the thread to backtrace.
- bt_memmap_t
- An opaque structure that holds the memory map of a given process. A memory map is made of a list of all of the object files (executable and shared libraries) in process memory, and its text segments' location and size.
- bt_accessor_t bt_acc_self
- A preinitialized accessor used to backtrace BT_SELF.

Don't call bt_init_accessor() or bt_release_accessor() for this global variable.
The library also defines the following functions:
- bt_get_backtrace()
- Collect a backtrace
- bt_init_accessor()
- Initialize a backtrace accessor
- bt_load_memmap()
- Load a memory map associated with a backtrace
- bt_release_accessor()
- Release an accessor for a backtrace
- bt_set_flags()
- Set or clear the flags for backtracing
- bt_sprn_memmap()
- Format the memory map information for a backtrace
- bt_sprnf_addrs()
- Format the addresses from a backtrace
- bt_translate_addrs()
- Translate the addresses from a backtrace
- bt_unload_memmap()
- Unload a memory map associated with a backtrace
In general, here's how you use these functions:
- Call bt_init_accessor() to set up the backtrace.
- Optionally call bt_set_flags() if you want to do a live backtrace. By default, bt_get_backtrace() freezes a thread before gathering the backtrace.
- Call bt_get_backtrace() to collect the backtrace addresses.
- Optionally load the memory map for the process by calling bt_load_memmap(). You need to do this if you want to format the information in certain ways.
- Optionally call bt_sprn_memmap() to produce a string of the memory map's contents.
- Call bt_sprnf_addrs() or bt_translate_addrs() to format the backtrace addresses.
- Call bt_unload_memmap() to unload the memory map.
- Call bt_release_accessor() to release the accessor.
Examples
- Obtaining and printing a memory map
- Backtracing a thread in another process
- Backtracing another thread within the same process
- Backtracing the current thread
- Doing a BT_SELF backtrace in a signal handler
- Backtracing a collection of threads
![]() |
To keep the samples short, these examples don't handle any errors. |
Obtaining and printing a memory map
Use the following sample code segment to obtain, and then print the contents of a memory map:
char out[1024]; bt_accessor_t acc; bt_memmap_t memmap; bt_init_accessor(&acc, BT_SELF); bt_load_memmap(&acc, &memmap); bt_sprn_memmap(&memmap, out, sizeof(out)); puts(out); bt_release_accessor(&acc);
Additional notes about memory:
- The formula for calculating memory used by a memory map is roughly
the following:
(16+strlen(filename))*num_files
- Some memory is temporarily allocated while the memory map is read. For example, an executable with three shared libraries loaded (assuming an average file name length of 40 characters, and excluding overhead from malloc()), would consume 224 bytes.
![]() |
There are no explicit links between the memory map and a backtrace. Consequently, you're responsible for ensuring that the memory map is reread to account for the proper handling of the removal of the dlopen() and dlclose() processes, as well as the recycling of process IDs. |
Backtracing a thread in another process
Use the following sample code segment to repeatedly backtrace a thread in another process by using the remote process's pid and thread tid:
char out[1024];
bt_addr_t pc[16];
bt_accessor_t acc;
bt_init_accessor(&acc, BT_PROCESS, remotepid, remotetid);
bt_load_memmap(&acc, &memmap);
bt_sprn_memmap(&acc, out, sizeof(out));
for (i=0; i<10; i++) {
cnt=bt_get_backtrace(&acc, pc, sizeof(pc)/sizeof(bt_addr_t));
bt_sprnf_addrs(&memmap, pc, cnt, "%a\n", out, sizeof(out), 0);
puts(out);
}
bt_unload_memmap(&memmap);
bt_release_accessor(&acc);
Backtracing another thread within the same process
Backtracing a different thread in the current process is similar to backtracing a thread in a different process (see above). The only difference is how you initialize the accessor structure. The flags passed to bt_init_accessor() define which process to backtrace. If you specify BT_THREAD, a different thread within the same process is set up for backtracing:
bt_init_accessor(&acc, BT_THREAD, tid);
To debug a thread within a different process, set the BT_PROCESS flag and supply the pid and tid to be backtraced:
bt_init_accessor(&acc, BT_PROCESS, getpid(), tid);
Backtracing the current thread
To backtrace the currently running process and thread, you can do one of the following:
- Use the BT_PROCESS accessor flag, as described in “Backtracing a thread in another process,” but specify the current pid and tid.
- Use the BT_THREAD accessor flag and select the currently running thread.
- Use the BT_SELF accessor flag.
This is the best way to insure that the currently active thread is
backtraced:
bt_init_accessor(&acc, BT_SELF);
You can also use a preinitialized accessor called bt_acc_self.
Doing a BT_SELF backtrace in a signal handler
Providing backtracing that's signal-handler-safe is a special case. From within a signal handler, you can use only bt_get_backtrace(), and you must ensure access to memory is without conflict. For example:
bt_accessor_t acc_sighandler1;
bt_addr_t pc_sighandler1[10];
int cnt_sighandler1;
void sighandler1(int sig)
{
cnt_sighandler1 =
bt_get_backtrace(&acc_sighandler1, pc_sighandler1,
sizeof(pc_sighandler1)/sizeof(bt_addr_t));
}
thread_func()
{
char out[512];
bt_init_accessor(&acc_sighandler1, BT_SELF);
signal(SIGUSR2, sighandler1);
...
bt_sprnf_addrs(&memmap, pc_sighandler1, cnt_sighandler1, "%a",
out, sizeof(out), 0);
...
bt_release_accessor(&acc);
...
}
Backtracing a collection of threads
Occasionally, it may be useful to backtrace a collection of threads in a coherent manner. You can accomplish this by freezing all the threads, backtracing the threads, and then unfreezing them. For example:
bt_accessor_t acc;
hold_all_threads();
for (i=0; i < max_thread; i++) {
bt_init_accessor(&acc, BT_PROCESS, pid, i);
bt_set_flags(acc, BT_LIVE_BACKTRACE, 1);
bt_get_backtrace(&acc, addrs, len);
bt_release_accessor(&acc);
save_addrs(addrs);
}
cont_all_threads();
![]() |
![]() |
![]() |

![[Previous]](prev.gif)
![[Contents]](contents.gif)
![[Next]](next.gif)