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:
The backtrace library is compatible with QNX Neutrino 6.3.0 SP2 or later.
Note the following:
The following notes apply only to PowerPC targets:
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. |
The libbacktrace library defines the following data types and variables:
Don't call bt_init_accessor() or bt_release_accessor() for this global variable. |
The library also defines the following functions:
In general, here's how you use these functions:
To keep the samples short, these examples don't handle any errors. |
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:
(16+strlen(filename))*num_files
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. |
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 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);
To backtrace the currently running process and thread, you can do one of the following:
bt_init_accessor(&acc, BT_SELF);
You can also use a preinitialized accessor called bt_acc_self.
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); ... }
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();