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 13883099 : void xorDigest(unsigned char *digest, void *ptr, size_t len) {
21 : SHA1_CTX ctx;
22 13883099 : unsigned char hash[20], *s = ptr;
23 : int j;
24 :
25 13883099 : SHA1Init(&ctx);
26 13883099 : SHA1Update(&ctx,s,len);
27 13883099 : SHA1Final(hash,&ctx);
28 :
29 291545079 : for (j = 0; j < 20; j++)
30 277661980 : digest[j] ^= hash[j];
31 13883099 : }
32 :
33 52708 : void xorObjectDigest(unsigned char *digest, robj *o) {
34 52708 : o = getDecodedObject(o);
35 105416 : xorDigest(digest,o->ptr,sdslen(o->ptr));
36 52708 : decrRefCount(o);
37 52708 : }
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 10334860 : void mixDigest(unsigned char *digest, void *ptr, size_t len) {
54 : SHA1_CTX ctx;
55 10334860 : char *s = ptr;
56 :
57 10334860 : xorDigest(digest,s,len);
58 10334860 : SHA1Init(&ctx);
59 10334860 : SHA1Update(&ctx,digest,20);
60 10334860 : SHA1Final(digest,&ctx);
61 10334860 : }
62 :
63 3414428 : void mixObjectDigest(unsigned char *digest, robj *o) {
64 3414428 : o = getDecodedObject(o);
65 6828856 : mixDigest(digest,o->ptr,sdslen(o->ptr));
66 3414428 : decrRefCount(o);
67 3414428 : }
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 61 : void computeDatasetDigest(unsigned char *final) {
76 : unsigned char digest[20];
77 : char buf[128];
78 61 : 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 1037 : for (j = 0; j < server.dbnum; j++) {
86 976 : redisDb *db = server.db+j;
87 :
88 976 : if (dictSize(db->dict) == 0) continue;
89 73 : 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 73 : aux = htonl(j);
94 73 : mixDigest(final,&aux,sizeof(aux));
95 :
96 : /* Iterate this DB writing every entry */
97 3416575 : 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 3416429 : key = dictGetKey(de);
104 3416429 : keyobj = createStringObject(key,sdslen(key));
105 :
106 3416429 : mixDigest(digest,key,sdslen(key));
107 :
108 : /* Make sure the key is loaded if VM is active */
109 3416429 : o = dictGetVal(de);
110 :
111 3416429 : aux = htonl(o->type);
112 3416429 : mixDigest(digest,&aux,sizeof(aux));
113 3416429 : expiretime = getExpire(db,keyobj);
114 :
115 : /* Save the key and associated value */
116 3416429 : if (o->type == REDIS_STRING) {
117 3307241 : mixObjectDigest(digest,o);
118 109188 : } else if (o->type == REDIS_LIST) {
119 16029 : listTypeIterator *li = listTypeInitIterator(o,0,REDIS_TAIL);
120 : listTypeEntry entry;
121 68542 : while(listTypeNext(li,&entry)) {
122 36484 : robj *eleobj = listTypeGet(&entry);
123 36484 : mixObjectDigest(digest,eleobj);
124 36484 : decrRefCount(eleobj);
125 : }
126 16029 : listTypeReleaseIterator(li);
127 93159 : } else if (o->type == REDIS_SET) {
128 38003 : setTypeIterator *si = setTypeInitIterator(o);
129 : robj *ele;
130 128714 : while((ele = setTypeNextObject(si)) != NULL) {
131 52708 : xorObjectDigest(digest,ele);
132 52708 : decrRefCount(ele);
133 : }
134 38003 : setTypeReleaseIterator(si);
135 55156 : } else if (o->type == REDIS_ZSET) {
136 : unsigned char eledigest[20];
137 :
138 34723 : if (o->encoding == REDIS_ENCODING_ZIPLIST) {
139 26711 : 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 26711 : eptr = ziplistIndex(zl,0);
147 26711 : redisAssert(eptr != NULL);
148 26711 : sptr = ziplistNext(zl,eptr);
149 26711 : redisAssert(sptr != NULL);
150 :
151 61426 : while (eptr != NULL) {
152 34715 : redisAssert(ziplistGet(eptr,&vstr,&vlen,&vll));
153 34715 : score = zzlGetScore(sptr);
154 :
155 : memset(eledigest,0,20);
156 34715 : if (vstr != NULL) {
157 2419 : mixDigest(eledigest,vstr,vlen);
158 : } else {
159 32296 : ll2string(buf,sizeof(buf),vll);
160 32296 : mixDigest(eledigest,buf,strlen(buf));
161 : }
162 :
163 34715 : snprintf(buf,sizeof(buf),"%.17g",score);
164 34715 : mixDigest(eledigest,buf,strlen(buf));
165 34715 : xorDigest(digest,eledigest,20);
166 34715 : zzlNext(zl,&eptr,&sptr);
167 : }
168 8012 : } else if (o->encoding == REDIS_ENCODING_SKIPLIST) {
169 8012 : zset *zs = o->ptr;
170 8012 : dictIterator *di = dictGetIterator(zs->dict);
171 : dictEntry *de;
172 :
173 34095 : while((de = dictNext(di)) != NULL) {
174 18071 : robj *eleobj = dictGetKey(de);
175 18071 : double *score = dictGetVal(de);
176 :
177 18071 : snprintf(buf,sizeof(buf),"%.17g",*score);
178 : memset(eledigest,0,20);
179 18071 : mixObjectDigest(eledigest,eleobj);
180 18071 : mixDigest(eledigest,buf,strlen(buf));
181 18071 : xorDigest(digest,eledigest,20);
182 : }
183 8012 : dictReleaseIterator(di);
184 : } else {
185 0 : redisPanic("Unknown sorted set encoding");
186 : }
187 20433 : } else if (o->type == REDIS_HASH) {
188 : hashTypeIterator *hi;
189 : robj *obj;
190 :
191 20433 : hi = hashTypeInitIterator(o);
192 67182 : while (hashTypeNext(hi) != REDIS_ERR) {
193 : unsigned char eledigest[20];
194 :
195 : memset(eledigest,0,20);
196 26316 : obj = hashTypeCurrentObject(hi,REDIS_HASH_KEY);
197 26316 : mixObjectDigest(eledigest,obj);
198 26316 : decrRefCount(obj);
199 26316 : obj = hashTypeCurrentObject(hi,REDIS_HASH_VALUE);
200 26316 : mixObjectDigest(eledigest,obj);
201 26316 : decrRefCount(obj);
202 26316 : xorDigest(digest,eledigest,20);
203 : }
204 20433 : hashTypeReleaseIterator(hi);
205 : } else {
206 0 : redisPanic("Unknown object type");
207 : }
208 : /* If the key has an expire, add it to the mix */
209 3416429 : if (expiretime != -1) xorDigest(digest,"!!expire!!",10);
210 : /* We can finally xor the key-val digest to the final digest */
211 3416429 : xorDigest(final,digest,20);
212 3416429 : decrRefCount(keyobj);
213 : }
214 73 : dictReleaseIterator(di);
215 : }
216 61 : }
217 :
218 3493 : void debugCommand(redisClient *c) {
219 3493 : if (!strcasecmp(c->argv[1]->ptr,"segfault")) {
220 0 : *((char*)-1) = 'x';
221 3493 : } else if (!strcasecmp(c->argv[1]->ptr,"assert")) {
222 0 : if (c->argc >= 3) c->argv[2] = tryObjectEncoding(c->argv[2]);
223 0 : redisAssertWithInfo(c,c->argv[0],1 == 2);
224 3493 : } else if (!strcasecmp(c->argv[1]->ptr,"reload")) {
225 10 : if (rdbSave(server.rdb_filename) != REDIS_OK) {
226 0 : addReply(c,shared.err);
227 0 : return;
228 : }
229 10 : emptyDb();
230 10 : if (rdbLoad(server.rdb_filename) != REDIS_OK) {
231 0 : addReplyError(c,"Error trying to load the RDB dump");
232 0 : return;
233 : }
234 10 : redisLog(REDIS_WARNING,"DB reloaded by DEBUG RELOAD");
235 10 : addReply(c,shared.ok);
236 3483 : } else if (!strcasecmp(c->argv[1]->ptr,"loadaof")) {
237 19 : emptyDb();
238 19 : if (loadAppendOnlyFile(server.aof_filename) != REDIS_OK) {
239 0 : addReply(c,shared.err);
240 0 : return;
241 : }
242 19 : server.dirty = 0; /* Prevent AOF / replication */
243 19 : redisLog(REDIS_WARNING,"Append Only File loaded by DEBUG LOADAOF");
244 19 : addReply(c,shared.ok);
245 6864 : } else if (!strcasecmp(c->argv[1]->ptr,"object") && c->argc == 3) {
246 : dictEntry *de;
247 : robj *val;
248 : char *strenc;
249 :
250 3400 : if ((de = dictFind(c->db->dict,c->argv[2]->ptr)) == NULL) {
251 0 : addReply(c,shared.nokeyerr);
252 0 : return;
253 : }
254 3400 : val = dictGetVal(de);
255 3400 : strenc = strEncoding(val->encoding);
256 :
257 6800 : 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 3400 : val->lru, estimateObjectIdleTime(val));
264 64 : } 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 0 : if (getLongFromObjectOrReply(c, c->argv[2], &keys, NULL) != REDIS_OK)
270 : return;
271 0 : for (j = 0; j < keys; j++) {
272 0 : snprintf(buf,sizeof(buf),"key:%lu",j);
273 0 : key = createStringObject(buf,strlen(buf));
274 0 : if (lookupKeyRead(c->db,key) != NULL) {
275 0 : decrRefCount(key);
276 0 : continue;
277 : }
278 0 : snprintf(buf,sizeof(buf),"value:%lu",j);
279 0 : val = createStringObject(buf,strlen(buf));
280 0 : dbAdd(c->db,key,val);
281 0 : decrRefCount(key);
282 : }
283 0 : addReply(c,shared.ok);
284 125 : } else if (!strcasecmp(c->argv[1]->ptr,"digest") && c->argc == 2) {
285 : unsigned char digest[20];
286 61 : sds d = sdsempty();
287 : int j;
288 :
289 61 : computeDatasetDigest(digest);
290 1281 : for (j = 0; j < 20; j++)
291 1220 : d = sdscatprintf(d, "%02x",digest[j]);
292 61 : addReplyStatus(c,d);
293 61 : sdsfree(d);
294 6 : } else if (!strcasecmp(c->argv[1]->ptr,"sleep") && c->argc == 3) {
295 3 : double dtime = strtod(c->argv[2]->ptr,NULL);
296 3 : long long utime = dtime*1000000;
297 :
298 3 : usleep(utime);
299 3 : addReply(c,shared.ok);
300 : } else {
301 0 : addReplyError(c,
302 : "Syntax error, try DEBUG [SEGFAULT|OBJECT <key>|SWAPIN <key>|SWAPOUT <key>|RELOAD]");
303 : }
304 : }
305 :
306 : /* =========================== Crash handling ============================== */
307 :
308 0 : void _redisAssert(char *estr, char *file, int line) {
309 0 : bugReportStart();
310 0 : redisLog(REDIS_WARNING,"=== ASSERTION FAILED ===");
311 0 : redisLog(REDIS_WARNING,"==> %s:%d '%s' is not true",file,line,estr);
312 : #ifdef HAVE_BACKTRACE
313 0 : server.assert_failed = estr;
314 0 : server.assert_file = file;
315 0 : server.assert_line = line;
316 0 : redisLog(REDIS_WARNING,"(forcing SIGSEGV to print the bug report.)");
317 : #endif
318 0 : *((char*)-1) = 'x';
319 0 : }
320 :
321 0 : void _redisAssertPrintClientInfo(redisClient *c) {
322 : int j;
323 :
324 0 : bugReportStart();
325 0 : redisLog(REDIS_WARNING,"=== ASSERTION FAILED CLIENT CONTEXT ===");
326 0 : redisLog(REDIS_WARNING,"client->flags = %d", c->flags);
327 0 : redisLog(REDIS_WARNING,"client->fd = %d", c->fd);
328 0 : redisLog(REDIS_WARNING,"client->argc = %d", c->argc);
329 0 : for (j=0; j < c->argc; j++) {
330 : char buf[128];
331 : char *arg;
332 :
333 0 : if (c->argv[j]->type == REDIS_STRING &&
334 : c->argv[j]->encoding == REDIS_ENCODING_RAW)
335 : {
336 0 : arg = (char*) c->argv[j]->ptr;
337 : } else {
338 0 : snprintf(buf,sizeof(buf),"Object type: %d, encoding: %d",
339 0 : c->argv[j]->type, c->argv[j]->encoding);
340 0 : arg = buf;
341 : }
342 0 : redisLog(REDIS_WARNING,"client->argv[%d] = \"%s\" (refcount: %d)",
343 0 : j, arg, c->argv[j]->refcount);
344 : }
345 0 : }
346 :
347 0 : void redisLogObjectDebugInfo(robj *o) {
348 0 : redisLog(REDIS_WARNING,"Object type: %d", o->type);
349 0 : redisLog(REDIS_WARNING,"Object encoding: %d", o->encoding);
350 0 : redisLog(REDIS_WARNING,"Object refcount: %d", o->refcount);
351 0 : if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_RAW) {
352 0 : redisLog(REDIS_WARNING,"Object raw string len: %d", sdslen(o->ptr));
353 0 : if (sdslen(o->ptr) < 4096)
354 0 : redisLog(REDIS_WARNING,"Object raw string content: \"%s\"", (char*)o->ptr);
355 0 : } else if (o->type == REDIS_LIST) {
356 0 : redisLog(REDIS_WARNING,"List length: %d", (int) listTypeLength(o));
357 0 : } else if (o->type == REDIS_SET) {
358 0 : redisLog(REDIS_WARNING,"Set size: %d", (int) setTypeSize(o));
359 0 : } else if (o->type == REDIS_HASH) {
360 0 : redisLog(REDIS_WARNING,"Hash size: %d", (int) hashTypeLength(o));
361 0 : } else if (o->type == REDIS_ZSET) {
362 0 : redisLog(REDIS_WARNING,"Sorted set size: %d", (int) zsetLength(o));
363 0 : if (o->encoding == REDIS_ENCODING_SKIPLIST)
364 0 : redisLog(REDIS_WARNING,"Skiplist level: %d", (int) ((zset*)o->ptr)->zsl->level);
365 : }
366 0 : }
367 :
368 0 : void _redisAssertPrintObject(robj *o) {
369 0 : bugReportStart();
370 0 : redisLog(REDIS_WARNING,"=== ASSERTION FAILED OBJECT CONTEXT ===");
371 0 : redisLogObjectDebugInfo(o);
372 0 : }
373 :
374 0 : void _redisAssertWithInfo(redisClient *c, robj *o, char *estr, char *file, int line) {
375 0 : if (c) _redisAssertPrintClientInfo(c);
376 0 : if (o) _redisAssertPrintObject(o);
377 0 : _redisAssert(estr,file,line);
378 0 : }
379 :
380 0 : void _redisPanic(char *msg, char *file, int line) {
381 0 : bugReportStart();
382 0 : redisLog(REDIS_WARNING,"------------------------------------------------");
383 0 : redisLog(REDIS_WARNING,"!!! Software Failure. Press left mouse button to continue");
384 0 : redisLog(REDIS_WARNING,"Guru Meditation: %s #%s:%d",msg,file,line);
385 : #ifdef HAVE_BACKTRACE
386 0 : redisLog(REDIS_WARNING,"(forcing SIGSEGV in order to print the stack trace)");
387 : #endif
388 0 : redisLog(REDIS_WARNING,"------------------------------------------------");
389 0 : *((char*)-1) = 'x';
390 0 : }
391 :
392 0 : void bugReportStart(void) {
393 0 : if (server.bug_report_start == 0) {
394 0 : redisLog(REDIS_WARNING,
395 : "\n\n=== REDIS BUG REPORT START: Cut & paste starting from here ===");
396 0 : server.bug_report_start = 1;
397 : }
398 0 : }
399 :
400 : #ifdef HAVE_BACKTRACE
401 : static void *getMcontextEip(ucontext_t *uc) {
402 : #if defined(__FreeBSD__)
403 : return (void*) uc->uc_mcontext.mc_eip;
404 : #elif defined(__dietlibc__)
405 : return (void*) uc->uc_mcontext.eip;
406 : #elif defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
407 : #if __x86_64__
408 : return (void*) uc->uc_mcontext->__ss.__rip;
409 : #elif __i386__
410 : return (void*) uc->uc_mcontext->__ss.__eip;
411 : #else
412 : return (void*) uc->uc_mcontext->__ss.__srr0;
413 : #endif
414 : #elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
415 : #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
416 : return (void*) uc->uc_mcontext->__ss.__rip;
417 : #else
418 : return (void*) uc->uc_mcontext->__ss.__eip;
419 : #endif
420 : #elif defined(__i386__)
421 : return (void*) uc->uc_mcontext.gregs[14]; /* Linux 32 */
422 : #elif defined(__X86_64__) || defined(__x86_64__)
423 0 : return (void*) uc->uc_mcontext.gregs[16]; /* Linux 64 */
424 : #elif defined(__ia64__) /* Linux IA64 */
425 : return (void*) uc->uc_mcontext.sc_ip;
426 : #else
427 : return NULL;
428 : #endif
429 : }
430 :
431 0 : void logStackContent(void **sp) {
432 : int i;
433 0 : for (i = 15; i >= 0; i--) {
434 : if (sizeof(long) == 4)
435 : redisLog(REDIS_WARNING, "(%08lx) -> %08lx", sp+i, sp[i]);
436 : else
437 0 : redisLog(REDIS_WARNING, "(%016lx) -> %016lx", sp+i, sp[i]);
438 : }
439 0 : }
440 :
441 0 : void logRegisters(ucontext_t *uc) {
442 0 : 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 0 : 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 0 : 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 0 : }
560 :
561 : /* Logs the stack trace using the backtrace() call. */
562 0 : sds getStackTrace(ucontext_t *uc) {
563 : void *trace[100];
564 0 : int i, trace_size = 0;
565 0 : char **messages = NULL;
566 0 : sds st = sdsempty();
567 :
568 : /* Generate the stack trace */
569 0 : trace_size = backtrace(trace, 100);
570 :
571 : /* overwrite sigaction with caller's address */
572 0 : if (getMcontextEip(uc) != NULL) {
573 0 : trace[1] = getMcontextEip(uc);
574 : }
575 0 : messages = backtrace_symbols(trace, trace_size);
576 0 : for (i=1; i<trace_size; ++i) {
577 0 : st = sdscat(st,messages[i]);
578 0 : st = sdscatlen(st,"\n",1);
579 : }
580 0 : zlibc_free(messages);
581 0 : return st;
582 : }
583 :
584 : /* Log information about the "current" client, that is, the client that is
585 : * currently being served by Redis. May be NULL if Redis is not serving a
586 : * client right now. */
587 0 : void logCurrentClient(void) {
588 0 : if (server.current_client == NULL) return;
589 :
590 0 : redisClient *cc = server.current_client;
591 : sds client;
592 : int j;
593 :
594 0 : redisLog(REDIS_WARNING, "--- CURRENT CLIENT INFO");
595 0 : client = getClientInfoString(cc);
596 0 : redisLog(REDIS_WARNING,"client: %s", client);
597 0 : sdsfree(client);
598 0 : for (j = 0; j < cc->argc; j++) {
599 : robj *decoded;
600 :
601 0 : decoded = getDecodedObject(cc->argv[j]);
602 0 : redisLog(REDIS_WARNING,"argv[%d]: '%s'", j, (char*)decoded->ptr);
603 0 : decrRefCount(decoded);
604 : }
605 : /* Check if the first argument, usually a key, is found inside the
606 : * selected DB, and if so print info about the associated object. */
607 0 : if (cc->argc >= 1) {
608 : robj *val, *key;
609 : dictEntry *de;
610 :
611 0 : key = getDecodedObject(cc->argv[1]);
612 0 : de = dictFind(cc->db->dict, key->ptr);
613 0 : if (de) {
614 0 : val = dictGetVal(de);
615 0 : redisLog(REDIS_WARNING,"key '%s' found in DB containing the following object:", key->ptr);
616 0 : redisLogObjectDebugInfo(val);
617 : }
618 0 : decrRefCount(key);
619 : }
620 : }
621 :
622 0 : void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
623 0 : ucontext_t *uc = (ucontext_t*) secret;
624 : sds infostring, clients, st;
625 : struct sigaction act;
626 : REDIS_NOTUSED(info);
627 :
628 0 : bugReportStart();
629 0 : redisLog(REDIS_WARNING,
630 : " Redis %s crashed by signal: %d", REDIS_VERSION, sig);
631 0 : redisLog(REDIS_WARNING,
632 : " Failed assertion: %s (%s:%d)", server.assert_failed,
633 : server.assert_file, server.assert_line);
634 :
635 : /* Log the stack trace */
636 0 : st = getStackTrace(uc);
637 0 : redisLog(REDIS_WARNING, "--- STACK TRACE\n%s", st);
638 0 : sdsfree(st);
639 :
640 : /* Log INFO and CLIENT LIST */
641 0 : redisLog(REDIS_WARNING, "--- INFO OUTPUT");
642 0 : infostring = genRedisInfoString("all");
643 0 : infostring = sdscatprintf(infostring, "hash_init_value: %u\n",
644 : dictGetHashFunctionSeed());
645 0 : redisLogRaw(REDIS_WARNING, infostring);
646 0 : redisLog(REDIS_WARNING, "--- CLIENT LIST OUTPUT");
647 0 : clients = getAllClientsInfoString();
648 0 : redisLogRaw(REDIS_WARNING, clients);
649 0 : sdsfree(infostring);
650 0 : sdsfree(clients);
651 :
652 : /* Log the current client */
653 0 : logCurrentClient();
654 :
655 : /* Log dump of processor registers */
656 0 : logRegisters(uc);
657 :
658 0 : redisLog(REDIS_WARNING,
659 : "\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n"
660 : " Please report the crash opening an issue on github:\n\n"
661 : " http://github.com/antirez/redis/issues\n\n"
662 : " Suspect RAM error? Use redis-server --test-memory to veryfy it.\n\n"
663 : );
664 : /* free(messages); Don't call free() with possibly corrupted memory. */
665 0 : if (server.daemonize) unlink(server.pidfile);
666 :
667 : /* Make sure we exit with the right signal at the end. So for instance
668 : * the core will be dumped if enabled. */
669 0 : sigemptyset (&act.sa_mask);
670 0 : act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND;
671 0 : act.sa_handler = SIG_DFL;
672 0 : sigaction (sig, &act, NULL);
673 0 : kill(getpid(),sig);
674 0 : }
675 : #endif /* HAVE_BACKTRACE */
676 :
677 : /* =========================== Software Watchdog ============================ */
678 : #include <sys/time.h>
679 :
680 0 : void watchdogSignalHandler(int sig, siginfo_t *info, void *secret) {
681 0 : ucontext_t *uc = (ucontext_t*) secret;
682 : REDIS_NOTUSED(info);
683 : REDIS_NOTUSED(sig);
684 : sds st, log;
685 :
686 0 : log = sdsnew("\n--- WATCHDOG TIMER EXPIRED ---\n");
687 : #ifdef HAVE_BACKTRACE
688 0 : st = getStackTrace(uc);
689 : #else
690 : st = sdsnew("Sorry: no support for backtrace().\n");
691 : #endif
692 0 : log = sdscatsds(log,st);
693 0 : log = sdscat(log,"------\n");
694 0 : redisLogFromHandler(REDIS_WARNING,log);
695 0 : sdsfree(st);
696 0 : sdsfree(log);
697 0 : }
698 :
699 : /* Schedule a SIGALRM delivery after the specified period in milliseconds.
700 : * If a timer is already scheduled, this function will re-schedule it to the
701 : * specified time. If period is 0 the current timer is disabled. */
702 0 : void watchdogScheduleSignal(int period) {
703 : struct itimerval it;
704 :
705 : /* Will stop the timer if period is 0. */
706 0 : it.it_value.tv_sec = period/1000;
707 0 : it.it_value.tv_usec = (period%1000)*1000;
708 : /* Don't automatically restart. */
709 0 : it.it_interval.tv_sec = 0;
710 0 : it.it_interval.tv_usec = 0;
711 0 : setitimer(ITIMER_REAL, &it, NULL);
712 0 : }
713 :
714 : /* Enable the software watchdong with the specified period in milliseconds. */
715 0 : void enableWatchdog(int period) {
716 0 : if (server.watchdog_period == 0) {
717 : struct sigaction act;
718 :
719 : /* Watchdog was actually disabled, so we have to setup the signal
720 : * handler. */
721 0 : sigemptyset(&act.sa_mask);
722 0 : act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_SIGINFO;
723 0 : act.sa_sigaction = watchdogSignalHandler;
724 0 : sigaction(SIGALRM, &act, NULL);
725 : }
726 0 : if (period < 200) period = 200; /* We don't accept periods < 200 ms. */
727 0 : watchdogScheduleSignal(period); /* Adjust the current timer. */
728 0 : server.watchdog_period = period;
729 0 : }
730 :
731 : /* Disable the software watchdog. */
732 0 : void disableWatchdog(void) {
733 : struct sigaction act;
734 0 : if (server.watchdog_period == 0) return; /* Already disabled. */
735 0 : watchdogScheduleSignal(0); /* Stop the current timer. */
736 :
737 : /* Set the signal handler to SIG_IGN, this will also remove pending
738 : * signals from the queue. */
739 0 : sigemptyset(&act.sa_mask);
740 0 : act.sa_flags = 0;
741 0 : act.sa_handler = SIG_IGN;
742 0 : sigaction(SIGALRM, &act, NULL);
743 0 : server.watchdog_period = 0;
744 : }
|