]>
git.saurik.com Git - redis.git/blob - src/debug.c
376e07125df988bf1942813879635619f2877bb3
   2 #include "sha1.h"   /* SHA1 is used for DEBUG DIGEST */ 
   6 /* ================================= Debugging ============================== */ 
   8 /* Compute the sha1 of string at 's' with 'len' bytes long. 
   9  * The SHA1 is then xored againt the string pointed by digest. 
  10  * Since xor is commutative, this operation is used in order to 
  11  * "add" digests relative to unordered elements. 
  13  * So digest(a,b,c,d) will be the same of digest(b,a,c,d) */ 
  14 void xorDigest(unsigned char *digest
, void *ptr
, size_t len
) { 
  16     unsigned char hash
[20], *s 
= ptr
; 
  20     SHA1Update(&ctx
,s
,len
); 
  23     for (j 
= 0; j 
< 20; j
++) 
  27 void xorObjectDigest(unsigned char *digest
, robj 
*o
) { 
  28     o 
= getDecodedObject(o
); 
  29     xorDigest(digest
,o
->ptr
,sdslen(o
->ptr
)); 
  33 /* This function instead of just computing the SHA1 and xoring it 
  34  * against diget, also perform the digest of "digest" itself and 
  35  * replace the old value with the new one. 
  37  * So the final digest will be: 
  39  * digest = SHA1(digest xor SHA1(data)) 
  41  * This function is used every time we want to preserve the order so 
  42  * that digest(a,b,c,d) will be different than digest(b,c,d,a) 
  44  * Also note that mixdigest("foo") followed by mixdigest("bar") 
  45  * will lead to a different digest compared to "fo", "obar". 
  47 void mixDigest(unsigned char *digest
, void *ptr
, size_t len
) { 
  51     xorDigest(digest
,s
,len
); 
  53     SHA1Update(&ctx
,digest
,20); 
  54     SHA1Final(digest
,&ctx
); 
  57 void mixObjectDigest(unsigned char *digest
, robj 
*o
) { 
  58     o 
= getDecodedObject(o
); 
  59     mixDigest(digest
,o
->ptr
,sdslen(o
->ptr
)); 
  63 /* Compute the dataset digest. Since keys, sets elements, hashes elements 
  64  * are not ordered, we use a trick: every aggregate digest is the xor 
  65  * of the digests of their elements. This way the order will not change 
  66  * the result. For list instead we use a feedback entering the output digest 
  67  * as input in order to ensure that a different ordered list will result in 
  68  * a different digest. */ 
  69 void computeDatasetDigest(unsigned char *final
) { 
  70     unsigned char digest
[20]; 
  72     dictIterator 
*di 
= NULL
; 
  77     memset(final
,0,20); /* Start with a clean result */ 
  79     for (j 
= 0; j 
< server
.dbnum
; j
++) { 
  80         redisDb 
*db 
= server
.db
+j
; 
  82         if (dictSize(db
->dict
) == 0) continue; 
  83         di 
= dictGetIterator(db
->dict
); 
  85         /* hash the DB id, so the same dataset moved in a different 
  86          * DB will lead to a different digest */ 
  88         mixDigest(final
,&aux
,sizeof(aux
)); 
  90         /* Iterate this DB writing every entry */ 
  91         while((de 
= dictNext(di
)) != NULL
) { 
  96             memset(digest
,0,20); /* This key-val digest */ 
  98             keyobj 
= createStringObject(key
,sdslen(key
)); 
 100             mixDigest(digest
,key
,sdslen(key
)); 
 102             /* Make sure the key is loaded if VM is active */ 
 105             aux 
= htonl(o
->type
); 
 106             mixDigest(digest
,&aux
,sizeof(aux
)); 
 107             expiretime 
= getExpire(db
,keyobj
); 
 109             /* Save the key and associated value */ 
 110             if (o
->type 
== REDIS_STRING
) { 
 111                 mixObjectDigest(digest
,o
); 
 112             } else if (o
->type 
== REDIS_LIST
) { 
 113                 listTypeIterator 
*li 
= listTypeInitIterator(o
,0,REDIS_TAIL
); 
 115                 while(listTypeNext(li
,&entry
)) { 
 116                     robj 
*eleobj 
= listTypeGet(&entry
); 
 117                     mixObjectDigest(digest
,eleobj
); 
 118                     decrRefCount(eleobj
); 
 120                 listTypeReleaseIterator(li
); 
 121             } else if (o
->type 
== REDIS_SET
) { 
 122                 setTypeIterator 
*si 
= setTypeInitIterator(o
); 
 124                 while((ele 
= setTypeNextObject(si
)) != NULL
) { 
 125                     xorObjectDigest(digest
,ele
); 
 128                 setTypeReleaseIterator(si
); 
 129             } else if (o
->type 
== REDIS_ZSET
) { 
 130                 unsigned char eledigest
[20]; 
 132                 if (o
->encoding 
== REDIS_ENCODING_ZIPLIST
) { 
 133                     unsigned char *zl 
= o
->ptr
; 
 134                     unsigned char *eptr
, *sptr
; 
 140                     eptr 
= ziplistIndex(zl
,0); 
 141                     redisAssert(eptr 
!= NULL
); 
 142                     sptr 
= ziplistNext(zl
,eptr
); 
 143                     redisAssert(sptr 
!= NULL
); 
 145                     while (eptr 
!= NULL
) { 
 146                         redisAssert(ziplistGet(eptr
,&vstr
,&vlen
,&vll
)); 
 147                         score 
= zzlGetScore(sptr
); 
 149                         memset(eledigest
,0,20); 
 151                             mixDigest(eledigest
,vstr
,vlen
); 
 153                             ll2string(buf
,sizeof(buf
),vll
); 
 154                             mixDigest(eledigest
,buf
,strlen(buf
)); 
 157                         snprintf(buf
,sizeof(buf
),"%.17g",score
); 
 158                         mixDigest(eledigest
,buf
,strlen(buf
)); 
 159                         xorDigest(digest
,eledigest
,20); 
 160                         zzlNext(zl
,&eptr
,&sptr
); 
 162                 } else if (o
->encoding 
== REDIS_ENCODING_SKIPLIST
) { 
 164                     dictIterator 
*di 
= dictGetIterator(zs
->dict
); 
 167                     while((de 
= dictNext(di
)) != NULL
) { 
 168                         robj 
*eleobj 
= dictGetKey(de
); 
 169                         double *score 
= dictGetVal(de
); 
 171                         snprintf(buf
,sizeof(buf
),"%.17g",*score
); 
 172                         memset(eledigest
,0,20); 
 173                         mixObjectDigest(eledigest
,eleobj
); 
 174                         mixDigest(eledigest
,buf
,strlen(buf
)); 
 175                         xorDigest(digest
,eledigest
,20); 
 177                     dictReleaseIterator(di
); 
 179                     redisPanic("Unknown sorted set encoding"); 
 181             } else if (o
->type 
== REDIS_HASH
) { 
 182                 hashTypeIterator 
*hi
; 
 185                 hi 
= hashTypeInitIterator(o
); 
 186                 while (hashTypeNext(hi
) != REDIS_ERR
) { 
 187                     unsigned char eledigest
[20]; 
 189                     memset(eledigest
,0,20); 
 190                     obj 
= hashTypeCurrentObject(hi
,REDIS_HASH_KEY
); 
 191                     mixObjectDigest(eledigest
,obj
); 
 193                     obj 
= hashTypeCurrentObject(hi
,REDIS_HASH_VALUE
); 
 194                     mixObjectDigest(eledigest
,obj
); 
 196                     xorDigest(digest
,eledigest
,20); 
 198                 hashTypeReleaseIterator(hi
); 
 200                 redisPanic("Unknown object type"); 
 202             /* If the key has an expire, add it to the mix */ 
 203             if (expiretime 
!= -1) xorDigest(digest
,"!!expire!!",10); 
 204             /* We can finally xor the key-val digest to the final digest */ 
 205             xorDigest(final
,digest
,20); 
 206             decrRefCount(keyobj
); 
 208         dictReleaseIterator(di
); 
 212 void debugCommand(redisClient 
*c
) { 
 213     if (!strcasecmp(c
->argv
[1]->ptr
,"segfault")) { 
 215     } else if (!strcasecmp(c
->argv
[1]->ptr
,"assert")) { 
 216         if (c
->argc 
>= 3) c
->argv
[2] = tryObjectEncoding(c
->argv
[2]); 
 217         redisAssertWithInfo(c
,c
->argv
[0],1 == 2); 
 218     } else if (!strcasecmp(c
->argv
[1]->ptr
,"reload")) { 
 219         if (rdbSave(server
.dbfilename
) != REDIS_OK
) { 
 220             addReply(c
,shared
.err
); 
 224         if (rdbLoad(server
.dbfilename
) != REDIS_OK
) { 
 225             addReplyError(c
,"Error trying to load the RDB dump"); 
 228         redisLog(REDIS_WARNING
,"DB reloaded by DEBUG RELOAD"); 
 229         addReply(c
,shared
.ok
); 
 230     } else if (!strcasecmp(c
->argv
[1]->ptr
,"loadaof")) { 
 232         if (loadAppendOnlyFile(server
.appendfilename
) != REDIS_OK
) { 
 233             addReply(c
,shared
.err
); 
 236         redisLog(REDIS_WARNING
,"Append Only File loaded by DEBUG LOADAOF"); 
 237         addReply(c
,shared
.ok
); 
 238     } else if (!strcasecmp(c
->argv
[1]->ptr
,"object") && c
->argc 
== 3) { 
 243         if ((de 
= dictFind(c
->db
->dict
,c
->argv
[2]->ptr
)) == NULL
) { 
 244             addReply(c
,shared
.nokeyerr
); 
 247         val 
= dictGetVal(de
); 
 248         strenc 
= strEncoding(val
->encoding
); 
 250         addReplyStatusFormat(c
, 
 251             "Value at:%p refcount:%d " 
 252             "encoding:%s serializedlength:%lld " 
 253             "lru:%d lru_seconds_idle:%lu", 
 254             (void*)val
, val
->refcount
, 
 255             strenc
, (long long) rdbSavedObjectLen(val
), 
 256             val
->lru
, estimateObjectIdleTime(val
)); 
 257     } else if (!strcasecmp(c
->argv
[1]->ptr
,"populate") && c
->argc 
== 3) { 
 262         if (getLongFromObjectOrReply(c
, c
->argv
[2], &keys
, NULL
) != REDIS_OK
) 
 264         for (j 
= 0; j 
< keys
; j
++) { 
 265             snprintf(buf
,sizeof(buf
),"key:%lu",j
); 
 266             key 
= createStringObject(buf
,strlen(buf
)); 
 267             if (lookupKeyRead(c
->db
,key
) != NULL
) { 
 271             snprintf(buf
,sizeof(buf
),"value:%lu",j
); 
 272             val 
= createStringObject(buf
,strlen(buf
)); 
 273             dbAdd(c
->db
,key
,val
); 
 276         addReply(c
,shared
.ok
); 
 277     } else if (!strcasecmp(c
->argv
[1]->ptr
,"digest") && c
->argc 
== 2) { 
 278         unsigned char digest
[20]; 
 282         computeDatasetDigest(digest
); 
 283         for (j 
= 0; j 
< 20; j
++) 
 284             d 
= sdscatprintf(d
, "%02x",digest
[j
]); 
 287     } else if (!strcasecmp(c
->argv
[1]->ptr
,"sleep") && c
->argc 
== 3) { 
 288         double dtime 
= strtod(c
->argv
[2]->ptr
,NULL
); 
 289         long long utime 
= dtime
*1000000; 
 292         addReply(c
,shared
.ok
); 
 295             "Syntax error, try DEBUG [SEGFAULT|OBJECT <key>|SWAPIN <key>|SWAPOUT <key>|RELOAD]"); 
 299 void _redisAssert(char *estr
, char *file
, int line
) { 
 300     redisLog(REDIS_WARNING
,"=== ASSERTION FAILED ==="); 
 301     redisLog(REDIS_WARNING
,"==> %s:%d '%s' is not true",file
,line
,estr
); 
 302 #ifdef HAVE_BACKTRACE 
 303     redisLog(REDIS_WARNING
,"(forcing SIGSEGV in order to print the stack trace)"); 
 308 void _redisAssertPrintClientInfo(redisClient 
*c
) { 
 311     redisLog(REDIS_WARNING
,"=== ASSERTION FAILED CLIENT CONTEXT ==="); 
 312     redisLog(REDIS_WARNING
,"client->flags = %d", c
->flags
); 
 313     redisLog(REDIS_WARNING
,"client->fd = %d", c
->fd
); 
 314     redisLog(REDIS_WARNING
,"client->argc = %d", c
->argc
); 
 315     for (j
=0; j 
< c
->argc
; j
++) { 
 319         if (c
->argv
[j
]->type 
== REDIS_STRING 
&& 
 320             c
->argv
[j
]->encoding 
== REDIS_ENCODING_RAW
) 
 322             arg 
= (char*) c
->argv
[j
]->ptr
; 
 324             snprintf(buf
,sizeof(buf
),"Object type: %d, encoding: %d", 
 325                 c
->argv
[j
]->type
, c
->argv
[j
]->encoding
); 
 328         redisLog(REDIS_WARNING
,"client->argv[%d] = \"%s\" (refcount: %d)", 
 329             j
, arg
, c
->argv
[j
]->refcount
); 
 333 void _redisAssertPrintObject(robj 
*o
) { 
 334     redisLog(REDIS_WARNING
,"=== ASSERTION FAILED OBJECT CONTEXT ==="); 
 335     redisLog(REDIS_WARNING
,"Object type: %d", o
->type
); 
 336     redisLog(REDIS_WARNING
,"Object encoding: %d", o
->encoding
); 
 337     redisLog(REDIS_WARNING
,"Object refcount: %d", o
->refcount
); 
 338     if (o
->type 
== REDIS_STRING 
&& o
->encoding 
== REDIS_ENCODING_RAW
) { 
 339         redisLog(REDIS_WARNING
,"Object raw string len: %d", sdslen(o
->ptr
)); 
 340         if (sdslen(o
->ptr
) < 4096) 
 341             redisLog(REDIS_WARNING
,"Object raw string content: \"%s\"", (char*)o
->ptr
); 
 345 void _redisAssertWithInfo(redisClient 
*c
, robj 
*o
, char *estr
, char *file
, int line
) { 
 346     if (c
) _redisAssertPrintClientInfo(c
); 
 347     if (o
) _redisAssertPrintObject(o
); 
 348     _redisAssert(estr
,file
,line
); 
 351 void _redisPanic(char *msg
, char *file
, int line
) { 
 352     redisLog(REDIS_WARNING
,"------------------------------------------------"); 
 353     redisLog(REDIS_WARNING
,"!!! Software Failure. Press left mouse button to continue"); 
 354     redisLog(REDIS_WARNING
,"Guru Meditation: %s #%s:%d",msg
,file
,line
); 
 355 #ifdef HAVE_BACKTRACE 
 356     redisLog(REDIS_WARNING
,"(forcing SIGSEGV in order to print the stack trace)"); 
 357     redisLog(REDIS_WARNING
,"------------------------------------------------");