1 #include "redis.h" 2 #include "sha1.h" /* SHA1 is used for DEBUG DIGEST */ 3 4 #include <arpa/inet.h> 5 #include <signal.h> 6 7 #ifdef HAVE_BACKTRACE 8 #include <execinfo.h> 9 #include <ucontext.h> 10 #endif /* HAVE_BACKTRACE */ 11 12 /* ================================= Debugging ============================== */ 13 14 /* Compute the sha1 of string at 's' with 'len' bytes long. 15 * The SHA1 is then xored againt the string pointed by digest. 16 * Since xor is commutative, this operation is used in order to 17 * "add" digests relative to unordered elements. 18 * 19 * So digest(a,b,c,d) will be the same of digest(b,a,c,d) */ 20 void xorDigest(unsigned char *digest, void *ptr, size_t len) { 21 SHA1_CTX ctx; 22 unsigned char hash[20], *s = ptr; 23 int j; 24 25 SHA1Init(&ctx); 26 SHA1Update(&ctx,s,len); 27 SHA1Final(hash,&ctx); 28 29 for (j = 0; j < 20; j++) 30 digest[j] ^= hash[j]; 31 } 32 33 void xorObjectDigest(unsigned char *digest, robj *o) { 34 o = getDecodedObject(o); 35 xorDigest(digest,o->ptr,sdslen(o->ptr)); 36 decrRefCount(o); 37 } 38 39 /* This function instead of just computing the SHA1 and xoring it 40 * against diget, also perform the digest of "digest" itself and 41 * replace the old value with the new one. 42 * 43 * So the final digest will be: 44 * 45 * digest = SHA1(digest xor SHA1(data)) 46 * 47 * This function is used every time we want to preserve the order so 48 * that digest(a,b,c,d) will be different than digest(b,c,d,a) 49 * 50 * Also note that mixdigest("foo") followed by mixdigest("bar") 51 * will lead to a different digest compared to "fo", "obar". 52 */ 53 void mixDigest(unsigned char *digest, void *ptr, size_t len) { 54 SHA1_CTX ctx; 55 char *s = ptr; 56 57 xorDigest(digest,s,len); 58 SHA1Init(&ctx); 59 SHA1Update(&ctx,digest,20); 60 SHA1Final(digest,&ctx); 61 } 62 63 void mixObjectDigest(unsigned char *digest, robj *o) { 64 o = getDecodedObject(o); 65 mixDigest(digest,o->ptr,sdslen(o->ptr)); 66 decrRefCount(o); 67 } 68 69 /* Compute the dataset digest. Since keys, sets elements, hashes elements 70 * are not ordered, we use a trick: every aggregate digest is the xor 71 * of the digests of their elements. This way the order will not change 72 * the result. For list instead we use a feedback entering the output digest 73 * as input in order to ensure that a different ordered list will result in 74 * a different digest. */ 75 void computeDatasetDigest(unsigned char *final) { 76 unsigned char digest[20]; 77 char buf[128]; 78 dictIterator *di = NULL; 79 dictEntry *de; 80 int j; 81 uint32_t aux; 82 83 memset(final,0,20); /* Start with a clean result */ 84 85 for (j = 0; j < server.dbnum; j++) { 86 redisDb *db = server.db+j; 87 88 if (dictSize(db->dict) == 0) continue; 89 di = dictGetIterator(db->dict); 90 91 /* hash the DB id, so the same dataset moved in a different 92 * DB will lead to a different digest */ 93 aux = htonl(j); 94 mixDigest(final,&aux,sizeof(aux)); 95 96 /* Iterate this DB writing every entry */ 97 while((de = dictNext(di)) != NULL) { 98 sds key; 99 robj *keyobj, *o; 100 long long expiretime; 101 102 memset(digest,0,20); /* This key-val digest */ 103 key = dictGetKey(de); 104 keyobj = createStringObject(key,sdslen(key)); 105 106 mixDigest(digest,key,sdslen(key)); 107 108 /* Make sure the key is loaded if VM is active */ 109 o = dictGetVal(de); 110 111 aux = htonl(o->type); 112 mixDigest(digest,&aux,sizeof(aux)); 113 expiretime = getExpire(db,keyobj); 114 115 /* Save the key and associated value */ 116 if (o->type == REDIS_STRING) { 117 mixObjectDigest(digest,o); 118 } else if (o->type == REDIS_LIST) { 119 listTypeIterator *li = listTypeInitIterator(o,0,REDIS_TAIL); 120 listTypeEntry entry; 121 while(listTypeNext(li,&entry)) { 122 robj *eleobj = listTypeGet(&entry); 123 mixObjectDigest(digest,eleobj); 124 decrRefCount(eleobj); 125 } 126 listTypeReleaseIterator(li); 127 } else if (o->type == REDIS_SET) { 128 setTypeIterator *si = setTypeInitIterator(o); 129 robj *ele; 130 while((ele = setTypeNextObject(si)) != NULL) { 131 xorObjectDigest(digest,ele); 132 decrRefCount(ele); 133 } 134 setTypeReleaseIterator(si); 135 } else if (o->type == REDIS_ZSET) { 136 unsigned char eledigest[20]; 137 138 if (o->encoding == REDIS_ENCODING_ZIPLIST) { 139 unsigned char *zl = o->ptr; 140 unsigned char *eptr, *sptr; 141 unsigned char *vstr; 142 unsigned int vlen; 143 long long vll; 144 double score; 145 146 eptr = ziplistIndex(zl,0); 147 redisAssert(eptr != NULL); 148 sptr = ziplistNext(zl,eptr); 149 redisAssert(sptr != NULL); 150 151 while (eptr != NULL) { 152 redisAssert(ziplistGet(eptr,&vstr,&vlen,&vll)); 153 score = zzlGetScore(sptr); 154 155 memset(eledigest,0,20); 156 if (vstr != NULL) { 157 mixDigest(eledigest,vstr,vlen); 158 } else { 159 ll2string(buf,sizeof(buf),vll); 160 mixDigest(eledigest,buf,strlen(buf)); 161 } 162 163 snprintf(buf,sizeof(buf),"%.17g",score); 164 mixDigest(eledigest,buf,strlen(buf)); 165 xorDigest(digest,eledigest,20); 166 zzlNext(zl,&eptr,&sptr); 167 } 168 } else if (o->encoding == REDIS_ENCODING_SKIPLIST) { 169 zset *zs = o->ptr; 170 dictIterator *di = dictGetIterator(zs->dict); 171 dictEntry *de; 172 173 while((de = dictNext(di)) != NULL) { 174 robj *eleobj = dictGetKey(de); 175 double *score = dictGetVal(de); 176 177 snprintf(buf,sizeof(buf),"%.17g",*score); 178 memset(eledigest,0,20); 179 mixObjectDigest(eledigest,eleobj); 180 mixDigest(eledigest,buf,strlen(buf)); 181 xorDigest(digest,eledigest,20); 182 } 183 dictReleaseIterator(di); 184 } else { 185 redisPanic("Unknown sorted set encoding"); 186 } 187 } else if (o->type == REDIS_HASH) { 188 hashTypeIterator *hi; 189 robj *obj; 190 191 hi = hashTypeInitIterator(o); 192 while (hashTypeNext(hi) != REDIS_ERR) { 193 unsigned char eledigest[20]; 194 195 memset(eledigest,0,20); 196 obj = hashTypeCurrentObject(hi,REDIS_HASH_KEY); 197 mixObjectDigest(eledigest,obj); 198 decrRefCount(obj); 199 obj = hashTypeCurrentObject(hi,REDIS_HASH_VALUE); 200 mixObjectDigest(eledigest,obj); 201 decrRefCount(obj); 202 xorDigest(digest,eledigest,20); 203 } 204 hashTypeReleaseIterator(hi); 205 } else { 206 redisPanic("Unknown object type"); 207 } 208 /* If the key has an expire, add it to the mix */ 209 if (expiretime != -1) xorDigest(digest,"!!expire!!",10); 210 /* We can finally xor the key-val digest to the final digest */ 211 xorDigest(final,digest,20); 212 decrRefCount(keyobj); 213 } 214 dictReleaseIterator(di); 215 } 216 } 217 218 void debugCommand(redisClient *c) { 219 if (!strcasecmp(c->argv[1]->ptr,"segfault")) { 220 *((char*)-1) = 'x'; 221 } else if (!strcasecmp(c->argv[1]->ptr,"assert")) { 222 if (c->argc >= 3) c->argv[2] = tryObjectEncoding(c->argv[2]); 223 redisAssertWithInfo(c,c->argv[0],1 == 2); 224 } else if (!strcasecmp(c->argv[1]->ptr,"reload")) { 225 if (rdbSave(server.rdb_filename) != REDIS_OK) { 226 addReply(c,shared.err); 227 return; 228 } 229 emptyDb(); 230 if (rdbLoad(server.rdb_filename) != REDIS_OK) { 231 addReplyError(c,"Error trying to load the RDB dump"); 232 return; 233 } 234 redisLog(REDIS_WARNING,"DB reloaded by DEBUG RELOAD"); 235 addReply(c,shared.ok); 236 } else if (!strcasecmp(c->argv[1]->ptr,"loadaof")) { 237 emptyDb(); 238 if (loadAppendOnlyFile(server.aof_filename) != REDIS_OK) { 239 addReply(c,shared.err); 240 return; 241 } 242 server.dirty = 0; /* Prevent AOF / replication */ 243 redisLog(REDIS_WARNING,"Append Only File loaded by DEBUG LOADAOF"); 244 addReply(c,shared.ok); 245 } else if (!strcasecmp(c->argv[1]->ptr,"object") && c->argc == 3) { 246 dictEntry *de; 247 robj *val; 248 char *strenc; 249 250 if ((de = dictFind(c->db->dict,c->argv[2]->ptr)) == NULL) { 251 addReply(c,shared.nokeyerr); 252 return; 253 } 254 val = dictGetVal(de); 255 strenc = strEncoding(val->encoding); 256 257 addReplyStatusFormat(c, 258 "Value at:%p refcount:%d " 259 "encoding:%s serializedlength:%lld " 260 "lru:%d lru_seconds_idle:%lu", 261 (void*)val, val->refcount, 262 strenc, (long long) rdbSavedObjectLen(val), 263 val->lru, estimateObjectIdleTime(val)); 264 } else if (!strcasecmp(c->argv[1]->ptr,"populate") && c->argc == 3) { 265 long keys, j; 266 robj *key, *val; 267 char buf[128]; 268 269 if (getLongFromObjectOrReply(c, c->argv[2], &keys, NULL) != REDIS_OK) 270 return; 271 for (j = 0; j < keys; j++) { 272 snprintf(buf,sizeof(buf),"key:%lu",j); 273 key = createStringObject(buf,strlen(buf)); 274 if (lookupKeyRead(c->db,key) != NULL) { 275 decrRefCount(key); 276 continue; 277 } 278 snprintf(buf,sizeof(buf),"value:%lu",j); 279 val = createStringObject(buf,strlen(buf)); 280 dbAdd(c->db,key,val); 281 decrRefCount(key); 282 } 283 addReply(c,shared.ok); 284 } else if (!strcasecmp(c->argv[1]->ptr,"digest") && c->argc == 2) { 285 unsigned char digest[20]; 286 sds d = sdsempty(); 287 int j; 288 289 computeDatasetDigest(digest); 290 for (j = 0; j < 20; j++) 291 d = sdscatprintf(d, "%02x",digest[j]); 292 addReplyStatus(c,d); 293 sdsfree(d); 294 } else if (!strcasecmp(c->argv[1]->ptr,"sleep") && c->argc == 3) { 295 double dtime = strtod(c->argv[2]->ptr,NULL); 296 long long utime = dtime*1000000; 297 298 usleep(utime); 299 addReply(c,shared.ok); 300 } else { 301 addReplyError(c, 302 "Syntax error, try DEBUG [SEGFAULT|OBJECT <key>|SWAPIN <key>|SWAPOUT <key>|RELOAD]"); 303 } 304 } 305 306 /* =========================== Crash handling ============================== */ 307 308 void _redisAssert(char *estr, char *file, int line) { 309 bugReportStart(); 310 redisLog(REDIS_WARNING,"=== ASSERTION FAILED ==="); 311 redisLog(REDIS_WARNING,"==> %s:%d '%s' is not true",file,line,estr); 312 #ifdef HAVE_BACKTRACE 313 server.assert_failed = estr; 314 server.assert_file = file; 315 server.assert_line = line; 316 redisLog(REDIS_WARNING,"(forcing SIGSEGV to print the bug report.)"); 317 *((char*)-1) = 'x'; 318 #endif 319 } 320 321 void _redisAssertPrintClientInfo(redisClient *c) { 322 int j; 323 324 bugReportStart(); 325 redisLog(REDIS_WARNING,"=== ASSERTION FAILED CLIENT CONTEXT ==="); 326 redisLog(REDIS_WARNING,"client->flags = %d", c->flags); 327 redisLog(REDIS_WARNING,"client->fd = %d", c->fd); 328 redisLog(REDIS_WARNING,"client->argc = %d", c->argc); 329 for (j=0; j < c->argc; j++) { 330 char buf[128]; 331 char *arg; 332 333 if (c->argv[j]->type == REDIS_STRING && 334 c->argv[j]->encoding == REDIS_ENCODING_RAW) 335 { 336 arg = (char*) c->argv[j]->ptr; 337 } else { 338 snprintf(buf,sizeof(buf),"Object type: %d, encoding: %d", 339 c->argv[j]->type, c->argv[j]->encoding); 340 arg = buf; 341 } 342 redisLog(REDIS_WARNING,"client->argv[%d] = \"%s\" (refcount: %d)", 343 j, arg, c->argv[j]->refcount); 344 } 345 } 346 347 void redisLogObjectDebugInfo(robj *o) { 348 redisLog(REDIS_WARNING,"Object type: %d", o->type); 349 redisLog(REDIS_WARNING,"Object encoding: %d", o->encoding); 350 redisLog(REDIS_WARNING,"Object refcount: %d", o->refcount); 351 if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_RAW) { 352 redisLog(REDIS_WARNING,"Object raw string len: %d", sdslen(o->ptr)); 353 if (sdslen(o->ptr) < 4096) 354 redisLog(REDIS_WARNING,"Object raw string content: \"%s\"", (char*)o->ptr); 355 } else if (o->type == REDIS_LIST) { 356 redisLog(REDIS_WARNING,"List length: %d", (int) listTypeLength(o)); 357 } else if (o->type == REDIS_SET) { 358 redisLog(REDIS_WARNING,"Set size: %d", (int) setTypeSize(o)); 359 } else if (o->type == REDIS_HASH) { 360 redisLog(REDIS_WARNING,"Hash size: %d", (int) hashTypeLength(o)); 361 } else if (o->type == REDIS_ZSET) { 362 redisLog(REDIS_WARNING,"Sorted set size: %d", (int) zsetLength(o)); 363 if (o->encoding == REDIS_ENCODING_SKIPLIST) 364 redisLog(REDIS_WARNING,"Skiplist level: %d", (int) ((zset*)o->ptr)->zsl->level); 365 } 366 } 367 368 void _redisAssertPrintObject(robj *o) { 369 bugReportStart(); 370 redisLog(REDIS_WARNING,"=== ASSERTION FAILED OBJECT CONTEXT ==="); 371 redisLogObjectDebugInfo(o); 372 } 373 374 void _redisAssertWithInfo(redisClient *c, robj *o, char *estr, char *file, int line) { 375 if (c) _redisAssertPrintClientInfo(c); 376 if (o) _redisAssertPrintObject(o); 377 _redisAssert(estr,file,line); 378 } 379 380 void _redisPanic(char *msg, char *file, int line) { 381 bugReportStart(); 382 redisLog(REDIS_WARNING,"------------------------------------------------"); 383 redisLog(REDIS_WARNING,"!!! Software Failure. Press left mouse button to continue"); 384 redisLog(REDIS_WARNING,"Guru Meditation: %s #%s:%d",msg,file,line); 385 #ifdef HAVE_BACKTRACE 386 redisLog(REDIS_WARNING,"(forcing SIGSEGV in order to print the stack trace)"); 387 redisLog(REDIS_WARNING,"------------------------------------------------"); 388 *((char*)-1) = 'x'; 389 #endif 390 } 391 392 #ifdef HAVE_BACKTRACE 393 static void *getMcontextEip(ucontext_t *uc) { 394 #if defined(__FreeBSD__) 395 return (void*) uc->uc_mcontext.mc_eip; 396 #elif defined(__dietlibc__) 397 return (void*) uc->uc_mcontext.eip; 398 #elif defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6) 399 #if __x86_64__ 400 return (void*) uc->uc_mcontext->__ss.__rip; 401 #elif __i386__ 402 return (void*) uc->uc_mcontext->__ss.__eip; 403 #else 404 return (void*) uc->uc_mcontext->__ss.__srr0; 405 #endif 406 #elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6) 407 #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__) 408 return (void*) uc->uc_mcontext->__ss.__rip; 409 #else 410 return (void*) uc->uc_mcontext->__ss.__eip; 411 #endif 412 #elif defined(__i386__) 413 return (void*) uc->uc_mcontext.gregs[14]; /* Linux 32 */ 414 #elif defined(__X86_64__) || defined(__x86_64__) 415 return (void*) uc->uc_mcontext.gregs[16]; /* Linux 64 */ 416 #elif defined(__ia64__) /* Linux IA64 */ 417 return (void*) uc->uc_mcontext.sc_ip; 418 #else 419 return NULL; 420 #endif 421 } 422 423 void bugReportStart(void) { 424 if (server.bug_report_start == 0) { 425 redisLog(REDIS_WARNING, 426 "\n\n=== REDIS BUG REPORT START: Cut & paste starting from here ==="); 427 server.bug_report_start = 1; 428 } 429 } 430 431 void logStackContent(void **sp) { 432 int i; 433 for (i = 15; i >= 0; i--) { 434 if (sizeof(long) == 4) 435 redisLog(REDIS_WARNING, "(%08lx) -> %08lx", sp+i, sp[i]); 436 else 437 redisLog(REDIS_WARNING, "(%016lx) -> %016lx", sp+i, sp[i]); 438 } 439 } 440 441 void logRegisters(ucontext_t *uc) { 442 redisLog(REDIS_WARNING, "--- REGISTERS"); 443 #if defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6) 444 #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__) 445 redisLog(REDIS_WARNING, 446 "\n" 447 "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n" 448 "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n" 449 "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n" 450 "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n" 451 "RIP:%016lx EFL:%016lx\nCS :%016lx FS:%016lx GS:%016lx", 452 uc->uc_mcontext->__ss.__rax, 453 uc->uc_mcontext->__ss.__rbx, 454 uc->uc_mcontext->__ss.__rcx, 455 uc->uc_mcontext->__ss.__rdx, 456 uc->uc_mcontext->__ss.__rdi, 457 uc->uc_mcontext->__ss.__rsi, 458 uc->uc_mcontext->__ss.__rbp, 459 uc->uc_mcontext->__ss.__rsp, 460 uc->uc_mcontext->__ss.__r8, 461 uc->uc_mcontext->__ss.__r9, 462 uc->uc_mcontext->__ss.__r10, 463 uc->uc_mcontext->__ss.__r11, 464 uc->uc_mcontext->__ss.__r12, 465 uc->uc_mcontext->__ss.__r13, 466 uc->uc_mcontext->__ss.__r14, 467 uc->uc_mcontext->__ss.__r15, 468 uc->uc_mcontext->__ss.__rip, 469 uc->uc_mcontext->__ss.__rflags, 470 uc->uc_mcontext->__ss.__cs, 471 uc->uc_mcontext->__ss.__fs, 472 uc->uc_mcontext->__ss.__gs 473 ); 474 logStackContent((void**)uc->uc_mcontext->__ss.__rsp); 475 #else 476 redisLog(REDIS_WARNING, 477 "\n" 478 "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n" 479 "EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n" 480 "SS:%08lx EFL:%08lx EIP:%08lx CS :%08lx\n" 481 "DS:%08lx ES:%08lx FS :%08lx GS :%08lx", 482 uc->uc_mcontext->__ss.__eax, 483 uc->uc_mcontext->__ss.__ebx, 484 uc->uc_mcontext->__ss.__ecx, 485 uc->uc_mcontext->__ss.__edx, 486 uc->uc_mcontext->__ss.__edi, 487 uc->uc_mcontext->__ss.__esi, 488 uc->uc_mcontext->__ss.__ebp, 489 uc->uc_mcontext->__ss.__esp, 490 uc->uc_mcontext->__ss.__ss, 491 uc->uc_mcontext->__ss.__eflags, 492 uc->uc_mcontext->__ss.__eip, 493 uc->uc_mcontext->__ss.__cs, 494 uc->uc_mcontext->__ss.__ds, 495 uc->uc_mcontext->__ss.__es, 496 uc->uc_mcontext->__ss.__fs, 497 uc->uc_mcontext->__ss.__gs 498 ); 499 logStackContent((void**)uc->uc_mcontext->__ss.__esp); 500 #endif 501 #elif defined(__i386__) 502 redisLog(REDIS_WARNING, 503 "\n" 504 "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n" 505 "EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n" 506 "SS :%08lx EFL:%08lx EIP:%08lx CS:%08lx\n" 507 "DS :%08lx ES :%08lx FS :%08lx GS:%08lx", 508 uc->uc_mcontext.gregs[11], 509 uc->uc_mcontext.gregs[8], 510 uc->uc_mcontext.gregs[10], 511 uc->uc_mcontext.gregs[9], 512 uc->uc_mcontext.gregs[4], 513 uc->uc_mcontext.gregs[5], 514 uc->uc_mcontext.gregs[6], 515 uc->uc_mcontext.gregs[7], 516 uc->uc_mcontext.gregs[18], 517 uc->uc_mcontext.gregs[17], 518 uc->uc_mcontext.gregs[14], 519 uc->uc_mcontext.gregs[15], 520 uc->uc_mcontext.gregs[3], 521 uc->uc_mcontext.gregs[2], 522 uc->uc_mcontext.gregs[1], 523 uc->uc_mcontext.gregs[0] 524 ); 525 logStackContent((void**)uc->uc_mcontext.gregs[7]); 526 #elif defined(__X86_64__) || defined(__x86_64__) 527 redisLog(REDIS_WARNING, 528 "\n" 529 "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n" 530 "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n" 531 "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n" 532 "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n" 533 "RIP:%016lx EFL:%016lx\nCSGSFS:%016lx", 534 uc->uc_mcontext.gregs[13], 535 uc->uc_mcontext.gregs[11], 536 uc->uc_mcontext.gregs[14], 537 uc->uc_mcontext.gregs[12], 538 uc->uc_mcontext.gregs[8], 539 uc->uc_mcontext.gregs[9], 540 uc->uc_mcontext.gregs[10], 541 uc->uc_mcontext.gregs[15], 542 uc->uc_mcontext.gregs[0], 543 uc->uc_mcontext.gregs[1], 544 uc->uc_mcontext.gregs[2], 545 uc->uc_mcontext.gregs[3], 546 uc->uc_mcontext.gregs[4], 547 uc->uc_mcontext.gregs[5], 548 uc->uc_mcontext.gregs[6], 549 uc->uc_mcontext.gregs[7], 550 uc->uc_mcontext.gregs[16], 551 uc->uc_mcontext.gregs[17], 552 uc->uc_mcontext.gregs[18] 553 ); 554 logStackContent((void**)uc->uc_mcontext.gregs[15]); 555 #else 556 redisLog(REDIS_WARNING, 557 " Dumping of registers not supported for this OS/arch"); 558 #endif 559 } 560 561 void sigsegvHandler(int sig, siginfo_t *info, void *secret) { 562 void *trace[100]; 563 char **messages = NULL; 564 int i, trace_size = 0; 565 ucontext_t *uc = (ucontext_t*) secret; 566 sds infostring, clients; 567 struct sigaction act; 568 REDIS_NOTUSED(info); 569 570 bugReportStart(); 571 redisLog(REDIS_WARNING, 572 " Redis %s crashed by signal: %d", REDIS_VERSION, sig); 573 redisLog(REDIS_WARNING, 574 " Failed assertion: %s (%s:%d)", server.assert_failed, 575 server.assert_file, server.assert_line); 576 577 /* Generate the stack trace */ 578 trace_size = backtrace(trace, 100); 579 580 /* overwrite sigaction with caller's address */ 581 if (getMcontextEip(uc) != NULL) { 582 trace[1] = getMcontextEip(uc); 583 } 584 messages = backtrace_symbols(trace, trace_size); 585 redisLog(REDIS_WARNING, "--- STACK TRACE"); 586 for (i=1; i<trace_size; ++i) 587 redisLog(REDIS_WARNING,"%s", messages[i]); 588 589 /* Log INFO and CLIENT LIST */ 590 redisLog(REDIS_WARNING, "--- INFO OUTPUT"); 591 infostring = genRedisInfoString("all"); 592 infostring = sdscatprintf(infostring, "hash_init_value: %u\n", 593 dictGetHashFunctionSeed()); 594 redisLogRaw(REDIS_WARNING, infostring); 595 redisLog(REDIS_WARNING, "--- CLIENT LIST OUTPUT"); 596 clients = getAllClientsInfoString(); 597 redisLogRaw(REDIS_WARNING, clients); 598 /* Don't sdsfree() strings to avoid a crash. Memory may be corrupted. */ 599 600 /* Log CURRENT CLIENT info */ 601 if (server.current_client) { 602 redisClient *cc = server.current_client; 603 sds client; 604 int j; 605 606 redisLog(REDIS_WARNING, "--- CURRENT CLIENT INFO"); 607 client = getClientInfoString(cc); 608 redisLog(REDIS_WARNING,"client: %s", client); 609 /* Missing sdsfree(client) to avoid crash if memory is corrupted. */ 610 for (j = 0; j < cc->argc; j++) { 611 robj *decoded; 612 613 decoded = getDecodedObject(cc->argv[j]); 614 redisLog(REDIS_WARNING,"argv[%d]: '%s'", j, (char*)decoded->ptr); 615 decrRefCount(decoded); 616 } 617 /* Check if the first argument, usually a key, is found inside the 618 * selected DB, and if so print info about the associated object. */ 619 if (cc->argc >= 1) { 620 robj *val, *key; 621 dictEntry *de; 622 623 key = getDecodedObject(cc->argv[1]); 624 de = dictFind(cc->db->dict, key->ptr); 625 if (de) { 626 val = dictGetVal(de); 627 redisLog(REDIS_WARNING,"key '%s' found in DB containing the following object:", key->ptr); 628 redisLogObjectDebugInfo(val); 629 } 630 decrRefCount(key); 631 } 632 } 633 634 /* Log dump of processor registers */ 635 logRegisters(uc); 636 637 redisLog(REDIS_WARNING, 638 "\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n" 639 " Please report the crash opening an issue on github:\n\n" 640 " http://github.com/antirez/redis/issues\n\n" 641 ); 642 /* free(messages); Don't call free() with possibly corrupted memory. */ 643 if (server.daemonize) unlink(server.pidfile); 644 645 /* Make sure we exit with the right signal at the end. So for instance 646 * the core will be dumped if enabled. */ 647 sigemptyset (&act.sa_mask); 648 /* When the SA_SIGINFO flag is set in sa_flags then sa_sigaction 649 * is used. Otherwise, sa_handler is used */ 650 act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND; 651 act.sa_handler = SIG_DFL; 652 sigaction (sig, &act, NULL); 653 kill(getpid(),sig); 654 } 655 #endif /* HAVE_BACKTRACE */ 656