diff --git a/src/debug.c b/src/debug.c index d218f0f93179fc74e4ff4fc53c12cbf04bee4a2d..728aa8a18cc052277f8520759d4cb916e845267e 100644 --- a/src/debug.c +++ b/src/debug.c @@ -336,9 +336,7 @@ void _redisAssertPrintClientInfo(redisClient *c) { } } -void _redisAssertPrintObject(robj *o) { - bugReportStart(); - redisLog(REDIS_WARNING,"=== ASSERTION FAILED OBJECT CONTEXT ==="); +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); @@ -346,9 +344,25 @@ void _redisAssertPrintObject(robj *o) { 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 _redisAssertPrintObject(robj *o) { + bugReportStart(); + redisLog(REDIS_WARNING,"=== ASSERTION FAILED OBJECT CONTEXT ==="); + redisLogObjectDebugInfo(o); +} + void _redisAssertWithInfo(redisClient *c, robj *o, char *estr, char *file, int line) { if (c) _redisAssertPrintClientInfo(c); if (o) _redisAssertPrintObject(o); diff --git a/src/networking.c b/src/networking.c index ddb171b32d470658558fdc0209e3d6c645000170..8a025db35f53225b7b65312b689c491ca8c16f93 100644 --- a/src/networking.c +++ b/src/networking.c @@ -481,6 +481,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 @@ -900,6 +903,7 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) { REDIS_NOTUSED(el); REDIS_NOTUSED(mask); + server.current_client = c; readlen = REDIS_IOBUF_LEN; /* If this is a multi bulk request, and we are processing a bulk reply * that is large enough, try to maximize the probabilty that the query @@ -935,6 +939,7 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) { sdsIncrLen(c->querybuf,nread); c->lastinteraction = time(NULL); } else { + server.current_client = NULL; return; } if (sdslen(c->querybuf) > server.client_max_querybuf_len) { @@ -948,6 +953,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 5b585c479828817689653d8169d029bb91a5aa0e..6e897d83c0ecf2c7dbbc4df45a69ddbf9df3b00c 100644 --- a/src/redis.c +++ b/src/redis.c @@ -787,8 +787,11 @@ void beforeSleep(struct aeEventLoop *eventLoop) { c->flags &= ~REDIS_UNBLOCKED; /* Process remaining data 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; + } } /* Write the AOF buffer on disk */ @@ -1002,6 +1005,7 @@ void initServer() { server.syslog_facility); } + server.current_client = NULL; server.clients = listCreate(); server.slaves = listCreate(); server.monitors = listCreate(); @@ -2005,6 +2009,40 @@ static void sigsegvHandler(int sig, siginfo_t *info, void *secret) { redisLogRaw(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 = dictGetVal(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 f3b3134e0042fb1528b29fd83700d09d5bb690b3..1ac2a2d287b7d74222a62520074e4a1d66c29327 100644 --- a/src/redis.h +++ b/src/redis.h @@ -519,6 +519,7 @@ struct redisServer { int cfd; /* Cluster bus lisetning socket */ list *clients; /* List of active clients */ list *slaves, *monitors; /* List of slaves and MONITORs */ + redisClient *current_client; /* Current client, only used on crash report */ char neterr[ANET_ERR_LEN]; /* Error buffer for anet.c */ /* RDB / AOF loading information */ int loading; /* We are loading data from disk if true */ @@ -1159,5 +1160,6 @@ void _redisAssertWithInfo(redisClient *c, robj *o, char *estr, char *file, int l void _redisAssert(char *estr, char *file, int line); void _redisPanic(char *msg, char *file, int line); void bugReportStart(void); +void redisLogObjectDebugInfo(robj *o); #endif