+/*
+ * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Redis nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
#include "redis.h"
#include "sha1.h" /* SHA1 is used for DEBUG DIGEST */
#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. */
-sds getStackTrace(ucontext_t *uc) {
+/* 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;
- sds st = sdsempty();
+ 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);
- for (i=1; i<trace_size; ++i) {
- st = sdscat(st,messages[i]);
- st = sdscatlen(st,"\n",1);
- }
- zlibc_free(messages);
- return st;
+
+ /* 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
void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
ucontext_t *uc = (ucontext_t*) secret;
- sds infostring, clients, st;
+ sds infostring, clients;
struct sigaction act;
REDIS_NOTUSED(info);
server.assert_file, server.assert_line);
/* Log the stack trace */
- st = getStackTrace(uc);
- redisLog(REDIS_WARNING, "--- STACK TRACE\n%s", st);
- sdsfree(st);
+ redisLog(REDIS_WARNING, "--- STACK TRACE");
+ logStackTrace(uc);
/* Log INFO and CLIENT LIST */
redisLog(REDIS_WARNING, "--- INFO OUTPUT");
}
#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);
- sds st, log;
- time_t now = time(NULL);
- char date[128];
- FILE *fp;
- fp = (server.logfile == NULL) ? stdout : fopen(server.logfile,"a");
- if (fp == NULL) return;
-
- strftime(date,sizeof(date),"%d %b %H:%M:%S",localtime(&now));
- log = sdscatprintf(sdsempty(),
- "\n--- WATCHDOG TIMER EXPIRED (%s) ---\n",date);
+ redisLogFromHandler(REDIS_WARNING,"\n--- WATCHDOG TIMER EXPIRED ---");
#ifdef HAVE_BACKTRACE
- st = getStackTrace(uc);
+ logStackTrace(uc);
#else
- st = sdsnew("Sorry: no support for backtrace().\n");
+ redisLogFromHandler(REDIS_WARNING,"Sorry: no support for backtrace().");
#endif
- log = sdscatsds(log,st);
- log = sdscat(log,"------\n\n");
- fprintf(fp,"%s",log);
- if (server.logfile) fclose(fp);
- sdsfree(st);
- sdsfree(log);
+ redisLogFromHandler(REDIS_WARNING,"--------\n");
}
/* Schedule a SIGALRM delivery after the specified period in milliseconds.
/* 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;
}