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.
|
|
|
|
*/
|
|
|
|
|
2015-07-26 09:14:57 -04:00
|
|
|
#include "server.h"
|
2010-06-21 18:07:48 -04:00
|
|
|
#include "sha1.h" /* SHA1 is used for DEBUG DIGEST */
|
2012-11-29 08:20:08 -05:00
|
|
|
#include "crc64.h"
|
2010-06-21 18:07:48 -04:00
|
|
|
|
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-11-22 16:52:39 -05:00
|
|
|
#include "bio.h"
|
2012-01-20 06:20:45 -05:00
|
|
|
#endif /* HAVE_BACKTRACE */
|
2010-07-01 15:13:38 -04:00
|
|
|
|
2014-05-12 10:56:43 -04:00
|
|
|
#ifdef __CYGWIN__
|
|
|
|
#ifndef SA_ONSTACK
|
|
|
|
#define SA_ONSTACK 0x08000000
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2010-06-21 18:07:48 -04:00
|
|
|
/* ================================= Debugging ============================== */
|
|
|
|
|
|
|
|
/* Compute the sha1 of string at 's' with 'len' bytes long.
|
2013-01-16 12:00:20 -05:00
|
|
|
* The SHA1 is then xored against the string pointed by digest.
|
2010-06-21 18:07:48 -04:00
|
|
|
* 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
|
2013-01-16 12:00:20 -05:00
|
|
|
* against digest, also perform the digest of "digest" itself and
|
2010-06-21 18:07:48 -04:00
|
|
|
* 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 */
|
2015-07-26 09:28:00 -04:00
|
|
|
if (o->type == OBJ_STRING) {
|
2010-06-21 18:07:48 -04:00
|
|
|
mixObjectDigest(digest,o);
|
2015-07-26 09:28:00 -04:00
|
|
|
} else if (o->type == OBJ_LIST) {
|
2010-06-21 18:07:48 -04:00
|
|
|
listTypeIterator *li = listTypeInitIterator(o,0,REDIS_TAIL);
|
|
|
|
listTypeEntry entry;
|
|
|
|
while(listTypeNext(li,&entry)) {
|
|
|
|
robj *eleobj = listTypeGet(&entry);
|
|
|
|
mixObjectDigest(digest,eleobj);
|
|
|
|
decrRefCount(eleobj);
|
|
|
|
}
|
|
|
|
listTypeReleaseIterator(li);
|
2015-07-26 09:28:00 -04:00
|
|
|
} else if (o->type == OBJ_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);
|
2015-07-26 09:28:00 -04:00
|
|
|
} else if (o->type == OBJ_ZSET) {
|
2011-03-14 08:30:06 -04:00
|
|
|
unsigned char eledigest[20];
|
|
|
|
|
2015-07-26 09:28:00 -04:00
|
|
|
if (o->encoding == OBJ_ENCODING_ZIPLIST) {
|
2011-03-14 08:30:06 -04:00
|
|
|
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);
|
|
|
|
}
|
2015-07-26 09:28:00 -04:00
|
|
|
} else if (o->encoding == OBJ_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
|
|
|
}
|
2015-07-26 09:28:00 -04:00
|
|
|
} else if (o->type == OBJ_HASH) {
|
2010-06-21 18:07:48 -04:00
|
|
|
hashTypeIterator *hi;
|
|
|
|
robj *obj;
|
|
|
|
|
|
|
|
hi = hashTypeInitIterator(o);
|
|
|
|
while (hashTypeNext(hi) != REDIS_ERR) {
|
|
|
|
unsigned char eledigest[20];
|
|
|
|
|
|
|
|
memset(eledigest,0,20);
|
2015-07-26 09:28:00 -04:00
|
|
|
obj = hashTypeCurrentObject(hi,OBJ_HASH_KEY);
|
2010-06-21 18:07:48 -04:00
|
|
|
mixObjectDigest(eledigest,obj);
|
|
|
|
decrRefCount(obj);
|
2015-07-26 09:28:00 -04:00
|
|
|
obj = hashTypeCurrentObject(hi,OBJ_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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-16 13:05:48 -05:00
|
|
|
void inputCatSds(void *result, const char *str) {
|
|
|
|
/* result is actually a (sds *), so re-cast it here */
|
|
|
|
sds *info = (sds *)result;
|
|
|
|
*info = sdscat(*info, str);
|
|
|
|
}
|
|
|
|
|
2015-07-26 09:20:46 -04:00
|
|
|
void debugCommand(client *c) {
|
2010-06-21 18:07:48 -04:00
|
|
|
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;
|
|
|
|
}
|
2013-12-10 12:18:24 -05:00
|
|
|
emptyDb(NULL);
|
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;
|
|
|
|
}
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,"DB reloaded by DEBUG RELOAD");
|
2010-06-21 18:07:48 -04:00
|
|
|
addReply(c,shared.ok);
|
|
|
|
} else if (!strcasecmp(c->argv[1]->ptr,"loadaof")) {
|
2013-12-10 12:18:24 -05:00
|
|
|
emptyDb(NULL);
|
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 */
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,"Append Only File loaded by DEBUG LOADAOF");
|
2010-06-21 18:07:48 -04:00
|
|
|
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
|
|
|
|
2014-12-10 22:54:19 -05:00
|
|
|
char extra[128] = {0};
|
2015-07-26 09:28:00 -04:00
|
|
|
if (val->encoding == OBJ_ENCODING_QUICKLIST) {
|
2014-12-10 22:54:19 -05:00
|
|
|
char *nextra = extra;
|
|
|
|
int remaining = sizeof(extra);
|
|
|
|
quicklist *ql = val->ptr;
|
2014-12-19 10:41:52 -05:00
|
|
|
/* Add number of quicklist nodes */
|
2014-12-10 21:26:31 -05:00
|
|
|
int used = snprintf(nextra, remaining, " ql_nodes:%u", ql->len);
|
2014-12-10 22:54:19 -05:00
|
|
|
nextra += used;
|
|
|
|
remaining -= used;
|
2014-12-19 10:41:52 -05:00
|
|
|
/* Add average quicklist fill factor */
|
|
|
|
double avg = (double)ql->count/ql->len;
|
|
|
|
used = snprintf(nextra, remaining, " ql_avg_node:%.2f", avg);
|
|
|
|
nextra += used;
|
|
|
|
remaining -= used;
|
|
|
|
/* Add quicklist fill level / max ziplist size */
|
|
|
|
used = snprintf(nextra, remaining, " ql_ziplist_max:%d", ql->fill);
|
|
|
|
nextra += used;
|
|
|
|
remaining -= used;
|
|
|
|
/* Add isCompressed? */
|
|
|
|
int compressed = ql->compress != 0;
|
|
|
|
used = snprintf(nextra, remaining, " ql_compressed:%d", compressed);
|
|
|
|
nextra += used;
|
|
|
|
remaining -= used;
|
|
|
|
/* Add total uncompressed size */
|
|
|
|
unsigned long sz = 0;
|
|
|
|
for (quicklistNode *node = ql->head; node; node = node->next) {
|
|
|
|
sz += node->sz;
|
|
|
|
}
|
|
|
|
used = snprintf(nextra, remaining, " ql_uncompressed_size:%lu", sz);
|
|
|
|
nextra += used;
|
|
|
|
remaining -= used;
|
2014-12-10 22:54:19 -05:00
|
|
|
}
|
|
|
|
|
2010-12-29 10:58:57 -05:00
|
|
|
addReplyStatusFormat(c,
|
|
|
|
"Value at:%p refcount:%d "
|
2015-01-18 15:54:30 -05:00
|
|
|
"encoding:%s serializedlength:%zu "
|
2014-12-10 22:54:19 -05:00
|
|
|
"lru:%d lru_seconds_idle:%llu%s",
|
2010-12-29 10:58:57 -05:00
|
|
|
(void*)val, val->refcount,
|
2015-01-18 15:54:30 -05:00
|
|
|
strenc, rdbSavedObjectLen(val),
|
2014-12-10 22:54:19 -05:00
|
|
|
val->lru, estimateObjectIdleTime(val)/1000, extra);
|
2013-08-27 05:52:12 -04:00
|
|
|
} else if (!strcasecmp(c->argv[1]->ptr,"sdslen") && c->argc == 3) {
|
|
|
|
dictEntry *de;
|
|
|
|
robj *val;
|
|
|
|
sds key;
|
|
|
|
|
|
|
|
if ((de = dictFind(c->db->dict,c->argv[2]->ptr)) == NULL) {
|
|
|
|
addReply(c,shared.nokeyerr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
val = dictGetVal(de);
|
|
|
|
key = dictGetKey(de);
|
|
|
|
|
2015-07-26 09:28:00 -04:00
|
|
|
if (val->type != OBJ_STRING || !sdsEncodedObject(val)) {
|
2013-08-27 05:52:12 -04:00
|
|
|
addReplyError(c,"Not an sds encoded string.");
|
|
|
|
} else {
|
|
|
|
addReplyStatusFormat(c,
|
|
|
|
"key_sds_len:%lld, key_sds_avail:%lld, "
|
|
|
|
"val_sds_len:%lld, val_sds_avail:%lld",
|
|
|
|
(long long) sdslen(key),
|
|
|
|
(long long) sdsavail(key),
|
|
|
|
(long long) sdslen(val->ptr),
|
|
|
|
(long long) sdsavail(val->ptr));
|
|
|
|
}
|
2014-09-25 11:01:56 -04:00
|
|
|
} else if (!strcasecmp(c->argv[1]->ptr,"populate") &&
|
|
|
|
(c->argc == 3 || c->argc == 4)) {
|
2010-06-21 18:07:48 -04:00
|
|
|
long keys, j;
|
|
|
|
robj *key, *val;
|
|
|
|
char buf[128];
|
|
|
|
|
|
|
|
if (getLongFromObjectOrReply(c, c->argv[2], &keys, NULL) != REDIS_OK)
|
|
|
|
return;
|
2014-05-09 09:02:29 -04:00
|
|
|
dictExpand(c->db->dict,keys);
|
2010-06-21 18:07:48 -04:00
|
|
|
for (j = 0; j < keys; j++) {
|
2014-09-25 11:01:56 -04:00
|
|
|
snprintf(buf,sizeof(buf),"%s:%lu",
|
2014-10-09 05:17:27 -04:00
|
|
|
(c->argc == 3) ? "key" : (char*)c->argv[3]->ptr, j);
|
2010-06-21 18:07:48 -04:00
|
|
|
key = createStringObject(buf,strlen(buf));
|
Better read-only behavior for expired keys in slaves.
Slaves key expire is orchestrated by the master. Sometimes the master
will send the synthesized DEL to expire keys on the slave with a non
trivial delay (when the key is not accessed, only the incremental expiry
algorithm will expire it in background).
During that time, a key is logically expired, but slaves still return
the key if you GET (or whatever) it. This is a bad behavior.
However we can't simply trust the slave view of the key, since we need
the master to be able to send write commands to update the slave data
set, and DELs should only happen when the key is expired in the master
in order to ensure consistency.
However 99.99% of the issues with this behavior is when a client which
is not a master sends a read only command. In this case we are safe and
can consider the key as non existing.
This commit does a few changes in order to make this sane:
1. lookupKeyRead() is modified in order to return NULL if the above
conditions are met.
2. Calls to lookupKeyRead() in commands actually writing to the data set
are repliaced with calls to lookupKeyWrite().
There are redundand checks, so for example, if in "2" something was
overlooked, we should be still safe, since anyway, when the master
writes the behavior is to don't care about what expireIfneeded()
returns.
This commit is related to #1768, #1770, #2131.
2014-12-10 10:10:21 -05:00
|
|
|
if (lookupKeyWrite(c->db,key) != NULL) {
|
2010-06-21 18:07:48 -04:00
|
|
|
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;
|
2012-07-19 08:37:34 -04:00
|
|
|
struct timespec tv;
|
2011-06-30 07:31:44 -04:00
|
|
|
|
2012-07-19 08:37:34 -04:00
|
|
|
tv.tv_sec = utime / 1000000;
|
|
|
|
tv.tv_nsec = (utime % 1000000) * 1000;
|
|
|
|
nanosleep(&tv, NULL);
|
2011-06-30 07:31:44 -04:00
|
|
|
addReply(c,shared.ok);
|
2013-03-27 12:55:02 -04:00
|
|
|
} else if (!strcasecmp(c->argv[1]->ptr,"set-active-expire") &&
|
|
|
|
c->argc == 3)
|
|
|
|
{
|
|
|
|
server.active_expire_enabled = atoi(c->argv[2]->ptr);
|
|
|
|
addReply(c,shared.ok);
|
2014-03-10 18:01:55 -04:00
|
|
|
} else if (!strcasecmp(c->argv[1]->ptr,"error") && c->argc == 3) {
|
|
|
|
sds errstr = sdsnewlen("-",1);
|
|
|
|
|
|
|
|
errstr = sdscatsds(errstr,c->argv[2]->ptr);
|
|
|
|
errstr = sdsmapchars(errstr,"\n\r"," ",2); /* no newlines in errors. */
|
|
|
|
errstr = sdscatlen(errstr,"\r\n",2);
|
|
|
|
addReplySds(c,errstr);
|
2015-01-23 12:10:14 -05:00
|
|
|
} else if (!strcasecmp(c->argv[1]->ptr,"structsize") && c->argc == 2) {
|
|
|
|
sds sizes = sdsempty();
|
2015-07-14 10:04:00 -04:00
|
|
|
sizes = sdscatprintf(sizes,"bits:%d ",(sizeof(void*) == 8)?64:32);
|
|
|
|
sizes = sdscatprintf(sizes,"robj:%d ",(int)sizeof(robj));
|
|
|
|
sizes = sdscatprintf(sizes,"dictentry:%d ",(int)sizeof(dictEntry));
|
2015-07-16 03:14:39 -04:00
|
|
|
sizes = sdscatprintf(sizes,"sdshdr5:%d ",(int)sizeof(struct sdshdr5));
|
2015-07-14 10:04:00 -04:00
|
|
|
sizes = sdscatprintf(sizes,"sdshdr8:%d ",(int)sizeof(struct sdshdr8));
|
|
|
|
sizes = sdscatprintf(sizes,"sdshdr16:%d ",(int)sizeof(struct sdshdr16));
|
|
|
|
sizes = sdscatprintf(sizes,"sdshdr32:%d ",(int)sizeof(struct sdshdr32));
|
|
|
|
sizes = sdscatprintf(sizes,"sdshdr64:%d ",(int)sizeof(struct sdshdr64));
|
2015-01-23 12:10:14 -05:00
|
|
|
addReplyBulkSds(c,sizes);
|
2015-07-14 11:15:37 -04:00
|
|
|
} else if (!strcasecmp(c->argv[1]->ptr,"htstats") && c->argc == 3) {
|
|
|
|
long dbid;
|
|
|
|
sds stats = sdsempty();
|
|
|
|
char buf[4096];
|
|
|
|
|
|
|
|
if (getLongFromObjectOrReply(c, c->argv[2], &dbid, NULL) != REDIS_OK)
|
|
|
|
return;
|
|
|
|
if (dbid < 0 || dbid >= server.dbnum) {
|
|
|
|
addReplyError(c,"Out of range database");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
stats = sdscatprintf(stats,"[Dictionary HT]\n");
|
|
|
|
dictGetStats(buf,sizeof(buf),server.db[dbid].dict);
|
|
|
|
stats = sdscat(stats,buf);
|
|
|
|
|
|
|
|
stats = sdscatprintf(stats,"[Expires HT]\n");
|
|
|
|
dictGetStats(buf,sizeof(buf),server.db[dbid].expires);
|
|
|
|
stats = sdscat(stats,buf);
|
|
|
|
|
|
|
|
addReplyBulkSds(c,stats);
|
2014-11-16 13:05:48 -05:00
|
|
|
} else if (!strcasecmp(c->argv[1]->ptr,"jemalloc") && c->argc == 3) {
|
|
|
|
#if defined(USE_JEMALLOC)
|
|
|
|
if (!strcasecmp(c->argv[2]->ptr, "info")) {
|
|
|
|
sds info = sdsempty();
|
|
|
|
je_malloc_stats_print(inputCatSds, &info, NULL);
|
|
|
|
addReplyBulkSds(c, info);
|
|
|
|
} else {
|
|
|
|
addReplyErrorFormat(c, "Valid jemalloc debug fields: info");
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
addReplyErrorFormat(c, "jemalloc support not available");
|
|
|
|
#endif
|
2010-06-21 18:07:48 -04:00
|
|
|
} else {
|
2013-03-28 06:39:02 -04:00
|
|
|
addReplyErrorFormat(c, "Unknown DEBUG subcommand or wrong number of arguments for '%s'",
|
2013-03-27 12:55:02 -04:00
|
|
|
(char*)c->argv[1]->ptr);
|
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();
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,"=== ASSERTION FAILED ===");
|
|
|
|
serverLog(REDIS_WARNING,"==> %s:%d '%s' is not true",file,line,estr);
|
2010-06-21 18:07:48 -04:00
|
|
|
#ifdef HAVE_BACKTRACE
|
2011-11-24 09:47:26 -05:00
|
|
|
server.assert_failed = estr;
|
|
|
|
server.assert_file = file;
|
|
|
|
server.assert_line = line;
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(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
|
|
|
}
|
|
|
|
|
2015-07-26 09:20:46 -04:00
|
|
|
void _redisAssertPrintClientInfo(client *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();
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,"=== ASSERTION FAILED CLIENT CONTEXT ===");
|
|
|
|
serverLog(REDIS_WARNING,"client->flags = %d", c->flags);
|
|
|
|
serverLog(REDIS_WARNING,"client->fd = %d", c->fd);
|
|
|
|
serverLog(REDIS_WARNING,"client->argc = %d", c->argc);
|
2011-10-04 12:05:26 -04:00
|
|
|
for (j=0; j < c->argc; j++) {
|
|
|
|
char buf[128];
|
|
|
|
char *arg;
|
|
|
|
|
2015-07-26 09:28:00 -04:00
|
|
|
if (c->argv[j]->type == OBJ_STRING && sdsEncodedObject(c->argv[j])) {
|
2011-10-04 12:05:26 -04:00
|
|
|
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
|
|
|
}
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,"client->argv[%d] = \"%s\" (refcount: %d)",
|
2011-10-04 12:05:26 -04:00
|
|
|
j, arg, c->argv[j]->refcount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-26 09:17:43 -04:00
|
|
|
void serverLogObjectDebugInfo(robj *o) {
|
|
|
|
serverLog(REDIS_WARNING,"Object type: %d", o->type);
|
|
|
|
serverLog(REDIS_WARNING,"Object encoding: %d", o->encoding);
|
|
|
|
serverLog(REDIS_WARNING,"Object refcount: %d", o->refcount);
|
2015-07-26 09:28:00 -04:00
|
|
|
if (o->type == OBJ_STRING && sdsEncodedObject(o)) {
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,"Object raw string len: %zu", sdslen(o->ptr));
|
2013-06-04 09:53:53 -04:00
|
|
|
if (sdslen(o->ptr) < 4096) {
|
|
|
|
sds repr = sdscatrepr(sdsempty(),o->ptr,sdslen(o->ptr));
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,"Object raw string content: %s", repr);
|
2013-06-04 09:53:53 -04:00
|
|
|
sdsfree(repr);
|
|
|
|
}
|
2015-07-26 09:28:00 -04:00
|
|
|
} else if (o->type == OBJ_LIST) {
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,"List length: %d", (int) listTypeLength(o));
|
2015-07-26 09:28:00 -04:00
|
|
|
} else if (o->type == OBJ_SET) {
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,"Set size: %d", (int) setTypeSize(o));
|
2015-07-26 09:28:00 -04:00
|
|
|
} else if (o->type == OBJ_HASH) {
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,"Hash size: %d", (int) hashTypeLength(o));
|
2015-07-26 09:28:00 -04:00
|
|
|
} else if (o->type == OBJ_ZSET) {
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,"Sorted set size: %d", (int) zsetLength(o));
|
2015-07-26 09:28:00 -04:00
|
|
|
if (o->encoding == OBJ_ENCODING_SKIPLIST)
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(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();
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,"=== ASSERTION FAILED OBJECT CONTEXT ===");
|
|
|
|
serverLogObjectDebugInfo(o);
|
2012-01-12 10:02:57 -05:00
|
|
|
}
|
|
|
|
|
2015-07-26 09:20:46 -04:00
|
|
|
void _redisAssertWithInfo(client *c, robj *o, char *estr, char *file, int line) {
|
2011-10-04 12:05:26 -04:00
|
|
|
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();
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,"------------------------------------------------");
|
|
|
|
serverLog(REDIS_WARNING,"!!! Software Failure. Press left mouse button to continue");
|
|
|
|
serverLog(REDIS_WARNING,"Guru Meditation: %s #%s:%d",msg,file,line);
|
2010-06-21 18:07:48 -04:00
|
|
|
#ifdef HAVE_BACKTRACE
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,"(forcing SIGSEGV in order to print the stack trace)");
|
2012-02-21 13:05:46 -05:00
|
|
|
#endif
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(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) {
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,
|
2012-02-08 16:24:59 -05:00
|
|
|
"\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--) {
|
2013-02-27 06:27:15 -05:00
|
|
|
unsigned long addr = (unsigned long) sp+i;
|
|
|
|
unsigned long val = (unsigned long) sp[i];
|
|
|
|
|
2012-01-20 10:40:43 -05:00
|
|
|
if (sizeof(long) == 4)
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING, "(%08lx) -> %08lx", addr, val);
|
2012-01-20 10:40:43 -05:00
|
|
|
else
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING, "(%016lx) -> %016lx", addr, val);
|
2012-01-20 06:20:45 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void logRegisters(ucontext_t *uc) {
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(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__)
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,
|
2012-01-20 06:20:45 -05:00
|
|
|
"\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",
|
2013-02-27 06:27:15 -05:00
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__rax,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__rbx,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__rcx,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__rdx,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__rdi,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__rsi,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__rbp,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__rsp,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__r8,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__r9,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__r10,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__r11,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__r12,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__r13,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__r14,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__r15,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__rip,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__rflags,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__cs,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__fs,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__gs
|
2012-01-20 06:20:45 -05:00
|
|
|
);
|
|
|
|
logStackContent((void**)uc->uc_mcontext->__ss.__rsp);
|
2012-04-24 05:07:15 -04:00
|
|
|
#else
|
|
|
|
/* OSX x86 */
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,
|
2012-01-20 06:20:45 -05:00
|
|
|
"\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",
|
2013-02-27 06:27:15 -05:00
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__eax,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__ebx,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__ecx,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__edx,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__edi,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__esi,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__ebp,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__esp,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__ss,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__eflags,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__eip,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__cs,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__ds,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__es,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__fs,
|
|
|
|
(unsigned long) uc->uc_mcontext->__ss.__gs
|
2012-01-20 06:20:45 -05:00
|
|
|
);
|
|
|
|
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__)
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,
|
2012-01-20 08:37:50 -05:00
|
|
|
"\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",
|
2013-02-27 06:27:15 -05:00
|
|
|
(unsigned long) uc->uc_mcontext.gregs[11],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[8],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[10],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[9],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[4],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[5],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[6],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[7],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[18],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[17],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[14],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[15],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[3],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[2],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[1],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[0]
|
2012-01-20 08:37:50 -05:00
|
|
|
);
|
|
|
|
logStackContent((void**)uc->uc_mcontext.gregs[7]);
|
2012-04-24 05:07:15 -04:00
|
|
|
#elif defined(__X86_64__) || defined(__x86_64__)
|
|
|
|
/* Linux AMD64 */
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,
|
2012-01-20 06:54:15 -05:00
|
|
|
"\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",
|
2013-02-27 06:27:15 -05:00
|
|
|
(unsigned long) uc->uc_mcontext.gregs[13],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[11],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[14],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[12],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[8],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[9],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[10],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[15],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[0],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[1],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[2],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[3],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[4],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[5],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[6],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[7],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[16],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[17],
|
|
|
|
(unsigned long) uc->uc_mcontext.gregs[18]
|
2012-01-20 06:54:15 -05:00
|
|
|
);
|
|
|
|
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
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,
|
2012-01-20 06:20:45 -05:00
|
|
|
" 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;
|
2013-06-19 08:44:40 -04:00
|
|
|
int log_to_stdout = server.logfile[0] == '\0';
|
2012-04-26 10:04:53 -04:00
|
|
|
|
|
|
|
/* Open the log file in append mode. */
|
2013-06-19 08:44:40 -04:00
|
|
|
fd = log_to_stdout ?
|
|
|
|
STDOUT_FILENO :
|
|
|
|
open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644);
|
2012-04-26 10:04:53 -04:00
|
|
|
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 */
|
2013-06-19 08:44:40 -04:00
|
|
|
if (!log_to_stdout) 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;
|
|
|
|
|
2015-07-26 09:20:46 -04:00
|
|
|
client *cc = server.current_client;
|
2012-03-27 04:33:45 -04:00
|
|
|
sds client;
|
|
|
|
int j;
|
|
|
|
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING, "--- CURRENT CLIENT INFO");
|
2014-04-28 11:36:57 -04:00
|
|
|
client = catClientInfoString(sdsempty(),cc);
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,"client: %s", client);
|
2012-03-27 04:33:45 -04:00
|
|
|
sdsfree(client);
|
|
|
|
for (j = 0; j < cc->argc; j++) {
|
|
|
|
robj *decoded;
|
|
|
|
|
|
|
|
decoded = getDecodedObject(cc->argv[j]);
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,"argv[%d]: '%s'", j, (char*)decoded->ptr);
|
2012-03-27 04:33:45 -04:00
|
|
|
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);
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,"key '%s' found in DB containing the following object:", (char*)key->ptr);
|
|
|
|
serverLogObjectDebugInfo(val);
|
2012-03-27 04:33:45 -04:00
|
|
|
}
|
|
|
|
decrRefCount(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-21 07:19:38 -05:00
|
|
|
#if defined(HAVE_PROC_MAPS)
|
2012-11-25 10:21:21 -05:00
|
|
|
void memtest_non_destructive_invert(void *addr, size_t size);
|
|
|
|
void memtest_non_destructive_swap(void *addr, size_t size);
|
|
|
|
#define MEMTEST_MAX_REGIONS 128
|
2012-11-21 07:19:38 -05:00
|
|
|
|
|
|
|
int memtest_test_linux_anonymous_maps(void) {
|
|
|
|
FILE *fp = fopen("/proc/self/maps","r");
|
|
|
|
char line[1024];
|
|
|
|
size_t start_addr, end_addr, size;
|
2012-11-25 10:21:21 -05:00
|
|
|
size_t start_vect[MEMTEST_MAX_REGIONS];
|
|
|
|
size_t size_vect[MEMTEST_MAX_REGIONS];
|
|
|
|
int regions = 0, j;
|
|
|
|
uint64_t crc1 = 0, crc2 = 0, crc3 = 0;
|
2012-11-21 07:19:38 -05:00
|
|
|
|
|
|
|
while(fgets(line,sizeof(line),fp) != NULL) {
|
|
|
|
char *start, *end, *p = line;
|
|
|
|
|
|
|
|
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;
|
2012-11-25 10:21:21 -05:00
|
|
|
|
|
|
|
start_vect[regions] = start_addr;
|
|
|
|
size_vect[regions] = size;
|
2013-02-13 07:38:20 -05:00
|
|
|
printf("Testing %lx %lu\n", (unsigned long) start_vect[regions],
|
|
|
|
(unsigned long) size_vect[regions]);
|
2012-11-25 10:21:21 -05:00
|
|
|
regions++;
|
2012-11-21 07:19:38 -05:00
|
|
|
}
|
2012-11-25 10:21:21 -05:00
|
|
|
|
|
|
|
/* Test all the regions as an unique sequential region.
|
|
|
|
* 1) Take the CRC64 of the memory region. */
|
|
|
|
for (j = 0; j < regions; j++) {
|
|
|
|
crc1 = crc64(crc1,(void*)start_vect[j],size_vect[j]);
|
|
|
|
}
|
|
|
|
|
2013-01-09 11:16:58 -05:00
|
|
|
/* 2) Invert bits, swap adjacent words, swap again, invert bits.
|
2012-11-25 10:21:21 -05:00
|
|
|
* This is the error amplification step. */
|
|
|
|
for (j = 0; j < regions; j++)
|
|
|
|
memtest_non_destructive_invert((void*)start_vect[j],size_vect[j]);
|
|
|
|
for (j = 0; j < regions; j++)
|
|
|
|
memtest_non_destructive_swap((void*)start_vect[j],size_vect[j]);
|
|
|
|
for (j = 0; j < regions; j++)
|
|
|
|
memtest_non_destructive_swap((void*)start_vect[j],size_vect[j]);
|
|
|
|
for (j = 0; j < regions; j++)
|
|
|
|
memtest_non_destructive_invert((void*)start_vect[j],size_vect[j]);
|
|
|
|
|
|
|
|
/* 3) Take the CRC64 sum again. */
|
|
|
|
for (j = 0; j < regions; j++)
|
|
|
|
crc2 = crc64(crc2,(void*)start_vect[j],size_vect[j]);
|
|
|
|
|
|
|
|
/* 4) Swap + Swap again */
|
|
|
|
for (j = 0; j < regions; j++)
|
|
|
|
memtest_non_destructive_swap((void*)start_vect[j],size_vect[j]);
|
|
|
|
for (j = 0; j < regions; j++)
|
|
|
|
memtest_non_destructive_swap((void*)start_vect[j],size_vect[j]);
|
|
|
|
|
|
|
|
/* 5) Take the CRC64 sum again. */
|
|
|
|
for (j = 0; j < regions; j++)
|
|
|
|
crc3 = crc64(crc3,(void*)start_vect[j],size_vect[j]);
|
|
|
|
|
|
|
|
/* NOTE: It is very important to close the file descriptor only now
|
|
|
|
* because closing it before may result into unmapping of some memory
|
|
|
|
* region that we are testing. */
|
2012-11-21 07:19:38 -05:00
|
|
|
fclose(fp);
|
2012-11-25 10:21:21 -05:00
|
|
|
|
|
|
|
/* If the two CRC are not the same, we trapped a memory error. */
|
|
|
|
return crc1 != crc2 || crc2 != crc3;
|
2012-11-21 07:19:38 -05:00
|
|
|
}
|
|
|
|
#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();
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,
|
2012-03-27 04:33:45 -04:00
|
|
|
" Redis %s crashed by signal: %d", REDIS_VERSION, sig);
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,
|
2012-03-27 04:33:45 -04:00
|
|
|
" Failed assertion: %s (%s:%d)", server.assert_failed,
|
|
|
|
server.assert_file, server.assert_line);
|
|
|
|
|
|
|
|
/* Log the stack trace */
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING, "--- STACK TRACE");
|
2012-04-26 10:04:53 -04:00
|
|
|
logStackTrace(uc);
|
2012-01-20 06:20:45 -05:00
|
|
|
|
|
|
|
/* Log INFO and CLIENT LIST */
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING, "--- INFO OUTPUT");
|
2012-01-20 06:20:45 -05:00
|
|
|
infostring = genRedisInfoString("all");
|
2012-01-21 17:05:32 -05:00
|
|
|
infostring = sdscatprintf(infostring, "hash_init_value: %u\n",
|
|
|
|
dictGetHashFunctionSeed());
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLogRaw(REDIS_WARNING, infostring);
|
|
|
|
serverLog(REDIS_WARNING, "--- CLIENT LIST OUTPUT");
|
2012-01-20 06:20:45 -05:00
|
|
|
clients = getAllClientsInfoString();
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLogRaw(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 */
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING, "--- FAST MEMORY TEST");
|
2012-11-22 04:08:44 -05:00
|
|
|
bioKillThreads();
|
2012-11-21 07:19:38 -05:00
|
|
|
if (memtest_test_linux_anonymous_maps()) {
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,
|
2012-11-21 07:19:38 -05:00
|
|
|
"!!! MEMORY ERROR DETECTED! Check your memory ASAP !!!");
|
|
|
|
} else {
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,
|
2012-11-21 07:19:38 -05:00
|
|
|
"Fast memory test PASSED, however your memory can still be broken. Please run a memory test for several hours if possible.");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(REDIS_WARNING,
|
2012-01-20 06:20:45 -05:00
|
|
|
"\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n"
|
2014-08-28 10:36:32 -04:00
|
|
|
" Please report the crash by opening an issue on github:\n\n"
|
2012-03-18 06:35:35 -04:00
|
|
|
" http://github.com/antirez/redis/issues\n\n"
|
2014-05-28 03:46:01 -04:00
|
|
|
" Suspect RAM error? Use redis-server --test-memory to verify it.\n\n"
|
2012-01-20 06:20:45 -05:00
|
|
|
);
|
|
|
|
/* free(messages); Don't call free() with possibly corrupted memory. */
|
2015-01-12 09:58:23 -05:00
|
|
|
if (server.daemonize && server.supervised == 0) unlink(server.pidfile);
|
2012-01-20 06:20:45 -05:00
|
|
|
|
|
|
|
/* 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 ===================== */
|
|
|
|
|
2015-07-26 09:17:43 -04:00
|
|
|
void serverLogHexDump(int level, char *descr, void *value, size_t len) {
|
2012-06-11 17:44:34 -04:00
|
|
|
char buf[65], *b;
|
|
|
|
unsigned char *v = value;
|
|
|
|
char charset[] = "0123456789abcdef";
|
|
|
|
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLog(level,"%s (hexdump):", descr);
|
2012-06-11 17:44:34 -04:00
|
|
|
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) {
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLogRaw(level|REDIS_LOG_RAW,buf);
|
2012-06-11 17:44:34 -04:00
|
|
|
b = buf;
|
|
|
|
}
|
|
|
|
}
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLogRaw(level|REDIS_LOG_RAW,"\n");
|
2012-06-11 17:44:34 -04:00
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLogFromHandler(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
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLogFromHandler(REDIS_WARNING,"Sorry: no support for backtrace().");
|
2012-03-27 05:47:51 -04:00
|
|
|
#endif
|
2015-07-26 09:17:43 -04:00
|
|
|
serverLogFromHandler(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);
|
|
|
|
}
|
|
|
|
|
2012-11-27 14:41:33 -05:00
|
|
|
/* Enable the software watchdog with the specified period in milliseconds. */
|
2012-03-27 05:47:51 -04:00
|
|
|
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);
|
2014-05-22 15:34:02 -04:00
|
|
|
act.sa_flags = SA_ONSTACK | SA_SIGINFO;
|
2012-03-27 05:47:51 -04:00
|
|
|
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. */
|
2012-12-14 11:10:40 -05:00
|
|
|
min_period = (1000/server.hz)*2;
|
2012-05-13 15:52:35 -04:00
|
|
|
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;
|
|
|
|
}
|