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