X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/18aa2b87b6db7fa79b0596590aa385adcbc64e53..5471b8babddbb99a50010593483f24187e51981a:/src/debug.c diff --git a/src/debug.c b/src/debug.c index fe21531b..e7b3ba40 100644 --- a/src/debug.c +++ b/src/debug.c @@ -314,8 +314,8 @@ void _redisAssert(char *estr, char *file, int line) { server.assert_file = file; server.assert_line = line; redisLog(REDIS_WARNING,"(forcing SIGSEGV to print the bug report.)"); - *((char*)-1) = 'x'; #endif + *((char*)-1) = 'x'; } void _redisAssertPrintClientInfo(redisClient *c) { @@ -384,9 +384,9 @@ void _redisPanic(char *msg, char *file, int line) { redisLog(REDIS_WARNING,"Guru Meditation: %s #%s:%d",msg,file,line); #ifdef HAVE_BACKTRACE redisLog(REDIS_WARNING,"(forcing SIGSEGV in order to print the stack trace)"); +#endif redisLog(REDIS_WARNING,"------------------------------------------------"); *((char*)-1) = 'x'; -#endif } void bugReportStart(void) { @@ -558,12 +558,70 @@ void logRegisters(ucontext_t *uc) { #endif } -void sigsegvHandler(int sig, siginfo_t *info, void *secret) { +/* Logs the stack trace using the backtrace() call. */ +sds getStackTrace(ucontext_t *uc) { void *trace[100]; - char **messages = NULL; int i, trace_size = 0; + char **messages = NULL; + sds st = sdsempty(); + + /* 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); + 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); + } +} + +void sigsegvHandler(int sig, siginfo_t *info, void *secret) { ucontext_t *uc = (ucontext_t*) secret; - sds infostring, clients; + sds infostring, clients, st; struct sigaction act; REDIS_NOTUSED(info); @@ -574,17 +632,10 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) { " 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; + sdsfree(infostring); + sdsfree(clients); - 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 the current client */ + logCurrentClient(); /* 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" +" Please report the crash opening an issue on github:\n\n" +" http://github.com/antirez/redis/issues\n\n" +" Suspect RAM error? Use redis-server --test-memory to veryfy it.\n\n" ); /* free(messages); Don't call free() with possibly corrupted memory. */ if (server.daemonize) unlink(server.pidfile); @@ -645,11 +667,78 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) { /* 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 */ + +/* =========================== Software Watchdog ============================ */ +#include + +void watchdogSignalHandler(int sig, siginfo_t *info, void *secret) { + ucontext_t *uc = (ucontext_t*) secret; + REDIS_NOTUSED(info); + REDIS_NOTUSED(sig); + sds st, log; + + log = sdsnew("\n--- WATCHDOG TIMER EXPIRED ---\n"); +#ifdef HAVE_BACKTRACE + st = getStackTrace(uc); +#else + st = sdsnew("Sorry: no support for backtrace().\n"); +#endif + log = sdscatsds(log,st); + log = sdscat(log,"------\n"); + redisLogFromHandler(REDIS_WARNING,log); + sdsfree(st); + sdsfree(log); +} + +/* Schedule a SIGALRM delivery after the specified period in milliseconds. + * If a timer is already scheduled, this function will re-schedule it to the + * specified time. If period is 0 the current timer is disabled. */ +void watchdogScheduleSignal(int period) { + struct itimerval it; + + /* Will stop the timer if period is 0. */ + it.it_value.tv_sec = period/1000; + it.it_value.tv_usec = (period%1000)*1000; + /* Don't automatically restart. */ + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + setitimer(ITIMER_REAL, &it, NULL); +} + +/* Enable the software watchdong with the specified period in milliseconds. */ +void enableWatchdog(int period) { + if (server.watchdog_period == 0) { + struct sigaction act; + + /* Watchdog was actually disabled, so we have to setup the signal + * handler. */ + sigemptyset(&act.sa_mask); + act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_SIGINFO; + act.sa_sigaction = watchdogSignalHandler; + sigaction(SIGALRM, &act, NULL); + } + if (period < 200) period = 200; /* We don't accept periods < 200 ms. */ + watchdogScheduleSignal(period); /* Adjust the current timer. */ + server.watchdog_period = period; +} + +/* Disable the software watchdog. */ +void disableWatchdog(void) { + struct sigaction act; + if (server.watchdog_period == 0) return; /* Already disabled. */ + watchdogScheduleSignal(0); /* Stop the current timer. */ + + /* Set the signal handler to SIG_IGN, this will also remove pending + * signals from the queue. */ + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = SIG_IGN; + sigaction(SIGALRM, &act, NULL); + server.watchdog_period = 0; +}