]>
git.saurik.com Git - redis.git/blob - src/debug.c
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
.rdb_filename
) != REDIS_OK
) {
220 addReply(c
,shared
.err
);
224 if (rdbLoad(server
.rdb_filename
) != 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
.aof_filename
) != REDIS_OK
) {
233 addReply(c
,shared
.err
);
236 server
.dirty
= 0; /* Prevent AOF / replication */
237 redisLog(REDIS_WARNING
,"Append Only File loaded by DEBUG LOADAOF");
238 addReply(c
,shared
.ok
);
239 } else if (!strcasecmp(c
->argv
[1]->ptr
,"object") && c
->argc
== 3) {
244 if ((de
= dictFind(c
->db
->dict
,c
->argv
[2]->ptr
)) == NULL
) {
245 addReply(c
,shared
.nokeyerr
);
248 val
= dictGetVal(de
);
249 strenc
= strEncoding(val
->encoding
);
251 addReplyStatusFormat(c
,
252 "Value at:%p refcount:%d "
253 "encoding:%s serializedlength:%lld "
254 "lru:%d lru_seconds_idle:%lu",
255 (void*)val
, val
->refcount
,
256 strenc
, (long long) rdbSavedObjectLen(val
),
257 val
->lru
, estimateObjectIdleTime(val
));
258 } else if (!strcasecmp(c
->argv
[1]->ptr
,"populate") && c
->argc
== 3) {
263 if (getLongFromObjectOrReply(c
, c
->argv
[2], &keys
, NULL
) != REDIS_OK
)
265 for (j
= 0; j
< keys
; j
++) {
266 snprintf(buf
,sizeof(buf
),"key:%lu",j
);
267 key
= createStringObject(buf
,strlen(buf
));
268 if (lookupKeyRead(c
->db
,key
) != NULL
) {
272 snprintf(buf
,sizeof(buf
),"value:%lu",j
);
273 val
= createStringObject(buf
,strlen(buf
));
274 dbAdd(c
->db
,key
,val
);
277 addReply(c
,shared
.ok
);
278 } else if (!strcasecmp(c
->argv
[1]->ptr
,"digest") && c
->argc
== 2) {
279 unsigned char digest
[20];
283 computeDatasetDigest(digest
);
284 for (j
= 0; j
< 20; j
++)
285 d
= sdscatprintf(d
, "%02x",digest
[j
]);
288 } else if (!strcasecmp(c
->argv
[1]->ptr
,"sleep") && c
->argc
== 3) {
289 double dtime
= strtod(c
->argv
[2]->ptr
,NULL
);
290 long long utime
= dtime
*1000000;
293 addReply(c
,shared
.ok
);
296 "Syntax error, try DEBUG [SEGFAULT|OBJECT <key>|SWAPIN <key>|SWAPOUT <key>|RELOAD]");
300 void _redisAssert(char *estr
, char *file
, int line
) {
302 redisLog(REDIS_WARNING
,"=== ASSERTION FAILED ===");
303 redisLog(REDIS_WARNING
,"==> %s:%d '%s' is not true",file
,line
,estr
);
304 #ifdef HAVE_BACKTRACE
305 server
.assert_failed
= estr
;
306 server
.assert_file
= file
;
307 server
.assert_line
= line
;
308 redisLog(REDIS_WARNING
,"(forcing SIGSEGV to print the bug report.)");
313 void _redisAssertPrintClientInfo(redisClient
*c
) {
317 redisLog(REDIS_WARNING
,"=== ASSERTION FAILED CLIENT CONTEXT ===");
318 redisLog(REDIS_WARNING
,"client->flags = %d", c
->flags
);
319 redisLog(REDIS_WARNING
,"client->fd = %d", c
->fd
);
320 redisLog(REDIS_WARNING
,"client->argc = %d", c
->argc
);
321 for (j
=0; j
< c
->argc
; j
++) {
325 if (c
->argv
[j
]->type
== REDIS_STRING
&&
326 c
->argv
[j
]->encoding
== REDIS_ENCODING_RAW
)
328 arg
= (char*) c
->argv
[j
]->ptr
;
330 snprintf(buf
,sizeof(buf
),"Object type: %d, encoding: %d",
331 c
->argv
[j
]->type
, c
->argv
[j
]->encoding
);
334 redisLog(REDIS_WARNING
,"client->argv[%d] = \"%s\" (refcount: %d)",
335 j
, arg
, c
->argv
[j
]->refcount
);
339 void redisLogObjectDebugInfo(robj
*o
) {
340 redisLog(REDIS_WARNING
,"Object type: %d", o
->type
);
341 redisLog(REDIS_WARNING
,"Object encoding: %d", o
->encoding
);
342 redisLog(REDIS_WARNING
,"Object refcount: %d", o
->refcount
);
343 if (o
->type
== REDIS_STRING
&& o
->encoding
== REDIS_ENCODING_RAW
) {
344 redisLog(REDIS_WARNING
,"Object raw string len: %d", sdslen(o
->ptr
));
345 if (sdslen(o
->ptr
) < 4096)
346 redisLog(REDIS_WARNING
,"Object raw string content: \"%s\"", (char*)o
->ptr
);
347 } else if (o
->type
== REDIS_LIST
) {
348 redisLog(REDIS_WARNING
,"List length: %d", (int) listTypeLength(o
));
349 } else if (o
->type
== REDIS_SET
) {
350 redisLog(REDIS_WARNING
,"Set size: %d", (int) setTypeSize(o
));
351 } else if (o
->type
== REDIS_HASH
) {
352 redisLog(REDIS_WARNING
,"Hash size: %d", (int) hashTypeLength(o
));
353 } else if (o
->type
== REDIS_ZSET
) {
354 redisLog(REDIS_WARNING
,"Sorted set size: %d", (int) zsetLength(o
));
355 if (o
->encoding
== REDIS_ENCODING_SKIPLIST
)
356 redisLog(REDIS_WARNING
,"Skiplist level: %d", (int) ((zset
*)o
->ptr
)->zsl
->level
);
360 void _redisAssertPrintObject(robj
*o
) {
362 redisLog(REDIS_WARNING
,"=== ASSERTION FAILED OBJECT CONTEXT ===");
363 redisLogObjectDebugInfo(o
);
366 void _redisAssertWithInfo(redisClient
*c
, robj
*o
, char *estr
, char *file
, int line
) {
367 if (c
) _redisAssertPrintClientInfo(c
);
368 if (o
) _redisAssertPrintObject(o
);
369 _redisAssert(estr
,file
,line
);
372 void _redisPanic(char *msg
, char *file
, int line
) {
374 redisLog(REDIS_WARNING
,"------------------------------------------------");
375 redisLog(REDIS_WARNING
,"!!! Software Failure. Press left mouse button to continue");
376 redisLog(REDIS_WARNING
,"Guru Meditation: %s #%s:%d",msg
,file
,line
);
377 #ifdef HAVE_BACKTRACE
378 redisLog(REDIS_WARNING
,"(forcing SIGSEGV in order to print the stack trace)");
379 redisLog(REDIS_WARNING
,"------------------------------------------------");