1 /*
2 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
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.
16 *
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.
28 */
29
30 #include "server.h"
31 #include "sha1.h" /* SHA1 is used for DEBUG DIGEST */
32 #include "crc64.h"
33
34 #include <arpa/inet.h>
35 #include <signal.h>
36 #include <dlfcn.h>
37
38 #ifdef HAVE_BACKTRACE
39 #include <execinfo.h>
40 #ifndef __OpenBSD__
41 #include <ucontext.h>
42 #else
43 typedef ucontext_t sigcontext_t;
44 #endif
45 #include <fcntl.h>
46 #include "bio.h"
47 #include <unistd.h>
48 #endif /* HAVE_BACKTRACE */
49
50 #ifdef __CYGWIN__
51 #ifndef SA_ONSTACK
52 #define SA_ONSTACK 0x08000000
53 #endif
54 #endif
55
56 /* ================================= Debugging ============================== */
57
58 /* Compute the sha1 of string at 's' with 'len' bytes long.
59 * The SHA1 is then xored against the string pointed by digest.
60 * Since xor is commutative, this operation is used in order to
61 * "add" digests relative to unordered elements.
62 *
63 * So digest(a,b,c,d) will be the same of digest(b,a,c,d) */
xorDigest(unsigned char * digest,void * ptr,size_t len)64 void xorDigest(unsigned char *digest, void *ptr, size_t len) {
65 SHA1_CTX ctx;
66 unsigned char hash[20], *s = ptr;
67 int j;
68
69 SHA1Init(&ctx);
70 SHA1Update(&ctx,s,len);
71 SHA1Final(hash,&ctx);
72
73 for (j = 0; j < 20; j++)
74 digest[j] ^= hash[j];
75 }
76
xorStringObjectDigest(unsigned char * digest,robj * o)77 void xorStringObjectDigest(unsigned char *digest, robj *o) {
78 o = getDecodedObject(o);
79 xorDigest(digest,o->ptr,sdslen(o->ptr));
80 decrRefCount(o);
81 }
82
83 /* This function instead of just computing the SHA1 and xoring it
84 * against digest, also perform the digest of "digest" itself and
85 * replace the old value with the new one.
86 *
87 * So the final digest will be:
88 *
89 * digest = SHA1(digest xor SHA1(data))
90 *
91 * This function is used every time we want to preserve the order so
92 * that digest(a,b,c,d) will be different than digest(b,c,d,a)
93 *
94 * Also note that mixdigest("foo") followed by mixdigest("bar")
95 * will lead to a different digest compared to "fo", "obar".
96 */
mixDigest(unsigned char * digest,void * ptr,size_t len)97 void mixDigest(unsigned char *digest, void *ptr, size_t len) {
98 SHA1_CTX ctx;
99 char *s = ptr;
100
101 xorDigest(digest,s,len);
102 SHA1Init(&ctx);
103 SHA1Update(&ctx,digest,20);
104 SHA1Final(digest,&ctx);
105 }
106
mixStringObjectDigest(unsigned char * digest,robj * o)107 void mixStringObjectDigest(unsigned char *digest, robj *o) {
108 o = getDecodedObject(o);
109 mixDigest(digest,o->ptr,sdslen(o->ptr));
110 decrRefCount(o);
111 }
112
113 /* This function computes the digest of a data structure stored in the
114 * object 'o'. It is the core of the DEBUG DIGEST command: when taking the
115 * digest of a whole dataset, we take the digest of the key and the value
116 * pair, and xor all those together.
117 *
118 * Note that this function does not reset the initial 'digest' passed, it
119 * will continue mixing this object digest to anything that was already
120 * present. */
xorObjectDigest(redisDb * db,robj * keyobj,unsigned char * digest,robj * o)121 void xorObjectDigest(redisDb *db, robj *keyobj, unsigned char *digest, robj *o) {
122 uint32_t aux = htonl(o->type);
123 mixDigest(digest,&aux,sizeof(aux));
124 long long expiretime = getExpire(db,keyobj);
125 char buf[128];
126
127 /* Save the key and associated value */
128 if (o->type == OBJ_STRING) {
129 mixStringObjectDigest(digest,o);
130 } else if (o->type == OBJ_LIST) {
131 listTypeIterator *li = listTypeInitIterator(o,0,LIST_TAIL);
132 listTypeEntry entry;
133 while(listTypeNext(li,&entry)) {
134 robj *eleobj = listTypeGet(&entry);
135 mixStringObjectDigest(digest,eleobj);
136 decrRefCount(eleobj);
137 }
138 listTypeReleaseIterator(li);
139 } else if (o->type == OBJ_SET) {
140 setTypeIterator *si = setTypeInitIterator(o);
141 sds sdsele;
142 while((sdsele = setTypeNextObject(si)) != NULL) {
143 xorDigest(digest,sdsele,sdslen(sdsele));
144 sdsfree(sdsele);
145 }
146 setTypeReleaseIterator(si);
147 } else if (o->type == OBJ_ZSET) {
148 unsigned char eledigest[20];
149
150 if (o->encoding == OBJ_ENCODING_ZIPLIST) {
151 unsigned char *zl = o->ptr;
152 unsigned char *eptr, *sptr;
153 unsigned char *vstr;
154 unsigned int vlen;
155 long long vll;
156 double score;
157
158 eptr = ziplistIndex(zl,0);
159 serverAssert(eptr != NULL);
160 sptr = ziplistNext(zl,eptr);
161 serverAssert(sptr != NULL);
162
163 while (eptr != NULL) {
164 serverAssert(ziplistGet(eptr,&vstr,&vlen,&vll));
165 score = zzlGetScore(sptr);
166
167 memset(eledigest,0,20);
168 if (vstr != NULL) {
169 mixDigest(eledigest,vstr,vlen);
170 } else {
171 ll2string(buf,sizeof(buf),vll);
172 mixDigest(eledigest,buf,strlen(buf));
173 }
174
175 snprintf(buf,sizeof(buf),"%.17g",score);
176 mixDigest(eledigest,buf,strlen(buf));
177 xorDigest(digest,eledigest,20);
178 zzlNext(zl,&eptr,&sptr);
179 }
180 } else if (o->encoding == OBJ_ENCODING_SKIPLIST) {
181 zset *zs = o->ptr;
182 dictIterator *di = dictGetIterator(zs->dict);
183 dictEntry *de;
184
185 while((de = dictNext(di)) != NULL) {
186 sds sdsele = dictGetKey(de);
187 double *score = dictGetVal(de);
188
189 snprintf(buf,sizeof(buf),"%.17g",*score);
190 memset(eledigest,0,20);
191 mixDigest(eledigest,sdsele,sdslen(sdsele));
192 mixDigest(eledigest,buf,strlen(buf));
193 xorDigest(digest,eledigest,20);
194 }
195 dictReleaseIterator(di);
196 } else {
197 serverPanic("Unknown sorted set encoding");
198 }
199 } else if (o->type == OBJ_HASH) {
200 hashTypeIterator *hi = hashTypeInitIterator(o);
201 while (hashTypeNext(hi) != C_ERR) {
202 unsigned char eledigest[20];
203 sds sdsele;
204
205 memset(eledigest,0,20);
206 sdsele = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_KEY);
207 mixDigest(eledigest,sdsele,sdslen(sdsele));
208 sdsfree(sdsele);
209 sdsele = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_VALUE);
210 mixDigest(eledigest,sdsele,sdslen(sdsele));
211 sdsfree(sdsele);
212 xorDigest(digest,eledigest,20);
213 }
214 hashTypeReleaseIterator(hi);
215 } else if (o->type == OBJ_STREAM) {
216 streamIterator si;
217 streamIteratorStart(&si,o->ptr,NULL,NULL,0);
218 streamID id;
219 int64_t numfields;
220
221 while(streamIteratorGetID(&si,&id,&numfields)) {
222 sds itemid = sdscatfmt(sdsempty(),"%U.%U",id.ms,id.seq);
223 mixDigest(digest,itemid,sdslen(itemid));
224 sdsfree(itemid);
225
226 while(numfields--) {
227 unsigned char *field, *value;
228 int64_t field_len, value_len;
229 streamIteratorGetField(&si,&field,&value,
230 &field_len,&value_len);
231 mixDigest(digest,field,field_len);
232 mixDigest(digest,value,value_len);
233 }
234 }
235 streamIteratorStop(&si);
236 } else if (o->type == OBJ_MODULE) {
237 RedisModuleDigest md;
238 moduleValue *mv = o->ptr;
239 moduleType *mt = mv->type;
240 moduleInitDigestContext(md);
241 if (mt->digest) {
242 mt->digest(&md,mv->value);
243 xorDigest(digest,md.x,sizeof(md.x));
244 }
245 } else {
246 serverPanic("Unknown object type");
247 }
248 /* If the key has an expire, add it to the mix */
249 if (expiretime != -1) xorDigest(digest,"!!expire!!",10);
250 }
251
252 /* Compute the dataset digest. Since keys, sets elements, hashes elements
253 * are not ordered, we use a trick: every aggregate digest is the xor
254 * of the digests of their elements. This way the order will not change
255 * the result. For list instead we use a feedback entering the output digest
256 * as input in order to ensure that a different ordered list will result in
257 * a different digest. */
computeDatasetDigest(unsigned char * final)258 void computeDatasetDigest(unsigned char *final) {
259 unsigned char digest[20];
260 dictIterator *di = NULL;
261 dictEntry *de;
262 int j;
263 uint32_t aux;
264
265 memset(final,0,20); /* Start with a clean result */
266
267 for (j = 0; j < server.dbnum; j++) {
268 redisDb *db = server.db+j;
269
270 if (dictSize(db->dict) == 0) continue;
271 di = dictGetSafeIterator(db->dict);
272
273 /* hash the DB id, so the same dataset moved in a different
274 * DB will lead to a different digest */
275 aux = htonl(j);
276 mixDigest(final,&aux,sizeof(aux));
277
278 /* Iterate this DB writing every entry */
279 while((de = dictNext(di)) != NULL) {
280 sds key;
281 robj *keyobj, *o;
282
283 memset(digest,0,20); /* This key-val digest */
284 key = dictGetKey(de);
285 keyobj = createStringObject(key,sdslen(key));
286
287 mixDigest(digest,key,sdslen(key));
288
289 o = dictGetVal(de);
290 xorObjectDigest(db,keyobj,digest,o);
291
292 /* We can finally xor the key-val digest to the final digest */
293 xorDigest(final,digest,20);
294 decrRefCount(keyobj);
295 }
296 dictReleaseIterator(di);
297 }
298 }
299
debugCommand(client * c)300 void debugCommand(client *c) {
301 if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) {
302 const char *help[] = {
303 "ASSERT -- Crash by assertion failed.",
304 "CHANGE-REPL-ID -- Change the replication IDs of the instance. Dangerous, should be used only for testing the replication subsystem.",
305 "CRASH-AND-RECOVER <milliseconds> -- Hard crash and restart after <milliseconds> delay.",
306 "DIGEST -- Output a hex signature representing the current DB content.",
307 "DIGEST-VALUE <key-1> ... <key-N>-- Output a hex signature of the values of all the specified keys.",
308 "ERROR <string> -- Return a Redis protocol error with <string> as message. Useful for clients unit tests to simulate Redis errors.",
309 "LOG <message> -- write message to the server log.",
310 "HTSTATS <dbid> -- Return hash table statistics of the specified Redis database.",
311 "HTSTATS-KEY <key> -- Like htstats but for the hash table stored as key's value.",
312 "LOADAOF -- Flush the AOF buffers on disk and reload the AOF in memory.",
313 "LUA-ALWAYS-REPLICATE-COMMANDS <0|1> -- Setting it to 1 makes Lua replication defaulting to replicating single commands, without the script having to enable effects replication.",
314 "OBJECT <key> -- Show low level info about key and associated value.",
315 "PANIC -- Crash the server simulating a panic.",
316 "POPULATE <count> [prefix] [size] -- Create <count> string keys named key:<num>. If a prefix is specified is used instead of the 'key' prefix.",
317 "RELOAD -- Save the RDB on disk and reload it back in memory.",
318 "RESTART -- Graceful restart: save config, db, restart.",
319 "SDSLEN <key> -- Show low level SDS string info representing key and value.",
320 "SEGFAULT -- Crash the server with sigsegv.",
321 "SET-ACTIVE-EXPIRE <0|1> -- Setting it to 0 disables expiring keys in background when they are not accessed (otherwise the Redis behavior). Setting it to 1 reenables back the default.",
322 "SLEEP <seconds> -- Stop the server for <seconds>. Decimals allowed.",
323 "STRUCTSIZE -- Return the size of different Redis core C structures.",
324 "ZIPLIST <key> -- Show low level info about the ziplist encoding.",
325 "STRINGMATCH-TEST -- Run a fuzz tester against the stringmatchlen() function.",
326 NULL
327 };
328 addReplyHelp(c, help);
329 } else if (!strcasecmp(c->argv[1]->ptr,"segfault")) {
330 *((char*)-1) = 'x';
331 } else if (!strcasecmp(c->argv[1]->ptr,"panic")) {
332 serverPanic("DEBUG PANIC called at Unix time %ld", time(NULL));
333 } else if (!strcasecmp(c->argv[1]->ptr,"restart") ||
334 !strcasecmp(c->argv[1]->ptr,"crash-and-recover"))
335 {
336 long long delay = 0;
337 if (c->argc >= 3) {
338 if (getLongLongFromObjectOrReply(c, c->argv[2], &delay, NULL)
339 != C_OK) return;
340 if (delay < 0) delay = 0;
341 }
342 int flags = !strcasecmp(c->argv[1]->ptr,"restart") ?
343 (RESTART_SERVER_GRACEFULLY|RESTART_SERVER_CONFIG_REWRITE) :
344 RESTART_SERVER_NONE;
345 restartServer(flags,delay);
346 addReplyError(c,"failed to restart the server. Check server logs.");
347 } else if (!strcasecmp(c->argv[1]->ptr,"oom")) {
348 void *ptr = zmalloc(ULONG_MAX); /* Should trigger an out of memory. */
349 zfree(ptr);
350 addReply(c,shared.ok);
351 } else if (!strcasecmp(c->argv[1]->ptr,"assert")) {
352 serverAssertWithInfo(c,c->argv[0],1 == 2);
353 } else if (!strcasecmp(c->argv[1]->ptr,"log") && c->argc == 3) {
354 serverLog(LL_WARNING, "DEBUG LOG: %s", (char*)c->argv[2]->ptr);
355 addReply(c,shared.ok);
356 } else if (!strcasecmp(c->argv[1]->ptr,"reload")) {
357 rdbSaveInfo rsi, *rsiptr;
358 rsiptr = rdbPopulateSaveInfo(&rsi);
359 if (rdbSave(server.rdb_filename,rsiptr) != C_OK) {
360 addReply(c,shared.err);
361 return;
362 }
363 emptyDb(-1,EMPTYDB_NO_FLAGS,NULL);
364 protectClient(c);
365 int ret = rdbLoad(server.rdb_filename,NULL);
366 unprotectClient(c);
367 if (ret != C_OK) {
368 addReplyError(c,"Error trying to load the RDB dump");
369 return;
370 }
371 serverLog(LL_WARNING,"DB reloaded by DEBUG RELOAD");
372 addReply(c,shared.ok);
373 } else if (!strcasecmp(c->argv[1]->ptr,"loadaof")) {
374 if (server.aof_state != AOF_OFF) flushAppendOnlyFile(1);
375 emptyDb(-1,EMPTYDB_NO_FLAGS,NULL);
376 protectClient(c);
377 int ret = loadAppendOnlyFile(server.aof_filename);
378 unprotectClient(c);
379 if (ret != C_OK) {
380 addReply(c,shared.err);
381 return;
382 }
383 server.dirty = 0; /* Prevent AOF / replication */
384 serverLog(LL_WARNING,"Append Only File loaded by DEBUG LOADAOF");
385 addReply(c,shared.ok);
386 } else if (!strcasecmp(c->argv[1]->ptr,"object") && c->argc == 3) {
387 dictEntry *de;
388 robj *val;
389 char *strenc;
390
391 if ((de = dictFind(c->db->dict,c->argv[2]->ptr)) == NULL) {
392 addReply(c,shared.nokeyerr);
393 return;
394 }
395 val = dictGetVal(de);
396 strenc = strEncoding(val->encoding);
397
398 char extra[138] = {0};
399 if (val->encoding == OBJ_ENCODING_QUICKLIST) {
400 char *nextra = extra;
401 int remaining = sizeof(extra);
402 quicklist *ql = val->ptr;
403 /* Add number of quicklist nodes */
404 int used = snprintf(nextra, remaining, " ql_nodes:%lu", ql->len);
405 nextra += used;
406 remaining -= used;
407 /* Add average quicklist fill factor */
408 double avg = (double)ql->count/ql->len;
409 used = snprintf(nextra, remaining, " ql_avg_node:%.2f", avg);
410 nextra += used;
411 remaining -= used;
412 /* Add quicklist fill level / max ziplist size */
413 used = snprintf(nextra, remaining, " ql_ziplist_max:%d", ql->fill);
414 nextra += used;
415 remaining -= used;
416 /* Add isCompressed? */
417 int compressed = ql->compress != 0;
418 used = snprintf(nextra, remaining, " ql_compressed:%d", compressed);
419 nextra += used;
420 remaining -= used;
421 /* Add total uncompressed size */
422 unsigned long sz = 0;
423 for (quicklistNode *node = ql->head; node; node = node->next) {
424 sz += node->sz;
425 }
426 used = snprintf(nextra, remaining, " ql_uncompressed_size:%lu", sz);
427 nextra += used;
428 remaining -= used;
429 }
430
431 addReplyStatusFormat(c,
432 "Value at:%p refcount:%d "
433 "encoding:%s serializedlength:%zu "
434 "lru:%d lru_seconds_idle:%llu%s",
435 (void*)val, val->refcount,
436 strenc, rdbSavedObjectLen(val),
437 val->lru, estimateObjectIdleTime(val)/1000, extra);
438 } else if (!strcasecmp(c->argv[1]->ptr,"sdslen") && c->argc == 3) {
439 dictEntry *de;
440 robj *val;
441 sds key;
442
443 if ((de = dictFind(c->db->dict,c->argv[2]->ptr)) == NULL) {
444 addReply(c,shared.nokeyerr);
445 return;
446 }
447 val = dictGetVal(de);
448 key = dictGetKey(de);
449
450 if (val->type != OBJ_STRING || !sdsEncodedObject(val)) {
451 addReplyError(c,"Not an sds encoded string.");
452 } else {
453 addReplyStatusFormat(c,
454 "key_sds_len:%lld, key_sds_avail:%lld, key_zmalloc: %lld, "
455 "val_sds_len:%lld, val_sds_avail:%lld, val_zmalloc: %lld",
456 (long long) sdslen(key),
457 (long long) sdsavail(key),
458 (long long) sdsZmallocSize(key),
459 (long long) sdslen(val->ptr),
460 (long long) sdsavail(val->ptr),
461 (long long) getStringObjectSdsUsedMemory(val));
462 }
463 } else if (!strcasecmp(c->argv[1]->ptr,"ziplist") && c->argc == 3) {
464 robj *o;
465
466 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nokeyerr))
467 == NULL) return;
468
469 if (o->encoding != OBJ_ENCODING_ZIPLIST) {
470 addReplyError(c,"Not an sds encoded string.");
471 } else {
472 ziplistRepr(o->ptr);
473 addReplyStatus(c,"Ziplist structure printed on stdout");
474 }
475 } else if (!strcasecmp(c->argv[1]->ptr,"populate") &&
476 c->argc >= 3 && c->argc <= 5) {
477 long keys, j;
478 robj *key, *val;
479 char buf[128];
480
481 if (getLongFromObjectOrReply(c, c->argv[2], &keys, NULL) != C_OK)
482 return;
483 dictExpand(c->db->dict,keys);
484 for (j = 0; j < keys; j++) {
485 long valsize = 0;
486 snprintf(buf,sizeof(buf),"%s:%lu",
487 (c->argc == 3) ? "key" : (char*)c->argv[3]->ptr, j);
488 key = createStringObject(buf,strlen(buf));
489 if (c->argc == 5)
490 if (getLongFromObjectOrReply(c, c->argv[4], &valsize, NULL) != C_OK)
491 return;
492 if (lookupKeyWrite(c->db,key) != NULL) {
493 decrRefCount(key);
494 continue;
495 }
496 snprintf(buf,sizeof(buf),"value:%lu",j);
497 if (valsize==0)
498 val = createStringObject(buf,strlen(buf));
499 else {
500 int buflen = strlen(buf);
501 val = createStringObject(NULL,valsize);
502 memcpy(val->ptr, buf, valsize<=buflen? valsize: buflen);
503 }
504 dbAdd(c->db,key,val);
505 signalModifiedKey(c->db,key);
506 decrRefCount(key);
507 }
508 addReply(c,shared.ok);
509 } else if (!strcasecmp(c->argv[1]->ptr,"digest") && c->argc == 2) {
510 /* DEBUG DIGEST (form without keys specified) */
511 unsigned char digest[20];
512 sds d = sdsempty();
513
514 computeDatasetDigest(digest);
515 for (int i = 0; i < 20; i++) d = sdscatprintf(d, "%02x",digest[i]);
516 addReplyStatus(c,d);
517 sdsfree(d);
518 } else if (!strcasecmp(c->argv[1]->ptr,"digest-value") && c->argc >= 2) {
519 /* DEBUG DIGEST-VALUE key key key ... key. */
520 addReplyMultiBulkLen(c,c->argc-2);
521 for (int j = 2; j < c->argc; j++) {
522 unsigned char digest[20];
523 memset(digest,0,20); /* Start with a clean result */
524 robj *o = lookupKeyReadWithFlags(c->db,c->argv[j],LOOKUP_NOTOUCH);
525 if (o) xorObjectDigest(c->db,c->argv[j],digest,o);
526
527 sds d = sdsempty();
528 for (int i = 0; i < 20; i++) d = sdscatprintf(d, "%02x",digest[i]);
529 addReplyStatus(c,d);
530 sdsfree(d);
531 }
532 } else if (!strcasecmp(c->argv[1]->ptr,"sleep") && c->argc == 3) {
533 double dtime = strtod(c->argv[2]->ptr,NULL);
534 long long utime = dtime*1000000;
535 struct timespec tv;
536
537 tv.tv_sec = utime / 1000000;
538 tv.tv_nsec = (utime % 1000000) * 1000;
539 nanosleep(&tv, NULL);
540 addReply(c,shared.ok);
541 } else if (!strcasecmp(c->argv[1]->ptr,"set-active-expire") &&
542 c->argc == 3)
543 {
544 server.active_expire_enabled = atoi(c->argv[2]->ptr);
545 addReply(c,shared.ok);
546 } else if (!strcasecmp(c->argv[1]->ptr,"lua-always-replicate-commands") &&
547 c->argc == 3)
548 {
549 server.lua_always_replicate_commands = atoi(c->argv[2]->ptr);
550 addReply(c,shared.ok);
551 } else if (!strcasecmp(c->argv[1]->ptr,"error") && c->argc == 3) {
552 sds errstr = sdsnewlen("-",1);
553
554 errstr = sdscatsds(errstr,c->argv[2]->ptr);
555 errstr = sdsmapchars(errstr,"\n\r"," ",2); /* no newlines in errors. */
556 errstr = sdscatlen(errstr,"\r\n",2);
557 addReplySds(c,errstr);
558 } else if (!strcasecmp(c->argv[1]->ptr,"structsize") && c->argc == 2) {
559 sds sizes = sdsempty();
560 sizes = sdscatprintf(sizes,"bits:%d ",(sizeof(void*) == 8)?64:32);
561 sizes = sdscatprintf(sizes,"robj:%d ",(int)sizeof(robj));
562 sizes = sdscatprintf(sizes,"dictentry:%d ",(int)sizeof(dictEntry));
563 sizes = sdscatprintf(sizes,"sdshdr5:%d ",(int)sizeof(struct sdshdr5));
564 sizes = sdscatprintf(sizes,"sdshdr8:%d ",(int)sizeof(struct sdshdr8));
565 sizes = sdscatprintf(sizes,"sdshdr16:%d ",(int)sizeof(struct sdshdr16));
566 sizes = sdscatprintf(sizes,"sdshdr32:%d ",(int)sizeof(struct sdshdr32));
567 sizes = sdscatprintf(sizes,"sdshdr64:%d ",(int)sizeof(struct sdshdr64));
568 addReplyBulkSds(c,sizes);
569 } else if (!strcasecmp(c->argv[1]->ptr,"htstats") && c->argc == 3) {
570 long dbid;
571 sds stats = sdsempty();
572 char buf[4096];
573
574 if (getLongFromObjectOrReply(c, c->argv[2], &dbid, NULL) != C_OK)
575 return;
576 if (dbid < 0 || dbid >= server.dbnum) {
577 addReplyError(c,"Out of range database");
578 return;
579 }
580
581 stats = sdscatprintf(stats,"[Dictionary HT]\n");
582 dictGetStats(buf,sizeof(buf),server.db[dbid].dict);
583 stats = sdscat(stats,buf);
584
585 stats = sdscatprintf(stats,"[Expires HT]\n");
586 dictGetStats(buf,sizeof(buf),server.db[dbid].expires);
587 stats = sdscat(stats,buf);
588
589 addReplyBulkSds(c,stats);
590 } else if (!strcasecmp(c->argv[1]->ptr,"htstats-key") && c->argc == 3) {
591 robj *o;
592 dict *ht = NULL;
593
594 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nokeyerr))
595 == NULL) return;
596
597 /* Get the hash table reference from the object, if possible. */
598 switch (o->encoding) {
599 case OBJ_ENCODING_SKIPLIST:
600 {
601 zset *zs = o->ptr;
602 ht = zs->dict;
603 }
604 break;
605 case OBJ_ENCODING_HT:
606 ht = o->ptr;
607 break;
608 }
609
610 if (ht == NULL) {
611 addReplyError(c,"The value stored at the specified key is not "
612 "represented using an hash table");
613 } else {
614 char buf[4096];
615 dictGetStats(buf,sizeof(buf),ht);
616 addReplyBulkCString(c,buf);
617 }
618 } else if (!strcasecmp(c->argv[1]->ptr,"change-repl-id") && c->argc == 2) {
619 serverLog(LL_WARNING,"Changing replication IDs after receiving DEBUG change-repl-id");
620 changeReplicationId();
621 clearReplicationId2();
622 addReply(c,shared.ok);
623 } else if (!strcasecmp(c->argv[1]->ptr,"stringmatch-test") && c->argc == 2)
624 {
625 stringmatchlen_fuzz_test();
626 addReplyStatus(c,"Apparently Redis did not crash: test passed");
627 } else {
628 addReplySubcommandSyntaxError(c);
629 return;
630 }
631 }
632
633 /* =========================== Crash handling ============================== */
634
_serverAssert(const char * estr,const char * file,int line)635 void _serverAssert(const char *estr, const char *file, int line) {
636 bugReportStart();
637 serverLog(LL_WARNING,"=== ASSERTION FAILED ===");
638 serverLog(LL_WARNING,"==> %s:%d '%s' is not true",file,line,estr);
639 #ifdef HAVE_BACKTRACE
640 server.assert_failed = estr;
641 server.assert_file = file;
642 server.assert_line = line;
643 serverLog(LL_WARNING,"(forcing SIGSEGV to print the bug report.)");
644 #endif
645 *((char*)-1) = 'x';
646 }
647
_serverAssertPrintClientInfo(const client * c)648 void _serverAssertPrintClientInfo(const client *c) {
649 int j;
650
651 bugReportStart();
652 serverLog(LL_WARNING,"=== ASSERTION FAILED CLIENT CONTEXT ===");
653 serverLog(LL_WARNING,"client->flags = %d", c->flags);
654 serverLog(LL_WARNING,"client->fd = %d", c->fd);
655 serverLog(LL_WARNING,"client->argc = %d", c->argc);
656 for (j=0; j < c->argc; j++) {
657 char buf[128];
658 char *arg;
659
660 if (c->argv[j]->type == OBJ_STRING && sdsEncodedObject(c->argv[j])) {
661 arg = (char*) c->argv[j]->ptr;
662 } else {
663 snprintf(buf,sizeof(buf),"Object type: %u, encoding: %u",
664 c->argv[j]->type, c->argv[j]->encoding);
665 arg = buf;
666 }
667 serverLog(LL_WARNING,"client->argv[%d] = \"%s\" (refcount: %d)",
668 j, arg, c->argv[j]->refcount);
669 }
670 }
671
serverLogObjectDebugInfo(const robj * o)672 void serverLogObjectDebugInfo(const robj *o) {
673 serverLog(LL_WARNING,"Object type: %d", o->type);
674 serverLog(LL_WARNING,"Object encoding: %d", o->encoding);
675 serverLog(LL_WARNING,"Object refcount: %d", o->refcount);
676 if (o->type == OBJ_STRING && sdsEncodedObject(o)) {
677 serverLog(LL_WARNING,"Object raw string len: %zu", sdslen(o->ptr));
678 if (sdslen(o->ptr) < 4096) {
679 sds repr = sdscatrepr(sdsempty(),o->ptr,sdslen(o->ptr));
680 serverLog(LL_WARNING,"Object raw string content: %s", repr);
681 sdsfree(repr);
682 }
683 } else if (o->type == OBJ_LIST) {
684 serverLog(LL_WARNING,"List length: %d", (int) listTypeLength(o));
685 } else if (o->type == OBJ_SET) {
686 serverLog(LL_WARNING,"Set size: %d", (int) setTypeSize(o));
687 } else if (o->type == OBJ_HASH) {
688 serverLog(LL_WARNING,"Hash size: %d", (int) hashTypeLength(o));
689 } else if (o->type == OBJ_ZSET) {
690 serverLog(LL_WARNING,"Sorted set size: %d", (int) zsetLength(o));
691 if (o->encoding == OBJ_ENCODING_SKIPLIST)
692 serverLog(LL_WARNING,"Skiplist level: %d", (int) ((const zset*)o->ptr)->zsl->level);
693 }
694 }
695
_serverAssertPrintObject(const robj * o)696 void _serverAssertPrintObject(const robj *o) {
697 bugReportStart();
698 serverLog(LL_WARNING,"=== ASSERTION FAILED OBJECT CONTEXT ===");
699 serverLogObjectDebugInfo(o);
700 }
701
_serverAssertWithInfo(const client * c,const robj * o,const char * estr,const char * file,int line)702 void _serverAssertWithInfo(const client *c, const robj *o, const char *estr, const char *file, int line) {
703 if (c) _serverAssertPrintClientInfo(c);
704 if (o) _serverAssertPrintObject(o);
705 _serverAssert(estr,file,line);
706 }
707
_serverPanic(const char * file,int line,const char * msg,...)708 void _serverPanic(const char *file, int line, const char *msg, ...) {
709 va_list ap;
710 va_start(ap,msg);
711 char fmtmsg[256];
712 vsnprintf(fmtmsg,sizeof(fmtmsg),msg,ap);
713 va_end(ap);
714
715 bugReportStart();
716 serverLog(LL_WARNING,"------------------------------------------------");
717 serverLog(LL_WARNING,"!!! Software Failure. Press left mouse button to continue");
718 serverLog(LL_WARNING,"Guru Meditation: %s #%s:%d",fmtmsg,file,line);
719 #ifdef HAVE_BACKTRACE
720 serverLog(LL_WARNING,"(forcing SIGSEGV in order to print the stack trace)");
721 #endif
722 serverLog(LL_WARNING,"------------------------------------------------");
723 *((char*)-1) = 'x';
724 }
725
bugReportStart(void)726 void bugReportStart(void) {
727 if (server.bug_report_start == 0) {
728 serverLogRaw(LL_WARNING|LL_RAW,
729 "\n\n=== REDIS BUG REPORT START: Cut & paste starting from here ===\n");
730 server.bug_report_start = 1;
731 }
732 }
733
734 #ifdef HAVE_BACKTRACE
getMcontextEip(ucontext_t * uc)735 static void *getMcontextEip(ucontext_t *uc) {
736 #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
737 /* OSX < 10.6 */
738 #if defined(__x86_64__)
739 return (void*) uc->uc_mcontext->__ss.__rip;
740 #elif defined(__i386__)
741 return (void*) uc->uc_mcontext->__ss.__eip;
742 #else
743 return (void*) uc->uc_mcontext->__ss.__srr0;
744 #endif
745 #elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
746 /* OSX >= 10.6 */
747 #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
748 return (void*) uc->uc_mcontext->__ss.__rip;
749 #else
750 return (void*) uc->uc_mcontext->__ss.__eip;
751 #endif
752 #elif defined(__linux__)
753 /* Linux */
754 #if defined(__i386__)
755 return (void*) uc->uc_mcontext.gregs[14]; /* Linux 32 */
756 #elif defined(__X86_64__) || defined(__x86_64__)
757 return (void*) uc->uc_mcontext.gregs[16]; /* Linux 64 */
758 #elif defined(__ia64__) /* Linux IA64 */
759 return (void*) uc->uc_mcontext.sc_ip;
760 #elif defined(__arm__) /* Linux ARM */
761 return (void*) uc->uc_mcontext.arm_pc;
762 #elif defined(__aarch64__) /* Linux AArch64 */
763 return (void*) uc->uc_mcontext.pc;
764 #endif
765 #elif defined(__FreeBSD__)
766 /* FreeBSD */
767 #if defined(__i386__)
768 return (void*) uc->uc_mcontext.mc_eip;
769 #elif defined(__x86_64__)
770 return (void*) uc->uc_mcontext.mc_rip;
771 #endif
772 #elif defined(__OpenBSD__)
773 /* OpenBSD */
774 #if defined(__i386__)
775 return (void*) uc->sc_eip;
776 #elif defined(__x86_64__)
777 return (void*) uc->sc_rip;
778 #endif
779 #elif defined(__DragonFly__)
780 return (void*) uc->uc_mcontext.mc_rip;
781 #else
782 return NULL;
783 #endif
784 }
785
logStackContent(void ** sp)786 void logStackContent(void **sp) {
787 int i;
788 for (i = 15; i >= 0; i--) {
789 unsigned long addr = (unsigned long) sp+i;
790 unsigned long val = (unsigned long) sp[i];
791
792 if (sizeof(long) == 4)
793 serverLog(LL_WARNING, "(%08lx) -> %08lx", addr, val);
794 else
795 serverLog(LL_WARNING, "(%016lx) -> %016lx", addr, val);
796 }
797 }
798
logRegisters(ucontext_t * uc)799 void logRegisters(ucontext_t *uc) {
800 serverLog(LL_WARNING|LL_RAW, "\n------ REGISTERS ------\n");
801
802 /* OSX */
803 #if defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
804 /* OSX AMD64 */
805 #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
806 serverLog(LL_WARNING,
807 "\n"
808 "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n"
809 "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n"
810 "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n"
811 "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n"
812 "RIP:%016lx EFL:%016lx\nCS :%016lx FS:%016lx GS:%016lx",
813 (unsigned long) uc->uc_mcontext->__ss.__rax,
814 (unsigned long) uc->uc_mcontext->__ss.__rbx,
815 (unsigned long) uc->uc_mcontext->__ss.__rcx,
816 (unsigned long) uc->uc_mcontext->__ss.__rdx,
817 (unsigned long) uc->uc_mcontext->__ss.__rdi,
818 (unsigned long) uc->uc_mcontext->__ss.__rsi,
819 (unsigned long) uc->uc_mcontext->__ss.__rbp,
820 (unsigned long) uc->uc_mcontext->__ss.__rsp,
821 (unsigned long) uc->uc_mcontext->__ss.__r8,
822 (unsigned long) uc->uc_mcontext->__ss.__r9,
823 (unsigned long) uc->uc_mcontext->__ss.__r10,
824 (unsigned long) uc->uc_mcontext->__ss.__r11,
825 (unsigned long) uc->uc_mcontext->__ss.__r12,
826 (unsigned long) uc->uc_mcontext->__ss.__r13,
827 (unsigned long) uc->uc_mcontext->__ss.__r14,
828 (unsigned long) uc->uc_mcontext->__ss.__r15,
829 (unsigned long) uc->uc_mcontext->__ss.__rip,
830 (unsigned long) uc->uc_mcontext->__ss.__rflags,
831 (unsigned long) uc->uc_mcontext->__ss.__cs,
832 (unsigned long) uc->uc_mcontext->__ss.__fs,
833 (unsigned long) uc->uc_mcontext->__ss.__gs
834 );
835 logStackContent((void**)uc->uc_mcontext->__ss.__rsp);
836 #else
837 /* OSX x86 */
838 serverLog(LL_WARNING,
839 "\n"
840 "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n"
841 "EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n"
842 "SS:%08lx EFL:%08lx EIP:%08lx CS :%08lx\n"
843 "DS:%08lx ES:%08lx FS :%08lx GS :%08lx",
844 (unsigned long) uc->uc_mcontext->__ss.__eax,
845 (unsigned long) uc->uc_mcontext->__ss.__ebx,
846 (unsigned long) uc->uc_mcontext->__ss.__ecx,
847 (unsigned long) uc->uc_mcontext->__ss.__edx,
848 (unsigned long) uc->uc_mcontext->__ss.__edi,
849 (unsigned long) uc->uc_mcontext->__ss.__esi,
850 (unsigned long) uc->uc_mcontext->__ss.__ebp,
851 (unsigned long) uc->uc_mcontext->__ss.__esp,
852 (unsigned long) uc->uc_mcontext->__ss.__ss,
853 (unsigned long) uc->uc_mcontext->__ss.__eflags,
854 (unsigned long) uc->uc_mcontext->__ss.__eip,
855 (unsigned long) uc->uc_mcontext->__ss.__cs,
856 (unsigned long) uc->uc_mcontext->__ss.__ds,
857 (unsigned long) uc->uc_mcontext->__ss.__es,
858 (unsigned long) uc->uc_mcontext->__ss.__fs,
859 (unsigned long) uc->uc_mcontext->__ss.__gs
860 );
861 logStackContent((void**)uc->uc_mcontext->__ss.__esp);
862 #endif
863 /* Linux */
864 #elif defined(__linux__)
865 /* Linux x86 */
866 #if defined(__i386__)
867 serverLog(LL_WARNING,
868 "\n"
869 "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n"
870 "EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n"
871 "SS :%08lx EFL:%08lx EIP:%08lx CS:%08lx\n"
872 "DS :%08lx ES :%08lx FS :%08lx GS:%08lx",
873 (unsigned long) uc->uc_mcontext.gregs[11],
874 (unsigned long) uc->uc_mcontext.gregs[8],
875 (unsigned long) uc->uc_mcontext.gregs[10],
876 (unsigned long) uc->uc_mcontext.gregs[9],
877 (unsigned long) uc->uc_mcontext.gregs[4],
878 (unsigned long) uc->uc_mcontext.gregs[5],
879 (unsigned long) uc->uc_mcontext.gregs[6],
880 (unsigned long) uc->uc_mcontext.gregs[7],
881 (unsigned long) uc->uc_mcontext.gregs[18],
882 (unsigned long) uc->uc_mcontext.gregs[17],
883 (unsigned long) uc->uc_mcontext.gregs[14],
884 (unsigned long) uc->uc_mcontext.gregs[15],
885 (unsigned long) uc->uc_mcontext.gregs[3],
886 (unsigned long) uc->uc_mcontext.gregs[2],
887 (unsigned long) uc->uc_mcontext.gregs[1],
888 (unsigned long) uc->uc_mcontext.gregs[0]
889 );
890 logStackContent((void**)uc->uc_mcontext.gregs[7]);
891 #elif defined(__X86_64__) || defined(__x86_64__)
892 /* Linux AMD64 */
893 serverLog(LL_WARNING,
894 "\n"
895 "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n"
896 "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n"
897 "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n"
898 "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n"
899 "RIP:%016lx EFL:%016lx\nCSGSFS:%016lx",
900 (unsigned long) uc->uc_mcontext.gregs[13],
901 (unsigned long) uc->uc_mcontext.gregs[11],
902 (unsigned long) uc->uc_mcontext.gregs[14],
903 (unsigned long) uc->uc_mcontext.gregs[12],
904 (unsigned long) uc->uc_mcontext.gregs[8],
905 (unsigned long) uc->uc_mcontext.gregs[9],
906 (unsigned long) uc->uc_mcontext.gregs[10],
907 (unsigned long) uc->uc_mcontext.gregs[15],
908 (unsigned long) uc->uc_mcontext.gregs[0],
909 (unsigned long) uc->uc_mcontext.gregs[1],
910 (unsigned long) uc->uc_mcontext.gregs[2],
911 (unsigned long) uc->uc_mcontext.gregs[3],
912 (unsigned long) uc->uc_mcontext.gregs[4],
913 (unsigned long) uc->uc_mcontext.gregs[5],
914 (unsigned long) uc->uc_mcontext.gregs[6],
915 (unsigned long) uc->uc_mcontext.gregs[7],
916 (unsigned long) uc->uc_mcontext.gregs[16],
917 (unsigned long) uc->uc_mcontext.gregs[17],
918 (unsigned long) uc->uc_mcontext.gregs[18]
919 );
920 logStackContent((void**)uc->uc_mcontext.gregs[15]);
921 #endif
922 #elif defined(__FreeBSD__)
923 #if defined(__x86_64__)
924 serverLog(LL_WARNING,
925 "\n"
926 "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n"
927 "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n"
928 "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n"
929 "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n"
930 "RIP:%016lx EFL:%016lx\nCSGSFS:%016lx",
931 (unsigned long) uc->uc_mcontext.mc_rax,
932 (unsigned long) uc->uc_mcontext.mc_rbx,
933 (unsigned long) uc->uc_mcontext.mc_rcx,
934 (unsigned long) uc->uc_mcontext.mc_rdx,
935 (unsigned long) uc->uc_mcontext.mc_rdi,
936 (unsigned long) uc->uc_mcontext.mc_rsi,
937 (unsigned long) uc->uc_mcontext.mc_rbp,
938 (unsigned long) uc->uc_mcontext.mc_rsp,
939 (unsigned long) uc->uc_mcontext.mc_r8,
940 (unsigned long) uc->uc_mcontext.mc_r9,
941 (unsigned long) uc->uc_mcontext.mc_r10,
942 (unsigned long) uc->uc_mcontext.mc_r11,
943 (unsigned long) uc->uc_mcontext.mc_r12,
944 (unsigned long) uc->uc_mcontext.mc_r13,
945 (unsigned long) uc->uc_mcontext.mc_r14,
946 (unsigned long) uc->uc_mcontext.mc_r15,
947 (unsigned long) uc->uc_mcontext.mc_rip,
948 (unsigned long) uc->uc_mcontext.mc_rflags,
949 (unsigned long) uc->uc_mcontext.mc_cs
950 );
951 logStackContent((void**)uc->uc_mcontext.mc_rsp);
952 #elif defined(__i386__)
953 serverLog(LL_WARNING,
954 "\n"
955 "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n"
956 "EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n"
957 "SS :%08lx EFL:%08lx EIP:%08lx CS:%08lx\n"
958 "DS :%08lx ES :%08lx FS :%08lx GS:%08lx",
959 (unsigned long) uc->uc_mcontext.mc_eax,
960 (unsigned long) uc->uc_mcontext.mc_ebx,
961 (unsigned long) uc->uc_mcontext.mc_ebx,
962 (unsigned long) uc->uc_mcontext.mc_edx,
963 (unsigned long) uc->uc_mcontext.mc_edi,
964 (unsigned long) uc->uc_mcontext.mc_esi,
965 (unsigned long) uc->uc_mcontext.mc_ebp,
966 (unsigned long) uc->uc_mcontext.mc_esp,
967 (unsigned long) uc->uc_mcontext.mc_ss,
968 (unsigned long) uc->uc_mcontext.mc_eflags,
969 (unsigned long) uc->uc_mcontext.mc_eip,
970 (unsigned long) uc->uc_mcontext.mc_cs,
971 (unsigned long) uc->uc_mcontext.mc_es,
972 (unsigned long) uc->uc_mcontext.mc_fs,
973 (unsigned long) uc->uc_mcontext.mc_gs
974 );
975 logStackContent((void**)uc->uc_mcontext.mc_esp);
976 #endif
977 #elif defined(__OpenBSD__)
978 #if defined(__x86_64__)
979 serverLog(LL_WARNING,
980 "\n"
981 "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n"
982 "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n"
983 "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n"
984 "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n"
985 "RIP:%016lx EFL:%016lx\nCSGSFS:%016lx",
986 (unsigned long) uc->sc_rax,
987 (unsigned long) uc->sc_rbx,
988 (unsigned long) uc->sc_rcx,
989 (unsigned long) uc->sc_rdx,
990 (unsigned long) uc->sc_rdi,
991 (unsigned long) uc->sc_rsi,
992 (unsigned long) uc->sc_rbp,
993 (unsigned long) uc->sc_rsp,
994 (unsigned long) uc->sc_r8,
995 (unsigned long) uc->sc_r9,
996 (unsigned long) uc->sc_r10,
997 (unsigned long) uc->sc_r11,
998 (unsigned long) uc->sc_r12,
999 (unsigned long) uc->sc_r13,
1000 (unsigned long) uc->sc_r14,
1001 (unsigned long) uc->sc_r15,
1002 (unsigned long) uc->sc_rip,
1003 (unsigned long) uc->sc_rflags,
1004 (unsigned long) uc->sc_cs
1005 );
1006 logStackContent((void**)uc->sc_rsp);
1007 #elif defined(__i386__)
1008 serverLog(LL_WARNING,
1009 "\n"
1010 "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n"
1011 "EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n"
1012 "SS :%08lx EFL:%08lx EIP:%08lx CS:%08lx\n"
1013 "DS :%08lx ES :%08lx FS :%08lx GS:%08lx",
1014 (unsigned long) uc->sc_eax,
1015 (unsigned long) uc->sc_ebx,
1016 (unsigned long) uc->sc_ebx,
1017 (unsigned long) uc->sc_edx,
1018 (unsigned long) uc->sc_edi,
1019 (unsigned long) uc->sc_esi,
1020 (unsigned long) uc->sc_ebp,
1021 (unsigned long) uc->sc_esp,
1022 (unsigned long) uc->sc_ss,
1023 (unsigned long) uc->sc_eflags,
1024 (unsigned long) uc->sc_eip,
1025 (unsigned long) uc->sc_cs,
1026 (unsigned long) uc->sc_es,
1027 (unsigned long) uc->sc_fs,
1028 (unsigned long) uc->sc_gs
1029 );
1030 logStackContent((void**)uc->sc_esp);
1031 #endif
1032 #elif defined(__DragonFly__)
1033 serverLog(LL_WARNING,
1034 "\n"
1035 "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n"
1036 "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n"
1037 "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n"
1038 "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n"
1039 "RIP:%016lx EFL:%016lx\nCSGSFS:%016lx",
1040 (unsigned long) uc->uc_mcontext.mc_rax,
1041 (unsigned long) uc->uc_mcontext.mc_rbx,
1042 (unsigned long) uc->uc_mcontext.mc_rcx,
1043 (unsigned long) uc->uc_mcontext.mc_rdx,
1044 (unsigned long) uc->uc_mcontext.mc_rdi,
1045 (unsigned long) uc->uc_mcontext.mc_rsi,
1046 (unsigned long) uc->uc_mcontext.mc_rbp,
1047 (unsigned long) uc->uc_mcontext.mc_rsp,
1048 (unsigned long) uc->uc_mcontext.mc_r8,
1049 (unsigned long) uc->uc_mcontext.mc_r9,
1050 (unsigned long) uc->uc_mcontext.mc_r10,
1051 (unsigned long) uc->uc_mcontext.mc_r11,
1052 (unsigned long) uc->uc_mcontext.mc_r12,
1053 (unsigned long) uc->uc_mcontext.mc_r13,
1054 (unsigned long) uc->uc_mcontext.mc_r14,
1055 (unsigned long) uc->uc_mcontext.mc_r15,
1056 (unsigned long) uc->uc_mcontext.mc_rip,
1057 (unsigned long) uc->uc_mcontext.mc_rflags,
1058 (unsigned long) uc->uc_mcontext.mc_cs
1059 );
1060 logStackContent((void**)uc->uc_mcontext.mc_rsp);
1061 #else
1062 serverLog(LL_WARNING,
1063 " Dumping of registers not supported for this OS/arch");
1064 #endif
1065 }
1066
1067 /* Return a file descriptor to write directly to the Redis log with the
1068 * write(2) syscall, that can be used in critical sections of the code
1069 * where the rest of Redis can't be trusted (for example during the memory
1070 * test) or when an API call requires a raw fd.
1071 *
1072 * Close it with closeDirectLogFiledes(). */
openDirectLogFiledes(void)1073 int openDirectLogFiledes(void) {
1074 int log_to_stdout = server.logfile[0] == '\0';
1075 int fd = log_to_stdout ?
1076 STDOUT_FILENO :
1077 open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644);
1078 return fd;
1079 }
1080
1081 /* Used to close what closeDirectLogFiledes() returns. */
closeDirectLogFiledes(int fd)1082 void closeDirectLogFiledes(int fd) {
1083 int log_to_stdout = server.logfile[0] == '\0';
1084 if (!log_to_stdout) close(fd);
1085 }
1086
1087 /* Logs the stack trace using the backtrace() call. This function is designed
1088 * to be called from signal handlers safely. */
logStackTrace(ucontext_t * uc)1089 void logStackTrace(ucontext_t *uc) {
1090 void *trace[101];
1091 int trace_size = 0, fd = openDirectLogFiledes();
1092
1093 if (fd == -1) return; /* If we can't log there is anything to do. */
1094
1095 /* Generate the stack trace */
1096 trace_size = backtrace(trace+1, 100);
1097
1098 if (getMcontextEip(uc) != NULL) {
1099 char *msg1 = "EIP:\n";
1100 char *msg2 = "\nBacktrace:\n";
1101 if (write(fd,msg1,strlen(msg1)) == -1) {/* Avoid warning. */};
1102 trace[0] = getMcontextEip(uc);
1103 backtrace_symbols_fd(trace, 1, fd);
1104 if (write(fd,msg2,strlen(msg2)) == -1) {/* Avoid warning. */};
1105 }
1106
1107 /* Write symbols to log file */
1108 backtrace_symbols_fd(trace+1, trace_size, fd);
1109
1110 /* Cleanup */
1111 closeDirectLogFiledes(fd);
1112 }
1113
1114 /* Log information about the "current" client, that is, the client that is
1115 * currently being served by Redis. May be NULL if Redis is not serving a
1116 * client right now. */
logCurrentClient(void)1117 void logCurrentClient(void) {
1118 if (server.current_client == NULL) return;
1119
1120 client *cc = server.current_client;
1121 sds client;
1122 int j;
1123
1124 serverLogRaw(LL_WARNING|LL_RAW, "\n------ CURRENT CLIENT INFO ------\n");
1125 client = catClientInfoString(sdsempty(),cc);
1126 serverLog(LL_WARNING|LL_RAW,"%s\n", client);
1127 sdsfree(client);
1128 for (j = 0; j < cc->argc; j++) {
1129 robj *decoded;
1130
1131 decoded = getDecodedObject(cc->argv[j]);
1132 serverLog(LL_WARNING|LL_RAW,"argv[%d]: '%s'\n", j,
1133 (char*)decoded->ptr);
1134 decrRefCount(decoded);
1135 }
1136 /* Check if the first argument, usually a key, is found inside the
1137 * selected DB, and if so print info about the associated object. */
1138 if (cc->argc >= 1) {
1139 robj *val, *key;
1140 dictEntry *de;
1141
1142 key = getDecodedObject(cc->argv[1]);
1143 de = dictFind(cc->db->dict, key->ptr);
1144 if (de) {
1145 val = dictGetVal(de);
1146 serverLog(LL_WARNING,"key '%s' found in DB containing the following object:", (char*)key->ptr);
1147 serverLogObjectDebugInfo(val);
1148 }
1149 decrRefCount(key);
1150 }
1151 }
1152
1153 #if defined(HAVE_PROC_MAPS)
1154
1155 #define MEMTEST_MAX_REGIONS 128
1156
1157 /* A non destructive memory test executed during segfauls. */
memtest_test_linux_anonymous_maps(void)1158 int memtest_test_linux_anonymous_maps(void) {
1159 FILE *fp;
1160 char line[1024];
1161 char logbuf[1024];
1162 size_t start_addr, end_addr, size;
1163 size_t start_vect[MEMTEST_MAX_REGIONS];
1164 size_t size_vect[MEMTEST_MAX_REGIONS];
1165 int regions = 0, j;
1166
1167 int fd = openDirectLogFiledes();
1168 if (!fd) return 0;
1169
1170 fp = fopen("/proc/self/maps","r");
1171 if (!fp) return 0;
1172 while(fgets(line,sizeof(line),fp) != NULL) {
1173 char *start, *end, *p = line;
1174
1175 start = p;
1176 p = strchr(p,'-');
1177 if (!p) continue;
1178 *p++ = '\0';
1179 end = p;
1180 p = strchr(p,' ');
1181 if (!p) continue;
1182 *p++ = '\0';
1183 if (strstr(p,"stack") ||
1184 strstr(p,"vdso") ||
1185 strstr(p,"vsyscall")) continue;
1186 if (!strstr(p,"00:00")) continue;
1187 if (!strstr(p,"rw")) continue;
1188
1189 start_addr = strtoul(start,NULL,16);
1190 end_addr = strtoul(end,NULL,16);
1191 size = end_addr-start_addr;
1192
1193 start_vect[regions] = start_addr;
1194 size_vect[regions] = size;
1195 snprintf(logbuf,sizeof(logbuf),
1196 "*** Preparing to test memory region %lx (%lu bytes)\n",
1197 (unsigned long) start_vect[regions],
1198 (unsigned long) size_vect[regions]);
1199 if (write(fd,logbuf,strlen(logbuf)) == -1) { /* Nothing to do. */ }
1200 regions++;
1201 }
1202
1203 int errors = 0;
1204 for (j = 0; j < regions; j++) {
1205 if (write(fd,".",1) == -1) { /* Nothing to do. */ }
1206 errors += memtest_preserving_test((void*)start_vect[j],size_vect[j],1);
1207 if (write(fd, errors ? "E" : "O",1) == -1) { /* Nothing to do. */ }
1208 }
1209 if (write(fd,"\n",1) == -1) { /* Nothing to do. */ }
1210
1211 /* NOTE: It is very important to close the file descriptor only now
1212 * because closing it before may result into unmapping of some memory
1213 * region that we are testing. */
1214 fclose(fp);
1215 closeDirectLogFiledes(fd);
1216 return errors;
1217 }
1218 #endif
1219
1220 /* Scans the (assumed) x86 code starting at addr, for a max of `len`
1221 * bytes, searching for E8 (callq) opcodes, and dumping the symbols
1222 * and the call offset if they appear to be valid. */
dumpX86Calls(void * addr,size_t len)1223 void dumpX86Calls(void *addr, size_t len) {
1224 size_t j;
1225 unsigned char *p = addr;
1226 Dl_info info;
1227 /* Hash table to best-effort avoid printing the same symbol
1228 * multiple times. */
1229 unsigned long ht[256] = {0};
1230
1231 if (len < 5) return;
1232 for (j = 0; j < len-4; j++) {
1233 if (p[j] != 0xE8) continue; /* Not an E8 CALL opcode. */
1234 unsigned long target = (unsigned long)addr+j+5;
1235 target += *((int32_t*)(p+j+1));
1236 if (dladdr((void*)target, &info) != 0 && info.dli_sname != NULL) {
1237 if (ht[target&0xff] != target) {
1238 printf("Function at 0x%lx is %s\n",target,info.dli_sname);
1239 ht[target&0xff] = target;
1240 }
1241 j += 4; /* Skip the 32 bit immediate. */
1242 }
1243 }
1244 }
1245
sigsegvHandler(int sig,siginfo_t * info,void * secret)1246 void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
1247 ucontext_t *uc = (ucontext_t*) secret;
1248 void *eip = getMcontextEip(uc);
1249 sds infostring, clients;
1250 struct sigaction act;
1251 UNUSED(info);
1252
1253 bugReportStart();
1254 serverLog(LL_WARNING,
1255 "Redis %s crashed by signal: %d", REDIS_VERSION, sig);
1256 if (eip != NULL) {
1257 serverLog(LL_WARNING,
1258 "Crashed running the instruction at: %p", eip);
1259 }
1260 if (sig == SIGSEGV || sig == SIGBUS) {
1261 serverLog(LL_WARNING,
1262 "Accessing address: %p", (void*)info->si_addr);
1263 }
1264 serverLog(LL_WARNING,
1265 "Failed assertion: %s (%s:%d)", server.assert_failed,
1266 server.assert_file, server.assert_line);
1267
1268 /* Log the stack trace */
1269 serverLogRaw(LL_WARNING|LL_RAW, "\n------ STACK TRACE ------\n");
1270 logStackTrace(uc);
1271
1272 /* Log INFO and CLIENT LIST */
1273 serverLogRaw(LL_WARNING|LL_RAW, "\n------ INFO OUTPUT ------\n");
1274 infostring = genRedisInfoString("all");
1275 serverLogRaw(LL_WARNING|LL_RAW, infostring);
1276 serverLogRaw(LL_WARNING|LL_RAW, "\n------ CLIENT LIST OUTPUT ------\n");
1277 clients = getAllClientsInfoString(-1);
1278 serverLogRaw(LL_WARNING|LL_RAW, clients);
1279 sdsfree(infostring);
1280 sdsfree(clients);
1281
1282 /* Log the current client */
1283 logCurrentClient();
1284
1285 /* Log dump of processor registers */
1286 logRegisters(uc);
1287
1288 #if defined(HAVE_PROC_MAPS)
1289 /* Test memory */
1290 serverLogRaw(LL_WARNING|LL_RAW, "\n------ FAST MEMORY TEST ------\n");
1291 bioKillThreads();
1292 if (memtest_test_linux_anonymous_maps()) {
1293 serverLogRaw(LL_WARNING|LL_RAW,
1294 "!!! MEMORY ERROR DETECTED! Check your memory ASAP !!!\n");
1295 } else {
1296 serverLogRaw(LL_WARNING|LL_RAW,
1297 "Fast memory test PASSED, however your memory can still be broken. Please run a memory test for several hours if possible.\n");
1298 }
1299 #endif
1300
1301 if (eip != NULL) {
1302 Dl_info info;
1303 if (dladdr(eip, &info) != 0) {
1304 serverLog(LL_WARNING|LL_RAW,
1305 "\n------ DUMPING CODE AROUND EIP ------\n"
1306 "Symbol: %s (base: %p)\n"
1307 "Module: %s (base %p)\n"
1308 "$ xxd -r -p /tmp/dump.hex /tmp/dump.bin\n"
1309 "$ objdump --adjust-vma=%p -D -b binary -m i386:x86-64 /tmp/dump.bin\n"
1310 "------\n",
1311 info.dli_sname, info.dli_saddr, info.dli_fname, info.dli_fbase,
1312 info.dli_saddr);
1313 size_t len = (long)eip - (long)info.dli_saddr;
1314 unsigned long sz = sysconf(_SC_PAGESIZE);
1315 if (len < 1<<13) { /* we don't have functions over 8k (verified) */
1316 /* Find the address of the next page, which is our "safety"
1317 * limit when dumping. Then try to dump just 128 bytes more
1318 * than EIP if there is room, or stop sooner. */
1319 unsigned long next = ((unsigned long)eip + sz) & ~(sz-1);
1320 unsigned long end = (unsigned long)eip + 128;
1321 if (end > next) end = next;
1322 len = end - (unsigned long)info.dli_saddr;
1323 serverLogHexDump(LL_WARNING, "dump of function",
1324 info.dli_saddr ,len);
1325 dumpX86Calls(info.dli_saddr,len);
1326 }
1327 }
1328 }
1329
1330 serverLogRaw(LL_WARNING|LL_RAW,
1331 "\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n"
1332 " Please report the crash by opening an issue on github:\n\n"
1333 " http://github.com/antirez/redis/issues\n\n"
1334 " Suspect RAM error? Use redis-server --test-memory to verify it.\n\n"
1335 );
1336
1337 /* free(messages); Don't call free() with possibly corrupted memory. */
1338 if (server.daemonize && server.supervised == 0) unlink(server.pidfile);
1339
1340 /* Make sure we exit with the right signal at the end. So for instance
1341 * the core will be dumped if enabled. */
1342 sigemptyset (&act.sa_mask);
1343 act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND;
1344 act.sa_handler = SIG_DFL;
1345 sigaction (sig, &act, NULL);
1346 kill(getpid(),sig);
1347 }
1348 #endif /* HAVE_BACKTRACE */
1349
1350 /* ==================== Logging functions for debugging ===================== */
1351
serverLogHexDump(int level,char * descr,void * value,size_t len)1352 void serverLogHexDump(int level, char *descr, void *value, size_t len) {
1353 char buf[65], *b;
1354 unsigned char *v = value;
1355 char charset[] = "0123456789abcdef";
1356
1357 serverLog(level,"%s (hexdump of %zu bytes):", descr, len);
1358 b = buf;
1359 while(len) {
1360 b[0] = charset[(*v)>>4];
1361 b[1] = charset[(*v)&0xf];
1362 b[2] = '\0';
1363 b += 2;
1364 len--;
1365 v++;
1366 if (b-buf == 64 || len == 0) {
1367 serverLogRaw(level|LL_RAW,buf);
1368 b = buf;
1369 }
1370 }
1371 serverLogRaw(level|LL_RAW,"\n");
1372 }
1373
1374 /* =========================== Software Watchdog ============================ */
1375 #include <sys/time.h>
1376
watchdogSignalHandler(int sig,siginfo_t * info,void * secret)1377 void watchdogSignalHandler(int sig, siginfo_t *info, void *secret) {
1378 #ifdef HAVE_BACKTRACE
1379 ucontext_t *uc = (ucontext_t*) secret;
1380 #else
1381 (void)secret;
1382 #endif
1383 UNUSED(info);
1384 UNUSED(sig);
1385
1386 serverLogFromHandler(LL_WARNING,"\n--- WATCHDOG TIMER EXPIRED ---");
1387 #ifdef HAVE_BACKTRACE
1388 logStackTrace(uc);
1389 #else
1390 serverLogFromHandler(LL_WARNING,"Sorry: no support for backtrace().");
1391 #endif
1392 serverLogFromHandler(LL_WARNING,"--------\n");
1393 }
1394
1395 /* Schedule a SIGALRM delivery after the specified period in milliseconds.
1396 * If a timer is already scheduled, this function will re-schedule it to the
1397 * specified time. If period is 0 the current timer is disabled. */
watchdogScheduleSignal(int period)1398 void watchdogScheduleSignal(int period) {
1399 struct itimerval it;
1400
1401 /* Will stop the timer if period is 0. */
1402 it.it_value.tv_sec = period/1000;
1403 it.it_value.tv_usec = (period%1000)*1000;
1404 /* Don't automatically restart. */
1405 it.it_interval.tv_sec = 0;
1406 it.it_interval.tv_usec = 0;
1407 setitimer(ITIMER_REAL, &it, NULL);
1408 }
1409
1410 /* Enable the software watchdog with the specified period in milliseconds. */
enableWatchdog(int period)1411 void enableWatchdog(int period) {
1412 int min_period;
1413
1414 if (server.watchdog_period == 0) {
1415 struct sigaction act;
1416
1417 /* Watchdog was actually disabled, so we have to setup the signal
1418 * handler. */
1419 sigemptyset(&act.sa_mask);
1420 act.sa_flags = SA_ONSTACK | SA_SIGINFO;
1421 act.sa_sigaction = watchdogSignalHandler;
1422 sigaction(SIGALRM, &act, NULL);
1423 }
1424 /* If the configured period is smaller than twice the timer period, it is
1425 * too short for the software watchdog to work reliably. Fix it now
1426 * if needed. */
1427 min_period = (1000/server.hz)*2;
1428 if (period < min_period) period = min_period;
1429 watchdogScheduleSignal(period); /* Adjust the current timer. */
1430 server.watchdog_period = period;
1431 }
1432
1433 /* Disable the software watchdog. */
disableWatchdog(void)1434 void disableWatchdog(void) {
1435 struct sigaction act;
1436 if (server.watchdog_period == 0) return; /* Already disabled. */
1437 watchdogScheduleSignal(0); /* Stop the current timer. */
1438
1439 /* Set the signal handler to SIG_IGN, this will also remove pending
1440 * signals from the queue. */
1441 sigemptyset(&act.sa_mask);
1442 act.sa_flags = 0;
1443 act.sa_handler = SIG_IGN;
1444 sigaction(SIGALRM, &act, NULL);
1445 server.watchdog_period = 0;
1446 }
1447