Signal backtrace function
Joakim Tjernlund
joakim.tjernlund at transmode.se
Thu Apr 10 04:16:20 EST 2008
Hi
I made my own backtrace function for printing
a trace from within a signal handler. Maybe it
can be useful for the kernel too? General
comments welcome.
Jocke
#include <stdio.h>
#include <signal.h>
#include <execinfo.h>
#include <unistd.h>
#define __USE_GNU
#include <ucontext.h>
/* This is the stack layout we see with every stack frame.
Note that every routine is required by the ABI to lay out the stack
like this.
+----------------+ +-----------------+
%r1 -> | %r1 last frame--------> | %r1 last frame--->... --> NULL
| | | |
| (unused) | | return address |
+----------------+ +-----------------+
*/
/* Prints a backtrace when invoked from a signal handler */
int
sigbacktrace (void **array, int size, void *sp,
void *lr, void *exec_addr)
{
int count=1, loop;
void *ret_addr;
struct layout {
struct layout * next;
void * return_address;
} *current;
if (!size)
return size;
current = sp;
array[0] = exec_addr;
for (loop = 0;
current != NULL && count < size;
current = current->next, loop++) {
ret_addr = current->return_address;
#if DEBUG
printf("next:%p, ret:%p, LR:%p\n", current->next, ret_addr, lr);
#endif
if (lr == ret_addr) /* false frame ? */
continue;
if (!loop)
if (lr != ret_addr) /* Leaf function ? */
ret_addr = lr;
else
continue;
array[count++] = ret_addr;
}
/* It's possible the second-last stack frame can't return in which
case the CRT startup code will have set its LR to 'NULL'. */
if (count > 0 && array[count-1] == NULL)
count--;
return count;
}
#define EXEC_ADDR (*regs)[32]
#define STK_PTR (*regs)[1]
#define LINK (*regs)[36]
void bt_sighandler(int sig, siginfo_t *info,
void *secret)
{
void *trace[16];
char **messages = (char **)NULL;
int i, trace_size = 0;
ucontext_t *uc = (ucontext_t *)secret;
gregset_t *regs = &uc->uc_mcontext.uc_regs->gregs;
/* Do something useful with siginfo_t */
if (sig == SIGSEGV || sig == SIGBUS)
printf("Got signal %d, faulty address is %p, "
"from %p\n", sig, info->si_addr,
EXEC_ADDR);
else
printf("Got signal %d\n", sig);
trace_size = sigbacktrace(trace, 16, (void*) STK_PTR,
(void*) LINK, (void*) EXEC_ADDR);
messages = backtrace_symbols(trace, trace_size);
printf("[bt] Execution path:\n");
for (i=0; i<trace_size; ++i)
printf("[bt] %s\n", messages[i]);
if (sig != SIGUSR1)
exit(0);
}
void dummy(void)
{
unsigned long *current;
asm volatile ("" : "=l"(current));
}
int func_a(int a, char b) {
char *p = (char *)0xdeadbeef;
//dummy(); /* Test with and without this call */
a = a + b;
*p = 10; /* CRASH here!! */
dummy(); /* Test with and without this call */
return 2*a;
}
int func_b() {
int res, a = 5;
res = 5 + func_a(a, 't');
return res;
}
int main() {
/* Install our signal handler */
struct sigaction sa;
sa.sa_sigaction = (void *)bt_sighandler;
sigemptyset (&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGBUS, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
/* ... add any other signal here */
/* Do something */
printf("%d\n", func_b());
}
More information about the Linuxppc-dev
mailing list