From: antirez Date: Sun, 7 Jun 2009 12:00:31 +0000 (+0200) Subject: Dump a backtrace on sigsegv/sigbus, original coded thanks to Diego Rosario Brogna... X-Git-Url: https://git.saurik.com/redis.git/commitdiff_plain/56906eefea4b77e1cd43d0b483d11470a3dab90d Dump a backtrace on sigsegv/sigbus, original coded thanks to Diego Rosario Brogna, modified in order to work on different OSes and to enhance reliability --- diff --git a/Makefile b/Makefile index fda703e1..4908de46 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # This file is released under the BSD license, see the COPYING file DEBUG?= -g -rdynamic -ggdb -CFLAGS?= -std=c99 -pedantic -O0 -Wall -W -DSDS_ABORT_ON_OOM +CFLAGS?= -std=c99 -pedantic -O2 -Wall -W CCOPT= $(CFLAGS) OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o diff --git a/TODO b/TODO index a0876807..e37e50f2 100644 --- a/TODO +++ b/TODO @@ -4,6 +4,7 @@ BEFORE REDIS 1.0.0-rc1 * Resize the expires and Sets hash tables if needed as well? For Sets the right moment to check for this is probably in SREM * check 'server.dirty' everywere. Make it proprotional to the number of objects modified. * Cover most of the source code with test-redis.tcl + * Remove tmp-.... files when saving child exits in the wrong way, to do so use tmp-pid.rdb as filename so that the parent can rebuild the file name just from the child pid. AFTER 1.0 stable release diff --git a/redis.c b/redis.c index 56ddee9e..a44be7bf 100644 --- a/redis.c +++ b/redis.c @@ -279,7 +279,7 @@ struct redisCommand { struct redisFunctionSym { char *name; - long pointer; + unsigned long pointer; }; typedef struct _redisSortObject { @@ -330,6 +330,8 @@ static int setExpire(redisDb *db, robj *key, time_t when); static void updateSalvesWaitingBgsave(int bgsaveerr); static void freeMemoryIfNeeded(void); static int processCommand(redisClient *c); +static void segvHandler(int sig, siginfo_t *info, void *secret); +static void setupSigSegvAction(void); static void authCommand(redisClient *c); static void pingCommand(redisClient *c); @@ -388,7 +390,6 @@ static void getSetCommand(redisClient *c); static void ttlCommand(redisClient *c); static void slaveofCommand(redisClient *c); static void debugCommand(redisClient *c); -static void setupSigSegvAction(); /*================================= Globals ================================= */ /* Global vars */ @@ -454,91 +455,6 @@ static struct redisCommand cmdTable[] = { {"debug",debugCommand,-2,REDIS_CMD_INLINE}, {NULL,NULL,0,0} }; -static struct redisFunctionSym symsTable[] = { -{"freeStringObject", (long)freeStringObject}, -{"freeListObject", (long)freeListObject}, -{"freeSetObject", (long)freeSetObject}, -{"decrRefCount", (long)decrRefCount}, -{"createObject", (long)createObject}, -{"freeClient", (long)freeClient}, -{"rdbLoad", (long)rdbLoad}, -{"addReply", (long)addReply}, -{"addReplySds", (long)addReplySds}, -{"incrRefCount", (long)incrRefCount}, -{"rdbSaveBackground", (long)rdbSaveBackground}, -{"createStringObject", (long)createStringObject}, -{"replicationFeedSlaves", (long)replicationFeedSlaves}, -{"syncWithMaster", (long)syncWithMaster}, -{"tryObjectSharing", (long)tryObjectSharing}, -{"removeExpire", (long)removeExpire}, -{"expireIfNeeded", (long)expireIfNeeded}, -{"deleteIfVolatile", (long)deleteIfVolatile}, -{"deleteKey", (long)deleteKey}, -{"getExpire", (long)getExpire}, -{"setExpire", (long)setExpire}, -{"updateSalvesWaitingBgsave", (long)updateSalvesWaitingBgsave}, -{"freeMemoryIfNeeded", (long)freeMemoryIfNeeded}, -{"authCommand", (long)authCommand}, -{"pingCommand", (long)pingCommand}, -{"echoCommand", (long)echoCommand}, -{"setCommand", (long)setCommand}, -{"setnxCommand", (long)setnxCommand}, -{"getCommand", (long)getCommand}, -{"delCommand", (long)delCommand}, -{"existsCommand", (long)existsCommand}, -{"incrCommand", (long)incrCommand}, -{"decrCommand", (long)decrCommand}, -{"incrbyCommand", (long)incrbyCommand}, -{"decrbyCommand", (long)decrbyCommand}, -{"selectCommand", (long)selectCommand}, -{"randomkeyCommand", (long)randomkeyCommand}, -{"keysCommand", (long)keysCommand}, -{"dbsizeCommand", (long)dbsizeCommand}, -{"lastsaveCommand", (long)lastsaveCommand}, -{"saveCommand", (long)saveCommand}, -{"bgsaveCommand", (long)bgsaveCommand}, -{"shutdownCommand", (long)shutdownCommand}, -{"moveCommand", (long)moveCommand}, -{"renameCommand", (long)renameCommand}, -{"renamenxCommand", (long)renamenxCommand}, -{"lpushCommand", (long)lpushCommand}, -{"rpushCommand", (long)rpushCommand}, -{"lpopCommand", (long)lpopCommand}, -{"rpopCommand", (long)rpopCommand}, -{"llenCommand", (long)llenCommand}, -{"lindexCommand", (long)lindexCommand}, -{"lrangeCommand", (long)lrangeCommand}, -{"ltrimCommand", (long)ltrimCommand}, -{"typeCommand", (long)typeCommand}, -{"lsetCommand", (long)lsetCommand}, -{"saddCommand", (long)saddCommand}, -{"sremCommand", (long)sremCommand}, -{"smoveCommand", (long)smoveCommand}, -{"sismemberCommand", (long)sismemberCommand}, -{"scardCommand", (long)scardCommand}, -{"sinterCommand", (long)sinterCommand}, -{"sinterstoreCommand", (long)sinterstoreCommand}, -{"sunionCommand", (long)sunionCommand}, -{"sunionstoreCommand", (long)sunionstoreCommand}, -{"sdiffCommand", (long)sdiffCommand}, -{"sdiffstoreCommand", (long)sdiffstoreCommand}, -{"syncCommand", (long)syncCommand}, -{"flushdbCommand", (long)flushdbCommand}, -{"flushallCommand", (long)flushallCommand}, -{"sortCommand", (long)sortCommand}, -{"lremCommand", (long)lremCommand}, -{"infoCommand", (long)infoCommand}, -{"mgetCommand", (long)mgetCommand}, -{"monitorCommand", (long)monitorCommand}, -{"expireCommand", (long)expireCommand}, -{"getSetCommand", (long)getSetCommand}, -{"ttlCommand", (long)ttlCommand}, -{"slaveofCommand", (long)slaveofCommand}, -{"debugCommand", (long)debugCommand}, -{"processCommand", (long)processCommand}, -{"setupSigSegvAction", (long)setupSigSegvAction}, -{NULL,0} -}; /*============================ Utility functions ============================ */ /* Glob-style pattern matching. */ @@ -664,8 +580,7 @@ int stringmatchlen(const char *pattern, int patternLen, return 0; } -void redisLog(int level, const char *fmt, ...) -{ +static void redisLog(int level, const char *fmt, ...) { va_list ap; FILE *fp; @@ -760,7 +675,7 @@ static void oom(const char *msg) { } /* ====================== Redis server networking stuff ===================== */ -void closeTimedoutClients(void) { +static void closeTimedoutClients(void) { redisClient *c; listNode *ln; time_t now = time(NULL); @@ -779,7 +694,7 @@ void closeTimedoutClients(void) { /* If the percentage of used slots in the HT reaches REDIS_HT_MINFILL * we resize the hash table to save memory */ -void tryResizeHashTables(void) { +static void tryResizeHashTables(void) { int j; for (j = 0; j < server.dbnum; j++) { @@ -796,7 +711,7 @@ void tryResizeHashTables(void) { } } -int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { +static int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { int j, loops = server.cronloops++; REDIS_NOTUSED(eventLoop); REDIS_NOTUSED(id); @@ -1869,7 +1784,7 @@ static int rdbSaveLen(FILE *fp, uint32_t len) { /* String objects in the form "2391" "-100" without any space and with a * range of values that can fit in an 8, 16 or 32 bit signed value can be * encoded as integers to save space */ -int rdbTryIntegerEncoding(sds s, unsigned char *enc) { +static int rdbTryIntegerEncoding(sds s, unsigned char *enc) { long long value; char *endptr, buf[32]; @@ -3361,7 +3276,7 @@ static void flushallCommand(redisClient *c) { server.dirty++; } -redisSortOperation *createSortOperation(int type, robj *pattern) { +static redisSortOperation *createSortOperation(int type, robj *pattern) { redisSortOperation *so = zmalloc(sizeof(*so)); if (!so) oom("createSortOperation"); so->type = type; @@ -3371,7 +3286,7 @@ redisSortOperation *createSortOperation(int type, robj *pattern) { /* Return the value associated to the key with a name obtained * substituting the first occurence of '*' in 'pattern' with 'subst' */ -robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) { +static robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) { char *p; sds spat, ssub; robj keyobj; @@ -4192,37 +4107,145 @@ static void debugCommand(redisClient *c) { "-ERR Syntax error, try DEBUG [SEGFAULT|OBJECT ]\r\n")); } } -char *findFuncName(void *pointer, long *offset){ - int i, ret=-1; - long val, off; - for(i=0; symsTable[i].pointer!=0; i++){ - val=(long)pointer-symsTable[i].pointer; - if(val>=0 && (off<0 || val <= off)){ - off=val; - ret=i; - } - } - if(ret<0) - *offset=0; - else - *offset=off; - return ret>=0?symsTable[ret].name:"unknown"; -} - -static void segvHandler (int sig, siginfo_t *info, void *secret) { - - void *trace[100]; - char **messages = (char **)NULL; - char *tmp; - int i, trace_size = 0; - long offset=0; - ucontext_t *uc = (ucontext_t *)secret; - time_t uptime = time(NULL)-server.stat_starttime; - - redisLog(REDIS_WARNING, "application: redis, signal: segmentation fault -%d-",REDIS_VERSION, sig); - redisLog(REDIS_WARNING, "%s", sdscatprintf(sdsempty(), + +static struct redisFunctionSym symsTable[] = { +{"freeStringObject", (unsigned long)freeStringObject}, +{"freeListObject", (unsigned long)freeListObject}, +{"freeSetObject", (unsigned long)freeSetObject}, +{"decrRefCount", (unsigned long)decrRefCount}, +{"createObject", (unsigned long)createObject}, +{"freeClient", (unsigned long)freeClient}, +{"rdbLoad", (unsigned long)rdbLoad}, +{"addReply", (unsigned long)addReply}, +{"addReplySds", (unsigned long)addReplySds}, +{"incrRefCount", (unsigned long)incrRefCount}, +{"rdbSaveBackground", (unsigned long)rdbSaveBackground}, +{"createStringObject", (unsigned long)createStringObject}, +{"replicationFeedSlaves", (unsigned long)replicationFeedSlaves}, +{"syncWithMaster", (unsigned long)syncWithMaster}, +{"tryObjectSharing", (unsigned long)tryObjectSharing}, +{"removeExpire", (unsigned long)removeExpire}, +{"expireIfNeeded", (unsigned long)expireIfNeeded}, +{"deleteIfVolatile", (unsigned long)deleteIfVolatile}, +{"deleteKey", (unsigned long)deleteKey}, +{"getExpire", (unsigned long)getExpire}, +{"setExpire", (unsigned long)setExpire}, +{"updateSalvesWaitingBgsave", (unsigned long)updateSalvesWaitingBgsave}, +{"freeMemoryIfNeeded", (unsigned long)freeMemoryIfNeeded}, +{"authCommand", (unsigned long)authCommand}, +{"pingCommand", (unsigned long)pingCommand}, +{"echoCommand", (unsigned long)echoCommand}, +{"setCommand", (unsigned long)setCommand}, +{"setnxCommand", (unsigned long)setnxCommand}, +{"getCommand", (unsigned long)getCommand}, +{"delCommand", (unsigned long)delCommand}, +{"existsCommand", (unsigned long)existsCommand}, +{"incrCommand", (unsigned long)incrCommand}, +{"decrCommand", (unsigned long)decrCommand}, +{"incrbyCommand", (unsigned long)incrbyCommand}, +{"decrbyCommand", (unsigned long)decrbyCommand}, +{"selectCommand", (unsigned long)selectCommand}, +{"randomkeyCommand", (unsigned long)randomkeyCommand}, +{"keysCommand", (unsigned long)keysCommand}, +{"dbsizeCommand", (unsigned long)dbsizeCommand}, +{"lastsaveCommand", (unsigned long)lastsaveCommand}, +{"saveCommand", (unsigned long)saveCommand}, +{"bgsaveCommand", (unsigned long)bgsaveCommand}, +{"shutdownCommand", (unsigned long)shutdownCommand}, +{"moveCommand", (unsigned long)moveCommand}, +{"renameCommand", (unsigned long)renameCommand}, +{"renamenxCommand", (unsigned long)renamenxCommand}, +{"lpushCommand", (unsigned long)lpushCommand}, +{"rpushCommand", (unsigned long)rpushCommand}, +{"lpopCommand", (unsigned long)lpopCommand}, +{"rpopCommand", (unsigned long)rpopCommand}, +{"llenCommand", (unsigned long)llenCommand}, +{"lindexCommand", (unsigned long)lindexCommand}, +{"lrangeCommand", (unsigned long)lrangeCommand}, +{"ltrimCommand", (unsigned long)ltrimCommand}, +{"typeCommand", (unsigned long)typeCommand}, +{"lsetCommand", (unsigned long)lsetCommand}, +{"saddCommand", (unsigned long)saddCommand}, +{"sremCommand", (unsigned long)sremCommand}, +{"smoveCommand", (unsigned long)smoveCommand}, +{"sismemberCommand", (unsigned long)sismemberCommand}, +{"scardCommand", (unsigned long)scardCommand}, +{"sinterCommand", (unsigned long)sinterCommand}, +{"sinterstoreCommand", (unsigned long)sinterstoreCommand}, +{"sunionCommand", (unsigned long)sunionCommand}, +{"sunionstoreCommand", (unsigned long)sunionstoreCommand}, +{"sdiffCommand", (unsigned long)sdiffCommand}, +{"sdiffstoreCommand", (unsigned long)sdiffstoreCommand}, +{"syncCommand", (unsigned long)syncCommand}, +{"flushdbCommand", (unsigned long)flushdbCommand}, +{"flushallCommand", (unsigned long)flushallCommand}, +{"sortCommand", (unsigned long)sortCommand}, +{"lremCommand", (unsigned long)lremCommand}, +{"infoCommand", (unsigned long)infoCommand}, +{"mgetCommand", (unsigned long)mgetCommand}, +{"monitorCommand", (unsigned long)monitorCommand}, +{"expireCommand", (unsigned long)expireCommand}, +{"getSetCommand", (unsigned long)getSetCommand}, +{"ttlCommand", (unsigned long)ttlCommand}, +{"slaveofCommand", (unsigned long)slaveofCommand}, +{"debugCommand", (unsigned long)debugCommand}, +{"processCommand", (unsigned long)processCommand}, +{"setupSigSegvAction", (unsigned long)setupSigSegvAction}, +{"segvHandler", (unsigned long)segvHandler}, +{"readQueryFromClient", (unsigned long)readQueryFromClient}, +{NULL,0} +}; + +/* This function try to convert a pointer into a function name. It's used in + * oreder to provide a backtrace under segmentation fault that's able to + * display functions declared as static (otherwise the backtrace is useless). */ +static char *findFuncName(void *pointer, unsigned long *offset){ + int i, ret = -1; + unsigned long off, minoff = 0; + + /* Try to match against the Symbol with the smallest offset */ + for (i=0; symsTable[i].pointer; i++) { + unsigned long lp = (unsigned long) pointer; + + if (lp != (unsigned long)-1 && lp >= symsTable[i].pointer) { + off=lp-symsTable[i].pointer; + if (ret < 0 || off < minoff) { + minoff=off; + ret=i; + } + } + } + if (ret == -1) return NULL; + *offset = minoff; + return symsTable[ret].name; +} + +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__) + return (void*) uc->uc_mcontext->__ss.__eip; +#else /* Linux */ + return (void*) uc->uc_mcontext.gregs[REG_EIP]; +#endif +} + +static void segvHandler(int sig, siginfo_t *info, void *secret) { + void *trace[100]; + char **messages = NULL; + int i, trace_size = 0; + unsigned long offset=0; + time_t uptime = time(NULL)-server.stat_starttime; + ucontext_t *uc = (ucontext_t*) secret; + REDIS_NOTUSED(info); + + redisLog(REDIS_WARNING, + "======= Ooops! Redis %s got signal: -%d- =======", REDIS_VERSION, sig); + redisLog(REDIS_WARNING, "%s", sdscatprintf(sdsempty(), "redis_version:%s; " - "uptime_in_days:%d; " + "uptime_in_seconds:%d; " "connected_clients:%d; " "connected_slaves:%d; " "used_memory:%zu; " @@ -4244,42 +4267,38 @@ static void segvHandler (int sig, siginfo_t *info, void *secret) { server.stat_numcommands, server.masterhost == NULL ? "master" : "slave" )); - - redisLog(REDIS_WARNING,"EIP %p", (void *)uc->uc_mcontext.gregs[REG_EIP]); - redisLog(REDIS_WARNING,"EAX %p, EBX %p, ECX %p, EDX %p", (void *)uc->uc_mcontext.gregs[REG_EAX], (void *)uc->uc_mcontext.gregs[REG_EBX], (void *)uc->uc_mcontext.gregs[REG_ECX], (void *)uc->uc_mcontext.gregs[REG_EDX]); - - - trace_size = backtrace(trace, 100); - char pointer[trace_size][11]; + + trace_size = backtrace(trace, 100); /* overwrite sigaction with caller's address */ - trace[1] = (void *) uc->uc_mcontext.gregs[REG_EIP]; - for (i=1; i #include