#ifdef HAVE_BACKTRACE
#include <execinfo.h>
#include <ucontext.h>
+#include <fcntl.h>
#endif /* HAVE_BACKTRACE */
/* ================================= Debugging ============================== */
mixDigest(digest,key,sdslen(key));
- /* Make sure the key is loaded if VM is active */
o = dictGetVal(de);
aux = htonl(o->type);
void debugCommand(redisClient *c) {
if (!strcasecmp(c->argv[1]->ptr,"segfault")) {
*((char*)-1) = 'x';
+ } else if (!strcasecmp(c->argv[1]->ptr,"oom")) {
+ void *ptr = zmalloc(ULONG_MAX); /* Should trigger an out of memory. */
+ zfree(ptr);
+ addReply(c,shared.ok);
} else if (!strcasecmp(c->argv[1]->ptr,"assert")) {
if (c->argc >= 3) c->argv[2] = tryObjectEncoding(c->argv[2]);
redisAssertWithInfo(c,c->argv[0],1 == 2);
#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__
+#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
+ /* OSX < 10.6 */
+ #if defined(__x86_64__)
return (void*) uc->uc_mcontext->__ss.__rip;
- #elif __i386__
+ #elif defined(__i386__)
return (void*) uc->uc_mcontext->__ss.__eip;
- #else
+ #else
return (void*) uc->uc_mcontext->__ss.__srr0;
- #endif
+ #endif
#elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
- #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
+ /* OSX >= 10.6 */
+ #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
return (void*) uc->uc_mcontext->__ss.__rip;
- #else
+ #else
return (void*) uc->uc_mcontext->__ss.__eip;
- #endif
-#elif defined(__i386__)
+ #endif
+#elif defined(__linux__)
+ /* Linux */
+ #if defined(__i386__)
return (void*) uc->uc_mcontext.gregs[14]; /* Linux 32 */
-#elif defined(__X86_64__) || defined(__x86_64__)
+ #elif defined(__X86_64__) || defined(__x86_64__)
return (void*) uc->uc_mcontext.gregs[16]; /* Linux 64 */
-#elif defined(__ia64__) /* Linux IA64 */
+ #elif defined(__ia64__) /* Linux IA64 */
return (void*) uc->uc_mcontext.sc_ip;
+ #endif
#else
return NULL;
#endif
void logRegisters(ucontext_t *uc) {
redisLog(REDIS_WARNING, "--- REGISTERS");
+
+/* OSX */
#if defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
- #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
+ /* OSX AMD64 */
+ #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
redisLog(REDIS_WARNING,
"\n"
"RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n"
uc->uc_mcontext->__ss.__gs
);
logStackContent((void**)uc->uc_mcontext->__ss.__rsp);
- #else
+ #else
+ /* OSX x86 */
redisLog(REDIS_WARNING,
"\n"
"EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n"
uc->uc_mcontext->__ss.__gs
);
logStackContent((void**)uc->uc_mcontext->__ss.__esp);
- #endif
-#elif defined(__i386__)
+ #endif
+/* Linux */
+#elif defined(__linux__)
+ /* Linux x86 */
+ #if defined(__i386__)
redisLog(REDIS_WARNING,
"\n"
"EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n"
uc->uc_mcontext.gregs[0]
);
logStackContent((void**)uc->uc_mcontext.gregs[7]);
-#elif defined(__X86_64__) || defined(__x86_64__)
+ #elif defined(__X86_64__) || defined(__x86_64__)
+ /* Linux AMD64 */
redisLog(REDIS_WARNING,
"\n"
"RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n"
uc->uc_mcontext.gregs[18]
);
logStackContent((void**)uc->uc_mcontext.gregs[15]);
+ #endif
#else
redisLog(REDIS_WARNING,
" Dumping of registers not supported for this OS/arch");
#endif
}
-/* Logs the stack trace using the backtrace() call. */
+/* 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 i, trace_size = 0;
- char **messages = NULL;
+ 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) {
+ 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]);
+
+ /* 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
server.assert_file, server.assert_line);
/* Log the stack trace */
+ redisLog(REDIS_WARNING, "--- STACK TRACE");
logStackTrace(uc);
/* Log INFO and CLIENT LIST */
}
#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);
- /* Log INFO and CLIENT LIST */
- redisLog(REDIS_WARNING, "--- WATCHDOG TIMER EXPIRED ---");
+ redisLogFromHandler(REDIS_WARNING,"\n--- WATCHDOG TIMER EXPIRED ---");
#ifdef HAVE_BACKTRACE
logStackTrace(uc);
- redisLog(REDIS_WARNING, "------");
+#else
+ redisLogFromHandler(REDIS_WARNING,"Sorry: no support for backtrace().");
#endif
+ redisLogFromHandler(REDIS_WARNING,"--------\n");
}
/* Schedule a SIGALRM delivery after the specified period in milliseconds.
/* Will stop the timer if period is 0. */
it.it_value.tv_sec = period/1000;
- it.it_value.tv_usec = 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;
/* 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;
act.sa_sigaction = watchdogSignalHandler;
sigaction(SIGALRM, &act, NULL);
}
- if (period < 200) period = 200; /* We don't accept periods < 200 ms. */
+ /* 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;
}