blob: cf7b283c40b1a5586e511c70aab4fd3e974a2bbd [file] [log] [blame]
#include "stacktrace.h"
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <syscall.h>
#include <sys/types.h> // for waitpid
#include <sys/wait.h> // for waitpid
#include <unistd.h>
// We need this because strlen() isn't safe to call from a signal handler.
static int safe_strlen(const char *s) {
int i = 0;
while (s[i] != '\0')
++i;
return i;
}
#define WRITELEN(s, l) do {if (write(2, s, l)) {}} while (0)
#define WRITE(s) do {if (write(2, s, safe_strlen(s))) {}} while (0)
#define WRITEINT(i) { \
char *str = format_uint(i); \
WRITELEN(str, safe_strlen(str)); \
}
// We need this because sprintf() isn't safe to call from a signal handler.
char *format_uint(unsigned int i) {
static char str[100];
char *p = str + sizeof(str) - 1;
*(--p) = '\0';
do {
*(--p) = '0' + (i % 10);
i /= 10;
} while (i > 0);
return p;
}
static pid_t gettid(void) {
// According to 'man gettid', this function is not in libc, so you need
// to call syscall() yourself. Seems to be true.
return syscall(__NR_gettid);
}
void stacktrace(void) {
pid_t pid, trace_tid = gettid();
if ((pid = fork()) > 0) {
// For some reason, if we call waitpid() here, gdb (7.3.1) isn't able to
// get a valid stack trace. If we use syscall(__NR_waitpid), then it
// works fine.
#if defined(__MIPSEL__) || defined(_MIPSEB_)
syscall(__NR_waitpid, pid, 0);
#else
waitpid(pid, NULL, 0);
#endif
} else if (pid == 0) {
char *argv[] = {(char*)"stacktrace", format_uint(trace_tid), NULL};
execv("/usr/bin/stacktrace", argv);
} else {
int e = errno;
WRITE("ERROR: fork failed?! code=");
WRITEINT(e);
}
}
void stacktrace_sighandler(int sig) {
WRITE("\nExiting thread ");
pid_t tid = gettid();
WRITEINT(tid);
WRITE(" on signal ");
WRITEINT(sig);
WRITE("\n");
stacktrace();
/*
* We have to generate a signal *other* than the one we received, because
* signals aren't re-entrant. But we want a proper "die!" signal so that
* we can still get a core dump. Stack traces are nice, but so are cores
* sometimes.
*/
sig = (sig == SIGSEGV) ? SIGBUS : SIGSEGV;
signal(sig, SIG_DFL);
kill(getpid(), sig);
// should never get here, but just in case...
abort();
}
void stacktrace_setup(void) {
signal(SIGSEGV, stacktrace_sighandler);
signal(SIGBUS, stacktrace_sighandler);
signal(SIGFPE, stacktrace_sighandler);
signal(SIGILL, stacktrace_sighandler);
}