X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/dbb27a0a90ca3800f5be1d8170e404b9e7b9bc44..fc030ac7deecab6769177c44fe2859a76cce4286:/src/debug.c diff --git a/src/debug.c b/src/debug.c index a355df05..2df913b0 100644 --- a/src/debug.c +++ b/src/debug.c @@ -558,21 +558,11 @@ void logRegisters(ucontext_t *uc) { #endif } -void sigsegvHandler(int sig, siginfo_t *info, void *secret) { +/* Logs the stack trace using the backtrace() call. */ +void logStackTrace(ucontext_t *uc) { 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); + char **messages = NULL; /* Generate the stack trace */ trace_size = backtrace(trace, 100); @@ -585,6 +575,61 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) { 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); + } +} + +void sigsegvHandler(int sig, siginfo_t *info, void *secret) { + 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); + + /* Log the stack trace */ + logStackTrace(uc); /* Log INFO and CLIENT LIST */ redisLog(REDIS_WARNING, "--- INFO OUTPUT"); @@ -595,49 +640,20 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) { 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; + 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 +661,72 @@ 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); + + /* Log INFO and CLIENT LIST */ + redisLog(REDIS_WARNING, "--- WATCHDOG TIMER EXPIRED ---"); +#ifdef HAVE_BACKTRACE + logStackTrace(uc); + redisLog(REDIS_WARNING, "------"); +#endif +} + +/* 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; + /* 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; +}