+
+/* Logs the stack trace using the backtrace() call. This function is designed
+ * to be called from signal handlers safely. */
+void logStackTrace(ucontext_t *uc) {
+ void *trace[100];
+ int trace_size = 0, fd;
+
+ /* Open the log file in append mode. */
+ fd = server.logfile ?
+ open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644) :
+ STDOUT_FILENO;
+ if (fd == -1) return;
+
+ /* Generate the stack trace */
+ trace_size = backtrace(trace, 100);
+
+ /* overwrite sigaction with caller's address */
+ if (getMcontextEip(uc) != NULL)
+ trace[1] = getMcontextEip(uc);
+
+ /* Write symbols to log file */
+ backtrace_symbols_fd(trace, trace_size, fd);
+
+ /* Cleanup */
+ if (server.logfile) close(fd);
+}
+
+/* Log information about the "current" client, that is, the client that is
+ * currently being served by Redis. May be NULL if Redis is not serving a
+ * client right now. */
+void logCurrentClient(void) {
+ if (server.current_client == NULL) return;
+
+ redisClient *cc = server.current_client;
+ sds client;
+ int j;
+
+ redisLog(REDIS_WARNING, "--- CURRENT CLIENT INFO");
+ client = getClientInfoString(cc);
+ redisLog(REDIS_WARNING,"client: %s", client);
+ sdsfree(client);
+ 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);
+ }
+}
+
+#if defined(HAVE_PROC_MAPS)
+int memtest_non_destructive(void *addr, size_t size); /* memtest.c */
+
+int memtest_test_linux_anonymous_maps(void) {
+ FILE *fp = fopen("/proc/self/maps","r");
+ char line[1024];
+ size_t start_addr, end_addr, size;
+
+ while(fgets(line,sizeof(line),fp) != NULL) {
+ char *start, *end, *p = line;
+ int j;
+
+ start = p;
+ p = strchr(p,'-');
+ if (!p) continue;
+ *p++ = '\0';
+ end = p;
+ p = strchr(p,' ');
+ if (!p) continue;
+ *p++ = '\0';
+ if (strstr(p,"stack") ||
+ strstr(p,"vdso") ||
+ strstr(p,"vsyscall")) continue;
+ if (!strstr(p,"00:00")) continue;
+ if (!strstr(p,"rw")) continue;
+
+ start_addr = strtoul(start,NULL,16);
+ end_addr = strtoul(end,NULL,16);
+ size = end_addr-start_addr;
+ redisLog(REDIS_WARNING,
+ "Testing memory at %lx (%lu bytes)", start_addr, size);
+ for (j = 0; j < 3; j++) {
+ if (memtest_non_destructive((void*)start_addr,size) != 0) {
+ fclose(fp);
+ return 1;
+ }
+ }
+ }
+ fclose(fp);
+ return 0;
+}
+#endif
+
+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 */
+ redisLog(REDIS_WARNING, "--- STACK TRACE");
+ logStackTrace(uc);
+
+ /* Log INFO and CLIENT LIST */
+ redisLog(REDIS_WARNING, "--- INFO OUTPUT");
+ infostring = genRedisInfoString("all");
+ infostring = sdscatprintf(infostring, "hash_init_value: %u\n",
+ dictGetHashFunctionSeed());
+ redisLogRaw(REDIS_WARNING, infostring);
+ redisLog(REDIS_WARNING, "--- CLIENT LIST OUTPUT");
+ clients = getAllClientsInfoString();
+ redisLogRaw(REDIS_WARNING, clients);
+ sdsfree(infostring);
+ sdsfree(clients);
+
+ /* Log the current client */
+ logCurrentClient();
+
+ /* Log dump of processor registers */
+ logRegisters(uc);
+
+#if defined(HAVE_PROC_MAPS)
+ /* Test memory */
+ redisLog(REDIS_WARNING, "--- FAST MEMORY TEST");
+ bioKillThreads();
+ if (memtest_test_linux_anonymous_maps()) {
+ redisLog(REDIS_WARNING,
+ "!!! MEMORY ERROR DETECTED! Check your memory ASAP !!!");
+ } else {
+ redisLog(REDIS_WARNING,
+ "Fast memory test PASSED, however your memory can still be broken. Please run a memory test for several hours if possible.");
+ }
+#endif
+
+ 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"
+" 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);
+
+ /* 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);
+ act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND;
+ act.sa_handler = SIG_DFL;
+ sigaction (sig, &act, NULL);
+ kill(getpid(),sig);
+}
+#endif /* HAVE_BACKTRACE */
+
+/* ==================== Logging functions for debugging ===================== */
+
+void redisLogHexDump(int level, char *descr, void *value, size_t len) {
+ char buf[65], *b;
+ unsigned char *v = value;
+ char charset[] = "0123456789abcdef";
+
+ redisLog(level,"%s (hexdump):", descr);
+ b = buf;
+ while(len) {
+ b[0] = charset[(*v)>>4];
+ b[1] = charset[(*v)&0xf];
+ b[2] = '\0';
+ b += 2;
+ len--;
+ v++;
+ if (b-buf == 64 || len == 0) {
+ redisLogRaw(level|REDIS_LOG_RAW,buf);
+ b = buf;
+ }
+ }
+ redisLogRaw(level|REDIS_LOG_RAW,"\n");
+}
+
+/* =========================== Software Watchdog ============================ */
+#include <sys/time.h>
+
+void watchdogSignalHandler(int sig, siginfo_t *info, void *secret) {
+#ifdef HAVE_BACKTRACE
+ ucontext_t *uc = (ucontext_t*) secret;
+#endif
+ REDIS_NOTUSED(info);
+ REDIS_NOTUSED(sig);
+
+ redisLogFromHandler(REDIS_WARNING,"\n--- WATCHDOG TIMER EXPIRED ---");
+#ifdef HAVE_BACKTRACE
+ logStackTrace(uc);
+#else
+ redisLogFromHandler(REDIS_WARNING,"Sorry: no support for backtrace().");
+#endif
+ redisLogFromHandler(REDIS_WARNING,"--------\n");
+}
+
+/* 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) {
+ int min_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 the configured period is smaller than twice the timer period, it is
+ * too short for the software watchdog to work reliably. Fix it now
+ * if needed. */
+ min_period = (1000/REDIS_HZ)*2;
+ if (period < min_period) period = min_period;
+ 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;
+}