mirror of
https://github.com/fluencelabs/redis
synced 2025-06-16 02:31:20 +00:00
redis.c split into many different C files.
networking related stuff moved into networking.c moved more code more work on layout of source code SDS instantaneuos memory saving. By Pieter and Salvatore at VMware ;) cleanly compiling again after the first split, now splitting it in more C files moving more things around... work in progress split replication code splitting more Sets split Hash split replication split even more splitting more splitting minor change
This commit is contained in:
309
src/debug.c
Normal file
309
src/debug.c
Normal file
@ -0,0 +1,309 @@
|
||||
#include "redis.h"
|
||||
#include "sha1.h" /* SHA1 is used for DEBUG DIGEST */
|
||||
|
||||
/* ================================= 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;
|
||||
time_t expiretime;
|
||||
|
||||
memset(digest,0,20); /* This key-val digest */
|
||||
key = dictGetEntryKey(de);
|
||||
keyobj = createStringObject(key,sdslen(key));
|
||||
|
||||
mixDigest(digest,key,sdslen(key));
|
||||
|
||||
/* Make sure the key is loaded if VM is active */
|
||||
o = lookupKeyRead(db,keyobj);
|
||||
|
||||
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) {
|
||||
dict *set = o->ptr;
|
||||
dictIterator *di = dictGetIterator(set);
|
||||
dictEntry *de;
|
||||
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
robj *eleobj = dictGetEntryKey(de);
|
||||
|
||||
xorObjectDigest(digest,eleobj);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
} else if (o->type == REDIS_ZSET) {
|
||||
zset *zs = o->ptr;
|
||||
dictIterator *di = dictGetIterator(zs->dict);
|
||||
dictEntry *de;
|
||||
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
robj *eleobj = dictGetEntryKey(de);
|
||||
double *score = dictGetEntryVal(de);
|
||||
unsigned char eledigest[20];
|
||||
|
||||
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 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);
|
||||
obj = hashTypeCurrent(hi,REDIS_HASH_KEY);
|
||||
mixObjectDigest(eledigest,obj);
|
||||
decrRefCount(obj);
|
||||
obj = hashTypeCurrent(hi,REDIS_HASH_VALUE);
|
||||
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';
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"reload")) {
|
||||
if (rdbSave(server.dbfilename) != REDIS_OK) {
|
||||
addReply(c,shared.err);
|
||||
return;
|
||||
}
|
||||
emptyDb();
|
||||
if (rdbLoad(server.dbfilename) != REDIS_OK) {
|
||||
addReply(c,shared.err);
|
||||
return;
|
||||
}
|
||||
redisLog(REDIS_WARNING,"DB reloaded by DEBUG RELOAD");
|
||||
addReply(c,shared.ok);
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"loadaof")) {
|
||||
emptyDb();
|
||||
if (loadAppendOnlyFile(server.appendfilename) != REDIS_OK) {
|
||||
addReply(c,shared.err);
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
dictEntry *de = dictFind(c->db->dict,c->argv[2]->ptr);
|
||||
robj *val;
|
||||
|
||||
if (!de) {
|
||||
addReply(c,shared.nokeyerr);
|
||||
return;
|
||||
}
|
||||
val = dictGetEntryVal(de);
|
||||
if (!server.vm_enabled || (val->storage == REDIS_VM_MEMORY ||
|
||||
val->storage == REDIS_VM_SWAPPING)) {
|
||||
char *strenc;
|
||||
|
||||
strenc = strEncoding(val->encoding);
|
||||
addReplySds(c,sdscatprintf(sdsempty(),
|
||||
"+Value at:%p refcount:%d "
|
||||
"encoding:%s serializedlength:%lld\r\n",
|
||||
(void*)val, val->refcount,
|
||||
strenc, (long long) rdbSavedObjectLen(val,NULL)));
|
||||
} else {
|
||||
vmpointer *vp = (vmpointer*) val;
|
||||
addReplySds(c,sdscatprintf(sdsempty(),
|
||||
"+Value swapped at: page %llu "
|
||||
"using %llu pages\r\n",
|
||||
(unsigned long long) vp->page,
|
||||
(unsigned long long) vp->usedpages));
|
||||
}
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"swapin") && c->argc == 3) {
|
||||
lookupKeyRead(c->db,c->argv[2]);
|
||||
addReply(c,shared.ok);
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"swapout") && c->argc == 3) {
|
||||
dictEntry *de = dictFind(c->db->dict,c->argv[2]->ptr);
|
||||
robj *val;
|
||||
vmpointer *vp;
|
||||
|
||||
if (!server.vm_enabled) {
|
||||
addReplySds(c,sdsnew("-ERR Virtual Memory is disabled\r\n"));
|
||||
return;
|
||||
}
|
||||
if (!de) {
|
||||
addReply(c,shared.nokeyerr);
|
||||
return;
|
||||
}
|
||||
val = dictGetEntryVal(de);
|
||||
/* Swap it */
|
||||
if (val->storage != REDIS_VM_MEMORY) {
|
||||
addReplySds(c,sdsnew("-ERR This key is not in memory\r\n"));
|
||||
} else if (val->refcount != 1) {
|
||||
addReplySds(c,sdsnew("-ERR Object is shared\r\n"));
|
||||
} else if ((vp = vmSwapObjectBlocking(val)) != NULL) {
|
||||
dictGetEntryVal(de) = vp;
|
||||
addReply(c,shared.ok);
|
||||
} else {
|
||||
addReply(c,shared.err);
|
||||
}
|
||||
} 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];
|
||||
sds d = sdsnew("+");
|
||||
int j;
|
||||
|
||||
computeDatasetDigest(digest);
|
||||
for (j = 0; j < 20; j++)
|
||||
d = sdscatprintf(d, "%02x",digest[j]);
|
||||
|
||||
d = sdscatlen(d,"\r\n",2);
|
||||
addReplySds(c,d);
|
||||
} else {
|
||||
addReplySds(c,sdsnew(
|
||||
"-ERR Syntax error, try DEBUG [SEGFAULT|OBJECT <key>|SWAPIN <key>|SWAPOUT <key>|RELOAD]\r\n"));
|
||||
}
|
||||
}
|
||||
|
||||
void _redisAssert(char *estr, char *file, int line) {
|
||||
redisLog(REDIS_WARNING,"=== ASSERTION FAILED ===");
|
||||
redisLog(REDIS_WARNING,"==> %s:%d '%s' is not true",file,line,estr);
|
||||
#ifdef HAVE_BACKTRACE
|
||||
redisLog(REDIS_WARNING,"(forcing SIGSEGV in order to print the stack trace)");
|
||||
*((char*)-1) = 'x';
|
||||
#endif
|
||||
}
|
||||
|
||||
void _redisPanic(char *msg, char *file, int line) {
|
||||
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)");
|
||||
*((char*)-1) = 'x';
|
||||
#endif
|
||||
}
|
Reference in New Issue
Block a user