2012-11-08 12:25:23 -05:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2010-06-21 18:07:48 -04:00
|
|
|
#include "redis.h"
|
|
|
|
#include "sha1.h" /* SHA1 is used for DEBUG DIGEST */
|
|
|
|
|
2010-07-01 15:13:38 -04:00
|
|
|
#include <arpa/inet.h>
|
2012-01-20 06:20:45 -05:00
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
#ifdef HAVE_BACKTRACE
|
|
|
|
#include <execinfo.h>
|
|
|
|
#include <ucontext.h>
|
2012-04-26 10:04:53 -04:00
|
|
|
#include <fcntl.h>
|
2012-01-20 06:20:45 -05:00
|
|
|
#endif /* HAVE_BACKTRACE */
|
2010-07-01 15:13:38 -04:00
|
|
|
|
2010-06-21 18:07:48 -04:00
|
|
|
/* ================================= Debugging ============================== */
|
|
|
|
|
|
|
|
/* Compute the sha1 of string at 's' with 'len' bytes long.
|
|
|
|
* The SHA1 is then xored againt the string pointed by digest.
|
|
|
|
* Since xor is commutative, this operation is used in order to
|
|
|
|
* "add" digests relative to unordered elements.
|
|
|
|
*
|
|
|
|
* So digest(a,b,c,d) will be the same of digest(b,a,c,d) */
|
|
|
|
void xorDigest(unsigned char *digest, void *ptr, size_t len) {
|
|
|
|
SHA1_CTX ctx;
|
|
|
|
unsigned char hash[20], *s = ptr;
|
|
|
|
int j;
|
|
|
|
|
|
|
|
SHA1Init(&ctx);
|
|
|
|
SHA1Update(&ctx,s,len);
|
|
|
|
SHA1Final(hash,&ctx);
|
|
|
|
|
|
|
|
for (j = 0; j < 20; j++)
|
|
|
|
digest[j] ^= hash[j];
|
|
|
|
}
|
|
|
|
|
|
|
|
void xorObjectDigest(unsigned char *digest, robj *o) {
|
|
|
|
o = getDecodedObject(o);
|
|
|
|
xorDigest(digest,o->ptr,sdslen(o->ptr));
|
|
|
|
decrRefCount(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This function instead of just computing the SHA1 and xoring it
|
|
|
|
* against diget, also perform the digest of "digest" itself and
|
|
|
|
* replace the old value with the new one.
|
|
|
|
*
|
|
|
|
* So the final digest will be:
|
|
|
|
*
|
|
|
|
* digest = SHA1(digest xor SHA1(data))
|
|
|
|
*
|
|
|
|
* This function is used every time we want to preserve the order so
|
|
|
|
* that digest(a,b,c,d) will be different than digest(b,c,d,a)
|
|
|
|
*
|
|
|
|
* Also note that mixdigest("foo") followed by mixdigest("bar")
|
|
|
|
* will lead to a different digest compared to "fo", "obar".
|
|
|
|
*/
|
|
|
|
void mixDigest(unsigned char *digest, void *ptr, size_t len) {
|
|
|
|
SHA1_CTX ctx;
|
|
|
|
char *s = ptr;
|
|
|
|
|
|
|
|
xorDigest(digest,s,len);
|
|
|
|
SHA1Init(&ctx);
|
|
|
|
SHA1Update(&ctx,digest,20);
|
|
|
|
SHA1Final(digest,&ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
void mixObjectDigest(unsigned char *digest, robj *o) {
|
|
|
|
o = getDecodedObject(o);
|
|
|
|
mixDigest(digest,o->ptr,sdslen(o->ptr));
|
|
|
|
decrRefCount(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compute the dataset digest. Since keys, sets elements, hashes elements
|
|
|
|
* are not ordered, we use a trick: every aggregate digest is the xor
|
|
|
|
* of the digests of their elements. This way the order will not change
|
|
|
|
* the result. For list instead we use a feedback entering the output digest
|
|
|
|
* as input in order to ensure that a different ordered list will result in
|
|
|
|
* a different digest. */
|
|
|
|
void computeDatasetDigest(unsigned char *final) {
|
|
|
|
unsigned char digest[20];
|
|
|
|
char buf[128];
|
|
|
|
dictIterator *di = NULL;
|
|
|
|
dictEntry *de;
|
|
|
|
int j;
|
|
|
|
uint32_t aux;
|
|
|
|
|
|
|
|
memset(final,0,20); /* Start with a clean result */
|
|
|
|
|
|
|
|
for (j = 0; j < server.dbnum; j++) {
|
|
|
|
redisDb *db = server.db+j;
|
|
|
|
|
|
|
|
if (dictSize(db->dict) == 0) continue;
|
|
|
|
di = dictGetIterator(db->dict);
|
|
|
|
|
|
|
|
/* hash the DB id, so the same dataset moved in a different
|
|
|
|
* DB will lead to a different digest */
|
|
|
|
aux = htonl(j);
|
|
|
|
mixDigest(final,&aux,sizeof(aux));
|
|
|
|
|
|
|
|
/* Iterate this DB writing every entry */
|
|
|
|
while((de = dictNext(di)) != NULL) {
|
|
|
|
sds key;
|
|
|
|
robj *keyobj, *o;
|
2011-11-11 19:04:27 -05:00
|
|
|
long long expiretime;
|
2010-06-21 18:07:48 -04:00
|
|
|
|
|
|
|
memset(digest,0,20); /* This key-val digest */
|
2011-11-08 11:07:55 -05:00
|
|
|
key = dictGetKey(de);
|
2010-06-21 18:07:48 -04:00
|
|
|
keyobj = createStringObject(key,sdslen(key));
|
|
|
|
|
|
|
|
mixDigest(digest,key,sdslen(key));
|
|
|
|
|
2011-11-08 11:07:55 -05:00
|
|
|
o = dictGetVal(de);
|
2010-06-21 18:07:48 -04:00
|
|
|
|
|
|
|
aux = htonl(o->type);
|
|
|
|
mixDigest(digest,&aux,sizeof(aux));
|
|
|
|
expiretime = getExpire(db,keyobj);
|
|
|
|
|
|
|
|
/* Save the key and associated value */
|
|
|
|
if (o->type == REDIS_STRING) {
|
|
|
|
mixObjectDigest(digest,o);
|
|
|
|
} else if (o->type == REDIS_LIST) {
|
|
|
|
listTypeIterator *li = listTypeInitIterator(o,0,REDIS_TAIL);
|
|
|
|
listTypeEntry entry;
|
|
|
|
while(listTypeNext(li,&entry)) {
|
|
|
|
robj *eleobj = listTypeGet(&entry);
|
|
|
|
mixObjectDigest(digest,eleobj);
|
|
|
|
decrRefCount(eleobj);
|
|
|
|
}
|
|
|
|
listTypeReleaseIterator(li);
|
|
|
|
} else if (o->type == REDIS_SET) {
|
2010-08-21 05:25:13 -04:00
|
|
|
setTypeIterator *si = setTypeInitIterator(o);
|
2010-07-02 14:42:20 -04:00
|
|
|
robj *ele;
|
2010-12-09 15:11:56 -05:00
|
|
|
while((ele = setTypeNextObject(si)) != NULL) {
|
2010-07-02 14:42:20 -04:00
|
|
|
xorObjectDigest(digest,ele);
|
|
|
|
decrRefCount(ele);
|
2010-06-21 18:07:48 -04:00
|
|
|
}
|
2010-07-02 14:42:20 -04:00
|
|
|
setTypeReleaseIterator(si);
|
2010-06-21 18:07:48 -04:00
|
|
|
} else if (o->type == REDIS_ZSET) {
|
2011-03-14 08:30:06 -04:00
|
|
|
unsigned char eledigest[20];
|
|
|
|
|
|
|
|
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
|
|
|
|
unsigned char *zl = o->ptr;
|
|
|
|
unsigned char *eptr, *sptr;
|
|
|
|
unsigned char *vstr;
|
|
|
|
unsigned int vlen;
|
|
|
|
long long vll;
|
|
|
|
double score;
|
|
|
|
|
|
|
|
eptr = ziplistIndex(zl,0);
|
|
|
|
redisAssert(eptr != NULL);
|
|
|
|
sptr = ziplistNext(zl,eptr);
|
|
|
|
redisAssert(sptr != NULL);
|
|
|
|
|
|
|
|
while (eptr != NULL) {
|
|
|
|
redisAssert(ziplistGet(eptr,&vstr,&vlen,&vll));
|
|
|
|
score = zzlGetScore(sptr);
|
|
|
|
|
|
|
|
memset(eledigest,0,20);
|
|
|
|
if (vstr != NULL) {
|
|
|
|
mixDigest(eledigest,vstr,vlen);
|
|
|
|
} else {
|
|
|
|
ll2string(buf,sizeof(buf),vll);
|
|
|
|
mixDigest(eledigest,buf,strlen(buf));
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(buf,sizeof(buf),"%.17g",score);
|
|
|
|
mixDigest(eledigest,buf,strlen(buf));
|
|
|
|
xorDigest(digest,eledigest,20);
|
|
|
|
zzlNext(zl,&eptr,&sptr);
|
|
|
|
}
|
2011-04-06 10:17:07 -04:00
|
|
|
} else if (o->encoding == REDIS_ENCODING_SKIPLIST) {
|
2011-03-14 08:30:06 -04:00
|
|
|
zset *zs = o->ptr;
|
|
|
|
dictIterator *di = dictGetIterator(zs->dict);
|
|
|
|
dictEntry *de;
|
|
|
|
|
|
|
|
while((de = dictNext(di)) != NULL) {
|
2011-11-08 11:07:55 -05:00
|
|
|
robj *eleobj = dictGetKey(de);
|
|
|
|
double *score = dictGetVal(de);
|
2011-03-14 08:30:06 -04:00
|
|
|
|
|
|
|
snprintf(buf,sizeof(buf),"%.17g",*score);
|
|
|
|
memset(eledigest,0,20);
|
|
|
|
mixObjectDigest(eledigest,eleobj);
|
|
|
|
mixDigest(eledigest,buf,strlen(buf));
|
|
|
|
xorDigest(digest,eledigest,20);
|
|
|
|
}
|
|
|
|
dictReleaseIterator(di);
|
|
|
|
} else {
|
|
|
|
redisPanic("Unknown sorted set encoding");
|
2010-06-21 18:07:48 -04:00
|
|
|
}
|
|
|
|
} else if (o->type == REDIS_HASH) {
|
|
|
|
hashTypeIterator *hi;
|
|
|
|
robj *obj;
|
|
|
|
|
|
|
|
hi = hashTypeInitIterator(o);
|
|
|
|
while (hashTypeNext(hi) != REDIS_ERR) {
|
|
|
|
unsigned char eledigest[20];
|
|
|
|
|
|
|
|
memset(eledigest,0,20);
|
2010-12-14 06:10:51 -05:00
|
|
|
obj = hashTypeCurrentObject(hi,REDIS_HASH_KEY);
|
2010-06-21 18:07:48 -04:00
|
|
|
mixObjectDigest(eledigest,obj);
|
|
|
|
decrRefCount(obj);
|
2010-12-14 06:10:51 -05:00
|
|
|
obj = hashTypeCurrentObject(hi,REDIS_HASH_VALUE);
|
2010-06-21 18:07:48 -04:00
|
|
|
mixObjectDigest(eledigest,obj);
|
|
|
|
decrRefCount(obj);
|
|
|
|
xorDigest(digest,eledigest,20);
|
|
|
|
}
|
|
|
|
hashTypeReleaseIterator(hi);
|
|
|
|
} else {
|
|
|
|
redisPanic("Unknown object type");
|
|
|
|
}
|
|
|
|
/* If the key has an expire, add it to the mix */
|
|
|
|
if (expiretime != -1) xorDigest(digest,"!!expire!!",10);
|
|
|
|
/* We can finally xor the key-val digest to the final digest */
|
|
|
|
xorDigest(final,digest,20);
|
|
|
|
decrRefCount(keyobj);
|
|
|
|
}
|
|
|
|
dictReleaseIterator(di);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void debugCommand(redisClient *c) {
|
|
|
|
if (!strcasecmp(c->argv[1]->ptr,"segfault")) {
|
|
|
|
*((char*)-1) = 'x';
|
2012-08-24 06:55:37 -04:00
|
|
|
} 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);
|
2011-10-04 11:22:29 -04:00
|
|
|
} else if (!strcasecmp(c->argv[1]->ptr,"assert")) {
|
|
|
|
if (c->argc >= 3) c->argv[2] = tryObjectEncoding(c->argv[2]);
|
2011-10-04 12:05:26 -04:00
|
|
|
redisAssertWithInfo(c,c->argv[0],1 == 2);
|
2010-06-21 18:07:48 -04:00
|
|
|
} else if (!strcasecmp(c->argv[1]->ptr,"reload")) {
|
2011-12-21 06:22:13 -05:00
|
|
|
if (rdbSave(server.rdb_filename) != REDIS_OK) {
|
2010-06-21 18:07:48 -04:00
|
|
|
addReply(c,shared.err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
emptyDb();
|
2011-12-21 06:22:13 -05:00
|
|
|
if (rdbLoad(server.rdb_filename) != REDIS_OK) {
|
2011-10-14 08:30:41 -04:00
|
|
|
addReplyError(c,"Error trying to load the RDB dump");
|
2010-06-21 18:07:48 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
redisLog(REDIS_WARNING,"DB reloaded by DEBUG RELOAD");
|
|
|
|
addReply(c,shared.ok);
|
|
|
|
} else if (!strcasecmp(c->argv[1]->ptr,"loadaof")) {
|
|
|
|
emptyDb();
|
2011-12-21 05:58:42 -05:00
|
|
|
if (loadAppendOnlyFile(server.aof_filename) != REDIS_OK) {
|
2010-06-21 18:07:48 -04:00
|
|
|
addReply(c,shared.err);
|
|
|
|
return;
|
|
|
|
}
|
2011-12-20 11:52:57 -05:00
|
|
|
server.dirty = 0; /* Prevent AOF / replication */
|
2010-06-21 18:07:48 -04:00
|
|
|
redisLog(REDIS_WARNING,"Append Only File loaded by DEBUG LOADAOF");
|
|
|
|
addReply(c,shared.ok);
|
|
|
|
} else if (!strcasecmp(c->argv[1]->ptr,"object") && c->argc == 3) {
|
2011-01-07 20:06:01 -05:00
|
|
|
dictEntry *de;
|
2010-06-21 18:07:48 -04:00
|
|
|
robj *val;
|
2010-12-29 10:58:57 -05:00
|
|
|
char *strenc;
|
2010-06-21 18:07:48 -04:00
|
|
|
|
2011-01-07 20:06:01 -05:00
|
|
|
if ((de = dictFind(c->db->dict,c->argv[2]->ptr)) == NULL) {
|
2010-06-21 18:07:48 -04:00
|
|
|
addReply(c,shared.nokeyerr);
|
|
|
|
return;
|
|
|
|
}
|
2011-11-08 11:07:55 -05:00
|
|
|
val = dictGetVal(de);
|
2010-12-29 10:58:57 -05:00
|
|
|
strenc = strEncoding(val->encoding);
|
2011-01-01 15:35:56 -05:00
|
|
|
|
2010-12-29 10:58:57 -05:00
|
|
|
addReplyStatusFormat(c,
|
|
|
|
"Value at:%p refcount:%d "
|
|
|
|
"encoding:%s serializedlength:%lld "
|
2011-01-01 15:35:56 -05:00
|
|
|
"lru:%d lru_seconds_idle:%lu",
|
2010-12-29 10:58:57 -05:00
|
|
|
(void*)val, val->refcount,
|
|
|
|
strenc, (long long) rdbSavedObjectLen(val),
|
2011-01-01 15:35:56 -05:00
|
|
|
val->lru, estimateObjectIdleTime(val));
|
2010-06-21 18:07:48 -04:00
|
|
|
} else if (!strcasecmp(c->argv[1]->ptr,"populate") && c->argc == 3) {
|
|
|
|
long keys, j;
|
|
|
|
robj *key, *val;
|
|
|
|
char buf[128];
|
|
|
|
|
|
|
|
if (getLongFromObjectOrReply(c, c->argv[2], &keys, NULL) != REDIS_OK)
|
|
|
|
return;
|
|
|
|
for (j = 0; j < keys; j++) {
|
|
|
|
snprintf(buf,sizeof(buf),"key:%lu",j);
|
|
|
|
key = createStringObject(buf,strlen(buf));
|
|
|
|
if (lookupKeyRead(c->db,key) != NULL) {
|
|
|
|
decrRefCount(key);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
snprintf(buf,sizeof(buf),"value:%lu",j);
|
|
|
|
val = createStringObject(buf,strlen(buf));
|
|
|
|
dbAdd(c->db,key,val);
|
|
|
|
decrRefCount(key);
|
|
|
|
}
|
|
|
|
addReply(c,shared.ok);
|
|
|
|
} else if (!strcasecmp(c->argv[1]->ptr,"digest") && c->argc == 2) {
|
|
|
|
unsigned char digest[20];
|
2010-09-02 13:52:24 -04:00
|
|
|
sds d = sdsempty();
|
2010-06-21 18:07:48 -04:00
|
|
|
int j;
|
|
|
|
|
|
|
|
computeDatasetDigest(digest);
|
|
|
|
for (j = 0; j < 20; j++)
|
|
|
|
d = sdscatprintf(d, "%02x",digest[j]);
|
2010-09-02 13:52:24 -04:00
|
|
|
addReplyStatus(c,d);
|
|
|
|
sdsfree(d);
|
2011-06-30 07:31:44 -04:00
|
|
|
} else if (!strcasecmp(c->argv[1]->ptr,"sleep") && c->argc == 3) {
|
|
|
|
double dtime = strtod(c->argv[2]->ptr,NULL);
|
|
|
|
long long utime = dtime*1000000;
|
|
|
|
|
|
|
|
usleep(utime);
|
|
|
|
addReply(c,shared.ok);
|
2010-06-21 18:07:48 -04:00
|
|
|
} else {
|
2010-09-02 13:52:24 -04:00
|
|
|
addReplyError(c,
|
|
|
|
"Syntax error, try DEBUG [SEGFAULT|OBJECT <key>|SWAPIN <key>|SWAPOUT <key>|RELOAD]");
|
2010-06-21 18:07:48 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-20 06:20:45 -05:00
|
|
|
/* =========================== Crash handling ============================== */
|
|
|
|
|
2010-06-21 18:07:48 -04:00
|
|
|
void _redisAssert(char *estr, char *file, int line) {
|
2011-11-24 09:47:26 -05:00
|
|
|
bugReportStart();
|
2010-06-21 18:07:48 -04:00
|
|
|
redisLog(REDIS_WARNING,"=== ASSERTION FAILED ===");
|
|
|
|
redisLog(REDIS_WARNING,"==> %s:%d '%s' is not true",file,line,estr);
|
|
|
|
#ifdef HAVE_BACKTRACE
|
2011-11-24 09:47:26 -05:00
|
|
|
server.assert_failed = estr;
|
|
|
|
server.assert_file = file;
|
|
|
|
server.assert_line = line;
|
|
|
|
redisLog(REDIS_WARNING,"(forcing SIGSEGV to print the bug report.)");
|
2010-06-21 18:07:48 -04:00
|
|
|
#endif
|
2012-02-21 13:05:46 -05:00
|
|
|
*((char*)-1) = 'x';
|
2010-06-21 18:07:48 -04:00
|
|
|
}
|
|
|
|
|
2011-10-04 11:22:29 -04:00
|
|
|
void _redisAssertPrintClientInfo(redisClient *c) {
|
2011-10-04 12:05:26 -04:00
|
|
|
int j;
|
2011-10-04 11:22:29 -04:00
|
|
|
|
2011-11-24 09:47:26 -05:00
|
|
|
bugReportStart();
|
2011-10-04 12:05:26 -04:00
|
|
|
redisLog(REDIS_WARNING,"=== ASSERTION FAILED CLIENT CONTEXT ===");
|
|
|
|
redisLog(REDIS_WARNING,"client->flags = %d", c->flags);
|
|
|
|
redisLog(REDIS_WARNING,"client->fd = %d", c->fd);
|
|
|
|
redisLog(REDIS_WARNING,"client->argc = %d", c->argc);
|
|
|
|
for (j=0; j < c->argc; j++) {
|
|
|
|
char buf[128];
|
|
|
|
char *arg;
|
|
|
|
|
|
|
|
if (c->argv[j]->type == REDIS_STRING &&
|
|
|
|
c->argv[j]->encoding == REDIS_ENCODING_RAW)
|
|
|
|
{
|
|
|
|
arg = (char*) c->argv[j]->ptr;
|
|
|
|
} else {
|
|
|
|
snprintf(buf,sizeof(buf),"Object type: %d, encoding: %d",
|
|
|
|
c->argv[j]->type, c->argv[j]->encoding);
|
|
|
|
arg = buf;
|
2011-10-04 11:22:29 -04:00
|
|
|
}
|
2011-10-04 12:05:26 -04:00
|
|
|
redisLog(REDIS_WARNING,"client->argv[%d] = \"%s\" (refcount: %d)",
|
|
|
|
j, arg, c->argv[j]->refcount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-12 10:02:57 -05:00
|
|
|
void redisLogObjectDebugInfo(robj *o) {
|
2011-10-04 12:05:26 -04:00
|
|
|
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);
|
2012-01-12 10:02:57 -05:00
|
|
|
} 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);
|
2011-10-04 11:22:29 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-12 10:02:57 -05:00
|
|
|
void _redisAssertPrintObject(robj *o) {
|
|
|
|
bugReportStart();
|
|
|
|
redisLog(REDIS_WARNING,"=== ASSERTION FAILED OBJECT CONTEXT ===");
|
|
|
|
redisLogObjectDebugInfo(o);
|
|
|
|
}
|
|
|
|
|
2011-10-04 12:05:26 -04:00
|
|
|
void _redisAssertWithInfo(redisClient *c, robj *o, char *estr, char *file, int line) {
|
|
|
|
if (c) _redisAssertPrintClientInfo(c);
|
|
|
|
if (o) _redisAssertPrintObject(o);
|
2011-10-04 11:22:29 -04:00
|
|
|
_redisAssert(estr,file,line);
|
|
|
|
}
|
|
|
|
|
2010-06-21 18:07:48 -04:00
|
|
|
void _redisPanic(char *msg, char *file, int line) {
|
2011-11-24 09:47:26 -05:00
|
|
|
bugReportStart();
|
2011-01-03 04:17:39 -05:00
|
|
|
redisLog(REDIS_WARNING,"------------------------------------------------");
|
2010-06-21 18:07:48 -04:00
|
|
|
redisLog(REDIS_WARNING,"!!! Software Failure. Press left mouse button to continue");
|
|
|
|
redisLog(REDIS_WARNING,"Guru Meditation: %s #%s:%d",msg,file,line);
|
|
|
|
#ifdef HAVE_BACKTRACE
|
|
|
|
redisLog(REDIS_WARNING,"(forcing SIGSEGV in order to print the stack trace)");
|
2012-02-21 13:05:46 -05:00
|
|
|
#endif
|
2011-01-03 04:17:39 -05:00
|
|
|
redisLog(REDIS_WARNING,"------------------------------------------------");
|
2010-06-21 18:07:48 -04:00
|
|
|
*((char*)-1) = 'x';
|
|
|
|
}
|
2012-01-20 06:20:45 -05:00
|
|
|
|
2012-02-08 16:24:59 -05:00
|
|
|
void bugReportStart(void) {
|
|
|
|
if (server.bug_report_start == 0) {
|
|
|
|
redisLog(REDIS_WARNING,
|
|
|
|
"\n\n=== REDIS BUG REPORT START: Cut & paste starting from here ===");
|
|
|
|
server.bug_report_start = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-20 06:20:45 -05:00
|
|
|
#ifdef HAVE_BACKTRACE
|
|
|
|
static void *getMcontextEip(ucontext_t *uc) {
|
2012-04-24 05:07:15 -04:00
|
|
|
#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
|
|
|
|
/* OSX < 10.6 */
|
|
|
|
#if defined(__x86_64__)
|
2012-01-20 06:20:45 -05:00
|
|
|
return (void*) uc->uc_mcontext->__ss.__rip;
|
2012-04-24 05:07:15 -04:00
|
|
|
#elif defined(__i386__)
|
2012-01-20 06:20:45 -05:00
|
|
|
return (void*) uc->uc_mcontext->__ss.__eip;
|
2012-04-24 05:07:15 -04:00
|
|
|
#else
|
2012-01-20 06:20:45 -05:00
|
|
|
return (void*) uc->uc_mcontext->__ss.__srr0;
|
2012-04-24 05:07:15 -04:00
|
|
|
#endif
|
2012-01-20 06:20:45 -05:00
|
|
|
#elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
|
2012-04-24 05:07:15 -04:00
|
|
|
/* OSX >= 10.6 */
|
|
|
|
#if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
|
2012-01-20 06:20:45 -05:00
|
|
|
return (void*) uc->uc_mcontext->__ss.__rip;
|
2012-04-24 05:07:15 -04:00
|
|
|
#else
|
2012-01-20 06:20:45 -05:00
|
|
|
return (void*) uc->uc_mcontext->__ss.__eip;
|
2012-04-24 05:07:15 -04:00
|
|
|
#endif
|
|
|
|
#elif defined(__linux__)
|
|
|
|
/* Linux */
|
|
|
|
#if defined(__i386__)
|
2012-01-20 06:20:45 -05:00
|
|
|
return (void*) uc->uc_mcontext.gregs[14]; /* Linux 32 */
|
2012-04-24 05:07:15 -04:00
|
|
|
#elif defined(__X86_64__) || defined(__x86_64__)
|
2012-01-20 06:20:45 -05:00
|
|
|
return (void*) uc->uc_mcontext.gregs[16]; /* Linux 64 */
|
2012-04-24 05:07:15 -04:00
|
|
|
#elif defined(__ia64__) /* Linux IA64 */
|
2012-01-20 06:20:45 -05:00
|
|
|
return (void*) uc->uc_mcontext.sc_ip;
|
2012-04-24 05:07:15 -04:00
|
|
|
#endif
|
2012-01-20 06:20:45 -05:00
|
|
|
#else
|
|
|
|
return NULL;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void logStackContent(void **sp) {
|
|
|
|
int i;
|
|
|
|
for (i = 15; i >= 0; i--) {
|
2012-01-20 10:40:43 -05:00
|
|
|
if (sizeof(long) == 4)
|
|
|
|
redisLog(REDIS_WARNING, "(%08lx) -> %08lx", sp+i, sp[i]);
|
|
|
|
else
|
|
|
|
redisLog(REDIS_WARNING, "(%016lx) -> %016lx", sp+i, sp[i]);
|
2012-01-20 06:20:45 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void logRegisters(ucontext_t *uc) {
|
|
|
|
redisLog(REDIS_WARNING, "--- REGISTERS");
|
2012-04-24 05:07:15 -04:00
|
|
|
|
|
|
|
/* OSX */
|
2012-01-20 06:20:45 -05:00
|
|
|
#if defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
|
2012-04-24 05:07:15 -04:00
|
|
|
/* OSX AMD64 */
|
|
|
|
#if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
|
2012-01-20 06:20:45 -05:00
|
|
|
redisLog(REDIS_WARNING,
|
|
|
|
"\n"
|
2012-01-20 10:40:43 -05:00
|
|
|
"RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n"
|
|
|
|
"RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n"
|
|
|
|
"R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n"
|
|
|
|
"R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n"
|
|
|
|
"RIP:%016lx EFL:%016lx\nCS :%016lx FS:%016lx GS:%016lx",
|
2012-01-20 06:20:45 -05:00
|
|
|
uc->uc_mcontext->__ss.__rax,
|
|
|
|
uc->uc_mcontext->__ss.__rbx,
|
|
|
|
uc->uc_mcontext->__ss.__rcx,
|
|
|
|
uc->uc_mcontext->__ss.__rdx,
|
|
|
|
uc->uc_mcontext->__ss.__rdi,
|
|
|
|
uc->uc_mcontext->__ss.__rsi,
|
|
|
|
uc->uc_mcontext->__ss.__rbp,
|
|
|
|
uc->uc_mcontext->__ss.__rsp,
|
|
|
|
uc->uc_mcontext->__ss.__r8,
|
|
|
|
uc->uc_mcontext->__ss.__r9,
|
|
|
|
uc->uc_mcontext->__ss.__r10,
|
|
|
|
uc->uc_mcontext->__ss.__r11,
|
|
|
|
uc->uc_mcontext->__ss.__r12,
|
|
|
|
uc->uc_mcontext->__ss.__r13,
|
|
|
|
uc->uc_mcontext->__ss.__r14,
|
|
|
|
uc->uc_mcontext->__ss.__r15,
|
|
|
|
uc->uc_mcontext->__ss.__rip,
|
|
|
|
uc->uc_mcontext->__ss.__rflags,
|
|
|
|
uc->uc_mcontext->__ss.__cs,
|
|
|
|
uc->uc_mcontext->__ss.__fs,
|
|
|
|
uc->uc_mcontext->__ss.__gs
|
|
|
|
);
|
|
|
|
logStackContent((void**)uc->uc_mcontext->__ss.__rsp);
|
2012-04-24 05:07:15 -04:00
|
|
|
#else
|
|
|
|
/* OSX x86 */
|
2012-01-20 06:20:45 -05:00
|
|
|
redisLog(REDIS_WARNING,
|
|
|
|
"\n"
|
2012-01-20 10:40:43 -05:00
|
|
|
"EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n"
|
|
|
|
"EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n"
|
|
|
|
"SS:%08lx EFL:%08lx EIP:%08lx CS :%08lx\n"
|
|
|
|
"DS:%08lx ES:%08lx FS :%08lx GS :%08lx",
|
2012-01-20 06:20:45 -05:00
|
|
|
uc->uc_mcontext->__ss.__eax,
|
|
|
|
uc->uc_mcontext->__ss.__ebx,
|
|
|
|
uc->uc_mcontext->__ss.__ecx,
|
|
|
|
uc->uc_mcontext->__ss.__edx,
|
|
|
|
uc->uc_mcontext->__ss.__edi,
|
|
|
|
uc->uc_mcontext->__ss.__esi,
|
|
|
|
uc->uc_mcontext->__ss.__ebp,
|
|
|
|
uc->uc_mcontext->__ss.__esp,
|
|
|
|
uc->uc_mcontext->__ss.__ss,
|
|
|
|
uc->uc_mcontext->__ss.__eflags,
|
|
|
|
uc->uc_mcontext->__ss.__eip,
|
|
|
|
uc->uc_mcontext->__ss.__cs,
|
|
|
|
uc->uc_mcontext->__ss.__ds,
|
|
|
|
uc->uc_mcontext->__ss.__es,
|
|
|
|
uc->uc_mcontext->__ss.__fs,
|
|
|
|
uc->uc_mcontext->__ss.__gs
|
|
|
|
);
|
|
|
|
logStackContent((void**)uc->uc_mcontext->__ss.__esp);
|
2012-04-24 05:07:15 -04:00
|
|
|
#endif
|
|
|
|
/* Linux */
|
|
|
|
#elif defined(__linux__)
|
|
|
|
/* Linux x86 */
|
|
|
|
#if defined(__i386__)
|
2012-01-20 08:37:50 -05:00
|
|
|
redisLog(REDIS_WARNING,
|
|
|
|
"\n"
|
2012-01-20 10:40:43 -05:00
|
|
|
"EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n"
|
|
|
|
"EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n"
|
|
|
|
"SS :%08lx EFL:%08lx EIP:%08lx CS:%08lx\n"
|
|
|
|
"DS :%08lx ES :%08lx FS :%08lx GS:%08lx",
|
2012-01-20 08:37:50 -05:00
|
|
|
uc->uc_mcontext.gregs[11],
|
|
|
|
uc->uc_mcontext.gregs[8],
|
|
|
|
uc->uc_mcontext.gregs[10],
|
|
|
|
uc->uc_mcontext.gregs[9],
|
|
|
|
uc->uc_mcontext.gregs[4],
|
|
|
|
uc->uc_mcontext.gregs[5],
|
|
|
|
uc->uc_mcontext.gregs[6],
|
|
|
|
uc->uc_mcontext.gregs[7],
|
|
|
|
uc->uc_mcontext.gregs[18],
|
|
|
|
uc->uc_mcontext.gregs[17],
|
|
|
|
uc->uc_mcontext.gregs[14],
|
|
|
|
uc->uc_mcontext.gregs[15],
|
|
|
|
uc->uc_mcontext.gregs[3],
|
|
|
|
uc->uc_mcontext.gregs[2],
|
|
|
|
uc->uc_mcontext.gregs[1],
|
|
|
|
uc->uc_mcontext.gregs[0]
|
|
|
|
);
|
|
|
|
logStackContent((void**)uc->uc_mcontext.gregs[7]);
|
2012-04-24 05:07:15 -04:00
|
|
|
#elif defined(__X86_64__) || defined(__x86_64__)
|
|
|
|
/* Linux AMD64 */
|
2012-01-20 06:54:15 -05:00
|
|
|
redisLog(REDIS_WARNING,
|
|
|
|
"\n"
|
2012-01-20 10:40:43 -05:00
|
|
|
"RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n"
|
|
|
|
"RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n"
|
|
|
|
"R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n"
|
|
|
|
"R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n"
|
|
|
|
"RIP:%016lx EFL:%016lx\nCSGSFS:%016lx",
|
2012-01-20 06:54:15 -05:00
|
|
|
uc->uc_mcontext.gregs[13],
|
|
|
|
uc->uc_mcontext.gregs[11],
|
|
|
|
uc->uc_mcontext.gregs[14],
|
|
|
|
uc->uc_mcontext.gregs[12],
|
|
|
|
uc->uc_mcontext.gregs[8],
|
|
|
|
uc->uc_mcontext.gregs[9],
|
|
|
|
uc->uc_mcontext.gregs[10],
|
|
|
|
uc->uc_mcontext.gregs[15],
|
|
|
|
uc->uc_mcontext.gregs[0],
|
|
|
|
uc->uc_mcontext.gregs[1],
|
|
|
|
uc->uc_mcontext.gregs[2],
|
|
|
|
uc->uc_mcontext.gregs[3],
|
|
|
|
uc->uc_mcontext.gregs[4],
|
|
|
|
uc->uc_mcontext.gregs[5],
|
|
|
|
uc->uc_mcontext.gregs[6],
|
|
|
|
uc->uc_mcontext.gregs[7],
|
|
|
|
uc->uc_mcontext.gregs[16],
|
|
|
|
uc->uc_mcontext.gregs[17],
|
|
|
|
uc->uc_mcontext.gregs[18]
|
|
|
|
);
|
|
|
|
logStackContent((void**)uc->uc_mcontext.gregs[15]);
|
2012-04-24 05:07:15 -04:00
|
|
|
#endif
|
2012-01-20 06:20:45 -05:00
|
|
|
#else
|
|
|
|
redisLog(REDIS_WARNING,
|
|
|
|
" Dumping of registers not supported for this OS/arch");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2012-04-26 10:04:53 -04:00
|
|
|
/* Logs the stack trace using the backtrace() call. This function is designed
|
|
|
|
* to be called from signal handlers safely. */
|
|
|
|
void logStackTrace(ucontext_t *uc) {
|
2012-01-20 06:20:45 -05:00
|
|
|
void *trace[100];
|
2012-04-26 10:04:53 -04:00
|
|
|
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;
|
2012-01-20 06:20:45 -05:00
|
|
|
|
|
|
|
/* Generate the stack trace */
|
|
|
|
trace_size = backtrace(trace, 100);
|
|
|
|
|
|
|
|
/* overwrite sigaction with caller's address */
|
2012-04-26 10:04:53 -04:00
|
|
|
if (getMcontextEip(uc) != NULL)
|
2012-01-20 06:20:45 -05:00
|
|
|
trace[1] = getMcontextEip(uc);
|
2012-04-26 10:04:53 -04:00
|
|
|
|
|
|
|
/* Write symbols to log file */
|
|
|
|
backtrace_symbols_fd(trace, trace_size, fd);
|
|
|
|
|
|
|
|
/* Cleanup */
|
|
|
|
if (server.logfile) close(fd);
|
2012-03-27 04:33:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Log information about the "current" client, that is, the client that is
|
|
|
|
* currently being served by Redis. May be NULL if Redis is not serving a
|
|
|
|
* client right now. */
|
|
|
|
void logCurrentClient(void) {
|
|
|
|
if (server.current_client == NULL) return;
|
|
|
|
|
|
|
|
redisClient *cc = server.current_client;
|
|
|
|
sds client;
|
|
|
|
int j;
|
|
|
|
|
|
|
|
redisLog(REDIS_WARNING, "--- CURRENT CLIENT INFO");
|
|
|
|
client = getClientInfoString(cc);
|
|
|
|
redisLog(REDIS_WARNING,"client: %s", client);
|
|
|
|
sdsfree(client);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-21 07:19:38 -05:00
|
|
|
#if defined(HAVE_PROC_MAPS)
|
|
|
|
int memtest_non_destructive(void *addr, size_t size); /* memtest.c */
|
|
|
|
|
|
|
|
int memtest_test_linux_anonymous_maps(void) {
|
|
|
|
FILE *fp = fopen("/proc/self/maps","r");
|
|
|
|
char line[1024];
|
|
|
|
size_t start_addr, end_addr, size;
|
|
|
|
|
|
|
|
while(fgets(line,sizeof(line),fp) != NULL) {
|
|
|
|
char *start, *end, *p = line;
|
|
|
|
int j;
|
|
|
|
|
|
|
|
start = p;
|
|
|
|
p = strchr(p,'-');
|
|
|
|
if (!p) continue;
|
|
|
|
*p++ = '\0';
|
|
|
|
end = p;
|
|
|
|
p = strchr(p,' ');
|
|
|
|
if (!p) continue;
|
|
|
|
*p++ = '\0';
|
|
|
|
if (strstr(p,"stack") ||
|
|
|
|
strstr(p,"vdso") ||
|
|
|
|
strstr(p,"vsyscall")) continue;
|
|
|
|
if (!strstr(p,"00:00")) continue;
|
|
|
|
if (!strstr(p,"rw")) continue;
|
|
|
|
|
|
|
|
start_addr = strtoul(start,NULL,16);
|
|
|
|
end_addr = strtoul(end,NULL,16);
|
|
|
|
size = end_addr-start_addr;
|
|
|
|
redisLog(REDIS_WARNING,
|
|
|
|
"Testing memory at %lx (%lu bytes)", start_addr, size);
|
|
|
|
for (j = 0; j < 3; j++) {
|
|
|
|
if (memtest_non_destructive((void*)start_addr,size) != 0) {
|
|
|
|
fclose(fp);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-03-27 04:33:45 -04:00
|
|
|
void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
|
|
|
|
ucontext_t *uc = (ucontext_t*) secret;
|
2012-04-26 10:04:53 -04:00
|
|
|
sds infostring, clients;
|
2012-03-27 04:33:45 -04:00
|
|
|
struct sigaction act;
|
|
|
|
REDIS_NOTUSED(info);
|
|
|
|
|
|
|
|
bugReportStart();
|
|
|
|
redisLog(REDIS_WARNING,
|
|
|
|
" Redis %s crashed by signal: %d", REDIS_VERSION, sig);
|
|
|
|
redisLog(REDIS_WARNING,
|
|
|
|
" Failed assertion: %s (%s:%d)", server.assert_failed,
|
|
|
|
server.assert_file, server.assert_line);
|
|
|
|
|
|
|
|
/* Log the stack trace */
|
2012-04-26 10:04:53 -04:00
|
|
|
redisLog(REDIS_WARNING, "--- STACK TRACE");
|
|
|
|
logStackTrace(uc);
|
2012-01-20 06:20:45 -05:00
|
|
|
|
|
|
|
/* Log INFO and CLIENT LIST */
|
|
|
|
redisLog(REDIS_WARNING, "--- INFO OUTPUT");
|
|
|
|
infostring = genRedisInfoString("all");
|
2012-01-21 17:05:32 -05:00
|
|
|
infostring = sdscatprintf(infostring, "hash_init_value: %u\n",
|
|
|
|
dictGetHashFunctionSeed());
|
2012-01-20 06:20:45 -05:00
|
|
|
redisLogRaw(REDIS_WARNING, infostring);
|
|
|
|
redisLog(REDIS_WARNING, "--- CLIENT LIST OUTPUT");
|
|
|
|
clients = getAllClientsInfoString();
|
|
|
|
redisLogRaw(REDIS_WARNING, clients);
|
2012-03-27 04:33:45 -04:00
|
|
|
sdsfree(infostring);
|
|
|
|
sdsfree(clients);
|
2012-01-20 06:20:45 -05:00
|
|
|
|
2012-03-27 04:33:45 -04:00
|
|
|
/* Log the current client */
|
|
|
|
logCurrentClient();
|
2012-01-20 06:20:45 -05:00
|
|
|
|
|
|
|
/* Log dump of processor registers */
|
|
|
|
logRegisters(uc);
|
|
|
|
|
2012-11-21 07:19:38 -05:00
|
|
|
#if defined(HAVE_PROC_MAPS)
|
|
|
|
/* Test memory */
|
|
|
|
redisLog(REDIS_WARNING, "--- FAST MEMORY TEST");
|
|
|
|
if (memtest_test_linux_anonymous_maps()) {
|
|
|
|
redisLog(REDIS_WARNING,
|
|
|
|
"!!! MEMORY ERROR DETECTED! Check your memory ASAP !!!");
|
|
|
|
} else {
|
|
|
|
redisLog(REDIS_WARNING,
|
|
|
|
"Fast memory test PASSED, however your memory can still be broken. Please run a memory test for several hours if possible.");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-01-20 06:20:45 -05:00
|
|
|
redisLog(REDIS_WARNING,
|
|
|
|
"\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n"
|
2012-03-18 06:35:35 -04:00
|
|
|
" Please report the crash opening an issue on github:\n\n"
|
|
|
|
" http://github.com/antirez/redis/issues\n\n"
|
|
|
|
" Suspect RAM error? Use redis-server --test-memory to veryfy it.\n\n"
|
2012-01-20 06:20:45 -05:00
|
|
|
);
|
|
|
|
/* free(messages); Don't call free() with possibly corrupted memory. */
|
|
|
|
if (server.daemonize) unlink(server.pidfile);
|
|
|
|
|
|
|
|
/* Make sure we exit with the right signal at the end. So for instance
|
|
|
|
* the core will be dumped if enabled. */
|
|
|
|
sigemptyset (&act.sa_mask);
|
|
|
|
act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND;
|
|
|
|
act.sa_handler = SIG_DFL;
|
|
|
|
sigaction (sig, &act, NULL);
|
|
|
|
kill(getpid(),sig);
|
|
|
|
}
|
|
|
|
#endif /* HAVE_BACKTRACE */
|
2012-03-27 05:47:51 -04:00
|
|
|
|
2012-06-11 17:44:34 -04:00
|
|
|
/* ==================== 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");
|
|
|
|
}
|
|
|
|
|
2012-03-27 05:47:51 -04:00
|
|
|
/* =========================== Software Watchdog ============================ */
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
|
|
|
void watchdogSignalHandler(int sig, siginfo_t *info, void *secret) {
|
2012-04-24 05:07:15 -04:00
|
|
|
#ifdef HAVE_BACKTRACE
|
2012-03-27 05:47:51 -04:00
|
|
|
ucontext_t *uc = (ucontext_t*) secret;
|
2012-04-24 05:07:15 -04:00
|
|
|
#endif
|
2012-03-27 05:47:51 -04:00
|
|
|
REDIS_NOTUSED(info);
|
|
|
|
REDIS_NOTUSED(sig);
|
|
|
|
|
2012-04-26 10:04:53 -04:00
|
|
|
redisLogFromHandler(REDIS_WARNING,"\n--- WATCHDOG TIMER EXPIRED ---");
|
2012-03-27 05:47:51 -04:00
|
|
|
#ifdef HAVE_BACKTRACE
|
2012-04-26 10:04:53 -04:00
|
|
|
logStackTrace(uc);
|
2012-03-27 09:24:33 -04:00
|
|
|
#else
|
2012-04-26 10:04:53 -04:00
|
|
|
redisLogFromHandler(REDIS_WARNING,"Sorry: no support for backtrace().");
|
2012-03-27 05:47:51 -04:00
|
|
|
#endif
|
2012-04-26 10:04:53 -04:00
|
|
|
redisLogFromHandler(REDIS_WARNING,"--------\n");
|
2012-03-27 05:47:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Schedule a SIGALRM delivery after the specified period in milliseconds.
|
|
|
|
* If a timer is already scheduled, this function will re-schedule it to the
|
|
|
|
* specified time. If period is 0 the current timer is disabled. */
|
|
|
|
void watchdogScheduleSignal(int period) {
|
|
|
|
struct itimerval it;
|
|
|
|
|
|
|
|
/* Will stop the timer if period is 0. */
|
|
|
|
it.it_value.tv_sec = period/1000;
|
2012-03-27 06:11:37 -04:00
|
|
|
it.it_value.tv_usec = (period%1000)*1000;
|
2012-03-27 05:47:51 -04:00
|
|
|
/* Don't automatically restart. */
|
|
|
|
it.it_interval.tv_sec = 0;
|
|
|
|
it.it_interval.tv_usec = 0;
|
|
|
|
setitimer(ITIMER_REAL, &it, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Enable the software watchdong with the specified period in milliseconds. */
|
|
|
|
void enableWatchdog(int period) {
|
2012-05-13 15:52:35 -04:00
|
|
|
int min_period;
|
|
|
|
|
2012-03-27 05:47:51 -04:00
|
|
|
if (server.watchdog_period == 0) {
|
|
|
|
struct sigaction act;
|
|
|
|
|
|
|
|
/* Watchdog was actually disabled, so we have to setup the signal
|
|
|
|
* handler. */
|
|
|
|
sigemptyset(&act.sa_mask);
|
|
|
|
act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_SIGINFO;
|
|
|
|
act.sa_sigaction = watchdogSignalHandler;
|
|
|
|
sigaction(SIGALRM, &act, NULL);
|
|
|
|
}
|
2012-05-13 15:52:35 -04:00
|
|
|
/* 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;
|
2012-03-27 05:47:51 -04:00
|
|
|
watchdogScheduleSignal(period); /* Adjust the current timer. */
|
|
|
|
server.watchdog_period = period;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Disable the software watchdog. */
|
|
|
|
void disableWatchdog(void) {
|
|
|
|
struct sigaction act;
|
|
|
|
if (server.watchdog_period == 0) return; /* Already disabled. */
|
|
|
|
watchdogScheduleSignal(0); /* Stop the current timer. */
|
|
|
|
|
|
|
|
/* Set the signal handler to SIG_IGN, this will also remove pending
|
|
|
|
* signals from the queue. */
|
|
|
|
sigemptyset(&act.sa_mask);
|
|
|
|
act.sa_flags = 0;
|
|
|
|
act.sa_handler = SIG_IGN;
|
|
|
|
sigaction(SIGALRM, &act, NULL);
|
|
|
|
server.watchdog_period = 0;
|
|
|
|
}
|