]>
git.saurik.com Git - redis.git/blob - src/debug.c
2 #include "sha1.h" /* SHA1 is used for DEBUG DIGEST */
4 /* ================================= Debugging ============================== */
6 /* Compute the sha1 of string at 's' with 'len' bytes long.
7 * The SHA1 is then xored againt the string pointed by digest.
8 * Since xor is commutative, this operation is used in order to
9 * "add" digests relative to unordered elements.
11 * So digest(a,b,c,d) will be the same of digest(b,a,c,d) */
12 void xorDigest(unsigned char *digest
, void *ptr
, size_t len
) {
14 unsigned char hash
[20], *s
= ptr
;
18 SHA1Update(&ctx
,s
,len
);
21 for (j
= 0; j
< 20; j
++)
25 void xorObjectDigest(unsigned char *digest
, robj
*o
) {
26 o
= getDecodedObject(o
);
27 xorDigest(digest
,o
->ptr
,sdslen(o
->ptr
));
31 /* This function instead of just computing the SHA1 and xoring it
32 * against diget, also perform the digest of "digest" itself and
33 * replace the old value with the new one.
35 * So the final digest will be:
37 * digest = SHA1(digest xor SHA1(data))
39 * This function is used every time we want to preserve the order so
40 * that digest(a,b,c,d) will be different than digest(b,c,d,a)
42 * Also note that mixdigest("foo") followed by mixdigest("bar")
43 * will lead to a different digest compared to "fo", "obar".
45 void mixDigest(unsigned char *digest
, void *ptr
, size_t len
) {
49 xorDigest(digest
,s
,len
);
51 SHA1Update(&ctx
,digest
,20);
52 SHA1Final(digest
,&ctx
);
55 void mixObjectDigest(unsigned char *digest
, robj
*o
) {
56 o
= getDecodedObject(o
);
57 mixDigest(digest
,o
->ptr
,sdslen(o
->ptr
));
61 /* Compute the dataset digest. Since keys, sets elements, hashes elements
62 * are not ordered, we use a trick: every aggregate digest is the xor
63 * of the digests of their elements. This way the order will not change
64 * the result. For list instead we use a feedback entering the output digest
65 * as input in order to ensure that a different ordered list will result in
66 * a different digest. */
67 void computeDatasetDigest(unsigned char *final
) {
68 unsigned char digest
[20];
70 dictIterator
*di
= NULL
;
75 memset(final
,0,20); /* Start with a clean result */
77 for (j
= 0; j
< server
.dbnum
; j
++) {
78 redisDb
*db
= server
.db
+j
;
80 if (dictSize(db
->dict
) == 0) continue;
81 di
= dictGetIterator(db
->dict
);
83 /* hash the DB id, so the same dataset moved in a different
84 * DB will lead to a different digest */
86 mixDigest(final
,&aux
,sizeof(aux
));
88 /* Iterate this DB writing every entry */
89 while((de
= dictNext(di
)) != NULL
) {
94 memset(digest
,0,20); /* This key-val digest */
95 key
= dictGetEntryKey(de
);
96 keyobj
= createStringObject(key
,sdslen(key
));
98 mixDigest(digest
,key
,sdslen(key
));
100 /* Make sure the key is loaded if VM is active */
101 o
= lookupKeyRead(db
,keyobj
);
103 aux
= htonl(o
->type
);
104 mixDigest(digest
,&aux
,sizeof(aux
));
105 expiretime
= getExpire(db
,keyobj
);
107 /* Save the key and associated value */
108 if (o
->type
== REDIS_STRING
) {
109 mixObjectDigest(digest
,o
);
110 } else if (o
->type
== REDIS_LIST
) {
111 listTypeIterator
*li
= listTypeInitIterator(o
,0,REDIS_TAIL
);
113 while(listTypeNext(li
,&entry
)) {
114 robj
*eleobj
= listTypeGet(&entry
);
115 mixObjectDigest(digest
,eleobj
);
116 decrRefCount(eleobj
);
118 listTypeReleaseIterator(li
);
119 } else if (o
->type
== REDIS_SET
) {
121 dictIterator
*di
= dictGetIterator(set
);
124 while((de
= dictNext(di
)) != NULL
) {
125 robj
*eleobj
= dictGetEntryKey(de
);
127 xorObjectDigest(digest
,eleobj
);
129 dictReleaseIterator(di
);
130 } else if (o
->type
== REDIS_ZSET
) {
132 dictIterator
*di
= dictGetIterator(zs
->dict
);
135 while((de
= dictNext(di
)) != NULL
) {
136 robj
*eleobj
= dictGetEntryKey(de
);
137 double *score
= dictGetEntryVal(de
);
138 unsigned char eledigest
[20];
140 snprintf(buf
,sizeof(buf
),"%.17g",*score
);
141 memset(eledigest
,0,20);
142 mixObjectDigest(eledigest
,eleobj
);
143 mixDigest(eledigest
,buf
,strlen(buf
));
144 xorDigest(digest
,eledigest
,20);
146 dictReleaseIterator(di
);
147 } else if (o
->type
== REDIS_HASH
) {
148 hashTypeIterator
*hi
;
151 hi
= hashTypeInitIterator(o
);
152 while (hashTypeNext(hi
) != REDIS_ERR
) {
153 unsigned char eledigest
[20];
155 memset(eledigest
,0,20);
156 obj
= hashTypeCurrent(hi
,REDIS_HASH_KEY
);
157 mixObjectDigest(eledigest
,obj
);
159 obj
= hashTypeCurrent(hi
,REDIS_HASH_VALUE
);
160 mixObjectDigest(eledigest
,obj
);
162 xorDigest(digest
,eledigest
,20);
164 hashTypeReleaseIterator(hi
);
166 redisPanic("Unknown object type");
168 /* If the key has an expire, add it to the mix */
169 if (expiretime
!= -1) xorDigest(digest
,"!!expire!!",10);
170 /* We can finally xor the key-val digest to the final digest */
171 xorDigest(final
,digest
,20);
172 decrRefCount(keyobj
);
174 dictReleaseIterator(di
);
178 void debugCommand(redisClient
*c
) {
179 if (!strcasecmp(c
->argv
[1]->ptr
,"segfault")) {
181 } else if (!strcasecmp(c
->argv
[1]->ptr
,"reload")) {
182 if (rdbSave(server
.dbfilename
) != REDIS_OK
) {
183 addReply(c
,shared
.err
);
187 if (rdbLoad(server
.dbfilename
) != REDIS_OK
) {
188 addReply(c
,shared
.err
);
191 redisLog(REDIS_WARNING
,"DB reloaded by DEBUG RELOAD");
192 addReply(c
,shared
.ok
);
193 } else if (!strcasecmp(c
->argv
[1]->ptr
,"loadaof")) {
195 if (loadAppendOnlyFile(server
.appendfilename
) != REDIS_OK
) {
196 addReply(c
,shared
.err
);
199 redisLog(REDIS_WARNING
,"Append Only File loaded by DEBUG LOADAOF");
200 addReply(c
,shared
.ok
);
201 } else if (!strcasecmp(c
->argv
[1]->ptr
,"object") && c
->argc
== 3) {
202 dictEntry
*de
= dictFind(c
->db
->dict
,c
->argv
[2]->ptr
);
206 addReply(c
,shared
.nokeyerr
);
209 val
= dictGetEntryVal(de
);
210 if (!server
.vm_enabled
|| (val
->storage
== REDIS_VM_MEMORY
||
211 val
->storage
== REDIS_VM_SWAPPING
)) {
214 strenc
= strEncoding(val
->encoding
);
215 addReplySds(c
,sdscatprintf(sdsempty(),
216 "+Value at:%p refcount:%d "
217 "encoding:%s serializedlength:%lld\r\n",
218 (void*)val
, val
->refcount
,
219 strenc
, (long long) rdbSavedObjectLen(val
,NULL
)));
221 vmpointer
*vp
= (vmpointer
*) val
;
222 addReplySds(c
,sdscatprintf(sdsempty(),
223 "+Value swapped at: page %llu "
224 "using %llu pages\r\n",
225 (unsigned long long) vp
->page
,
226 (unsigned long long) vp
->usedpages
));
228 } else if (!strcasecmp(c
->argv
[1]->ptr
,"swapin") && c
->argc
== 3) {
229 lookupKeyRead(c
->db
,c
->argv
[2]);
230 addReply(c
,shared
.ok
);
231 } else if (!strcasecmp(c
->argv
[1]->ptr
,"swapout") && c
->argc
== 3) {
232 dictEntry
*de
= dictFind(c
->db
->dict
,c
->argv
[2]->ptr
);
236 if (!server
.vm_enabled
) {
237 addReplySds(c
,sdsnew("-ERR Virtual Memory is disabled\r\n"));
241 addReply(c
,shared
.nokeyerr
);
244 val
= dictGetEntryVal(de
);
246 if (val
->storage
!= REDIS_VM_MEMORY
) {
247 addReplySds(c
,sdsnew("-ERR This key is not in memory\r\n"));
248 } else if (val
->refcount
!= 1) {
249 addReplySds(c
,sdsnew("-ERR Object is shared\r\n"));
250 } else if ((vp
= vmSwapObjectBlocking(val
)) != NULL
) {
251 dictGetEntryVal(de
) = vp
;
252 addReply(c
,shared
.ok
);
254 addReply(c
,shared
.err
);
256 } else if (!strcasecmp(c
->argv
[1]->ptr
,"populate") && c
->argc
== 3) {
261 if (getLongFromObjectOrReply(c
, c
->argv
[2], &keys
, NULL
) != REDIS_OK
)
263 for (j
= 0; j
< keys
; j
++) {
264 snprintf(buf
,sizeof(buf
),"key:%lu",j
);
265 key
= createStringObject(buf
,strlen(buf
));
266 if (lookupKeyRead(c
->db
,key
) != NULL
) {
270 snprintf(buf
,sizeof(buf
),"value:%lu",j
);
271 val
= createStringObject(buf
,strlen(buf
));
272 dbAdd(c
->db
,key
,val
);
275 addReply(c
,shared
.ok
);
276 } else if (!strcasecmp(c
->argv
[1]->ptr
,"digest") && c
->argc
== 2) {
277 unsigned char digest
[20];
281 computeDatasetDigest(digest
);
282 for (j
= 0; j
< 20; j
++)
283 d
= sdscatprintf(d
, "%02x",digest
[j
]);
285 d
= sdscatlen(d
,"\r\n",2);
288 addReplySds(c
,sdsnew(
289 "-ERR Syntax error, try DEBUG [SEGFAULT|OBJECT <key>|SWAPIN <key>|SWAPOUT <key>|RELOAD]\r\n"));
293 void _redisAssert(char *estr
, char *file
, int line
) {
294 redisLog(REDIS_WARNING
,"=== ASSERTION FAILED ===");
295 redisLog(REDIS_WARNING
,"==> %s:%d '%s' is not true",file
,line
,estr
);
296 #ifdef HAVE_BACKTRACE
297 redisLog(REDIS_WARNING
,"(forcing SIGSEGV in order to print the stack trace)");
302 void _redisPanic(char *msg
, char *file
, int line
) {
303 redisLog(REDIS_WARNING
,"!!! Software Failure. Press left mouse button to continue");
304 redisLog(REDIS_WARNING
,"Guru Meditation: %s #%s:%d",msg
,file
,line
);
305 #ifdef HAVE_BACKTRACE
306 redisLog(REDIS_WARNING
,"(forcing SIGSEGV in order to print the stack trace)");