From fbfe656236ac5cd96b25a71d4aa393b6816ac8b2 Mon Sep 17 00:00:00 2001 From: antirez Date: Thu, 12 Jan 2012 16:02:57 +0100 Subject: On crash print information about the current client (if any), command vector, and object associated to first argument assuming it is a key. --- src/debug.c | 21 +++++++++++++++++++++ src/networking.c | 6 ++++++ src/redis.c | 40 +++++++++++++++++++++++++++++++++++++++- src/redis.h | 3 +++ 4 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/debug.c b/src/debug.c index 4d34f62a5..123edb735 100644 --- a/src/debug.c +++ b/src/debug.c @@ -337,6 +337,27 @@ void debugCommand(redisClient *c) { } } +void redisLogObjectDebugInfo(robj *o) { + redisLog(REDIS_WARNING,"Object type: %d", o->type); + redisLog(REDIS_WARNING,"Object encoding: %d", o->encoding); + redisLog(REDIS_WARNING,"Object refcount: %d", o->refcount); + if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_RAW) { + redisLog(REDIS_WARNING,"Object raw string len: %d", sdslen(o->ptr)); + if (sdslen(o->ptr) < 4096) + redisLog(REDIS_WARNING,"Object raw string content: \"%s\"", (char*)o->ptr); + } else if (o->type == REDIS_LIST) { + redisLog(REDIS_WARNING,"List length: %d", (int) listTypeLength(o)); + } else if (o->type == REDIS_SET) { + redisLog(REDIS_WARNING,"Set size: %d", (int) setTypeSize(o)); + } else if (o->type == REDIS_HASH) { + redisLog(REDIS_WARNING,"Hash size: %d", (int) hashTypeLength(o)); + } else if (o->type == REDIS_ZSET) { + redisLog(REDIS_WARNING,"Sorted set size: %d", (int) zsetLength(o)); + if (o->encoding == REDIS_ENCODING_SKIPLIST) + redisLog(REDIS_WARNING,"Skiplist level: %d", (int) ((zset*)o->ptr)->zsl->level); + } +} + void _redisAssert(char *estr, char *file, int line) { bugReportStart(); redisLog(REDIS_WARNING,"=== ASSERTION FAILED ==="); diff --git a/src/networking.c b/src/networking.c index 811853877..b48927df7 100644 --- a/src/networking.c +++ b/src/networking.c @@ -467,6 +467,9 @@ static void freeClientArgv(redisClient *c) { void freeClient(redisClient *c) { listNode *ln; + /* If this is marked as current client unset it */ + if (server.current_client == c) server.current_client = NULL; + /* Note that if the client we are freeing is blocked into a blocking * call, we have to set querybuf to NULL *before* to call * unblockClientWaitingData() to avoid processInputBuffer() will get @@ -875,6 +878,7 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) { REDIS_NOTUSED(el); REDIS_NOTUSED(mask); + server.current_client = c; nread = read(fd, buf, REDIS_IOBUF_LEN); if (nread == -1) { if (errno == EAGAIN) { @@ -893,6 +897,7 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) { c->querybuf = sdscatlen(c->querybuf,buf,nread); c->lastinteraction = time(NULL); } else { + server.current_client = NULL; return; } if (sdslen(c->querybuf) > server.client_max_querybuf_len) { @@ -906,6 +911,7 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) { return; } processInputBuffer(c); + server.current_client = NULL; } void getClientsMaxBuffers(unsigned long *longest_output_list, diff --git a/src/redis.c b/src/redis.c index d4a9a30d9..34b3d418e 100644 --- a/src/redis.c +++ b/src/redis.c @@ -716,8 +716,11 @@ void beforeSleep(struct aeEventLoop *eventLoop) { call(c); resetClient(c); /* There may be more data to process in the input buffer. */ - if (c->querybuf && sdslen(c->querybuf) > 0) + if (c->querybuf && sdslen(c->querybuf) > 0) { + server.current_client = c; processInputBuffer(c); + server.current_client = NULL; + } } } @@ -907,6 +910,7 @@ void initServer() { } server.mainthread = pthread_self(); + server.current_client = NULL; server.clients = listCreate(); server.slaves = listCreate(); server.monitors = listCreate(); @@ -1797,6 +1801,40 @@ static void sigsegvHandler(int sig, siginfo_t *info, void *secret) { redisLog(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; + + 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 = dictGetEntryVal(de); + redisLog(REDIS_WARNING,"key '%s' found in DB containing the following object:", key->ptr); + redisLogObjectDebugInfo(val); + } + decrRefCount(key); + } + } + redisLog(REDIS_WARNING, "=== 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" diff --git a/src/redis.h b/src/redis.h index aca45ae8c..8a89bf00a 100644 --- a/src/redis.h +++ b/src/redis.h @@ -400,6 +400,7 @@ struct redisServer { /* Fast pointers to often looked up command */ struct redisCommand *delCommand, *multiCommand; list *slaves, *monitors; + redisClient *current_client; /* Current client, only used on crash report */ char neterr[ANET_ERR_LEN]; aeEventLoop *el; int cronloops; /* number of times the cron function run */ @@ -1072,4 +1073,6 @@ void *malloc(size_t size) __attribute__ ((deprecated)); void *realloc(void *ptr, size_t size) __attribute__ ((deprecated)); #endif +void redisLogObjectDebugInfo(robj *o); + #endif -- cgit v1.2.1