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 #include <fcntl.h> 11 #endif /* HAVE_BACKTRACE */ 12 13 /* ================================= Debugging ============================== */ 14 15 /* Compute the sha1 of string at 's' with 'len' bytes long. 16 * The SHA1 is then xored againt the string pointed by digest. 17 * Since xor is commutative, this operation is used in order to 18 * "add" digests relative to unordered elements. 19 * 20 * So digest(a,b,c,d) will be the same of digest(b,a,c,d) */ 21 void xorDigest(unsigned char *digest, void *ptr, size_t len) { 22 SHA1_CTX ctx; 23 unsigned char hash[20], *s = ptr; 24 int j; 25 26 SHA1Init(&ctx); 27 SHA1Update(&ctx,s,len); 28 SHA1Final(hash,&ctx); 29 30 for (j = 0; j < 20; j++) 31 digest[j] ^= hash[j]; 32 } 33 34 void xorObjectDigest(unsigned char *digest, robj *o) { 35 o = getDecodedObject(o); 36 xorDigest(digest,o->ptr,sdslen(o->ptr)); 37 decrRefCount(o); 38 } 39 40 /* This function instead of just computing the SHA1 and xoring it 41 * against diget, also perform the digest of "digest" itself and 42 * replace the old value with the new one. 43 * 44 * So the final digest will be: 45 * 46 * digest = SHA1(digest xor SHA1(data)) 47 * 48 * This function is used every time we want to preserve the order so 49 * that digest(a,b,c,d) will be different than digest(b,c,d,a) 50 * 51 * Also note that mixdigest("foo") followed by mixdigest("bar") 52 * will lead to a different digest compared to "fo", "obar". 53 */ 54 void mixDigest(unsigned char *digest, void *ptr, size_t len) { 55 SHA1_CTX ctx; 56 char *s = ptr; 57 58 xorDigest(digest,s,len); 59 SHA1Init(&ctx); 60 SHA1Update(&ctx,digest,20); 61 SHA1Final(digest,&ctx); 62 } 63 64 void mixObjectDigest(unsigned char *digest, robj *o) { 65 o = getDecodedObject(o); 66 mixDigest(digest,o->ptr,sdslen(o->ptr)); 67 decrRefCount(o); 68 } 69 70 /* Compute the dataset digest. Since keys, sets elements, hashes elements 71 * are not ordered, we use a trick: every aggregate digest is the xor 72 * of the digests of their elements. This way the order will not change 73 * the result. For list instead we use a feedback entering the output digest 74 * as input in order to ensure that a different ordered list will result in 75 * a different digest. */ 76 void computeDatasetDigest(unsigned char *final) { 77 unsigned char digest[20]; 78 char buf[128]; 79 dictIterator *di = NULL; 80 dictEntry *de; 81 int j; 82 uint32_t aux; 83 84 memset(final,0,20); /* Start with a clean result */ 85 86 for (j = 0; j < server.dbnum; j++) { 87 redisDb *db = server.db+j; 88 89 if (dictSize(db->dict) == 0) continue; 90 di = dictGetIterator(db->dict); 91 92 /* hash the DB id, so the same dataset moved in a different 93 * DB will lead to a different digest */ 94 aux = htonl(j); 95 mixDigest(final,&aux,sizeof(aux)); 96 97 /* Iterate this DB writing every entry */ 98 while((de = dictNext(di)) != NULL) { 99 sds key; 100 robj *keyobj, *o; 101 long long expiretime; 102 103 memset(digest,0,20); /* This key-val digest */ 104 key = dictGetKey(de); 105 keyobj = createStringObject(key,sdslen(key)); 106 107 mixDigest(digest,key,sdslen(key)); 108 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 #endif 318 *((char*)-1) = 'x'; 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 #endif 388 redisLog(REDIS_WARNING,"------------------------------------------------"); 389 *((char*)-1) = 'x'; 390 } 391 392 void bugReportStart(void) { 393 if (server.bug_report_start == 0) { 394 redisLog(REDIS_WARNING, 395 "\n\n=== REDIS BUG REPORT START: Cut & paste starting from here ==="); 396 server.bug_report_start = 1; 397 } 398 } 399 400 #ifdef HAVE_BACKTRACE 401 static void *getMcontextEip(ucontext_t *uc) { 402 #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6) 403 /* OSX < 10.6 */ 404 #if defined(__x86_64__) 405 return (void*) uc->uc_mcontext->__ss.__rip; 406 #elif defined(__i386__) 407 return (void*) uc->uc_mcontext->__ss.__eip; 408 #else 409 return (void*) uc->uc_mcontext->__ss.__srr0; 410 #endif 411 #elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6) 412 /* OSX >= 10.6 */ 413 #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__) 414 return (void*) uc->uc_mcontext->__ss.__rip; 415 #else 416 return (void*) uc->uc_mcontext->__ss.__eip; 417 #endif 418 #elif defined(__linux__) 419 /* Linux */ 420 #if defined(__i386__) 421 return (void*) uc->uc_mcontext.gregs[14]; /* Linux 32 */ 422 #elif defined(__X86_64__) || defined(__x86_64__) 423 return (void*) uc->uc_mcontext.gregs[16]; /* Linux 64 */ 424 #elif defined(__ia64__) /* Linux IA64 */ 425 return (void*) uc->uc_mcontext.sc_ip; 426 #endif 427 #else 428 return NULL; 429 #endif 430 } 431 432 void logStackContent(void **sp) { 433 int i; 434 for (i = 15; i >= 0; i--) { 435 if (sizeof(long) == 4) 436 redisLog(REDIS_WARNING, "(%08lx) -> %08lx", sp+i, sp[i]); 437 else 438 redisLog(REDIS_WARNING, "(%016lx) -> %016lx", sp+i, sp[i]); 439 } 440 } 441 442 void logRegisters(ucontext_t *uc) { 443 redisLog(REDIS_WARNING, "--- REGISTERS"); 444 445 /* OSX */ 446 #if defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6) 447 /* OSX AMD64 */ 448 #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__) 449 redisLog(REDIS_WARNING, 450 "\n" 451 "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n" 452 "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n" 453 "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n" 454 "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n" 455 "RIP:%016lx EFL:%016lx\nCS :%016lx FS:%016lx GS:%016lx", 456 uc->uc_mcontext->__ss.__rax, 457 uc->uc_mcontext->__ss.__rbx, 458 uc->uc_mcontext->__ss.__rcx, 459 uc->uc_mcontext->__ss.__rdx, 460 uc->uc_mcontext->__ss.__rdi, 461 uc->uc_mcontext->__ss.__rsi, 462 uc->uc_mcontext->__ss.__rbp, 463 uc->uc_mcontext->__ss.__rsp, 464 uc->uc_mcontext->__ss.__r8, 465 uc->uc_mcontext->__ss.__r9, 466 uc->uc_mcontext->__ss.__r10, 467 uc->uc_mcontext->__ss.__r11, 468 uc->uc_mcontext->__ss.__r12, 469 uc->uc_mcontext->__ss.__r13, 470 uc->uc_mcontext->__ss.__r14, 471 uc->uc_mcontext->__ss.__r15, 472 uc->uc_mcontext->__ss.__rip, 473 uc->uc_mcontext->__ss.__rflags, 474 uc->uc_mcontext->__ss.__cs, 475 uc->uc_mcontext->__ss.__fs, 476 uc->uc_mcontext->__ss.__gs 477 ); 478 logStackContent((void**)uc->uc_mcontext->__ss.__rsp); 479 #else 480 /* OSX x86 */ 481 redisLog(REDIS_WARNING, 482 "\n" 483 "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n" 484 "EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n" 485 "SS:%08lx EFL:%08lx EIP:%08lx CS :%08lx\n" 486 "DS:%08lx ES:%08lx FS :%08lx GS :%08lx", 487 uc->uc_mcontext->__ss.__eax, 488 uc->uc_mcontext->__ss.__ebx, 489 uc->uc_mcontext->__ss.__ecx, 490 uc->uc_mcontext->__ss.__edx, 491 uc->uc_mcontext->__ss.__edi, 492 uc->uc_mcontext->__ss.__esi, 493 uc->uc_mcontext->__ss.__ebp, 494 uc->uc_mcontext->__ss.__esp, 495 uc->uc_mcontext->__ss.__ss, 496 uc->uc_mcontext->__ss.__eflags, 497 uc->uc_mcontext->__ss.__eip, 498 uc->uc_mcontext->__ss.__cs, 499 uc->uc_mcontext->__ss.__ds, 500 uc->uc_mcontext->__ss.__es, 501 uc->uc_mcontext->__ss.__fs, 502 uc->uc_mcontext->__ss.__gs 503 ); 504 logStackContent((void**)uc->uc_mcontext->__ss.__esp); 505 #endif 506 /* Linux */ 507 #elif defined(__linux__) 508 /* Linux x86 */ 509 #if defined(__i386__) 510 redisLog(REDIS_WARNING, 511 "\n" 512 "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n" 513 "EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n" 514 "SS :%08lx EFL:%08lx EIP:%08lx CS:%08lx\n" 515 "DS :%08lx ES :%08lx FS :%08lx GS:%08lx", 516 uc->uc_mcontext.gregs[11], 517 uc->uc_mcontext.gregs[8], 518 uc->uc_mcontext.gregs[10], 519 uc->uc_mcontext.gregs[9], 520 uc->uc_mcontext.gregs[4], 521 uc->uc_mcontext.gregs[5], 522 uc->uc_mcontext.gregs[6], 523 uc->uc_mcontext.gregs[7], 524 uc->uc_mcontext.gregs[18], 525 uc->uc_mcontext.gregs[17], 526 uc->uc_mcontext.gregs[14], 527 uc->uc_mcontext.gregs[15], 528 uc->uc_mcontext.gregs[3], 529 uc->uc_mcontext.gregs[2], 530 uc->uc_mcontext.gregs[1], 531 uc->uc_mcontext.gregs[0] 532 ); 533 logStackContent((void**)uc->uc_mcontext.gregs[7]); 534 #elif defined(__X86_64__) || defined(__x86_64__) 535 /* Linux AMD64 */ 536 redisLog(REDIS_WARNING, 537 "\n" 538 "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n" 539 "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n" 540 "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n" 541 "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n" 542 "RIP:%016lx EFL:%016lx\nCSGSFS:%016lx", 543 uc->uc_mcontext.gregs[13], 544 uc->uc_mcontext.gregs[11], 545 uc->uc_mcontext.gregs[14], 546 uc->uc_mcontext.gregs[12], 547 uc->uc_mcontext.gregs[8], 548 uc->uc_mcontext.gregs[9], 549 uc->uc_mcontext.gregs[10], 550 uc->uc_mcontext.gregs[15], 551 uc->uc_mcontext.gregs[0], 552 uc->uc_mcontext.gregs[1], 553 uc->uc_mcontext.gregs[2], 554 uc->uc_mcontext.gregs[3], 555 uc->uc_mcontext.gregs[4], 556 uc->uc_mcontext.gregs[5], 557 uc->uc_mcontext.gregs[6], 558 uc->uc_mcontext.gregs[7], 559 uc->uc_mcontext.gregs[16], 560 uc->uc_mcontext.gregs[17], 561 uc->uc_mcontext.gregs[18] 562 ); 563 logStackContent((void**)uc->uc_mcontext.gregs[15]); 564 #endif 565 #else 566 redisLog(REDIS_WARNING, 567 " Dumping of registers not supported for this OS/arch"); 568 #endif 569 } 570 571 /* Logs the stack trace using the backtrace() call. This function is designed 572 * to be called from signal handlers safely. */ 573 void logStackTrace(ucontext_t *uc) { 574 void *trace[100]; 575 int trace_size = 0, fd; 576 577 /* Open the log file in append mode. */ 578 fd = server.logfile ? 579 open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644) : 580 STDOUT_FILENO; 581 if (fd == -1) return; 582 583 /* Generate the stack trace */ 584 trace_size = backtrace(trace, 100); 585 586 /* overwrite sigaction with caller's address */ 587 if (getMcontextEip(uc) != NULL) 588 trace[1] = getMcontextEip(uc); 589 590 /* Write symbols to log file */ 591 backtrace_symbols_fd(trace, trace_size, fd); 592 593 /* Cleanup */ 594 if (server.logfile) close(fd); 595 } 596 597 /* Log information about the "current" client, that is, the client that is 598 * currently being served by Redis. May be NULL if Redis is not serving a 599 * client right now. */ 600 void logCurrentClient(void) { 601 if (server.current_client == NULL) return; 602 603 redisClient *cc = server.current_client; 604 sds client; 605 int j; 606 607 redisLog(REDIS_WARNING, "--- CURRENT CLIENT INFO"); 608 client = getClientInfoString(cc); 609 redisLog(REDIS_WARNING,"client: %s", client); 610 sdsfree(client); 611 for (j = 0; j < cc->argc; j++) { 612 robj *decoded; 613 614 decoded = getDecodedObject(cc->argv[j]); 615 redisLog(REDIS_WARNING,"argv[%d]: '%s'", j, (char*)decoded->ptr); 616 decrRefCount(decoded); 617 } 618 /* Check if the first argument, usually a key, is found inside the 619 * selected DB, and if so print info about the associated object. */ 620 if (cc->argc >= 1) { 621 robj *val, *key; 622 dictEntry *de; 623 624 key = getDecodedObject(cc->argv[1]); 625 de = dictFind(cc->db->dict, key->ptr); 626 if (de) { 627 val = dictGetVal(de); 628 redisLog(REDIS_WARNING,"key '%s' found in DB containing the following object:", key->ptr); 629 redisLogObjectDebugInfo(val); 630 } 631 decrRefCount(key); 632 } 633 } 634 635 void sigsegvHandler(int sig, siginfo_t *info, void *secret) { 636 ucontext_t *uc = (ucontext_t*) secret; 637 sds infostring, clients; 638 struct sigaction act; 639 REDIS_NOTUSED(info); 640 641 bugReportStart(); 642 redisLog(REDIS_WARNING, 643 " Redis %s crashed by signal: %d", REDIS_VERSION, sig); 644 redisLog(REDIS_WARNING, 645 " Failed assertion: %s (%s:%d)", server.assert_failed, 646 server.assert_file, server.assert_line); 647 648 /* Log the stack trace */ 649 redisLog(REDIS_WARNING, "--- STACK TRACE"); 650 logStackTrace(uc); 651 652 /* Log INFO and CLIENT LIST */ 653 redisLog(REDIS_WARNING, "--- INFO OUTPUT"); 654 infostring = genRedisInfoString("all"); 655 infostring = sdscatprintf(infostring, "hash_init_value: %u\n", 656 dictGetHashFunctionSeed()); 657 redisLogRaw(REDIS_WARNING, infostring); 658 redisLog(REDIS_WARNING, "--- CLIENT LIST OUTPUT"); 659 clients = getAllClientsInfoString(); 660 redisLogRaw(REDIS_WARNING, clients); 661 sdsfree(infostring); 662 sdsfree(clients); 663 664 /* Log the current client */ 665 logCurrentClient(); 666 667 /* Log dump of processor registers */ 668 logRegisters(uc); 669 670 redisLog(REDIS_WARNING, 671 "\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n" 672 " Please report the crash opening an issue on github:\n\n" 673 " http://github.com/antirez/redis/issues\n\n" 674 " Suspect RAM error? Use redis-server --test-memory to veryfy it.\n\n" 675 ); 676 /* free(messages); Don't call free() with possibly corrupted memory. */ 677 if (server.daemonize) unlink(server.pidfile); 678 679 /* Make sure we exit with the right signal at the end. So for instance 680 * the core will be dumped if enabled. */ 681 sigemptyset (&act.sa_mask); 682 act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND; 683 act.sa_handler = SIG_DFL; 684 sigaction (sig, &act, NULL); 685 kill(getpid(),sig); 686 } 687 #endif /* HAVE_BACKTRACE */ 688 689 /* =========================== Software Watchdog ============================ */ 690 #include <sys/time.h> 691 692 void watchdogSignalHandler(int sig, siginfo_t *info, void *secret) { 693 #ifdef HAVE_BACKTRACE 694 ucontext_t *uc = (ucontext_t*) secret; 695 #endif 696 REDIS_NOTUSED(info); 697 REDIS_NOTUSED(sig); 698 699 redisLogFromHandler(REDIS_WARNING,"\n--- WATCHDOG TIMER EXPIRED ---"); 700 #ifdef HAVE_BACKTRACE 701 logStackTrace(uc); 702 #else 703 redisLogFromHandler(REDIS_WARNING,"Sorry: no support for backtrace()."); 704 #endif 705 redisLogFromHandler(REDIS_WARNING,"--------\n"); 706 } 707 708 /* Schedule a SIGALRM delivery after the specified period in milliseconds. 709 * If a timer is already scheduled, this function will re-schedule it to the 710 * specified time. If period is 0 the current timer is disabled. */ 711 void watchdogScheduleSignal(int period) { 712 struct itimerval it; 713 714 /* Will stop the timer if period is 0. */ 715 it.it_value.tv_sec = period/1000; 716 it.it_value.tv_usec = (period%1000)*1000; 717 /* Don't automatically restart. */ 718 it.it_interval.tv_sec = 0; 719 it.it_interval.tv_usec = 0; 720 setitimer(ITIMER_REAL, &it, NULL); 721 } 722 723 /* Enable the software watchdong with the specified period in milliseconds. */ 724 void enableWatchdog(int period) { 725 int min_period; 726 727 if (server.watchdog_period == 0) { 728 struct sigaction act; 729 730 /* Watchdog was actually disabled, so we have to setup the signal 731 * handler. */ 732 sigemptyset(&act.sa_mask); 733 act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_SIGINFO; 734 act.sa_sigaction = watchdogSignalHandler; 735 sigaction(SIGALRM, &act, NULL); 736 } 737 /* If the configured period is smaller than twice the timer period, it is 738 * too short for the software watchdog to work reliably. Fix it now 739 * if needed. */ 740 min_period = (1000/REDIS_HZ)*2; 741 if (period < min_period) period = min_period; 742 watchdogScheduleSignal(period); /* Adjust the current timer. */ 743 server.watchdog_period = period; 744 } 745 746 /* Disable the software watchdog. */ 747 void disableWatchdog(void) { 748 struct sigaction act; 749 if (server.watchdog_period == 0) return; /* Already disabled. */ 750 watchdogScheduleSignal(0); /* Stop the current timer. */ 751 752 /* Set the signal handler to SIG_IGN, this will also remove pending 753 * signals from the queue. */ 754 sigemptyset(&act.sa_mask); 755 act.sa_flags = 0; 756 act.sa_handler = SIG_IGN; 757 sigaction(SIGALRM, &act, NULL); 758 server.watchdog_period = 0; 759 } 760