]>
git.saurik.com Git - redis.git/blob - src/debug.c
31cfac65ef94621a7d5709011cdb3c24dcecceb3
2 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of Redis nor the names of its contributors may be used
14 * to endorse or promote products derived from this software without
15 * specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
31 #include "sha1.h" /* SHA1 is used for DEBUG DIGEST */
33 #include <arpa/inet.h>
41 #endif /* HAVE_BACKTRACE */
43 /* ================================= Debugging ============================== */
45 /* Compute the sha1 of string at 's' with 'len' bytes long.
46 * The SHA1 is then xored againt the string pointed by digest.
47 * Since xor is commutative, this operation is used in order to
48 * "add" digests relative to unordered elements.
50 * So digest(a,b,c,d) will be the same of digest(b,a,c,d) */
51 void xorDigest(unsigned char *digest
, void *ptr
, size_t len
) {
53 unsigned char hash
[20], *s
= ptr
;
57 SHA1Update(&ctx
,s
,len
);
60 for (j
= 0; j
< 20; j
++)
64 void xorObjectDigest(unsigned char *digest
, robj
*o
) {
65 o
= getDecodedObject(o
);
66 xorDigest(digest
,o
->ptr
,sdslen(o
->ptr
));
70 /* This function instead of just computing the SHA1 and xoring it
71 * against diget, also perform the digest of "digest" itself and
72 * replace the old value with the new one.
74 * So the final digest will be:
76 * digest = SHA1(digest xor SHA1(data))
78 * This function is used every time we want to preserve the order so
79 * that digest(a,b,c,d) will be different than digest(b,c,d,a)
81 * Also note that mixdigest("foo") followed by mixdigest("bar")
82 * will lead to a different digest compared to "fo", "obar".
84 void mixDigest(unsigned char *digest
, void *ptr
, size_t len
) {
88 xorDigest(digest
,s
,len
);
90 SHA1Update(&ctx
,digest
,20);
91 SHA1Final(digest
,&ctx
);
94 void mixObjectDigest(unsigned char *digest
, robj
*o
) {
95 o
= getDecodedObject(o
);
96 mixDigest(digest
,o
->ptr
,sdslen(o
->ptr
));
100 /* Compute the dataset digest. Since keys, sets elements, hashes elements
101 * are not ordered, we use a trick: every aggregate digest is the xor
102 * of the digests of their elements. This way the order will not change
103 * the result. For list instead we use a feedback entering the output digest
104 * as input in order to ensure that a different ordered list will result in
105 * a different digest. */
106 void computeDatasetDigest(unsigned char *final
) {
107 unsigned char digest
[20];
109 dictIterator
*di
= NULL
;
114 memset(final
,0,20); /* Start with a clean result */
116 for (j
= 0; j
< server
.dbnum
; j
++) {
117 redisDb
*db
= server
.db
+j
;
119 if (dictSize(db
->dict
) == 0) continue;
120 di
= dictGetIterator(db
->dict
);
122 /* hash the DB id, so the same dataset moved in a different
123 * DB will lead to a different digest */
125 mixDigest(final
,&aux
,sizeof(aux
));
127 /* Iterate this DB writing every entry */
128 while((de
= dictNext(di
)) != NULL
) {
131 long long expiretime
;
133 memset(digest
,0,20); /* This key-val digest */
134 key
= dictGetKey(de
);
135 keyobj
= createStringObject(key
,sdslen(key
));
137 mixDigest(digest
,key
,sdslen(key
));
141 aux
= htonl(o
->type
);
142 mixDigest(digest
,&aux
,sizeof(aux
));
143 expiretime
= getExpire(db
,keyobj
);
145 /* Save the key and associated value */
146 if (o
->type
== REDIS_STRING
) {
147 mixObjectDigest(digest
,o
);
148 } else if (o
->type
== REDIS_LIST
) {
149 listTypeIterator
*li
= listTypeInitIterator(o
,0,REDIS_TAIL
);
151 while(listTypeNext(li
,&entry
)) {
152 robj
*eleobj
= listTypeGet(&entry
);
153 mixObjectDigest(digest
,eleobj
);
154 decrRefCount(eleobj
);
156 listTypeReleaseIterator(li
);
157 } else if (o
->type
== REDIS_SET
) {
158 setTypeIterator
*si
= setTypeInitIterator(o
);
160 while((ele
= setTypeNextObject(si
)) != NULL
) {
161 xorObjectDigest(digest
,ele
);
164 setTypeReleaseIterator(si
);
165 } else if (o
->type
== REDIS_ZSET
) {
166 unsigned char eledigest
[20];
168 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
169 unsigned char *zl
= o
->ptr
;
170 unsigned char *eptr
, *sptr
;
176 eptr
= ziplistIndex(zl
,0);
177 redisAssert(eptr
!= NULL
);
178 sptr
= ziplistNext(zl
,eptr
);
179 redisAssert(sptr
!= NULL
);
181 while (eptr
!= NULL
) {
182 redisAssert(ziplistGet(eptr
,&vstr
,&vlen
,&vll
));
183 score
= zzlGetScore(sptr
);
185 memset(eledigest
,0,20);
187 mixDigest(eledigest
,vstr
,vlen
);
189 ll2string(buf
,sizeof(buf
),vll
);
190 mixDigest(eledigest
,buf
,strlen(buf
));
193 snprintf(buf
,sizeof(buf
),"%.17g",score
);
194 mixDigest(eledigest
,buf
,strlen(buf
));
195 xorDigest(digest
,eledigest
,20);
196 zzlNext(zl
,&eptr
,&sptr
);
198 } else if (o
->encoding
== REDIS_ENCODING_SKIPLIST
) {
200 dictIterator
*di
= dictGetIterator(zs
->dict
);
203 while((de
= dictNext(di
)) != NULL
) {
204 robj
*eleobj
= dictGetKey(de
);
205 double *score
= dictGetVal(de
);
207 snprintf(buf
,sizeof(buf
),"%.17g",*score
);
208 memset(eledigest
,0,20);
209 mixObjectDigest(eledigest
,eleobj
);
210 mixDigest(eledigest
,buf
,strlen(buf
));
211 xorDigest(digest
,eledigest
,20);
213 dictReleaseIterator(di
);
215 redisPanic("Unknown sorted set encoding");
217 } else if (o
->type
== REDIS_HASH
) {
218 hashTypeIterator
*hi
;
221 hi
= hashTypeInitIterator(o
);
222 while (hashTypeNext(hi
) != REDIS_ERR
) {
223 unsigned char eledigest
[20];
225 memset(eledigest
,0,20);
226 obj
= hashTypeCurrentObject(hi
,REDIS_HASH_KEY
);
227 mixObjectDigest(eledigest
,obj
);
229 obj
= hashTypeCurrentObject(hi
,REDIS_HASH_VALUE
);
230 mixObjectDigest(eledigest
,obj
);
232 xorDigest(digest
,eledigest
,20);
234 hashTypeReleaseIterator(hi
);
236 redisPanic("Unknown object type");
238 /* If the key has an expire, add it to the mix */
239 if (expiretime
!= -1) xorDigest(digest
,"!!expire!!",10);
240 /* We can finally xor the key-val digest to the final digest */
241 xorDigest(final
,digest
,20);
242 decrRefCount(keyobj
);
244 dictReleaseIterator(di
);
248 void debugCommand(redisClient
*c
) {
249 if (!strcasecmp(c
->argv
[1]->ptr
,"segfault")) {
251 } else if (!strcasecmp(c
->argv
[1]->ptr
,"oom")) {
252 void *ptr
= zmalloc(ULONG_MAX
); /* Should trigger an out of memory. */
254 addReply(c
,shared
.ok
);
255 } else if (!strcasecmp(c
->argv
[1]->ptr
,"assert")) {
256 if (c
->argc
>= 3) c
->argv
[2] = tryObjectEncoding(c
->argv
[2]);
257 redisAssertWithInfo(c
,c
->argv
[0],1 == 2);
258 } else if (!strcasecmp(c
->argv
[1]->ptr
,"reload")) {
259 if (rdbSave(server
.rdb_filename
) != REDIS_OK
) {
260 addReply(c
,shared
.err
);
264 if (rdbLoad(server
.rdb_filename
) != REDIS_OK
) {
265 addReplyError(c
,"Error trying to load the RDB dump");
268 redisLog(REDIS_WARNING
,"DB reloaded by DEBUG RELOAD");
269 addReply(c
,shared
.ok
);
270 } else if (!strcasecmp(c
->argv
[1]->ptr
,"loadaof")) {
272 if (loadAppendOnlyFile(server
.aof_filename
) != REDIS_OK
) {
273 addReply(c
,shared
.err
);
276 server
.dirty
= 0; /* Prevent AOF / replication */
277 redisLog(REDIS_WARNING
,"Append Only File loaded by DEBUG LOADAOF");
278 addReply(c
,shared
.ok
);
279 } else if (!strcasecmp(c
->argv
[1]->ptr
,"object") && c
->argc
== 3) {
284 if ((de
= dictFind(c
->db
->dict
,c
->argv
[2]->ptr
)) == NULL
) {
285 addReply(c
,shared
.nokeyerr
);
288 val
= dictGetVal(de
);
289 strenc
= strEncoding(val
->encoding
);
291 addReplyStatusFormat(c
,
292 "Value at:%p refcount:%d "
293 "encoding:%s serializedlength:%lld "
294 "lru:%d lru_seconds_idle:%lu",
295 (void*)val
, val
->refcount
,
296 strenc
, (long long) rdbSavedObjectLen(val
),
297 val
->lru
, estimateObjectIdleTime(val
));
298 } else if (!strcasecmp(c
->argv
[1]->ptr
,"populate") && c
->argc
== 3) {
303 if (getLongFromObjectOrReply(c
, c
->argv
[2], &keys
, NULL
) != REDIS_OK
)
305 for (j
= 0; j
< keys
; j
++) {
306 snprintf(buf
,sizeof(buf
),"key:%lu",j
);
307 key
= createStringObject(buf
,strlen(buf
));
308 if (lookupKeyRead(c
->db
,key
) != NULL
) {
312 snprintf(buf
,sizeof(buf
),"value:%lu",j
);
313 val
= createStringObject(buf
,strlen(buf
));
314 dbAdd(c
->db
,key
,val
);
317 addReply(c
,shared
.ok
);
318 } else if (!strcasecmp(c
->argv
[1]->ptr
,"digest") && c
->argc
== 2) {
319 unsigned char digest
[20];
323 computeDatasetDigest(digest
);
324 for (j
= 0; j
< 20; j
++)
325 d
= sdscatprintf(d
, "%02x",digest
[j
]);
328 } else if (!strcasecmp(c
->argv
[1]->ptr
,"sleep") && c
->argc
== 3) {
329 double dtime
= strtod(c
->argv
[2]->ptr
,NULL
);
330 long long utime
= dtime
*1000000;
333 addReply(c
,shared
.ok
);
336 "Syntax error, try DEBUG [SEGFAULT|OBJECT <key>|SWAPIN <key>|SWAPOUT <key>|RELOAD]");
340 /* =========================== Crash handling ============================== */
342 void _redisAssert(char *estr
, char *file
, int line
) {
344 redisLog(REDIS_WARNING
,"=== ASSERTION FAILED ===");
345 redisLog(REDIS_WARNING
,"==> %s:%d '%s' is not true",file
,line
,estr
);
346 #ifdef HAVE_BACKTRACE
347 server
.assert_failed
= estr
;
348 server
.assert_file
= file
;
349 server
.assert_line
= line
;
350 redisLog(REDIS_WARNING
,"(forcing SIGSEGV to print the bug report.)");
355 void _redisAssertPrintClientInfo(redisClient
*c
) {
359 redisLog(REDIS_WARNING
,"=== ASSERTION FAILED CLIENT CONTEXT ===");
360 redisLog(REDIS_WARNING
,"client->flags = %d", c
->flags
);
361 redisLog(REDIS_WARNING
,"client->fd = %d", c
->fd
);
362 redisLog(REDIS_WARNING
,"client->argc = %d", c
->argc
);
363 for (j
=0; j
< c
->argc
; j
++) {
367 if (c
->argv
[j
]->type
== REDIS_STRING
&&
368 c
->argv
[j
]->encoding
== REDIS_ENCODING_RAW
)
370 arg
= (char*) c
->argv
[j
]->ptr
;
372 snprintf(buf
,sizeof(buf
),"Object type: %d, encoding: %d",
373 c
->argv
[j
]->type
, c
->argv
[j
]->encoding
);
376 redisLog(REDIS_WARNING
,"client->argv[%d] = \"%s\" (refcount: %d)",
377 j
, arg
, c
->argv
[j
]->refcount
);
381 void redisLogObjectDebugInfo(robj
*o
) {
382 redisLog(REDIS_WARNING
,"Object type: %d", o
->type
);
383 redisLog(REDIS_WARNING
,"Object encoding: %d", o
->encoding
);
384 redisLog(REDIS_WARNING
,"Object refcount: %d", o
->refcount
);
385 if (o
->type
== REDIS_STRING
&& o
->encoding
== REDIS_ENCODING_RAW
) {
386 redisLog(REDIS_WARNING
,"Object raw string len: %d", sdslen(o
->ptr
));
387 if (sdslen(o
->ptr
) < 4096)
388 redisLog(REDIS_WARNING
,"Object raw string content: \"%s\"", (char*)o
->ptr
);
389 } else if (o
->type
== REDIS_LIST
) {
390 redisLog(REDIS_WARNING
,"List length: %d", (int) listTypeLength(o
));
391 } else if (o
->type
== REDIS_SET
) {
392 redisLog(REDIS_WARNING
,"Set size: %d", (int) setTypeSize(o
));
393 } else if (o
->type
== REDIS_HASH
) {
394 redisLog(REDIS_WARNING
,"Hash size: %d", (int) hashTypeLength(o
));
395 } else if (o
->type
== REDIS_ZSET
) {
396 redisLog(REDIS_WARNING
,"Sorted set size: %d", (int) zsetLength(o
));
397 if (o
->encoding
== REDIS_ENCODING_SKIPLIST
)
398 redisLog(REDIS_WARNING
,"Skiplist level: %d", (int) ((zset
*)o
->ptr
)->zsl
->level
);
402 void _redisAssertPrintObject(robj
*o
) {
404 redisLog(REDIS_WARNING
,"=== ASSERTION FAILED OBJECT CONTEXT ===");
405 redisLogObjectDebugInfo(o
);
408 void _redisAssertWithInfo(redisClient
*c
, robj
*o
, char *estr
, char *file
, int line
) {
409 if (c
) _redisAssertPrintClientInfo(c
);
410 if (o
) _redisAssertPrintObject(o
);
411 _redisAssert(estr
,file
,line
);
414 void _redisPanic(char *msg
, char *file
, int line
) {
416 redisLog(REDIS_WARNING
,"------------------------------------------------");
417 redisLog(REDIS_WARNING
,"!!! Software Failure. Press left mouse button to continue");
418 redisLog(REDIS_WARNING
,"Guru Meditation: %s #%s:%d",msg
,file
,line
);
419 #ifdef HAVE_BACKTRACE
420 redisLog(REDIS_WARNING
,"(forcing SIGSEGV in order to print the stack trace)");
422 redisLog(REDIS_WARNING
,"------------------------------------------------");
426 void bugReportStart(void) {
427 if (server
.bug_report_start
== 0) {
428 redisLog(REDIS_WARNING
,
429 "\n\n=== REDIS BUG REPORT START: Cut & paste starting from here ===");
430 server
.bug_report_start
= 1;
434 #ifdef HAVE_BACKTRACE
435 static void *getMcontextEip(ucontext_t
*uc
) {
436 #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
438 #if defined(__x86_64__)
439 return (void*) uc
->uc_mcontext
->__ss
.__rip
;
440 #elif defined(__i386__)
441 return (void*) uc
->uc_mcontext
->__ss
.__eip
;
443 return (void*) uc
->uc_mcontext
->__ss
.__srr0
;
445 #elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
447 #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
448 return (void*) uc
->uc_mcontext
->__ss
.__rip
;
450 return (void*) uc
->uc_mcontext
->__ss
.__eip
;
452 #elif defined(__linux__)
454 #if defined(__i386__)
455 return (void*) uc
->uc_mcontext
.gregs
[14]; /* Linux 32 */
456 #elif defined(__X86_64__) || defined(__x86_64__)
457 return (void*) uc
->uc_mcontext
.gregs
[16]; /* Linux 64 */
458 #elif defined(__ia64__) /* Linux IA64 */
459 return (void*) uc
->uc_mcontext
.sc_ip
;
466 void logStackContent(void **sp
) {
468 for (i
= 15; i
>= 0; i
--) {
469 if (sizeof(long) == 4)
470 redisLog(REDIS_WARNING
, "(%08lx) -> %08lx", sp
+i
, sp
[i
]);
472 redisLog(REDIS_WARNING
, "(%016lx) -> %016lx", sp
+i
, sp
[i
]);
476 void logRegisters(ucontext_t
*uc
) {
477 redisLog(REDIS_WARNING
, "--- REGISTERS");
480 #if defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
482 #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
483 redisLog(REDIS_WARNING
,
485 "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n"
486 "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n"
487 "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n"
488 "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n"
489 "RIP:%016lx EFL:%016lx\nCS :%016lx FS:%016lx GS:%016lx",
490 uc
->uc_mcontext
->__ss
.__rax
,
491 uc
->uc_mcontext
->__ss
.__rbx
,
492 uc
->uc_mcontext
->__ss
.__rcx
,
493 uc
->uc_mcontext
->__ss
.__rdx
,
494 uc
->uc_mcontext
->__ss
.__rdi
,
495 uc
->uc_mcontext
->__ss
.__rsi
,
496 uc
->uc_mcontext
->__ss
.__rbp
,
497 uc
->uc_mcontext
->__ss
.__rsp
,
498 uc
->uc_mcontext
->__ss
.__r8
,
499 uc
->uc_mcontext
->__ss
.__r9
,
500 uc
->uc_mcontext
->__ss
.__r10
,
501 uc
->uc_mcontext
->__ss
.__r11
,
502 uc
->uc_mcontext
->__ss
.__r12
,
503 uc
->uc_mcontext
->__ss
.__r13
,
504 uc
->uc_mcontext
->__ss
.__r14
,
505 uc
->uc_mcontext
->__ss
.__r15
,
506 uc
->uc_mcontext
->__ss
.__rip
,
507 uc
->uc_mcontext
->__ss
.__rflags
,
508 uc
->uc_mcontext
->__ss
.__cs
,
509 uc
->uc_mcontext
->__ss
.__fs
,
510 uc
->uc_mcontext
->__ss
.__gs
512 logStackContent((void**)uc
->uc_mcontext
->__ss
.__rsp
);
515 redisLog(REDIS_WARNING
,
517 "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n"
518 "EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n"
519 "SS:%08lx EFL:%08lx EIP:%08lx CS :%08lx\n"
520 "DS:%08lx ES:%08lx FS :%08lx GS :%08lx",
521 uc
->uc_mcontext
->__ss
.__eax
,
522 uc
->uc_mcontext
->__ss
.__ebx
,
523 uc
->uc_mcontext
->__ss
.__ecx
,
524 uc
->uc_mcontext
->__ss
.__edx
,
525 uc
->uc_mcontext
->__ss
.__edi
,
526 uc
->uc_mcontext
->__ss
.__esi
,
527 uc
->uc_mcontext
->__ss
.__ebp
,
528 uc
->uc_mcontext
->__ss
.__esp
,
529 uc
->uc_mcontext
->__ss
.__ss
,
530 uc
->uc_mcontext
->__ss
.__eflags
,
531 uc
->uc_mcontext
->__ss
.__eip
,
532 uc
->uc_mcontext
->__ss
.__cs
,
533 uc
->uc_mcontext
->__ss
.__ds
,
534 uc
->uc_mcontext
->__ss
.__es
,
535 uc
->uc_mcontext
->__ss
.__fs
,
536 uc
->uc_mcontext
->__ss
.__gs
538 logStackContent((void**)uc
->uc_mcontext
->__ss
.__esp
);
541 #elif defined(__linux__)
543 #if defined(__i386__)
544 redisLog(REDIS_WARNING
,
546 "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n"
547 "EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n"
548 "SS :%08lx EFL:%08lx EIP:%08lx CS:%08lx\n"
549 "DS :%08lx ES :%08lx FS :%08lx GS:%08lx",
550 uc
->uc_mcontext
.gregs
[11],
551 uc
->uc_mcontext
.gregs
[8],
552 uc
->uc_mcontext
.gregs
[10],
553 uc
->uc_mcontext
.gregs
[9],
554 uc
->uc_mcontext
.gregs
[4],
555 uc
->uc_mcontext
.gregs
[5],
556 uc
->uc_mcontext
.gregs
[6],
557 uc
->uc_mcontext
.gregs
[7],
558 uc
->uc_mcontext
.gregs
[18],
559 uc
->uc_mcontext
.gregs
[17],
560 uc
->uc_mcontext
.gregs
[14],
561 uc
->uc_mcontext
.gregs
[15],
562 uc
->uc_mcontext
.gregs
[3],
563 uc
->uc_mcontext
.gregs
[2],
564 uc
->uc_mcontext
.gregs
[1],
565 uc
->uc_mcontext
.gregs
[0]
567 logStackContent((void**)uc
->uc_mcontext
.gregs
[7]);
568 #elif defined(__X86_64__) || defined(__x86_64__)
570 redisLog(REDIS_WARNING
,
572 "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n"
573 "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n"
574 "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n"
575 "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n"
576 "RIP:%016lx EFL:%016lx\nCSGSFS:%016lx",
577 uc
->uc_mcontext
.gregs
[13],
578 uc
->uc_mcontext
.gregs
[11],
579 uc
->uc_mcontext
.gregs
[14],
580 uc
->uc_mcontext
.gregs
[12],
581 uc
->uc_mcontext
.gregs
[8],
582 uc
->uc_mcontext
.gregs
[9],
583 uc
->uc_mcontext
.gregs
[10],
584 uc
->uc_mcontext
.gregs
[15],
585 uc
->uc_mcontext
.gregs
[0],
586 uc
->uc_mcontext
.gregs
[1],
587 uc
->uc_mcontext
.gregs
[2],
588 uc
->uc_mcontext
.gregs
[3],
589 uc
->uc_mcontext
.gregs
[4],
590 uc
->uc_mcontext
.gregs
[5],
591 uc
->uc_mcontext
.gregs
[6],
592 uc
->uc_mcontext
.gregs
[7],
593 uc
->uc_mcontext
.gregs
[16],
594 uc
->uc_mcontext
.gregs
[17],
595 uc
->uc_mcontext
.gregs
[18]
597 logStackContent((void**)uc
->uc_mcontext
.gregs
[15]);
600 redisLog(REDIS_WARNING
,
601 " Dumping of registers not supported for this OS/arch");
605 /* Logs the stack trace using the backtrace() call. This function is designed
606 * to be called from signal handlers safely. */
607 void logStackTrace(ucontext_t
*uc
) {
609 int trace_size
= 0, fd
;
611 /* Open the log file in append mode. */
612 fd
= server
.logfile
?
613 open(server
.logfile
, O_APPEND
|O_CREAT
|O_WRONLY
, 0644) :
615 if (fd
== -1) return;
617 /* Generate the stack trace */
618 trace_size
= backtrace(trace
, 100);
620 /* overwrite sigaction with caller's address */
621 if (getMcontextEip(uc
) != NULL
)
622 trace
[1] = getMcontextEip(uc
);
624 /* Write symbols to log file */
625 backtrace_symbols_fd(trace
, trace_size
, fd
);
628 if (server
.logfile
) close(fd
);
631 /* Log information about the "current" client, that is, the client that is
632 * currently being served by Redis. May be NULL if Redis is not serving a
633 * client right now. */
634 void logCurrentClient(void) {
635 if (server
.current_client
== NULL
) return;
637 redisClient
*cc
= server
.current_client
;
641 redisLog(REDIS_WARNING
, "--- CURRENT CLIENT INFO");
642 client
= getClientInfoString(cc
);
643 redisLog(REDIS_WARNING
,"client: %s", client
);
645 for (j
= 0; j
< cc
->argc
; j
++) {
648 decoded
= getDecodedObject(cc
->argv
[j
]);
649 redisLog(REDIS_WARNING
,"argv[%d]: '%s'", j
, (char*)decoded
->ptr
);
650 decrRefCount(decoded
);
652 /* Check if the first argument, usually a key, is found inside the
653 * selected DB, and if so print info about the associated object. */
658 key
= getDecodedObject(cc
->argv
[1]);
659 de
= dictFind(cc
->db
->dict
, key
->ptr
);
661 val
= dictGetVal(de
);
662 redisLog(REDIS_WARNING
,"key '%s' found in DB containing the following object:", key
->ptr
);
663 redisLogObjectDebugInfo(val
);
669 #if defined(HAVE_PROC_MAPS)
670 uint64_t crc64(uint64_t crc
, const unsigned char *s
, uint64_t l
);
671 void memtest_non_destructive_invert(void *addr
, size_t size
);
672 void memtest_non_destructive_swap(void *addr
, size_t size
);
673 #define MEMTEST_MAX_REGIONS 128
675 int memtest_test_linux_anonymous_maps(void) {
676 FILE *fp
= fopen("/proc/self/maps","r");
678 size_t start_addr
, end_addr
, size
;
679 size_t start_vect
[MEMTEST_MAX_REGIONS
];
680 size_t size_vect
[MEMTEST_MAX_REGIONS
];
682 uint64_t crc1
= 0, crc2
= 0, crc3
= 0;
684 while(fgets(line
,sizeof(line
),fp
) != NULL
) {
685 char *start
, *end
, *p
= line
;
695 if (strstr(p
,"stack") ||
697 strstr(p
,"vsyscall")) continue;
698 if (!strstr(p
,"00:00")) continue;
699 if (!strstr(p
,"rw")) continue;
701 start_addr
= strtoul(start
,NULL
,16);
702 end_addr
= strtoul(end
,NULL
,16);
703 size
= end_addr
-start_addr
;
705 start_vect
[regions
] = start_addr
;
706 size_vect
[regions
] = size
;
707 printf("Testing %lx %lu\n", start_vect
[regions
], size_vect
[regions
]);
711 /* Test all the regions as an unique sequential region.
712 * 1) Take the CRC64 of the memory region. */
713 for (j
= 0; j
< regions
; j
++) {
714 crc1
= crc64(crc1
,(void*)start_vect
[j
],size_vect
[j
]);
717 /* 2) Invert bits, swap adiacent words, swap again, invert bits.
718 * This is the error amplification step. */
719 for (j
= 0; j
< regions
; j
++)
720 memtest_non_destructive_invert((void*)start_vect
[j
],size_vect
[j
]);
721 for (j
= 0; j
< regions
; j
++)
722 memtest_non_destructive_swap((void*)start_vect
[j
],size_vect
[j
]);
723 for (j
= 0; j
< regions
; j
++)
724 memtest_non_destructive_swap((void*)start_vect
[j
],size_vect
[j
]);
725 for (j
= 0; j
< regions
; j
++)
726 memtest_non_destructive_invert((void*)start_vect
[j
],size_vect
[j
]);
728 /* 3) Take the CRC64 sum again. */
729 for (j
= 0; j
< regions
; j
++)
730 crc2
= crc64(crc2
,(void*)start_vect
[j
],size_vect
[j
]);
732 /* 4) Swap + Swap again */
733 for (j
= 0; j
< regions
; j
++)
734 memtest_non_destructive_swap((void*)start_vect
[j
],size_vect
[j
]);
735 for (j
= 0; j
< regions
; j
++)
736 memtest_non_destructive_swap((void*)start_vect
[j
],size_vect
[j
]);
738 /* 5) Take the CRC64 sum again. */
739 for (j
= 0; j
< regions
; j
++)
740 crc3
= crc64(crc3
,(void*)start_vect
[j
],size_vect
[j
]);
742 /* NOTE: It is very important to close the file descriptor only now
743 * because closing it before may result into unmapping of some memory
744 * region that we are testing. */
747 /* If the two CRC are not the same, we trapped a memory error. */
748 return crc1
!= crc2
|| crc2
!= crc3
;
752 void sigsegvHandler(int sig
, siginfo_t
*info
, void *secret
) {
753 ucontext_t
*uc
= (ucontext_t
*) secret
;
754 sds infostring
, clients
;
755 struct sigaction act
;
759 redisLog(REDIS_WARNING
,
760 " Redis %s crashed by signal: %d", REDIS_VERSION
, sig
);
761 redisLog(REDIS_WARNING
,
762 " Failed assertion: %s (%s:%d)", server
.assert_failed
,
763 server
.assert_file
, server
.assert_line
);
765 /* Log the stack trace */
766 redisLog(REDIS_WARNING
, "--- STACK TRACE");
769 /* Log INFO and CLIENT LIST */
770 redisLog(REDIS_WARNING
, "--- INFO OUTPUT");
771 infostring
= genRedisInfoString("all");
772 infostring
= sdscatprintf(infostring
, "hash_init_value: %u\n",
773 dictGetHashFunctionSeed());
774 redisLogRaw(REDIS_WARNING
, infostring
);
775 redisLog(REDIS_WARNING
, "--- CLIENT LIST OUTPUT");
776 clients
= getAllClientsInfoString();
777 redisLogRaw(REDIS_WARNING
, clients
);
781 /* Log the current client */
784 /* Log dump of processor registers */
787 #if defined(HAVE_PROC_MAPS)
789 redisLog(REDIS_WARNING
, "--- FAST MEMORY TEST");
791 if (memtest_test_linux_anonymous_maps()) {
792 redisLog(REDIS_WARNING
,
793 "!!! MEMORY ERROR DETECTED! Check your memory ASAP !!!");
795 redisLog(REDIS_WARNING
,
796 "Fast memory test PASSED, however your memory can still be broken. Please run a memory test for several hours if possible.");
800 redisLog(REDIS_WARNING
,
801 "\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n"
802 " Please report the crash opening an issue on github:\n\n"
803 " http://github.com/antirez/redis/issues\n\n"
804 " Suspect RAM error? Use redis-server --test-memory to veryfy it.\n\n"
806 /* free(messages); Don't call free() with possibly corrupted memory. */
807 if (server
.daemonize
) unlink(server
.pidfile
);
809 /* Make sure we exit with the right signal at the end. So for instance
810 * the core will be dumped if enabled. */
811 sigemptyset (&act
.sa_mask
);
812 act
.sa_flags
= SA_NODEFER
| SA_ONSTACK
| SA_RESETHAND
;
813 act
.sa_handler
= SIG_DFL
;
814 sigaction (sig
, &act
, NULL
);
817 #endif /* HAVE_BACKTRACE */
819 /* ==================== Logging functions for debugging ===================== */
821 void redisLogHexDump(int level
, char *descr
, void *value
, size_t len
) {
823 unsigned char *v
= value
;
824 char charset
[] = "0123456789abcdef";
826 redisLog(level
,"%s (hexdump):", descr
);
829 b
[0] = charset
[(*v
)>>4];
830 b
[1] = charset
[(*v
)&0xf];
835 if (b
-buf
== 64 || len
== 0) {
836 redisLogRaw(level
|REDIS_LOG_RAW
,buf
);
840 redisLogRaw(level
|REDIS_LOG_RAW
,"\n");
843 /* =========================== Software Watchdog ============================ */
844 #include <sys/time.h>
846 void watchdogSignalHandler(int sig
, siginfo_t
*info
, void *secret
) {
847 #ifdef HAVE_BACKTRACE
848 ucontext_t
*uc
= (ucontext_t
*) secret
;
853 redisLogFromHandler(REDIS_WARNING
,"\n--- WATCHDOG TIMER EXPIRED ---");
854 #ifdef HAVE_BACKTRACE
857 redisLogFromHandler(REDIS_WARNING
,"Sorry: no support for backtrace().");
859 redisLogFromHandler(REDIS_WARNING
,"--------\n");
862 /* Schedule a SIGALRM delivery after the specified period in milliseconds.
863 * If a timer is already scheduled, this function will re-schedule it to the
864 * specified time. If period is 0 the current timer is disabled. */
865 void watchdogScheduleSignal(int period
) {
868 /* Will stop the timer if period is 0. */
869 it
.it_value
.tv_sec
= period
/1000;
870 it
.it_value
.tv_usec
= (period%1000
)*1000;
871 /* Don't automatically restart. */
872 it
.it_interval
.tv_sec
= 0;
873 it
.it_interval
.tv_usec
= 0;
874 setitimer(ITIMER_REAL
, &it
, NULL
);
877 /* Enable the software watchdog with the specified period in milliseconds. */
878 void enableWatchdog(int period
) {
881 if (server
.watchdog_period
== 0) {
882 struct sigaction act
;
884 /* Watchdog was actually disabled, so we have to setup the signal
886 sigemptyset(&act
.sa_mask
);
887 act
.sa_flags
= SA_NODEFER
| SA_ONSTACK
| SA_SIGINFO
;
888 act
.sa_sigaction
= watchdogSignalHandler
;
889 sigaction(SIGALRM
, &act
, NULL
);
891 /* If the configured period is smaller than twice the timer period, it is
892 * too short for the software watchdog to work reliably. Fix it now
894 min_period
= (1000/REDIS_HZ
)*2;
895 if (period
< min_period
) period
= min_period
;
896 watchdogScheduleSignal(period
); /* Adjust the current timer. */
897 server
.watchdog_period
= period
;
900 /* Disable the software watchdog. */
901 void disableWatchdog(void) {
902 struct sigaction act
;
903 if (server
.watchdog_period
== 0) return; /* Already disabled. */
904 watchdogScheduleSignal(0); /* Stop the current timer. */
906 /* Set the signal handler to SIG_IGN, this will also remove pending
907 * signals from the queue. */
908 sigemptyset(&act
.sa_mask
);
910 act
.sa_handler
= SIG_IGN
;
911 sigaction(SIGALRM
, &act
, NULL
);
912 server
.watchdog_period
= 0;