all the stack trace related functions are now in debug.c. Now Redis dumps registers and stack content on crash. Currently osx supported, adding Linux right now.

This commit is contained in:
antirez 2012-01-20 12:20:45 +01:00
parent f7ccc4830b
commit d4d208595c
3 changed files with 220 additions and 136 deletions

View File

@ -2,6 +2,12 @@
#include "sha1.h" /* SHA1 is used for DEBUG DIGEST */
#include <arpa/inet.h>
#include <signal.h>
#ifdef HAVE_BACKTRACE
#include <execinfo.h>
#include <ucontext.h>
#endif /* HAVE_BACKTRACE */
/* ================================= Debugging ============================== */
@ -297,6 +303,8 @@ void debugCommand(redisClient *c) {
}
}
/* =========================== Crash handling ============================== */
void _redisAssert(char *estr, char *file, int line) {
bugReportStart();
redisLog(REDIS_WARNING,"=== ASSERTION FAILED ===");
@ -380,3 +388,213 @@ void _redisPanic(char *msg, char *file, int line) {
*((char*)-1) = 'x';
#endif
}
#ifdef HAVE_BACKTRACE
static void *getMcontextEip(ucontext_t *uc) {
#if defined(__FreeBSD__)
return (void*) uc->uc_mcontext.mc_eip;
#elif defined(__dietlibc__)
return (void*) uc->uc_mcontext.eip;
#elif defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
#if __x86_64__
return (void*) uc->uc_mcontext->__ss.__rip;
#elif __i386__
return (void*) uc->uc_mcontext->__ss.__eip;
#else
return (void*) uc->uc_mcontext->__ss.__srr0;
#endif
#elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
#if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
return (void*) uc->uc_mcontext->__ss.__rip;
#else
return (void*) uc->uc_mcontext->__ss.__eip;
#endif
#elif defined(__i386__)
return (void*) uc->uc_mcontext.gregs[14]; /* Linux 32 */
#elif defined(__X86_64__) || defined(__x86_64__)
return (void*) uc->uc_mcontext.gregs[16]; /* Linux 64 */
#elif defined(__ia64__) /* Linux IA64 */
return (void*) uc->uc_mcontext.sc_ip;
#else
return NULL;
#endif
}
void bugReportStart(void) {
if (server.bug_report_start == 0) {
redisLog(REDIS_WARNING,
"\n\n=== REDIS BUG REPORT START: Cut & paste starting from here ===");
server.bug_report_start = 1;
}
}
void logStackContent(void **sp) {
int i;
for (i = 15; i >= 0; i--) {
redisLog(REDIS_WARNING, "(%p) -> %p", sp+i, sp[i]);
}
}
void logRegisters(ucontext_t *uc) {
redisLog(REDIS_WARNING, "--- REGISTERS");
#if defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
#if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
redisLog(REDIS_WARNING,
"\n"
"RAX:%p RBX:%p RCX:%p RDX:%p\n"
"RDI:%p RSI:%p RBP:%p RSP:%p\n"
"R8:%p R9:%p R10:%p R11:%p\n"
"R12:%p R13:%p R14:%p R15:%p\n"
"RIP:%p EFL:%p CS:%p FS:%p GS:%p",
uc->uc_mcontext->__ss.__rax,
uc->uc_mcontext->__ss.__rbx,
uc->uc_mcontext->__ss.__rcx,
uc->uc_mcontext->__ss.__rdx,
uc->uc_mcontext->__ss.__rdi,
uc->uc_mcontext->__ss.__rsi,
uc->uc_mcontext->__ss.__rbp,
uc->uc_mcontext->__ss.__rsp,
uc->uc_mcontext->__ss.__r8,
uc->uc_mcontext->__ss.__r9,
uc->uc_mcontext->__ss.__r10,
uc->uc_mcontext->__ss.__r11,
uc->uc_mcontext->__ss.__r12,
uc->uc_mcontext->__ss.__r13,
uc->uc_mcontext->__ss.__r14,
uc->uc_mcontext->__ss.__r15,
uc->uc_mcontext->__ss.__rip,
uc->uc_mcontext->__ss.__rflags,
uc->uc_mcontext->__ss.__cs,
uc->uc_mcontext->__ss.__fs,
uc->uc_mcontext->__ss.__gs
);
logStackContent((void**)uc->uc_mcontext->__ss.__rsp);
#else
redisLog(REDIS_WARNING,
"\n"
"EAX:%p EBX:%p ECX:%p EDX:%p\n"
"EDI:%p ESI:%p EBP:%p ESP:%p\n"
"SS:%p EFL:%p EIP:%p CS:%p\n"
"DS:%p ES:%p FS:%p GS:%p",
uc->uc_mcontext->__ss.__eax,
uc->uc_mcontext->__ss.__ebx,
uc->uc_mcontext->__ss.__ecx,
uc->uc_mcontext->__ss.__edx,
uc->uc_mcontext->__ss.__edi,
uc->uc_mcontext->__ss.__esi,
uc->uc_mcontext->__ss.__ebp,
uc->uc_mcontext->__ss.__esp,
uc->uc_mcontext->__ss.__ss,
uc->uc_mcontext->__ss.__eflags,
uc->uc_mcontext->__ss.__eip,
uc->uc_mcontext->__ss.__cs,
uc->uc_mcontext->__ss.__ds,
uc->uc_mcontext->__ss.__es,
uc->uc_mcontext->__ss.__fs,
uc->uc_mcontext->__ss.__gs
);
logStackContent((void**)uc->uc_mcontext->__ss.__esp);
#endif
#elif defined(__i386__)
return (void*) uc->uc_mcontext.gregs[14]; /* Linux 32 */
#elif defined(__X86_64__) || defined(__x86_64__)
return (void*) uc->uc_mcontext.gregs[16]; /* Linux 64 */
#else
redisLog(REDIS_WARNING,
" Dumping of registers not supported for this OS/arch");
#endif
}
void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
void *trace[100];
char **messages = NULL;
int i, trace_size = 0;
ucontext_t *uc = (ucontext_t*) secret;
sds infostring, clients;
struct sigaction act;
REDIS_NOTUSED(info);
bugReportStart();
redisLog(REDIS_WARNING,
" Redis %s crashed by signal: %d", REDIS_VERSION, sig);
redisLog(REDIS_WARNING,
" Failed assertion: %s (%s:%d)", server.assert_failed,
server.assert_file, server.assert_line);
/* Generate the stack trace */
trace_size = backtrace(trace, 100);
/* overwrite sigaction with caller's address */
if (getMcontextEip(uc) != NULL) {
trace[1] = getMcontextEip(uc);
}
messages = backtrace_symbols(trace, trace_size);
redisLog(REDIS_WARNING, "--- STACK TRACE");
for (i=1; i<trace_size; ++i)
redisLog(REDIS_WARNING,"%s", messages[i]);
/* Log INFO and CLIENT LIST */
redisLog(REDIS_WARNING, "--- INFO OUTPUT");
infostring = genRedisInfoString("all");
redisLogRaw(REDIS_WARNING, infostring);
redisLog(REDIS_WARNING, "--- CLIENT LIST OUTPUT");
clients = getAllClientsInfoString();
redisLogRaw(REDIS_WARNING, clients);
/* Don't sdsfree() strings to avoid a crash. Memory may be corrupted. */
/* Log CURRENT CLIENT info */
if (server.current_client) {
redisClient *cc = server.current_client;
sds client;
int j;
redisLog(REDIS_WARNING, "--- CURRENT CLIENT INFO");
client = getClientInfoString(cc);
redisLog(REDIS_WARNING,"client: %s", client);
/* Missing sdsfree(client) to avoid crash if memory is corrupted. */
for (j = 0; j < cc->argc; j++) {
robj *decoded;
decoded = getDecodedObject(cc->argv[j]);
redisLog(REDIS_WARNING,"argv[%d]: '%s'", j, (char*)decoded->ptr);
decrRefCount(decoded);
}
/* Check if the first argument, usually a key, is found inside the
* selected DB, and if so print info about the associated object. */
if (cc->argc >= 1) {
robj *val, *key;
dictEntry *de;
key = getDecodedObject(cc->argv[1]);
de = dictFind(cc->db->dict, key->ptr);
if (de) {
val = dictGetVal(de);
redisLog(REDIS_WARNING,"key '%s' found in DB containing the following object:", key->ptr);
redisLogObjectDebugInfo(val);
}
decrRefCount(key);
}
}
/* Log dump of processor registers */
logRegisters(uc);
redisLog(REDIS_WARNING,
"\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n"
" Please report the crash opening an issue on github:\n\n"
" http://github.com/antirez/redis/issues\n\n"
);
/* free(messages); Don't call free() with possibly corrupted memory. */
if (server.daemonize) unlink(server.pidfile);
/* Make sure we exit with the right signal at the end. So for instance
* the core will be dumped if enabled. */
sigemptyset (&act.sa_mask);
/* When the SA_SIGINFO flag is set in sa_flags then sa_sigaction
* is used. Otherwise, sa_handler is used */
act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND;
act.sa_handler = SIG_DFL;
sigaction (sig, &act, NULL);
kill(getpid(),sig);
}
#endif /* HAVE_BACKTRACE */

View File

@ -31,11 +31,6 @@
#include "slowlog.h"
#include "bio.h"
#ifdef HAVE_BACKTRACE
#include <execinfo.h>
#include <ucontext.h>
#endif /* HAVE_BACKTRACE */
#include <time.h>
#include <signal.h>
#include <sys/wait.h>
@ -1933,136 +1928,6 @@ void redisAsciiArt(void) {
zfree(buf);
}
#ifdef HAVE_BACKTRACE
static void *getMcontextEip(ucontext_t *uc) {
#if defined(__FreeBSD__)
return (void*) uc->uc_mcontext.mc_eip;
#elif defined(__dietlibc__)
return (void*) uc->uc_mcontext.eip;
#elif defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
#if __x86_64__
return (void*) uc->uc_mcontext->__ss.__rip;
#elif __i386__
return (void*) uc->uc_mcontext->__ss.__eip;
#else
return (void*) uc->uc_mcontext->__ss.__srr0;
#endif
#elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
#if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
return (void*) uc->uc_mcontext->__ss.__rip;
#else
return (void*) uc->uc_mcontext->__ss.__eip;
#endif
#elif defined(__i386__)
return (void*) uc->uc_mcontext.gregs[14]; /* Linux 32 */
#elif defined(__X86_64__) || defined(__x86_64__)
return (void*) uc->uc_mcontext.gregs[16]; /* Linux 64 */
#elif defined(__ia64__) /* Linux IA64 */
return (void*) uc->uc_mcontext.sc_ip;
#else
return NULL;
#endif
}
void bugReportStart(void) {
if (server.bug_report_start == 0) {
redisLog(REDIS_WARNING,
"=== REDIS BUG REPORT START: Cut & paste starting from here ===");
server.bug_report_start = 1;
}
}
static void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
void *trace[100];
char **messages = NULL;
int i, trace_size = 0;
ucontext_t *uc = (ucontext_t*) secret;
sds infostring, clients;
struct sigaction act;
REDIS_NOTUSED(info);
bugReportStart();
redisLog(REDIS_WARNING,
" Redis %s crashed by signal: %d", REDIS_VERSION, sig);
redisLog(REDIS_WARNING,
" Failed assertion: %s (%s:%d)", server.assert_failed,
server.assert_file, server.assert_line);
/* Generate the stack trace */
trace_size = backtrace(trace, 100);
/* overwrite sigaction with caller's address */
if (getMcontextEip(uc) != NULL) {
trace[1] = getMcontextEip(uc);
}
messages = backtrace_symbols(trace, trace_size);
redisLog(REDIS_WARNING, "--- STACK TRACE");
for (i=1; i<trace_size; ++i)
redisLog(REDIS_WARNING,"%s", messages[i]);
/* Log INFO and CLIENT LIST */
redisLog(REDIS_WARNING, "--- INFO OUTPUT");
infostring = genRedisInfoString("all");
redisLogRaw(REDIS_WARNING, infostring);
redisLog(REDIS_WARNING, "--- CLIENT LIST OUTPUT");
clients = getAllClientsInfoString();
redisLogRaw(REDIS_WARNING, clients);
/* Don't sdsfree() strings to avoid a crash. Memory may be corrupted. */
/* Log CURRENT CLIENT info */
if (server.current_client) {
redisClient *cc = server.current_client;
sds client;
int j;
redisLog(REDIS_WARNING, "--- CURRENT CLIENT INFO");
client = getClientInfoString(cc);
redisLog(REDIS_WARNING,"client: %s", client);
/* Missing sdsfree(client) to avoid crash if memory is corrupted. */
for (j = 0; j < cc->argc; j++) {
robj *decoded;
decoded = getDecodedObject(cc->argv[j]);
redisLog(REDIS_WARNING,"argv[%d]: '%s'", j, (char*)decoded->ptr);
decrRefCount(decoded);
}
/* Check if the first argument, usually a key, is found inside the
* selected DB, and if so print info about the associated object. */
if (cc->argc >= 1) {
robj *val, *key;
dictEntry *de;
key = getDecodedObject(cc->argv[1]);
de = dictFind(cc->db->dict, key->ptr);
if (de) {
val = dictGetVal(de);
redisLog(REDIS_WARNING,"key '%s' found in DB containing the following object:", key->ptr);
redisLogObjectDebugInfo(val);
}
decrRefCount(key);
}
}
redisLog(REDIS_WARNING,
"=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n"
" Please report the crash opening an issue on github:\n\n"
" http://github.com/antirez/redis/issues\n\n"
);
/* free(messages); Don't call free() with possibly corrupted memory. */
if (server.daemonize) unlink(server.pidfile);
/* Make sure we exit with the right signal at the end. So for instance
* the core will be dumped if enabled. */
sigemptyset (&act.sa_mask);
/* When the SA_SIGINFO flag is set in sa_flags then sa_sigaction
* is used. Otherwise, sa_handler is used */
act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND;
act.sa_handler = SIG_DFL;
sigaction (sig, &act, NULL);
kill(getpid(),sig);
}
#endif /* HAVE_BACKTRACE */
static void sigtermHandler(int sig) {
REDIS_NOTUSED(sig);

View File

@ -1161,5 +1161,6 @@ void _redisAssert(char *estr, char *file, int line);
void _redisPanic(char *msg, char *file, int line);
void bugReportStart(void);
void redisLogObjectDebugInfo(robj *o);
void sigsegvHandler(int sig, siginfo_t *info, void *secret);
sds genRedisInfoString(char *section);
#endif