Printing the stack trace is a common debugging technique, especially useful when a system encounters an exception. By capturing the stack at the time of the anomaly, it becomes much easier to pinpoint errors.
This article will focus on using backtrace for stack printing. More advanced techniques, such as analyzing core files with gdb, may be explored in future discussions.
Backtrace Stack Printing Method
To enable backtrace functionality, ensure the folllowing compilation flags are used:
CFLAGS ?= -O0 -rdynamic -g -funwind-tables -ffunction-sections
ASFLAGS += -O0
Code Implementation
Below is an example implementation that captures and prints the stack trace upon receiving specific signals.
#include <signal.h> /* For signal handling */
#include <execinfo.h> /* For backtrace() */
#define TRACE_BUFFER_SIZE 32
void print_stack_trace(void) {
int i, count;
void *trace_buffer[TRACE_BUFFER_SIZE];
char **symbols;
printf("Capturing stack trace...\n");
count = backtrace(trace_buffer, TRACE_BUFFER_SIZE);
printf("backtrace() captured %d addresses\n", count);
symbols = backtrace_symbols(trace_buffer, count);
if (symbols == NULL) {
perror("backtrace_symbols");
return;
}
for (i = 0; i < count; i++) {
printf("[%02d] %s\n", i, symbols[i]);
}
free(symbols);
}
void handle_signal(int signum) {
printf("\n=========>>> Caught signal %d <<<=========\n", signum);
printf("Dumping stack trace...\n");
print_stack_trace();
printf("Stack trace dumped.\n");
// Restore default signal handler and re-raise the signal
signal(signum, SIG_DFL);
raise(signum);
printf("Application exiting...\n");
}
int main(void) {
signal(SIGABRT, handle_signal);
signal(SIGBUS, handle_signal);
signal(SIGSEGV, handle_signal);
return 0;
}
Execution Result
When the program receives a segmentation fault (signal 11), the output might look like this:
=========>>> Caught signal 11 <<<=========
Dumping stack trace...
Capturing stack trace...
backtrace() captured 7 addresses
[00] ./app-demo() [0xbf6e]
[01] ./app-demo() [0xc086]
[02] [0x2aac6000]
[03] /lib/libc.so.6(memcpy+0x30) [0x2ab516a0]
[04] ./app-demo() [0xbb62]
[05] ./app-demo() [0x9e92]
[06] /lib/libpthread.so.0(+0x61d0) [0x2aacd1d0]
Stack trace dumped.
Application exiting...
Segmentation fault
Analyzing the Output
To map the memory addresses from the stack trace to their respective source code locations, use the addr2line tool:
# addr2line -e app/app-demo 0xbb62
/home/cql/smb/fuxi_h/demo-linux-wjc-20201211/app/main.c:538
This reveals that the issue originates from line 538 of main.c.
Signal Reference
Below is a reference table for some commonly encountered signals in Linux:
| Signal | Number | Description |
|---|---|---|
| SIGHUP | 1 | Terminal hangup or control process termination |
| SIGINT | 2 | Keyboard interrupt (e.g., break key) |
| SIGQUIT | 3 | Keyboard quit key pressed |
| SIGILL | 4 | Illegal instruction |
| SIGABRT | 6 | Abort signal from abort(3) |
| SIGKILL | 9 | Ultimate kill signal |
| SIGSEGV | 11 | Invalid memory reference |
| SIGPIPE | 13 | Broken pipe |
| SIGTERM | 15 | Termination signal |
| SIGUSR1 | 30,10,16 | User-defined signal 1 |
| SIGUSR2 | 31,12,17 | User-defined signal 2 |