From d4d208595c78d78196c926c55799bc81ae06f230 Mon Sep 17 00:00:00 2001 From: antirez Date: Fri, 20 Jan 2012 12:20:45 +0100 Subject: [PATCH 1/1] 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. --- src/debug.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/redis.c | 135 -------------------------------- src/redis.h | 3 +- 3 files changed, 220 insertions(+), 136 deletions(-) diff --git a/src/debug.c b/src/debug.c index 728aa8a1..59e15844 100644 --- a/src/debug.c +++ b/src/debug.c @@ -2,6 +2,12 @@ #include "sha1.h" /* SHA1 is used for DEBUG DIGEST */ #include +#include + +#ifdef HAVE_BACKTRACE +#include +#include +#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; iargc; 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 */ diff --git a/src/redis.c b/src/redis.c index 6e897d83..3914003d 100644 --- a/src/redis.c +++ b/src/redis.c @@ -31,11 +31,6 @@ #include "slowlog.h" #include "bio.h" -#ifdef HAVE_BACKTRACE -#include -#include -#endif /* HAVE_BACKTRACE */ - #include #include #include @@ -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; iargc; 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); diff --git a/src/redis.h b/src/redis.h index 1ac2a2d2..37f0b2ab 100644 --- a/src/redis.h +++ b/src/redis.h @@ -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 -- 2.45.2