1 | /* |
2 | * Copyright (c) 2009-2020, Salvatore Sanfilippo <antirez at gmail dot com> |
3 | * Copyright (c) 2020, Redis Labs, Inc |
4 | * All rights reserved. |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions are met: |
8 | * |
9 | * * Redistributions of source code must retain the above copyright notice, |
10 | * this list of conditions and the following disclaimer. |
11 | * * Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. |
14 | * * Neither the name of Redis nor the names of its contributors may be used |
15 | * to endorse or promote products derived from this software without |
16 | * specific prior written permission. |
17 | * |
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
28 | * POSSIBILITY OF SUCH DAMAGE. |
29 | */ |
30 | |
31 | #include "server.h" |
32 | #include "util.h" |
33 | #include "sha1.h" /* SHA1 is used for DEBUG DIGEST */ |
34 | #include "crc64.h" |
35 | #include "bio.h" |
36 | #include "quicklist.h" |
37 | |
38 | #include <arpa/inet.h> |
39 | #include <signal.h> |
40 | #include <dlfcn.h> |
41 | #include <fcntl.h> |
42 | #include <sys/mman.h> |
43 | #include <unistd.h> |
44 | |
45 | #ifdef HAVE_BACKTRACE |
46 | #include <execinfo.h> |
47 | #ifndef __OpenBSD__ |
48 | #include <ucontext.h> |
49 | #else |
50 | typedef ucontext_t sigcontext_t; |
51 | #endif |
52 | #endif /* HAVE_BACKTRACE */ |
53 | |
54 | #ifdef __CYGWIN__ |
55 | #ifndef SA_ONSTACK |
56 | #define SA_ONSTACK 0x08000000 |
57 | #endif |
58 | #endif |
59 | |
60 | #if defined(__APPLE__) && defined(__arm64__) |
61 | #include <mach/mach.h> |
62 | #endif |
63 | |
64 | /* Globals */ |
65 | static int bug_report_start = 0; /* True if bug report header was already logged. */ |
66 | static pthread_mutex_t bug_report_start_mutex = PTHREAD_MUTEX_INITIALIZER; |
67 | |
68 | /* Forward declarations */ |
69 | void bugReportStart(void); |
70 | void printCrashReport(void); |
71 | void bugReportEnd(int killViaSignal, int sig); |
72 | void logStackTrace(void *eip, int uplevel); |
73 | |
74 | /* ================================= Debugging ============================== */ |
75 | |
76 | /* Compute the sha1 of string at 's' with 'len' bytes long. |
77 | * The SHA1 is then xored against the string pointed by digest. |
78 | * Since xor is commutative, this operation is used in order to |
79 | * "add" digests relative to unordered elements. |
80 | * |
81 | * So digest(a,b,c,d) will be the same of digest(b,a,c,d) */ |
82 | void xorDigest(unsigned char *digest, const void *ptr, size_t len) { |
83 | SHA1_CTX ctx; |
84 | unsigned char hash[20]; |
85 | int j; |
86 | |
87 | SHA1Init(&ctx); |
88 | SHA1Update(&ctx,ptr,len); |
89 | SHA1Final(hash,&ctx); |
90 | |
91 | for (j = 0; j < 20; j++) |
92 | digest[j] ^= hash[j]; |
93 | } |
94 | |
95 | void xorStringObjectDigest(unsigned char *digest, robj *o) { |
96 | o = getDecodedObject(o); |
97 | xorDigest(digest,o->ptr,sdslen(o->ptr)); |
98 | decrRefCount(o); |
99 | } |
100 | |
101 | /* This function instead of just computing the SHA1 and xoring it |
102 | * against digest, also perform the digest of "digest" itself and |
103 | * replace the old value with the new one. |
104 | * |
105 | * So the final digest will be: |
106 | * |
107 | * digest = SHA1(digest xor SHA1(data)) |
108 | * |
109 | * This function is used every time we want to preserve the order so |
110 | * that digest(a,b,c,d) will be different than digest(b,c,d,a) |
111 | * |
112 | * Also note that mixdigest("foo") followed by mixdigest("bar") |
113 | * will lead to a different digest compared to "fo", "obar". |
114 | */ |
115 | void mixDigest(unsigned char *digest, const void *ptr, size_t len) { |
116 | SHA1_CTX ctx; |
117 | |
118 | xorDigest(digest,ptr,len); |
119 | SHA1Init(&ctx); |
120 | SHA1Update(&ctx,digest,20); |
121 | SHA1Final(digest,&ctx); |
122 | } |
123 | |
124 | void mixStringObjectDigest(unsigned char *digest, robj *o) { |
125 | o = getDecodedObject(o); |
126 | mixDigest(digest,o->ptr,sdslen(o->ptr)); |
127 | decrRefCount(o); |
128 | } |
129 | |
130 | /* This function computes the digest of a data structure stored in the |
131 | * object 'o'. It is the core of the DEBUG DIGEST command: when taking the |
132 | * digest of a whole dataset, we take the digest of the key and the value |
133 | * pair, and xor all those together. |
134 | * |
135 | * Note that this function does not reset the initial 'digest' passed, it |
136 | * will continue mixing this object digest to anything that was already |
137 | * present. */ |
138 | void xorObjectDigest(redisDb *db, robj *keyobj, unsigned char *digest, robj *o) { |
139 | uint32_t aux = htonl(o->type); |
140 | mixDigest(digest,&aux,sizeof(aux)); |
141 | long long expiretime = getExpire(db,keyobj); |
142 | char buf[128]; |
143 | |
144 | /* Save the key and associated value */ |
145 | if (o->type == OBJ_STRING) { |
146 | mixStringObjectDigest(digest,o); |
147 | } else if (o->type == OBJ_LIST) { |
148 | listTypeIterator *li = listTypeInitIterator(o,0,LIST_TAIL); |
149 | listTypeEntry entry; |
150 | while(listTypeNext(li,&entry)) { |
151 | robj *eleobj = listTypeGet(&entry); |
152 | mixStringObjectDigest(digest,eleobj); |
153 | decrRefCount(eleobj); |
154 | } |
155 | listTypeReleaseIterator(li); |
156 | } else if (o->type == OBJ_SET) { |
157 | setTypeIterator *si = setTypeInitIterator(o); |
158 | sds sdsele; |
159 | while((sdsele = setTypeNextObject(si)) != NULL) { |
160 | xorDigest(digest,sdsele,sdslen(sdsele)); |
161 | sdsfree(sdsele); |
162 | } |
163 | setTypeReleaseIterator(si); |
164 | } else if (o->type == OBJ_ZSET) { |
165 | unsigned char eledigest[20]; |
166 | |
167 | if (o->encoding == OBJ_ENCODING_LISTPACK) { |
168 | unsigned char *zl = o->ptr; |
169 | unsigned char *eptr, *sptr; |
170 | unsigned char *vstr; |
171 | unsigned int vlen; |
172 | long long vll; |
173 | double score; |
174 | |
175 | eptr = lpSeek(zl,0); |
176 | serverAssert(eptr != NULL); |
177 | sptr = lpNext(zl,eptr); |
178 | serverAssert(sptr != NULL); |
179 | |
180 | while (eptr != NULL) { |
181 | vstr = lpGetValue(eptr,&vlen,&vll); |
182 | score = zzlGetScore(sptr); |
183 | |
184 | memset(eledigest,0,20); |
185 | if (vstr != NULL) { |
186 | mixDigest(eledigest,vstr,vlen); |
187 | } else { |
188 | ll2string(buf,sizeof(buf),vll); |
189 | mixDigest(eledigest,buf,strlen(buf)); |
190 | } |
191 | |
192 | snprintf(buf,sizeof(buf),"%.17g" ,score); |
193 | mixDigest(eledigest,buf,strlen(buf)); |
194 | xorDigest(digest,eledigest,20); |
195 | zzlNext(zl,&eptr,&sptr); |
196 | } |
197 | } else if (o->encoding == OBJ_ENCODING_SKIPLIST) { |
198 | zset *zs = o->ptr; |
199 | dictIterator *di = dictGetIterator(zs->dict); |
200 | dictEntry *de; |
201 | |
202 | while((de = dictNext(di)) != NULL) { |
203 | sds sdsele = dictGetKey(de); |
204 | double *score = dictGetVal(de); |
205 | |
206 | snprintf(buf,sizeof(buf),"%.17g" ,*score); |
207 | memset(eledigest,0,20); |
208 | mixDigest(eledigest,sdsele,sdslen(sdsele)); |
209 | mixDigest(eledigest,buf,strlen(buf)); |
210 | xorDigest(digest,eledigest,20); |
211 | } |
212 | dictReleaseIterator(di); |
213 | } else { |
214 | serverPanic("Unknown sorted set encoding" ); |
215 | } |
216 | } else if (o->type == OBJ_HASH) { |
217 | hashTypeIterator *hi = hashTypeInitIterator(o); |
218 | while (hashTypeNext(hi) != C_ERR) { |
219 | unsigned char eledigest[20]; |
220 | sds sdsele; |
221 | |
222 | memset(eledigest,0,20); |
223 | sdsele = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_KEY); |
224 | mixDigest(eledigest,sdsele,sdslen(sdsele)); |
225 | sdsfree(sdsele); |
226 | sdsele = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_VALUE); |
227 | mixDigest(eledigest,sdsele,sdslen(sdsele)); |
228 | sdsfree(sdsele); |
229 | xorDigest(digest,eledigest,20); |
230 | } |
231 | hashTypeReleaseIterator(hi); |
232 | } else if (o->type == OBJ_STREAM) { |
233 | streamIterator si; |
234 | streamIteratorStart(&si,o->ptr,NULL,NULL,0); |
235 | streamID id; |
236 | int64_t numfields; |
237 | |
238 | while(streamIteratorGetID(&si,&id,&numfields)) { |
239 | sds itemid = sdscatfmt(sdsempty(),"%U.%U" ,id.ms,id.seq); |
240 | mixDigest(digest,itemid,sdslen(itemid)); |
241 | sdsfree(itemid); |
242 | |
243 | while(numfields--) { |
244 | unsigned char *field, *value; |
245 | int64_t field_len, value_len; |
246 | streamIteratorGetField(&si,&field,&value, |
247 | &field_len,&value_len); |
248 | mixDigest(digest,field,field_len); |
249 | mixDigest(digest,value,value_len); |
250 | } |
251 | } |
252 | streamIteratorStop(&si); |
253 | } else if (o->type == OBJ_MODULE) { |
254 | RedisModuleDigest md = {{0},{0},keyobj,db->id}; |
255 | moduleValue *mv = o->ptr; |
256 | moduleType *mt = mv->type; |
257 | moduleInitDigestContext(md); |
258 | if (mt->digest) { |
259 | mt->digest(&md,mv->value); |
260 | xorDigest(digest,md.x,sizeof(md.x)); |
261 | } |
262 | } else { |
263 | serverPanic("Unknown object type" ); |
264 | } |
265 | /* If the key has an expire, add it to the mix */ |
266 | if (expiretime != -1) xorDigest(digest,"!!expire!!" ,10); |
267 | } |
268 | |
269 | /* Compute the dataset digest. Since keys, sets elements, hashes elements |
270 | * are not ordered, we use a trick: every aggregate digest is the xor |
271 | * of the digests of their elements. This way the order will not change |
272 | * the result. For list instead we use a feedback entering the output digest |
273 | * as input in order to ensure that a different ordered list will result in |
274 | * a different digest. */ |
275 | void computeDatasetDigest(unsigned char *final) { |
276 | unsigned char digest[20]; |
277 | dictIterator *di = NULL; |
278 | dictEntry *de; |
279 | int j; |
280 | uint32_t aux; |
281 | |
282 | memset(final,0,20); /* Start with a clean result */ |
283 | |
284 | for (j = 0; j < server.dbnum; j++) { |
285 | redisDb *db = server.db+j; |
286 | |
287 | if (dictSize(db->dict) == 0) continue; |
288 | di = dictGetSafeIterator(db->dict); |
289 | |
290 | /* hash the DB id, so the same dataset moved in a different |
291 | * DB will lead to a different digest */ |
292 | aux = htonl(j); |
293 | mixDigest(final,&aux,sizeof(aux)); |
294 | |
295 | /* Iterate this DB writing every entry */ |
296 | while((de = dictNext(di)) != NULL) { |
297 | sds key; |
298 | robj *keyobj, *o; |
299 | |
300 | memset(digest,0,20); /* This key-val digest */ |
301 | key = dictGetKey(de); |
302 | keyobj = createStringObject(key,sdslen(key)); |
303 | |
304 | mixDigest(digest,key,sdslen(key)); |
305 | |
306 | o = dictGetVal(de); |
307 | xorObjectDigest(db,keyobj,digest,o); |
308 | |
309 | /* We can finally xor the key-val digest to the final digest */ |
310 | xorDigest(final,digest,20); |
311 | decrRefCount(keyobj); |
312 | } |
313 | dictReleaseIterator(di); |
314 | } |
315 | } |
316 | |
317 | #ifdef USE_JEMALLOC |
318 | void mallctl_int(client *c, robj **argv, int argc) { |
319 | int ret; |
320 | /* start with the biggest size (int64), and if that fails, try smaller sizes (int32, bool) */ |
321 | int64_t old = 0, val; |
322 | if (argc > 1) { |
323 | long long ll; |
324 | if (getLongLongFromObjectOrReply(c, argv[1], &ll, NULL) != C_OK) |
325 | return; |
326 | val = ll; |
327 | } |
328 | size_t sz = sizeof(old); |
329 | while (sz > 0) { |
330 | if ((ret=je_mallctl(argv[0]->ptr, &old, &sz, argc > 1? &val: NULL, argc > 1?sz: 0))) { |
331 | if (ret == EPERM && argc > 1) { |
332 | /* if this option is write only, try just writing to it. */ |
333 | if (!(ret=je_mallctl(argv[0]->ptr, NULL, 0, &val, sz))) { |
334 | addReply(c, shared.ok); |
335 | return; |
336 | } |
337 | } |
338 | if (ret==EINVAL) { |
339 | /* size might be wrong, try a smaller one */ |
340 | sz /= 2; |
341 | #if BYTE_ORDER == BIG_ENDIAN |
342 | val <<= 8*sz; |
343 | #endif |
344 | continue; |
345 | } |
346 | addReplyErrorFormat(c,"%s" , strerror(ret)); |
347 | return; |
348 | } else { |
349 | #if BYTE_ORDER == BIG_ENDIAN |
350 | old >>= 64 - 8*sz; |
351 | #endif |
352 | addReplyLongLong(c, old); |
353 | return; |
354 | } |
355 | } |
356 | addReplyErrorFormat(c,"%s" , strerror(EINVAL)); |
357 | } |
358 | |
359 | void mallctl_string(client *c, robj **argv, int argc) { |
360 | int rret, wret; |
361 | char *old; |
362 | size_t sz = sizeof(old); |
363 | /* for strings, it seems we need to first get the old value, before overriding it. */ |
364 | if ((rret=je_mallctl(argv[0]->ptr, &old, &sz, NULL, 0))) { |
365 | /* return error unless this option is write only. */ |
366 | if (!(rret == EPERM && argc > 1)) { |
367 | addReplyErrorFormat(c,"%s" , strerror(rret)); |
368 | return; |
369 | } |
370 | } |
371 | if(argc > 1) { |
372 | char *val = argv[1]->ptr; |
373 | char **valref = &val; |
374 | if ((!strcmp(val,"VOID" ))) |
375 | valref = NULL, sz = 0; |
376 | wret = je_mallctl(argv[0]->ptr, NULL, 0, valref, sz); |
377 | } |
378 | if (!rret) |
379 | addReplyBulkCString(c, old); |
380 | else if (wret) |
381 | addReplyErrorFormat(c,"%s" , strerror(wret)); |
382 | else |
383 | addReply(c, shared.ok); |
384 | } |
385 | #endif |
386 | |
387 | void debugCommand(client *c) { |
388 | if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help" )) { |
389 | const char *help[] = { |
390 | "AOF-FLUSH-SLEEP <microsec>" , |
391 | " Server will sleep before flushing the AOF, this is used for testing." , |
392 | "ASSERT" , |
393 | " Crash by assertion failed." , |
394 | "CHANGE-REPL-ID" , |
395 | " Change the replication IDs of the instance." , |
396 | " Dangerous: should be used only for testing the replication subsystem." , |
397 | "CONFIG-REWRITE-FORCE-ALL" , |
398 | " Like CONFIG REWRITE but writes all configuration options, including" , |
399 | " keywords not listed in original configuration file or default values." , |
400 | "CRASH-AND-RECOVER [<milliseconds>]" , |
401 | " Hard crash and restart after a <milliseconds> delay (default 0)." , |
402 | "DIGEST" , |
403 | " Output a hex signature representing the current DB content." , |
404 | "DIGEST-VALUE <key> [<key> ...]" , |
405 | " Output a hex signature of the values of all the specified keys." , |
406 | "ERROR <string>" , |
407 | " Return a Redis protocol error with <string> as message. Useful for clients" , |
408 | " unit tests to simulate Redis errors." , |
409 | "LEAK <string>" , |
410 | " Create a memory leak of the input string." , |
411 | "LOG <message>" , |
412 | " Write <message> to the server log." , |
413 | "HTSTATS <dbid>" , |
414 | " Return hash table statistics of the specified Redis database." , |
415 | "HTSTATS-KEY <key>" , |
416 | " Like HTSTATS but for the hash table stored at <key>'s value." , |
417 | "LOADAOF" , |
418 | " Flush the AOF buffers on disk and reload the AOF in memory." , |
419 | "REPLICATE <string>" , |
420 | " Replicates the provided string to replicas, allowing data divergence." , |
421 | #ifdef USE_JEMALLOC |
422 | "MALLCTL <key> [<val>]" , |
423 | " Get or set a malloc tuning integer." , |
424 | "MALLCTL-STR <key> [<val>]" , |
425 | " Get or set a malloc tuning string." , |
426 | #endif |
427 | "OBJECT <key>" , |
428 | " Show low level info about `key` and associated value." , |
429 | "DROP-CLUSTER-PACKET-FILTER <packet-type>" , |
430 | " Drop all packets that match the filtered type. Set to -1 allow all packets." , |
431 | "OOM" , |
432 | " Crash the server simulating an out-of-memory error." , |
433 | "PANIC" , |
434 | " Crash the server simulating a panic." , |
435 | "POPULATE <count> [<prefix>] [<size>]" , |
436 | " Create <count> string keys named key:<num>. If <prefix> is specified then" , |
437 | " it is used instead of the 'key' prefix. These are not propagated to" , |
438 | " replicas. Cluster slots are not respected so keys not belonging to the" , |
439 | " current node can be created in cluster mode." , |
440 | "PROTOCOL <type>" , |
441 | " Reply with a test value of the specified type. <type> can be: string," , |
442 | " integer, double, bignum, null, array, set, map, attrib, push, verbatim," , |
443 | " true, false." , |
444 | "RELOAD [option ...]" , |
445 | " Save the RDB on disk and reload it back to memory. Valid <option> values:" , |
446 | " * MERGE: conflicting keys will be loaded from RDB." , |
447 | " * NOFLUSH: the existing database will not be removed before load, but" , |
448 | " conflicting keys will generate an exception and kill the server." , |
449 | " * NOSAVE: the database will be loaded from an existing RDB file." , |
450 | " Examples:" , |
451 | " * DEBUG RELOAD: verify that the server is able to persist, flush and reload" , |
452 | " the database." , |
453 | " * DEBUG RELOAD NOSAVE: replace the current database with the contents of an" , |
454 | " existing RDB file." , |
455 | " * DEBUG RELOAD NOSAVE NOFLUSH MERGE: add the contents of an existing RDB" , |
456 | " file to the database." , |
457 | "RESTART [<milliseconds>]" , |
458 | " Graceful restart: save config, db, restart after a <milliseconds> delay (default 0)." , |
459 | "SDSLEN <key>" , |
460 | " Show low level SDS string info representing `key` and value." , |
461 | "SEGFAULT" , |
462 | " Crash the server with sigsegv." , |
463 | "SET-ACTIVE-EXPIRE <0|1>" , |
464 | " Setting it to 0 disables expiring keys in background when they are not" , |
465 | " accessed (otherwise the Redis behavior). Setting it to 1 reenables back the" , |
466 | " default." , |
467 | "QUICKLIST-PACKED-THRESHOLD <size>" , |
468 | " Sets the threshold for elements to be inserted as plain vs packed nodes" , |
469 | " Default value is 1GB, allows values up to 4GB" , |
470 | "SET-SKIP-CHECKSUM-VALIDATION <0|1>" , |
471 | " Enables or disables checksum checks for RDB files and RESTORE's payload." , |
472 | "SLEEP <seconds>" , |
473 | " Stop the server for <seconds>. Decimals allowed." , |
474 | "STRINGMATCH-TEST" , |
475 | " Run a fuzz tester against the stringmatchlen() function." , |
476 | "STRUCTSIZE" , |
477 | " Return the size of different Redis core C structures." , |
478 | "LISTPACK <key>" , |
479 | " Show low level info about the listpack encoding of <key>." , |
480 | "QUICKLIST <key> [<0|1>]" , |
481 | " Show low level info about the quicklist encoding of <key>." , |
482 | " The optional argument (0 by default) sets the level of detail" , |
483 | "CLIENT-EVICTION" , |
484 | " Show low level client eviction pools info (maxmemory-clients)." , |
485 | "PAUSE-CRON <0|1>" , |
486 | " Stop periodic cron job processing." , |
487 | "REPLYBUFFER PEAK-RESET-TIME <NEVER||RESET|time>" , |
488 | " Sets the time (in milliseconds) to wait between client reply buffer peak resets." , |
489 | " In case NEVER is provided the last observed peak will never be reset" , |
490 | " In case RESET is provided the peak reset time will be restored to the default value" , |
491 | "REPLYBUFFER RESIZING <0|1>" , |
492 | " Enable or disable the replay buffer resize cron job" , |
493 | NULL |
494 | }; |
495 | addReplyHelp(c, help); |
496 | } else if (!strcasecmp(c->argv[1]->ptr,"segfault" )) { |
497 | /* Compiler gives warnings about writing to a random address |
498 | * e.g "*((char*)-1) = 'x';". As a workaround, we map a read-only area |
499 | * and try to write there to trigger segmentation fault. */ |
500 | char* p = mmap(NULL, 4096, PROT_READ, MAP_PRIVATE | MAP_ANON, -1, 0); |
501 | *p = 'x'; |
502 | } else if (!strcasecmp(c->argv[1]->ptr,"panic" )) { |
503 | serverPanic("DEBUG PANIC called at Unix time %lld" , (long long)time(NULL)); |
504 | } else if (!strcasecmp(c->argv[1]->ptr,"restart" ) || |
505 | !strcasecmp(c->argv[1]->ptr,"crash-and-recover" )) |
506 | { |
507 | long long delay = 0; |
508 | if (c->argc >= 3) { |
509 | if (getLongLongFromObjectOrReply(c, c->argv[2], &delay, NULL) |
510 | != C_OK) return; |
511 | if (delay < 0) delay = 0; |
512 | } |
513 | int flags = !strcasecmp(c->argv[1]->ptr,"restart" ) ? |
514 | (RESTART_SERVER_GRACEFULLY|RESTART_SERVER_CONFIG_REWRITE) : |
515 | RESTART_SERVER_NONE; |
516 | restartServer(flags,delay); |
517 | addReplyError(c,"failed to restart the server. Check server logs." ); |
518 | } else if (!strcasecmp(c->argv[1]->ptr,"oom" )) { |
519 | void *ptr = zmalloc(ULONG_MAX); /* Should trigger an out of memory. */ |
520 | zfree(ptr); |
521 | addReply(c,shared.ok); |
522 | } else if (!strcasecmp(c->argv[1]->ptr,"assert" )) { |
523 | serverAssertWithInfo(c,c->argv[0],1 == 2); |
524 | } else if (!strcasecmp(c->argv[1]->ptr,"log" ) && c->argc == 3) { |
525 | serverLog(LL_WARNING, "DEBUG LOG: %s" , (char*)c->argv[2]->ptr); |
526 | addReply(c,shared.ok); |
527 | } else if (!strcasecmp(c->argv[1]->ptr,"leak" ) && c->argc == 3) { |
528 | sdsdup(c->argv[2]->ptr); |
529 | addReply(c,shared.ok); |
530 | } else if (!strcasecmp(c->argv[1]->ptr,"reload" )) { |
531 | int flush = 1, save = 1; |
532 | int flags = RDBFLAGS_NONE; |
533 | |
534 | /* Parse the additional options that modify the RELOAD |
535 | * behavior. */ |
536 | for (int j = 2; j < c->argc; j++) { |
537 | char *opt = c->argv[j]->ptr; |
538 | if (!strcasecmp(opt,"MERGE" )) { |
539 | flags |= RDBFLAGS_ALLOW_DUP; |
540 | } else if (!strcasecmp(opt,"NOFLUSH" )) { |
541 | flush = 0; |
542 | } else if (!strcasecmp(opt,"NOSAVE" )) { |
543 | save = 0; |
544 | } else { |
545 | addReplyError(c,"DEBUG RELOAD only supports the " |
546 | "MERGE, NOFLUSH and NOSAVE options." ); |
547 | return; |
548 | } |
549 | } |
550 | |
551 | /* The default behavior is to save the RDB file before loading |
552 | * it back. */ |
553 | if (save) { |
554 | rdbSaveInfo rsi, *rsiptr; |
555 | rsiptr = rdbPopulateSaveInfo(&rsi); |
556 | if (rdbSave(SLAVE_REQ_NONE,server.rdb_filename,rsiptr) != C_OK) { |
557 | addReplyErrorObject(c,shared.err); |
558 | return; |
559 | } |
560 | } |
561 | |
562 | /* The default behavior is to remove the current dataset from |
563 | * memory before loading the RDB file, however when MERGE is |
564 | * used together with NOFLUSH, we are able to merge two datasets. */ |
565 | if (flush) emptyData(-1,EMPTYDB_NO_FLAGS,NULL); |
566 | |
567 | protectClient(c); |
568 | int ret = rdbLoad(server.rdb_filename,NULL,flags); |
569 | unprotectClient(c); |
570 | if (ret != C_OK) { |
571 | addReplyError(c,"Error trying to load the RDB dump" ); |
572 | return; |
573 | } |
574 | serverLog(LL_WARNING,"DB reloaded by DEBUG RELOAD" ); |
575 | addReply(c,shared.ok); |
576 | } else if (!strcasecmp(c->argv[1]->ptr,"loadaof" )) { |
577 | if (server.aof_state != AOF_OFF) flushAppendOnlyFile(1); |
578 | emptyData(-1,EMPTYDB_NO_FLAGS,NULL); |
579 | protectClient(c); |
580 | if (server.aof_manifest) aofManifestFree(server.aof_manifest); |
581 | aofLoadManifestFromDisk(); |
582 | aofDelHistoryFiles(); |
583 | int ret = loadAppendOnlyFiles(server.aof_manifest); |
584 | if (ret != AOF_OK && ret != AOF_EMPTY) |
585 | exit(1); |
586 | unprotectClient(c); |
587 | server.dirty = 0; /* Prevent AOF / replication */ |
588 | serverLog(LL_WARNING,"Append Only File loaded by DEBUG LOADAOF" ); |
589 | addReply(c,shared.ok); |
590 | } else if (!strcasecmp(c->argv[1]->ptr,"drop-cluster-packet-filter" ) && c->argc == 3) { |
591 | long packet_type; |
592 | if (getLongFromObjectOrReply(c, c->argv[2], &packet_type, NULL) != C_OK) |
593 | return; |
594 | server.cluster_drop_packet_filter = packet_type; |
595 | addReply(c,shared.ok); |
596 | } else if (!strcasecmp(c->argv[1]->ptr,"object" ) && c->argc == 3) { |
597 | dictEntry *de; |
598 | robj *val; |
599 | char *strenc; |
600 | |
601 | if ((de = dictFind(c->db->dict,c->argv[2]->ptr)) == NULL) { |
602 | addReplyErrorObject(c,shared.nokeyerr); |
603 | return; |
604 | } |
605 | val = dictGetVal(de); |
606 | strenc = strEncoding(val->encoding); |
607 | |
608 | char [138] = {0}; |
609 | if (val->encoding == OBJ_ENCODING_QUICKLIST) { |
610 | char * = extra; |
611 | int remaining = sizeof(extra); |
612 | quicklist *ql = val->ptr; |
613 | /* Add number of quicklist nodes */ |
614 | int used = snprintf(nextra, remaining, " ql_nodes:%lu" , ql->len); |
615 | nextra += used; |
616 | remaining -= used; |
617 | /* Add average quicklist fill factor */ |
618 | double avg = (double)ql->count/ql->len; |
619 | used = snprintf(nextra, remaining, " ql_avg_node:%.2f" , avg); |
620 | nextra += used; |
621 | remaining -= used; |
622 | /* Add quicklist fill level / max listpack size */ |
623 | used = snprintf(nextra, remaining, " ql_listpack_max:%d" , ql->fill); |
624 | nextra += used; |
625 | remaining -= used; |
626 | /* Add isCompressed? */ |
627 | int compressed = ql->compress != 0; |
628 | used = snprintf(nextra, remaining, " ql_compressed:%d" , compressed); |
629 | nextra += used; |
630 | remaining -= used; |
631 | /* Add total uncompressed size */ |
632 | unsigned long sz = 0; |
633 | for (quicklistNode *node = ql->head; node; node = node->next) { |
634 | sz += node->sz; |
635 | } |
636 | used = snprintf(nextra, remaining, " ql_uncompressed_size:%lu" , sz); |
637 | nextra += used; |
638 | remaining -= used; |
639 | } |
640 | |
641 | addReplyStatusFormat(c, |
642 | "Value at:%p refcount:%d " |
643 | "encoding:%s serializedlength:%zu " |
644 | "lru:%d lru_seconds_idle:%llu%s" , |
645 | (void*)val, val->refcount, |
646 | strenc, rdbSavedObjectLen(val, c->argv[2], c->db->id), |
647 | val->lru, estimateObjectIdleTime(val)/1000, extra); |
648 | } else if (!strcasecmp(c->argv[1]->ptr,"sdslen" ) && c->argc == 3) { |
649 | dictEntry *de; |
650 | robj *val; |
651 | sds key; |
652 | |
653 | if ((de = dictFind(c->db->dict,c->argv[2]->ptr)) == NULL) { |
654 | addReplyErrorObject(c,shared.nokeyerr); |
655 | return; |
656 | } |
657 | val = dictGetVal(de); |
658 | key = dictGetKey(de); |
659 | |
660 | if (val->type != OBJ_STRING || !sdsEncodedObject(val)) { |
661 | addReplyError(c,"Not an sds encoded string." ); |
662 | } else { |
663 | addReplyStatusFormat(c, |
664 | "key_sds_len:%lld, key_sds_avail:%lld, key_zmalloc: %lld, " |
665 | "val_sds_len:%lld, val_sds_avail:%lld, val_zmalloc: %lld" , |
666 | (long long) sdslen(key), |
667 | (long long) sdsavail(key), |
668 | (long long) sdsZmallocSize(key), |
669 | (long long) sdslen(val->ptr), |
670 | (long long) sdsavail(val->ptr), |
671 | (long long) getStringObjectSdsUsedMemory(val)); |
672 | } |
673 | } else if (!strcasecmp(c->argv[1]->ptr,"listpack" ) && c->argc == 3) { |
674 | robj *o; |
675 | |
676 | if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nokeyerr)) |
677 | == NULL) return; |
678 | |
679 | if (o->encoding != OBJ_ENCODING_LISTPACK) { |
680 | addReplyError(c,"Not a listpack encoded object." ); |
681 | } else { |
682 | lpRepr(o->ptr); |
683 | addReplyStatus(c,"Listpack structure printed on stdout" ); |
684 | } |
685 | } else if (!strcasecmp(c->argv[1]->ptr,"quicklist" ) && (c->argc == 3 || c->argc == 4)) { |
686 | robj *o; |
687 | |
688 | if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nokeyerr)) |
689 | == NULL) return; |
690 | |
691 | int full = 0; |
692 | if (c->argc == 4) |
693 | full = atoi(c->argv[3]->ptr); |
694 | if (o->encoding != OBJ_ENCODING_QUICKLIST) { |
695 | addReplyError(c,"Not a quicklist encoded object." ); |
696 | } else { |
697 | quicklistRepr(o->ptr, full); |
698 | addReplyStatus(c,"Quicklist structure printed on stdout" ); |
699 | } |
700 | } else if (!strcasecmp(c->argv[1]->ptr,"populate" ) && |
701 | c->argc >= 3 && c->argc <= 5) { |
702 | long keys, j; |
703 | robj *key, *val; |
704 | char buf[128]; |
705 | |
706 | if (getPositiveLongFromObjectOrReply(c, c->argv[2], &keys, NULL) != C_OK) |
707 | return; |
708 | |
709 | dictExpand(c->db->dict,keys); |
710 | long valsize = 0; |
711 | if ( c->argc == 5 && getPositiveLongFromObjectOrReply(c, c->argv[4], &valsize, NULL) != C_OK ) |
712 | return; |
713 | |
714 | for (j = 0; j < keys; j++) { |
715 | snprintf(buf,sizeof(buf),"%s:%lu" , |
716 | (c->argc == 3) ? "key" : (char*)c->argv[3]->ptr, j); |
717 | key = createStringObject(buf,strlen(buf)); |
718 | if (lookupKeyWrite(c->db,key) != NULL) { |
719 | decrRefCount(key); |
720 | continue; |
721 | } |
722 | snprintf(buf,sizeof(buf),"value:%lu" ,j); |
723 | if (valsize==0) |
724 | val = createStringObject(buf,strlen(buf)); |
725 | else { |
726 | int buflen = strlen(buf); |
727 | val = createStringObject(NULL,valsize); |
728 | memcpy(val->ptr, buf, valsize<=buflen? valsize: buflen); |
729 | } |
730 | dbAdd(c->db,key,val); |
731 | signalModifiedKey(c,c->db,key); |
732 | decrRefCount(key); |
733 | } |
734 | addReply(c,shared.ok); |
735 | } else if (!strcasecmp(c->argv[1]->ptr,"digest" ) && c->argc == 2) { |
736 | /* DEBUG DIGEST (form without keys specified) */ |
737 | unsigned char digest[20]; |
738 | sds d = sdsempty(); |
739 | |
740 | computeDatasetDigest(digest); |
741 | for (int i = 0; i < 20; i++) d = sdscatprintf(d, "%02x" ,digest[i]); |
742 | addReplyStatus(c,d); |
743 | sdsfree(d); |
744 | } else if (!strcasecmp(c->argv[1]->ptr,"digest-value" ) && c->argc >= 2) { |
745 | /* DEBUG DIGEST-VALUE key key key ... key. */ |
746 | addReplyArrayLen(c,c->argc-2); |
747 | for (int j = 2; j < c->argc; j++) { |
748 | unsigned char digest[20]; |
749 | memset(digest,0,20); /* Start with a clean result */ |
750 | |
751 | /* We don't use lookupKey because a debug command should |
752 | * work on logically expired keys */ |
753 | dictEntry *de; |
754 | robj *o = ((de = dictFind(c->db->dict,c->argv[j]->ptr)) == NULL) ? NULL : dictGetVal(de); |
755 | if (o) xorObjectDigest(c->db,c->argv[j],digest,o); |
756 | |
757 | sds d = sdsempty(); |
758 | for (int i = 0; i < 20; i++) d = sdscatprintf(d, "%02x" ,digest[i]); |
759 | addReplyStatus(c,d); |
760 | sdsfree(d); |
761 | } |
762 | } else if (!strcasecmp(c->argv[1]->ptr,"protocol" ) && c->argc == 3) { |
763 | /* DEBUG PROTOCOL [string|integer|double|bignum|null|array|set|map| |
764 | * attrib|push|verbatim|true|false] */ |
765 | char *name = c->argv[2]->ptr; |
766 | if (!strcasecmp(name,"string" )) { |
767 | addReplyBulkCString(c,"Hello World" ); |
768 | } else if (!strcasecmp(name,"integer" )) { |
769 | addReplyLongLong(c,12345); |
770 | } else if (!strcasecmp(name,"double" )) { |
771 | addReplyDouble(c,3.141); |
772 | } else if (!strcasecmp(name,"bignum" )) { |
773 | addReplyBigNum(c,"1234567999999999999999999999999999999" ,37); |
774 | } else if (!strcasecmp(name,"null" )) { |
775 | addReplyNull(c); |
776 | } else if (!strcasecmp(name,"array" )) { |
777 | addReplyArrayLen(c,3); |
778 | for (int j = 0; j < 3; j++) addReplyLongLong(c,j); |
779 | } else if (!strcasecmp(name,"set" )) { |
780 | addReplySetLen(c,3); |
781 | for (int j = 0; j < 3; j++) addReplyLongLong(c,j); |
782 | } else if (!strcasecmp(name,"map" )) { |
783 | addReplyMapLen(c,3); |
784 | for (int j = 0; j < 3; j++) { |
785 | addReplyLongLong(c,j); |
786 | addReplyBool(c, j == 1); |
787 | } |
788 | } else if (!strcasecmp(name,"attrib" )) { |
789 | if (c->resp >= 3) { |
790 | addReplyAttributeLen(c,1); |
791 | addReplyBulkCString(c,"key-popularity" ); |
792 | addReplyArrayLen(c,2); |
793 | addReplyBulkCString(c,"key:123" ); |
794 | addReplyLongLong(c,90); |
795 | } |
796 | /* Attributes are not real replies, so a well formed reply should |
797 | * also have a normal reply type after the attribute. */ |
798 | addReplyBulkCString(c,"Some real reply following the attribute" ); |
799 | } else if (!strcasecmp(name,"push" )) { |
800 | if (c->resp < 3) { |
801 | addReplyError(c,"RESP2 is not supported by this command" ); |
802 | return; |
803 | } |
804 | addReplyPushLen(c,2); |
805 | addReplyBulkCString(c,"server-cpu-usage" ); |
806 | addReplyLongLong(c,42); |
807 | /* Push replies are not synchronous replies, so we emit also a |
808 | * normal reply in order for blocking clients just discarding the |
809 | * push reply, to actually consume the reply and continue. */ |
810 | addReplyBulkCString(c,"Some real reply following the push reply" ); |
811 | } else if (!strcasecmp(name,"true" )) { |
812 | addReplyBool(c,1); |
813 | } else if (!strcasecmp(name,"false" )) { |
814 | addReplyBool(c,0); |
815 | } else if (!strcasecmp(name,"verbatim" )) { |
816 | addReplyVerbatim(c,"This is a verbatim\nstring" ,25,"txt" ); |
817 | } else { |
818 | addReplyError(c,"Wrong protocol type name. Please use one of the following: string|integer|double|bignum|null|array|set|map|attrib|push|verbatim|true|false" ); |
819 | } |
820 | } else if (!strcasecmp(c->argv[1]->ptr,"sleep" ) && c->argc == 3) { |
821 | double dtime = strtod(c->argv[2]->ptr,NULL); |
822 | long long utime = dtime*1000000; |
823 | struct timespec tv; |
824 | |
825 | tv.tv_sec = utime / 1000000; |
826 | tv.tv_nsec = (utime % 1000000) * 1000; |
827 | nanosleep(&tv, NULL); |
828 | addReply(c,shared.ok); |
829 | } else if (!strcasecmp(c->argv[1]->ptr,"set-active-expire" ) && |
830 | c->argc == 3) |
831 | { |
832 | server.active_expire_enabled = atoi(c->argv[2]->ptr); |
833 | addReply(c,shared.ok); |
834 | } else if (!strcasecmp(c->argv[1]->ptr,"quicklist-packed-threshold" ) && |
835 | c->argc == 3) |
836 | { |
837 | int memerr; |
838 | unsigned long long sz = memtoull((const char *)c->argv[2]->ptr, &memerr); |
839 | if (memerr || !quicklistisSetPackedThreshold(sz)) { |
840 | addReplyError(c, "argument must be a memory value bigger than 1 and smaller than 4gb" ); |
841 | } else { |
842 | addReply(c,shared.ok); |
843 | } |
844 | } else if (!strcasecmp(c->argv[1]->ptr,"set-skip-checksum-validation" ) && |
845 | c->argc == 3) |
846 | { |
847 | server.skip_checksum_validation = atoi(c->argv[2]->ptr); |
848 | addReply(c,shared.ok); |
849 | } else if (!strcasecmp(c->argv[1]->ptr,"aof-flush-sleep" ) && |
850 | c->argc == 3) |
851 | { |
852 | server.aof_flush_sleep = atoi(c->argv[2]->ptr); |
853 | addReply(c,shared.ok); |
854 | } else if (!strcasecmp(c->argv[1]->ptr,"replicate" ) && c->argc >= 3) { |
855 | replicationFeedSlaves(server.slaves, server.slaveseldb, |
856 | c->argv + 2, c->argc - 2); |
857 | addReply(c,shared.ok); |
858 | } else if (!strcasecmp(c->argv[1]->ptr,"error" ) && c->argc == 3) { |
859 | sds errstr = sdsnewlen("-" ,1); |
860 | |
861 | errstr = sdscatsds(errstr,c->argv[2]->ptr); |
862 | errstr = sdsmapchars(errstr,"\n\r" ," " ,2); /* no newlines in errors. */ |
863 | errstr = sdscatlen(errstr,"\r\n" ,2); |
864 | addReplySds(c,errstr); |
865 | } else if (!strcasecmp(c->argv[1]->ptr,"structsize" ) && c->argc == 2) { |
866 | sds sizes = sdsempty(); |
867 | sizes = sdscatprintf(sizes,"bits:%d " ,(sizeof(void*) == 8)?64:32); |
868 | sizes = sdscatprintf(sizes,"robj:%d " ,(int)sizeof(robj)); |
869 | sizes = sdscatprintf(sizes,"dictentry:%d " ,(int)sizeof(dictEntry)); |
870 | sizes = sdscatprintf(sizes,"sdshdr5:%d " ,(int)sizeof(struct sdshdr5)); |
871 | sizes = sdscatprintf(sizes,"sdshdr8:%d " ,(int)sizeof(struct sdshdr8)); |
872 | sizes = sdscatprintf(sizes,"sdshdr16:%d " ,(int)sizeof(struct sdshdr16)); |
873 | sizes = sdscatprintf(sizes,"sdshdr32:%d " ,(int)sizeof(struct sdshdr32)); |
874 | sizes = sdscatprintf(sizes,"sdshdr64:%d " ,(int)sizeof(struct sdshdr64)); |
875 | addReplyBulkSds(c,sizes); |
876 | } else if (!strcasecmp(c->argv[1]->ptr,"htstats" ) && c->argc == 3) { |
877 | long dbid; |
878 | sds stats = sdsempty(); |
879 | char buf[4096]; |
880 | |
881 | if (getLongFromObjectOrReply(c, c->argv[2], &dbid, NULL) != C_OK) { |
882 | sdsfree(stats); |
883 | return; |
884 | } |
885 | if (dbid < 0 || dbid >= server.dbnum) { |
886 | sdsfree(stats); |
887 | addReplyError(c,"Out of range database" ); |
888 | return; |
889 | } |
890 | |
891 | stats = sdscatprintf(stats,"[Dictionary HT]\n" ); |
892 | dictGetStats(buf,sizeof(buf),server.db[dbid].dict); |
893 | stats = sdscat(stats,buf); |
894 | |
895 | stats = sdscatprintf(stats,"[Expires HT]\n" ); |
896 | dictGetStats(buf,sizeof(buf),server.db[dbid].expires); |
897 | stats = sdscat(stats,buf); |
898 | |
899 | addReplyVerbatim(c,stats,sdslen(stats),"txt" ); |
900 | sdsfree(stats); |
901 | } else if (!strcasecmp(c->argv[1]->ptr,"htstats-key" ) && c->argc == 3) { |
902 | robj *o; |
903 | dict *ht = NULL; |
904 | |
905 | if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nokeyerr)) |
906 | == NULL) return; |
907 | |
908 | /* Get the hash table reference from the object, if possible. */ |
909 | switch (o->encoding) { |
910 | case OBJ_ENCODING_SKIPLIST: |
911 | { |
912 | zset *zs = o->ptr; |
913 | ht = zs->dict; |
914 | } |
915 | break; |
916 | case OBJ_ENCODING_HT: |
917 | ht = o->ptr; |
918 | break; |
919 | } |
920 | |
921 | if (ht == NULL) { |
922 | addReplyError(c,"The value stored at the specified key is not " |
923 | "represented using an hash table" ); |
924 | } else { |
925 | char buf[4096]; |
926 | dictGetStats(buf,sizeof(buf),ht); |
927 | addReplyVerbatim(c,buf,strlen(buf),"txt" ); |
928 | } |
929 | } else if (!strcasecmp(c->argv[1]->ptr,"change-repl-id" ) && c->argc == 2) { |
930 | serverLog(LL_WARNING,"Changing replication IDs after receiving DEBUG change-repl-id" ); |
931 | changeReplicationId(); |
932 | clearReplicationId2(); |
933 | addReply(c,shared.ok); |
934 | } else if (!strcasecmp(c->argv[1]->ptr,"stringmatch-test" ) && c->argc == 2) |
935 | { |
936 | stringmatchlen_fuzz_test(); |
937 | addReplyStatus(c,"Apparently Redis did not crash: test passed" ); |
938 | } else if (!strcasecmp(c->argv[1]->ptr,"set-disable-deny-scripts" ) && c->argc == 3) |
939 | { |
940 | server.script_disable_deny_script = atoi(c->argv[2]->ptr); |
941 | addReply(c,shared.ok); |
942 | } else if (!strcasecmp(c->argv[1]->ptr,"config-rewrite-force-all" ) && c->argc == 2) |
943 | { |
944 | if (rewriteConfig(server.configfile, 1) == -1) |
945 | addReplyErrorFormat(c, "CONFIG-REWRITE-FORCE-ALL failed: %s" , strerror(errno)); |
946 | else |
947 | addReply(c, shared.ok); |
948 | } else if(!strcasecmp(c->argv[1]->ptr,"client-eviction" ) && c->argc == 2) { |
949 | sds bucket_info = sdsempty(); |
950 | for (int j = 0; j < CLIENT_MEM_USAGE_BUCKETS; j++) { |
951 | if (j == 0) |
952 | bucket_info = sdscatprintf(bucket_info, "bucket 0" ); |
953 | else |
954 | bucket_info = sdscatprintf(bucket_info, "bucket %10zu" , (size_t)1<<(j-1+CLIENT_MEM_USAGE_BUCKET_MIN_LOG)); |
955 | if (j == CLIENT_MEM_USAGE_BUCKETS-1) |
956 | bucket_info = sdscatprintf(bucket_info, "+ : " ); |
957 | else |
958 | bucket_info = sdscatprintf(bucket_info, " - %10zu: " , ((size_t)1<<(j+CLIENT_MEM_USAGE_BUCKET_MIN_LOG))-1); |
959 | bucket_info = sdscatprintf(bucket_info, "tot-mem: %10zu, clients: %lu\n" , |
960 | server.client_mem_usage_buckets[j].mem_usage_sum, |
961 | server.client_mem_usage_buckets[j].clients->len); |
962 | } |
963 | addReplyVerbatim(c,bucket_info,sdslen(bucket_info),"txt" ); |
964 | sdsfree(bucket_info); |
965 | #ifdef USE_JEMALLOC |
966 | } else if(!strcasecmp(c->argv[1]->ptr,"mallctl" ) && c->argc >= 3) { |
967 | mallctl_int(c, c->argv+2, c->argc-2); |
968 | return; |
969 | } else if(!strcasecmp(c->argv[1]->ptr,"mallctl-str" ) && c->argc >= 3) { |
970 | mallctl_string(c, c->argv+2, c->argc-2); |
971 | return; |
972 | #endif |
973 | } else if (!strcasecmp(c->argv[1]->ptr,"pause-cron" ) && c->argc == 3) |
974 | { |
975 | server.pause_cron = atoi(c->argv[2]->ptr); |
976 | addReply(c,shared.ok); |
977 | } else if (!strcasecmp(c->argv[1]->ptr,"replybuffer" ) && c->argc == 4 ) { |
978 | if(!strcasecmp(c->argv[2]->ptr, "peak-reset-time" )) { |
979 | if (!strcasecmp(c->argv[3]->ptr, "never" )) { |
980 | server.reply_buffer_peak_reset_time = -1; |
981 | } else if(!strcasecmp(c->argv[3]->ptr, "reset" )) { |
982 | server.reply_buffer_peak_reset_time = REPLY_BUFFER_DEFAULT_PEAK_RESET_TIME; |
983 | } else { |
984 | if (getLongFromObjectOrReply(c, c->argv[3], &server.reply_buffer_peak_reset_time, NULL) != C_OK) |
985 | return; |
986 | } |
987 | } else if(!strcasecmp(c->argv[2]->ptr,"resizing" )) { |
988 | server.reply_buffer_resizing_enabled = atoi(c->argv[3]->ptr); |
989 | } else { |
990 | addReplySubcommandSyntaxError(c); |
991 | return; |
992 | } |
993 | addReply(c, shared.ok); |
994 | } else { |
995 | addReplySubcommandSyntaxError(c); |
996 | return; |
997 | } |
998 | } |
999 | |
1000 | /* =========================== Crash handling ============================== */ |
1001 | |
1002 | void _serverAssert(const char *estr, const char *file, int line) { |
1003 | bugReportStart(); |
1004 | serverLog(LL_WARNING,"=== ASSERTION FAILED ===" ); |
1005 | serverLog(LL_WARNING,"==> %s:%d '%s' is not true" ,file,line,estr); |
1006 | |
1007 | if (server.crashlog_enabled) { |
1008 | #ifdef HAVE_BACKTRACE |
1009 | logStackTrace(NULL, 1); |
1010 | #endif |
1011 | printCrashReport(); |
1012 | } |
1013 | |
1014 | // remove the signal handler so on abort() we will output the crash report. |
1015 | removeSignalHandlers(); |
1016 | bugReportEnd(0, 0); |
1017 | } |
1018 | |
1019 | void _serverAssertPrintClientInfo(const client *c) { |
1020 | int j; |
1021 | char conninfo[CONN_INFO_LEN]; |
1022 | |
1023 | bugReportStart(); |
1024 | serverLog(LL_WARNING,"=== ASSERTION FAILED CLIENT CONTEXT ===" ); |
1025 | serverLog(LL_WARNING,"client->flags = %llu" , (unsigned long long) c->flags); |
1026 | serverLog(LL_WARNING,"client->conn = %s" , connGetInfo(c->conn, conninfo, sizeof(conninfo))); |
1027 | serverLog(LL_WARNING,"client->argc = %d" , c->argc); |
1028 | for (j=0; j < c->argc; j++) { |
1029 | char buf[128]; |
1030 | char *arg; |
1031 | |
1032 | if (c->argv[j]->type == OBJ_STRING && sdsEncodedObject(c->argv[j])) { |
1033 | arg = (char*) c->argv[j]->ptr; |
1034 | } else { |
1035 | snprintf(buf,sizeof(buf),"Object type: %u, encoding: %u" , |
1036 | c->argv[j]->type, c->argv[j]->encoding); |
1037 | arg = buf; |
1038 | } |
1039 | serverLog(LL_WARNING,"client->argv[%d] = \"%s\" (refcount: %d)" , |
1040 | j, arg, c->argv[j]->refcount); |
1041 | } |
1042 | } |
1043 | |
1044 | void serverLogObjectDebugInfo(const robj *o) { |
1045 | serverLog(LL_WARNING,"Object type: %u" , o->type); |
1046 | serverLog(LL_WARNING,"Object encoding: %u" , o->encoding); |
1047 | serverLog(LL_WARNING,"Object refcount: %d" , o->refcount); |
1048 | #if UNSAFE_CRASH_REPORT |
1049 | /* This code is now disabled. o->ptr may be unreliable to print. in some |
1050 | * cases a ziplist could have already been freed by realloc, but not yet |
1051 | * updated to o->ptr. in other cases the call to ziplistLen may need to |
1052 | * iterate on all the items in the list (and possibly crash again). |
1053 | * For some cases it may be ok to crash here again, but these could cause |
1054 | * invalid memory access which will bother valgrind and also possibly cause |
1055 | * random memory portion to be "leaked" into the logfile. */ |
1056 | if (o->type == OBJ_STRING && sdsEncodedObject(o)) { |
1057 | serverLog(LL_WARNING,"Object raw string len: %zu" , sdslen(o->ptr)); |
1058 | if (sdslen(o->ptr) < 4096) { |
1059 | sds repr = sdscatrepr(sdsempty(),o->ptr,sdslen(o->ptr)); |
1060 | serverLog(LL_WARNING,"Object raw string content: %s" , repr); |
1061 | sdsfree(repr); |
1062 | } |
1063 | } else if (o->type == OBJ_LIST) { |
1064 | serverLog(LL_WARNING,"List length: %d" , (int) listTypeLength(o)); |
1065 | } else if (o->type == OBJ_SET) { |
1066 | serverLog(LL_WARNING,"Set size: %d" , (int) setTypeSize(o)); |
1067 | } else if (o->type == OBJ_HASH) { |
1068 | serverLog(LL_WARNING,"Hash size: %d" , (int) hashTypeLength(o)); |
1069 | } else if (o->type == OBJ_ZSET) { |
1070 | serverLog(LL_WARNING,"Sorted set size: %d" , (int) zsetLength(o)); |
1071 | if (o->encoding == OBJ_ENCODING_SKIPLIST) |
1072 | serverLog(LL_WARNING,"Skiplist level: %d" , (int) ((const zset*)o->ptr)->zsl->level); |
1073 | } else if (o->type == OBJ_STREAM) { |
1074 | serverLog(LL_WARNING,"Stream size: %d" , (int) streamLength(o)); |
1075 | } |
1076 | #endif |
1077 | } |
1078 | |
1079 | void _serverAssertPrintObject(const robj *o) { |
1080 | bugReportStart(); |
1081 | serverLog(LL_WARNING,"=== ASSERTION FAILED OBJECT CONTEXT ===" ); |
1082 | serverLogObjectDebugInfo(o); |
1083 | } |
1084 | |
1085 | void _serverAssertWithInfo(const client *c, const robj *o, const char *estr, const char *file, int line) { |
1086 | if (c) _serverAssertPrintClientInfo(c); |
1087 | if (o) _serverAssertPrintObject(o); |
1088 | _serverAssert(estr,file,line); |
1089 | } |
1090 | |
1091 | void _serverPanic(const char *file, int line, const char *msg, ...) { |
1092 | va_list ap; |
1093 | va_start(ap,msg); |
1094 | char fmtmsg[256]; |
1095 | vsnprintf(fmtmsg,sizeof(fmtmsg),msg,ap); |
1096 | va_end(ap); |
1097 | |
1098 | bugReportStart(); |
1099 | serverLog(LL_WARNING,"------------------------------------------------" ); |
1100 | serverLog(LL_WARNING,"!!! Software Failure. Press left mouse button to continue" ); |
1101 | serverLog(LL_WARNING,"Guru Meditation: %s #%s:%d" ,fmtmsg,file,line); |
1102 | |
1103 | if (server.crashlog_enabled) { |
1104 | #ifdef HAVE_BACKTRACE |
1105 | logStackTrace(NULL, 1); |
1106 | #endif |
1107 | printCrashReport(); |
1108 | } |
1109 | |
1110 | // remove the signal handler so on abort() we will output the crash report. |
1111 | removeSignalHandlers(); |
1112 | bugReportEnd(0, 0); |
1113 | } |
1114 | |
1115 | void bugReportStart(void) { |
1116 | pthread_mutex_lock(&bug_report_start_mutex); |
1117 | if (bug_report_start == 0) { |
1118 | serverLogRaw(LL_WARNING|LL_RAW, |
1119 | "\n\n=== REDIS BUG REPORT START: Cut & paste starting from here ===\n" ); |
1120 | bug_report_start = 1; |
1121 | } |
1122 | pthread_mutex_unlock(&bug_report_start_mutex); |
1123 | } |
1124 | |
1125 | #ifdef HAVE_BACKTRACE |
1126 | static void *getMcontextEip(ucontext_t *uc) { |
1127 | #define NOT_SUPPORTED() do {\ |
1128 | UNUSED(uc);\ |
1129 | return NULL;\ |
1130 | } while(0) |
1131 | #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6) |
1132 | /* OSX < 10.6 */ |
1133 | #if defined(__x86_64__) |
1134 | return (void*) uc->uc_mcontext->__ss.__rip; |
1135 | #elif defined(__i386__) |
1136 | return (void*) uc->uc_mcontext->__ss.__eip; |
1137 | #else |
1138 | return (void*) uc->uc_mcontext->__ss.__srr0; |
1139 | #endif |
1140 | #elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6) |
1141 | /* OSX >= 10.6 */ |
1142 | #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__) |
1143 | return (void*) uc->uc_mcontext->__ss.__rip; |
1144 | #elif defined(__i386__) |
1145 | return (void*) uc->uc_mcontext->__ss.__eip; |
1146 | #else |
1147 | /* OSX ARM64 */ |
1148 | return (void*) arm_thread_state64_get_pc(uc->uc_mcontext->__ss); |
1149 | #endif |
1150 | #elif defined(__linux__) |
1151 | /* Linux */ |
1152 | #if defined(__i386__) || ((defined(__X86_64__) || defined(__x86_64__)) && defined(__ILP32__)) |
1153 | return (void*) uc->uc_mcontext.gregs[14]; /* Linux 32 */ |
1154 | #elif defined(__X86_64__) || defined(__x86_64__) |
1155 | return (void*) uc->uc_mcontext.gregs[16]; /* Linux 64 */ |
1156 | #elif defined(__ia64__) /* Linux IA64 */ |
1157 | return (void*) uc->uc_mcontext.sc_ip; |
1158 | #elif defined(__arm__) /* Linux ARM */ |
1159 | return (void*) uc->uc_mcontext.arm_pc; |
1160 | #elif defined(__aarch64__) /* Linux AArch64 */ |
1161 | return (void*) uc->uc_mcontext.pc; |
1162 | #else |
1163 | NOT_SUPPORTED(); |
1164 | #endif |
1165 | #elif defined(__FreeBSD__) |
1166 | /* FreeBSD */ |
1167 | #if defined(__i386__) |
1168 | return (void*) uc->uc_mcontext.mc_eip; |
1169 | #elif defined(__x86_64__) |
1170 | return (void*) uc->uc_mcontext.mc_rip; |
1171 | #else |
1172 | NOT_SUPPORTED(); |
1173 | #endif |
1174 | #elif defined(__OpenBSD__) |
1175 | /* OpenBSD */ |
1176 | #if defined(__i386__) |
1177 | return (void*) uc->sc_eip; |
1178 | #elif defined(__x86_64__) |
1179 | return (void*) uc->sc_rip; |
1180 | #else |
1181 | NOT_SUPPORTED(); |
1182 | #endif |
1183 | #elif defined(__NetBSD__) |
1184 | #if defined(__i386__) |
1185 | return (void*) uc->uc_mcontext.__gregs[_REG_EIP]; |
1186 | #elif defined(__x86_64__) |
1187 | return (void*) uc->uc_mcontext.__gregs[_REG_RIP]; |
1188 | #else |
1189 | NOT_SUPPORTED(); |
1190 | #endif |
1191 | #elif defined(__DragonFly__) |
1192 | return (void*) uc->uc_mcontext.mc_rip; |
1193 | #else |
1194 | NOT_SUPPORTED(); |
1195 | #endif |
1196 | #undef NOT_SUPPORTED |
1197 | } |
1198 | |
1199 | REDIS_NO_SANITIZE("address" ) |
1200 | void logStackContent(void **sp) { |
1201 | int i; |
1202 | for (i = 15; i >= 0; i--) { |
1203 | unsigned long addr = (unsigned long) sp+i; |
1204 | unsigned long val = (unsigned long) sp[i]; |
1205 | |
1206 | if (sizeof(long) == 4) |
1207 | serverLog(LL_WARNING, "(%08lx) -> %08lx" , addr, val); |
1208 | else |
1209 | serverLog(LL_WARNING, "(%016lx) -> %016lx" , addr, val); |
1210 | } |
1211 | } |
1212 | |
1213 | /* Log dump of processor registers */ |
1214 | void logRegisters(ucontext_t *uc) { |
1215 | serverLog(LL_WARNING|LL_RAW, "\n------ REGISTERS ------\n" ); |
1216 | #define NOT_SUPPORTED() do {\ |
1217 | UNUSED(uc);\ |
1218 | serverLog(LL_WARNING,\ |
1219 | " Dumping of registers not supported for this OS/arch");\ |
1220 | } while(0) |
1221 | |
1222 | /* OSX */ |
1223 | #if defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6) |
1224 | /* OSX AMD64 */ |
1225 | #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__) |
1226 | serverLog(LL_WARNING, |
1227 | "\n" |
1228 | "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n" |
1229 | "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n" |
1230 | "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n" |
1231 | "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n" |
1232 | "RIP:%016lx EFL:%016lx\nCS :%016lx FS:%016lx GS:%016lx" , |
1233 | (unsigned long) uc->uc_mcontext->__ss.__rax, |
1234 | (unsigned long) uc->uc_mcontext->__ss.__rbx, |
1235 | (unsigned long) uc->uc_mcontext->__ss.__rcx, |
1236 | (unsigned long) uc->uc_mcontext->__ss.__rdx, |
1237 | (unsigned long) uc->uc_mcontext->__ss.__rdi, |
1238 | (unsigned long) uc->uc_mcontext->__ss.__rsi, |
1239 | (unsigned long) uc->uc_mcontext->__ss.__rbp, |
1240 | (unsigned long) uc->uc_mcontext->__ss.__rsp, |
1241 | (unsigned long) uc->uc_mcontext->__ss.__r8, |
1242 | (unsigned long) uc->uc_mcontext->__ss.__r9, |
1243 | (unsigned long) uc->uc_mcontext->__ss.__r10, |
1244 | (unsigned long) uc->uc_mcontext->__ss.__r11, |
1245 | (unsigned long) uc->uc_mcontext->__ss.__r12, |
1246 | (unsigned long) uc->uc_mcontext->__ss.__r13, |
1247 | (unsigned long) uc->uc_mcontext->__ss.__r14, |
1248 | (unsigned long) uc->uc_mcontext->__ss.__r15, |
1249 | (unsigned long) uc->uc_mcontext->__ss.__rip, |
1250 | (unsigned long) uc->uc_mcontext->__ss.__rflags, |
1251 | (unsigned long) uc->uc_mcontext->__ss.__cs, |
1252 | (unsigned long) uc->uc_mcontext->__ss.__fs, |
1253 | (unsigned long) uc->uc_mcontext->__ss.__gs |
1254 | ); |
1255 | logStackContent((void**)uc->uc_mcontext->__ss.__rsp); |
1256 | #elif defined(__i386__) |
1257 | /* OSX x86 */ |
1258 | serverLog(LL_WARNING, |
1259 | "\n" |
1260 | "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n" |
1261 | "EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n" |
1262 | "SS:%08lx EFL:%08lx EIP:%08lx CS :%08lx\n" |
1263 | "DS:%08lx ES:%08lx FS :%08lx GS :%08lx" , |
1264 | (unsigned long) uc->uc_mcontext->__ss.__eax, |
1265 | (unsigned long) uc->uc_mcontext->__ss.__ebx, |
1266 | (unsigned long) uc->uc_mcontext->__ss.__ecx, |
1267 | (unsigned long) uc->uc_mcontext->__ss.__edx, |
1268 | (unsigned long) uc->uc_mcontext->__ss.__edi, |
1269 | (unsigned long) uc->uc_mcontext->__ss.__esi, |
1270 | (unsigned long) uc->uc_mcontext->__ss.__ebp, |
1271 | (unsigned long) uc->uc_mcontext->__ss.__esp, |
1272 | (unsigned long) uc->uc_mcontext->__ss.__ss, |
1273 | (unsigned long) uc->uc_mcontext->__ss.__eflags, |
1274 | (unsigned long) uc->uc_mcontext->__ss.__eip, |
1275 | (unsigned long) uc->uc_mcontext->__ss.__cs, |
1276 | (unsigned long) uc->uc_mcontext->__ss.__ds, |
1277 | (unsigned long) uc->uc_mcontext->__ss.__es, |
1278 | (unsigned long) uc->uc_mcontext->__ss.__fs, |
1279 | (unsigned long) uc->uc_mcontext->__ss.__gs |
1280 | ); |
1281 | logStackContent((void**)uc->uc_mcontext->__ss.__esp); |
1282 | #else |
1283 | /* OSX ARM64 */ |
1284 | serverLog(LL_WARNING, |
1285 | "\n" |
1286 | "x0:%016lx x1:%016lx x2:%016lx x3:%016lx\n" |
1287 | "x4:%016lx x5:%016lx x6:%016lx x7:%016lx\n" |
1288 | "x8:%016lx x9:%016lx x10:%016lx x11:%016lx\n" |
1289 | "x12:%016lx x13:%016lx x14:%016lx x15:%016lx\n" |
1290 | "x16:%016lx x17:%016lx x18:%016lx x19:%016lx\n" |
1291 | "x20:%016lx x21:%016lx x22:%016lx x23:%016lx\n" |
1292 | "x24:%016lx x25:%016lx x26:%016lx x27:%016lx\n" |
1293 | "x28:%016lx fp:%016lx lr:%016lx\n" |
1294 | "sp:%016lx pc:%016lx cpsr:%08lx\n" , |
1295 | (unsigned long) uc->uc_mcontext->__ss.__x[0], |
1296 | (unsigned long) uc->uc_mcontext->__ss.__x[1], |
1297 | (unsigned long) uc->uc_mcontext->__ss.__x[2], |
1298 | (unsigned long) uc->uc_mcontext->__ss.__x[3], |
1299 | (unsigned long) uc->uc_mcontext->__ss.__x[4], |
1300 | (unsigned long) uc->uc_mcontext->__ss.__x[5], |
1301 | (unsigned long) uc->uc_mcontext->__ss.__x[6], |
1302 | (unsigned long) uc->uc_mcontext->__ss.__x[7], |
1303 | (unsigned long) uc->uc_mcontext->__ss.__x[8], |
1304 | (unsigned long) uc->uc_mcontext->__ss.__x[9], |
1305 | (unsigned long) uc->uc_mcontext->__ss.__x[10], |
1306 | (unsigned long) uc->uc_mcontext->__ss.__x[11], |
1307 | (unsigned long) uc->uc_mcontext->__ss.__x[12], |
1308 | (unsigned long) uc->uc_mcontext->__ss.__x[13], |
1309 | (unsigned long) uc->uc_mcontext->__ss.__x[14], |
1310 | (unsigned long) uc->uc_mcontext->__ss.__x[15], |
1311 | (unsigned long) uc->uc_mcontext->__ss.__x[16], |
1312 | (unsigned long) uc->uc_mcontext->__ss.__x[17], |
1313 | (unsigned long) uc->uc_mcontext->__ss.__x[18], |
1314 | (unsigned long) uc->uc_mcontext->__ss.__x[19], |
1315 | (unsigned long) uc->uc_mcontext->__ss.__x[20], |
1316 | (unsigned long) uc->uc_mcontext->__ss.__x[21], |
1317 | (unsigned long) uc->uc_mcontext->__ss.__x[22], |
1318 | (unsigned long) uc->uc_mcontext->__ss.__x[23], |
1319 | (unsigned long) uc->uc_mcontext->__ss.__x[24], |
1320 | (unsigned long) uc->uc_mcontext->__ss.__x[25], |
1321 | (unsigned long) uc->uc_mcontext->__ss.__x[26], |
1322 | (unsigned long) uc->uc_mcontext->__ss.__x[27], |
1323 | (unsigned long) uc->uc_mcontext->__ss.__x[28], |
1324 | (unsigned long) arm_thread_state64_get_fp(uc->uc_mcontext->__ss), |
1325 | (unsigned long) arm_thread_state64_get_lr(uc->uc_mcontext->__ss), |
1326 | (unsigned long) arm_thread_state64_get_sp(uc->uc_mcontext->__ss), |
1327 | (unsigned long) arm_thread_state64_get_pc(uc->uc_mcontext->__ss), |
1328 | (unsigned long) uc->uc_mcontext->__ss.__cpsr |
1329 | ); |
1330 | logStackContent((void**) arm_thread_state64_get_sp(uc->uc_mcontext->__ss)); |
1331 | #endif |
1332 | /* Linux */ |
1333 | #elif defined(__linux__) |
1334 | /* Linux x86 */ |
1335 | #if defined(__i386__) || ((defined(__X86_64__) || defined(__x86_64__)) && defined(__ILP32__)) |
1336 | serverLog(LL_WARNING, |
1337 | "\n" |
1338 | "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n" |
1339 | "EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n" |
1340 | "SS :%08lx EFL:%08lx EIP:%08lx CS:%08lx\n" |
1341 | "DS :%08lx ES :%08lx FS :%08lx GS:%08lx" , |
1342 | (unsigned long) uc->uc_mcontext.gregs[11], |
1343 | (unsigned long) uc->uc_mcontext.gregs[8], |
1344 | (unsigned long) uc->uc_mcontext.gregs[10], |
1345 | (unsigned long) uc->uc_mcontext.gregs[9], |
1346 | (unsigned long) uc->uc_mcontext.gregs[4], |
1347 | (unsigned long) uc->uc_mcontext.gregs[5], |
1348 | (unsigned long) uc->uc_mcontext.gregs[6], |
1349 | (unsigned long) uc->uc_mcontext.gregs[7], |
1350 | (unsigned long) uc->uc_mcontext.gregs[18], |
1351 | (unsigned long) uc->uc_mcontext.gregs[17], |
1352 | (unsigned long) uc->uc_mcontext.gregs[14], |
1353 | (unsigned long) uc->uc_mcontext.gregs[15], |
1354 | (unsigned long) uc->uc_mcontext.gregs[3], |
1355 | (unsigned long) uc->uc_mcontext.gregs[2], |
1356 | (unsigned long) uc->uc_mcontext.gregs[1], |
1357 | (unsigned long) uc->uc_mcontext.gregs[0] |
1358 | ); |
1359 | logStackContent((void**)uc->uc_mcontext.gregs[7]); |
1360 | #elif defined(__X86_64__) || defined(__x86_64__) |
1361 | /* Linux AMD64 */ |
1362 | serverLog(LL_WARNING, |
1363 | "\n" |
1364 | "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n" |
1365 | "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n" |
1366 | "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n" |
1367 | "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n" |
1368 | "RIP:%016lx EFL:%016lx\nCSGSFS:%016lx" , |
1369 | (unsigned long) uc->uc_mcontext.gregs[13], |
1370 | (unsigned long) uc->uc_mcontext.gregs[11], |
1371 | (unsigned long) uc->uc_mcontext.gregs[14], |
1372 | (unsigned long) uc->uc_mcontext.gregs[12], |
1373 | (unsigned long) uc->uc_mcontext.gregs[8], |
1374 | (unsigned long) uc->uc_mcontext.gregs[9], |
1375 | (unsigned long) uc->uc_mcontext.gregs[10], |
1376 | (unsigned long) uc->uc_mcontext.gregs[15], |
1377 | (unsigned long) uc->uc_mcontext.gregs[0], |
1378 | (unsigned long) uc->uc_mcontext.gregs[1], |
1379 | (unsigned long) uc->uc_mcontext.gregs[2], |
1380 | (unsigned long) uc->uc_mcontext.gregs[3], |
1381 | (unsigned long) uc->uc_mcontext.gregs[4], |
1382 | (unsigned long) uc->uc_mcontext.gregs[5], |
1383 | (unsigned long) uc->uc_mcontext.gregs[6], |
1384 | (unsigned long) uc->uc_mcontext.gregs[7], |
1385 | (unsigned long) uc->uc_mcontext.gregs[16], |
1386 | (unsigned long) uc->uc_mcontext.gregs[17], |
1387 | (unsigned long) uc->uc_mcontext.gregs[18] |
1388 | ); |
1389 | logStackContent((void**)uc->uc_mcontext.gregs[15]); |
1390 | #elif defined(__aarch64__) /* Linux AArch64 */ |
1391 | serverLog(LL_WARNING, |
1392 | "\n" |
1393 | "X18:%016lx X19:%016lx\nX20:%016lx X21:%016lx\n" |
1394 | "X22:%016lx X23:%016lx\nX24:%016lx X25:%016lx\n" |
1395 | "X26:%016lx X27:%016lx\nX28:%016lx X29:%016lx\n" |
1396 | "X30:%016lx\n" |
1397 | "pc:%016lx sp:%016lx\npstate:%016lx fault_address:%016lx\n" , |
1398 | (unsigned long) uc->uc_mcontext.regs[18], |
1399 | (unsigned long) uc->uc_mcontext.regs[19], |
1400 | (unsigned long) uc->uc_mcontext.regs[20], |
1401 | (unsigned long) uc->uc_mcontext.regs[21], |
1402 | (unsigned long) uc->uc_mcontext.regs[22], |
1403 | (unsigned long) uc->uc_mcontext.regs[23], |
1404 | (unsigned long) uc->uc_mcontext.regs[24], |
1405 | (unsigned long) uc->uc_mcontext.regs[25], |
1406 | (unsigned long) uc->uc_mcontext.regs[26], |
1407 | (unsigned long) uc->uc_mcontext.regs[27], |
1408 | (unsigned long) uc->uc_mcontext.regs[28], |
1409 | (unsigned long) uc->uc_mcontext.regs[29], |
1410 | (unsigned long) uc->uc_mcontext.regs[30], |
1411 | (unsigned long) uc->uc_mcontext.pc, |
1412 | (unsigned long) uc->uc_mcontext.sp, |
1413 | (unsigned long) uc->uc_mcontext.pstate, |
1414 | (unsigned long) uc->uc_mcontext.fault_address |
1415 | ); |
1416 | logStackContent((void**)uc->uc_mcontext.sp); |
1417 | #elif defined(__arm__) /* Linux ARM */ |
1418 | serverLog(LL_WARNING, |
1419 | "\n" |
1420 | "R10:%016lx R9 :%016lx\nR8 :%016lx R7 :%016lx\n" |
1421 | "R6 :%016lx R5 :%016lx\nR4 :%016lx R3 :%016lx\n" |
1422 | "R2 :%016lx R1 :%016lx\nR0 :%016lx EC :%016lx\n" |
1423 | "fp: %016lx ip:%016lx\n" |
1424 | "pc:%016lx sp:%016lx\ncpsr:%016lx fault_address:%016lx\n" , |
1425 | (unsigned long) uc->uc_mcontext.arm_r10, |
1426 | (unsigned long) uc->uc_mcontext.arm_r9, |
1427 | (unsigned long) uc->uc_mcontext.arm_r8, |
1428 | (unsigned long) uc->uc_mcontext.arm_r7, |
1429 | (unsigned long) uc->uc_mcontext.arm_r6, |
1430 | (unsigned long) uc->uc_mcontext.arm_r5, |
1431 | (unsigned long) uc->uc_mcontext.arm_r4, |
1432 | (unsigned long) uc->uc_mcontext.arm_r3, |
1433 | (unsigned long) uc->uc_mcontext.arm_r2, |
1434 | (unsigned long) uc->uc_mcontext.arm_r1, |
1435 | (unsigned long) uc->uc_mcontext.arm_r0, |
1436 | (unsigned long) uc->uc_mcontext.error_code, |
1437 | (unsigned long) uc->uc_mcontext.arm_fp, |
1438 | (unsigned long) uc->uc_mcontext.arm_ip, |
1439 | (unsigned long) uc->uc_mcontext.arm_pc, |
1440 | (unsigned long) uc->uc_mcontext.arm_sp, |
1441 | (unsigned long) uc->uc_mcontext.arm_cpsr, |
1442 | (unsigned long) uc->uc_mcontext.fault_address |
1443 | ); |
1444 | logStackContent((void**)uc->uc_mcontext.arm_sp); |
1445 | #else |
1446 | NOT_SUPPORTED(); |
1447 | #endif |
1448 | #elif defined(__FreeBSD__) |
1449 | #if defined(__x86_64__) |
1450 | serverLog(LL_WARNING, |
1451 | "\n" |
1452 | "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n" |
1453 | "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n" |
1454 | "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n" |
1455 | "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n" |
1456 | "RIP:%016lx EFL:%016lx\nCSGSFS:%016lx" , |
1457 | (unsigned long) uc->uc_mcontext.mc_rax, |
1458 | (unsigned long) uc->uc_mcontext.mc_rbx, |
1459 | (unsigned long) uc->uc_mcontext.mc_rcx, |
1460 | (unsigned long) uc->uc_mcontext.mc_rdx, |
1461 | (unsigned long) uc->uc_mcontext.mc_rdi, |
1462 | (unsigned long) uc->uc_mcontext.mc_rsi, |
1463 | (unsigned long) uc->uc_mcontext.mc_rbp, |
1464 | (unsigned long) uc->uc_mcontext.mc_rsp, |
1465 | (unsigned long) uc->uc_mcontext.mc_r8, |
1466 | (unsigned long) uc->uc_mcontext.mc_r9, |
1467 | (unsigned long) uc->uc_mcontext.mc_r10, |
1468 | (unsigned long) uc->uc_mcontext.mc_r11, |
1469 | (unsigned long) uc->uc_mcontext.mc_r12, |
1470 | (unsigned long) uc->uc_mcontext.mc_r13, |
1471 | (unsigned long) uc->uc_mcontext.mc_r14, |
1472 | (unsigned long) uc->uc_mcontext.mc_r15, |
1473 | (unsigned long) uc->uc_mcontext.mc_rip, |
1474 | (unsigned long) uc->uc_mcontext.mc_rflags, |
1475 | (unsigned long) uc->uc_mcontext.mc_cs |
1476 | ); |
1477 | logStackContent((void**)uc->uc_mcontext.mc_rsp); |
1478 | #elif defined(__i386__) |
1479 | serverLog(LL_WARNING, |
1480 | "\n" |
1481 | "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n" |
1482 | "EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n" |
1483 | "SS :%08lx EFL:%08lx EIP:%08lx CS:%08lx\n" |
1484 | "DS :%08lx ES :%08lx FS :%08lx GS:%08lx" , |
1485 | (unsigned long) uc->uc_mcontext.mc_eax, |
1486 | (unsigned long) uc->uc_mcontext.mc_ebx, |
1487 | (unsigned long) uc->uc_mcontext.mc_ebx, |
1488 | (unsigned long) uc->uc_mcontext.mc_edx, |
1489 | (unsigned long) uc->uc_mcontext.mc_edi, |
1490 | (unsigned long) uc->uc_mcontext.mc_esi, |
1491 | (unsigned long) uc->uc_mcontext.mc_ebp, |
1492 | (unsigned long) uc->uc_mcontext.mc_esp, |
1493 | (unsigned long) uc->uc_mcontext.mc_ss, |
1494 | (unsigned long) uc->uc_mcontext.mc_eflags, |
1495 | (unsigned long) uc->uc_mcontext.mc_eip, |
1496 | (unsigned long) uc->uc_mcontext.mc_cs, |
1497 | (unsigned long) uc->uc_mcontext.mc_es, |
1498 | (unsigned long) uc->uc_mcontext.mc_fs, |
1499 | (unsigned long) uc->uc_mcontext.mc_gs |
1500 | ); |
1501 | logStackContent((void**)uc->uc_mcontext.mc_esp); |
1502 | #else |
1503 | NOT_SUPPORTED(); |
1504 | #endif |
1505 | #elif defined(__OpenBSD__) |
1506 | #if defined(__x86_64__) |
1507 | serverLog(LL_WARNING, |
1508 | "\n" |
1509 | "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n" |
1510 | "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n" |
1511 | "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n" |
1512 | "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n" |
1513 | "RIP:%016lx EFL:%016lx\nCSGSFS:%016lx" , |
1514 | (unsigned long) uc->sc_rax, |
1515 | (unsigned long) uc->sc_rbx, |
1516 | (unsigned long) uc->sc_rcx, |
1517 | (unsigned long) uc->sc_rdx, |
1518 | (unsigned long) uc->sc_rdi, |
1519 | (unsigned long) uc->sc_rsi, |
1520 | (unsigned long) uc->sc_rbp, |
1521 | (unsigned long) uc->sc_rsp, |
1522 | (unsigned long) uc->sc_r8, |
1523 | (unsigned long) uc->sc_r9, |
1524 | (unsigned long) uc->sc_r10, |
1525 | (unsigned long) uc->sc_r11, |
1526 | (unsigned long) uc->sc_r12, |
1527 | (unsigned long) uc->sc_r13, |
1528 | (unsigned long) uc->sc_r14, |
1529 | (unsigned long) uc->sc_r15, |
1530 | (unsigned long) uc->sc_rip, |
1531 | (unsigned long) uc->sc_rflags, |
1532 | (unsigned long) uc->sc_cs |
1533 | ); |
1534 | logStackContent((void**)uc->sc_rsp); |
1535 | #elif defined(__i386__) |
1536 | serverLog(LL_WARNING, |
1537 | "\n" |
1538 | "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n" |
1539 | "EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n" |
1540 | "SS :%08lx EFL:%08lx EIP:%08lx CS:%08lx\n" |
1541 | "DS :%08lx ES :%08lx FS :%08lx GS:%08lx" , |
1542 | (unsigned long) uc->sc_eax, |
1543 | (unsigned long) uc->sc_ebx, |
1544 | (unsigned long) uc->sc_ebx, |
1545 | (unsigned long) uc->sc_edx, |
1546 | (unsigned long) uc->sc_edi, |
1547 | (unsigned long) uc->sc_esi, |
1548 | (unsigned long) uc->sc_ebp, |
1549 | (unsigned long) uc->sc_esp, |
1550 | (unsigned long) uc->sc_ss, |
1551 | (unsigned long) uc->sc_eflags, |
1552 | (unsigned long) uc->sc_eip, |
1553 | (unsigned long) uc->sc_cs, |
1554 | (unsigned long) uc->sc_es, |
1555 | (unsigned long) uc->sc_fs, |
1556 | (unsigned long) uc->sc_gs |
1557 | ); |
1558 | logStackContent((void**)uc->sc_esp); |
1559 | #else |
1560 | NOT_SUPPORTED(); |
1561 | #endif |
1562 | #elif defined(__NetBSD__) |
1563 | #if defined(__x86_64__) |
1564 | serverLog(LL_WARNING, |
1565 | "\n" |
1566 | "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n" |
1567 | "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n" |
1568 | "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n" |
1569 | "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n" |
1570 | "RIP:%016lx EFL:%016lx\nCSGSFS:%016lx" , |
1571 | (unsigned long) uc->uc_mcontext.__gregs[_REG_RAX], |
1572 | (unsigned long) uc->uc_mcontext.__gregs[_REG_RBX], |
1573 | (unsigned long) uc->uc_mcontext.__gregs[_REG_RCX], |
1574 | (unsigned long) uc->uc_mcontext.__gregs[_REG_RDX], |
1575 | (unsigned long) uc->uc_mcontext.__gregs[_REG_RDI], |
1576 | (unsigned long) uc->uc_mcontext.__gregs[_REG_RSI], |
1577 | (unsigned long) uc->uc_mcontext.__gregs[_REG_RBP], |
1578 | (unsigned long) uc->uc_mcontext.__gregs[_REG_RSP], |
1579 | (unsigned long) uc->uc_mcontext.__gregs[_REG_R8], |
1580 | (unsigned long) uc->uc_mcontext.__gregs[_REG_R9], |
1581 | (unsigned long) uc->uc_mcontext.__gregs[_REG_R10], |
1582 | (unsigned long) uc->uc_mcontext.__gregs[_REG_R11], |
1583 | (unsigned long) uc->uc_mcontext.__gregs[_REG_R12], |
1584 | (unsigned long) uc->uc_mcontext.__gregs[_REG_R13], |
1585 | (unsigned long) uc->uc_mcontext.__gregs[_REG_R14], |
1586 | (unsigned long) uc->uc_mcontext.__gregs[_REG_R15], |
1587 | (unsigned long) uc->uc_mcontext.__gregs[_REG_RIP], |
1588 | (unsigned long) uc->uc_mcontext.__gregs[_REG_RFLAGS], |
1589 | (unsigned long) uc->uc_mcontext.__gregs[_REG_CS] |
1590 | ); |
1591 | logStackContent((void**)uc->uc_mcontext.__gregs[_REG_RSP]); |
1592 | #elif defined(__i386__) |
1593 | serverLog(LL_WARNING, |
1594 | "\n" |
1595 | "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n" |
1596 | "EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n" |
1597 | "SS :%08lx EFL:%08lx EIP:%08lx CS:%08lx\n" |
1598 | "DS :%08lx ES :%08lx FS :%08lx GS:%08lx" , |
1599 | (unsigned long) uc->uc_mcontext.__gregs[_REG_EAX], |
1600 | (unsigned long) uc->uc_mcontext.__gregs[_REG_EBX], |
1601 | (unsigned long) uc->uc_mcontext.__gregs[_REG_EDX], |
1602 | (unsigned long) uc->uc_mcontext.__gregs[_REG_EDI], |
1603 | (unsigned long) uc->uc_mcontext.__gregs[_REG_ESI], |
1604 | (unsigned long) uc->uc_mcontext.__gregs[_REG_EBP], |
1605 | (unsigned long) uc->uc_mcontext.__gregs[_REG_ESP], |
1606 | (unsigned long) uc->uc_mcontext.__gregs[_REG_SS], |
1607 | (unsigned long) uc->uc_mcontext.__gregs[_REG_EFLAGS], |
1608 | (unsigned long) uc->uc_mcontext.__gregs[_REG_EIP], |
1609 | (unsigned long) uc->uc_mcontext.__gregs[_REG_CS], |
1610 | (unsigned long) uc->uc_mcontext.__gregs[_REG_ES], |
1611 | (unsigned long) uc->uc_mcontext.__gregs[_REG_FS], |
1612 | (unsigned long) uc->uc_mcontext.__gregs[_REG_GS] |
1613 | ); |
1614 | #else |
1615 | NOT_SUPPORTED(); |
1616 | #endif |
1617 | #elif defined(__DragonFly__) |
1618 | serverLog(LL_WARNING, |
1619 | "\n" |
1620 | "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n" |
1621 | "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n" |
1622 | "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n" |
1623 | "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n" |
1624 | "RIP:%016lx EFL:%016lx\nCSGSFS:%016lx" , |
1625 | (unsigned long) uc->uc_mcontext.mc_rax, |
1626 | (unsigned long) uc->uc_mcontext.mc_rbx, |
1627 | (unsigned long) uc->uc_mcontext.mc_rcx, |
1628 | (unsigned long) uc->uc_mcontext.mc_rdx, |
1629 | (unsigned long) uc->uc_mcontext.mc_rdi, |
1630 | (unsigned long) uc->uc_mcontext.mc_rsi, |
1631 | (unsigned long) uc->uc_mcontext.mc_rbp, |
1632 | (unsigned long) uc->uc_mcontext.mc_rsp, |
1633 | (unsigned long) uc->uc_mcontext.mc_r8, |
1634 | (unsigned long) uc->uc_mcontext.mc_r9, |
1635 | (unsigned long) uc->uc_mcontext.mc_r10, |
1636 | (unsigned long) uc->uc_mcontext.mc_r11, |
1637 | (unsigned long) uc->uc_mcontext.mc_r12, |
1638 | (unsigned long) uc->uc_mcontext.mc_r13, |
1639 | (unsigned long) uc->uc_mcontext.mc_r14, |
1640 | (unsigned long) uc->uc_mcontext.mc_r15, |
1641 | (unsigned long) uc->uc_mcontext.mc_rip, |
1642 | (unsigned long) uc->uc_mcontext.mc_rflags, |
1643 | (unsigned long) uc->uc_mcontext.mc_cs |
1644 | ); |
1645 | logStackContent((void**)uc->uc_mcontext.mc_rsp); |
1646 | #else |
1647 | NOT_SUPPORTED(); |
1648 | #endif |
1649 | #undef NOT_SUPPORTED |
1650 | } |
1651 | |
1652 | #endif /* HAVE_BACKTRACE */ |
1653 | |
1654 | /* Return a file descriptor to write directly to the Redis log with the |
1655 | * write(2) syscall, that can be used in critical sections of the code |
1656 | * where the rest of Redis can't be trusted (for example during the memory |
1657 | * test) or when an API call requires a raw fd. |
1658 | * |
1659 | * Close it with closeDirectLogFiledes(). */ |
1660 | int openDirectLogFiledes(void) { |
1661 | int log_to_stdout = server.logfile[0] == '\0'; |
1662 | int fd = log_to_stdout ? |
1663 | STDOUT_FILENO : |
1664 | open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644); |
1665 | return fd; |
1666 | } |
1667 | |
1668 | /* Used to close what closeDirectLogFiledes() returns. */ |
1669 | void closeDirectLogFiledes(int fd) { |
1670 | int log_to_stdout = server.logfile[0] == '\0'; |
1671 | if (!log_to_stdout) close(fd); |
1672 | } |
1673 | |
1674 | #ifdef HAVE_BACKTRACE |
1675 | |
1676 | /* Logs the stack trace using the backtrace() call. This function is designed |
1677 | * to be called from signal handlers safely. |
1678 | * The eip argument is optional (can take NULL). |
1679 | * The uplevel argument indicates how many of the calling functions to skip. |
1680 | */ |
1681 | void logStackTrace(void *eip, int uplevel) { |
1682 | void *trace[100]; |
1683 | int trace_size = 0, fd = openDirectLogFiledes(); |
1684 | char *msg; |
1685 | uplevel++; /* skip this function */ |
1686 | |
1687 | if (fd == -1) return; /* If we can't log there is anything to do. */ |
1688 | |
1689 | /* Get the stack trace first! */ |
1690 | trace_size = backtrace(trace, 100); |
1691 | |
1692 | msg = "\n------ STACK TRACE ------\n" ; |
1693 | if (write(fd,msg,strlen(msg)) == -1) {/* Avoid warning. */}; |
1694 | |
1695 | if (eip) { |
1696 | /* Write EIP to the log file*/ |
1697 | msg = "EIP:\n" ; |
1698 | if (write(fd,msg,strlen(msg)) == -1) {/* Avoid warning. */}; |
1699 | backtrace_symbols_fd(&eip, 1, fd); |
1700 | } |
1701 | |
1702 | /* Write symbols to log file */ |
1703 | msg = "\nBacktrace:\n" ; |
1704 | if (write(fd,msg,strlen(msg)) == -1) {/* Avoid warning. */}; |
1705 | backtrace_symbols_fd(trace+uplevel, trace_size-uplevel, fd); |
1706 | |
1707 | /* Cleanup */ |
1708 | closeDirectLogFiledes(fd); |
1709 | } |
1710 | |
1711 | #endif /* HAVE_BACKTRACE */ |
1712 | |
1713 | /* Log global server info */ |
1714 | void logServerInfo(void) { |
1715 | sds infostring, clients; |
1716 | serverLogRaw(LL_WARNING|LL_RAW, "\n------ INFO OUTPUT ------\n" ); |
1717 | int all = 0, everything = 0; |
1718 | robj *argv[1]; |
1719 | argv[0] = createStringObject("all" , strlen("all" )); |
1720 | dict *section_dict = genInfoSectionDict(argv, 1, NULL, &all, &everything); |
1721 | infostring = genRedisInfoString(section_dict, all, everything); |
1722 | serverLogRaw(LL_WARNING|LL_RAW, infostring); |
1723 | serverLogRaw(LL_WARNING|LL_RAW, "\n------ CLIENT LIST OUTPUT ------\n" ); |
1724 | clients = getAllClientsInfoString(-1); |
1725 | serverLogRaw(LL_WARNING|LL_RAW, clients); |
1726 | sdsfree(infostring); |
1727 | sdsfree(clients); |
1728 | releaseInfoSectionDict(section_dict); |
1729 | decrRefCount(argv[0]); |
1730 | } |
1731 | |
1732 | /* Log certain config values, which can be used for debuggin */ |
1733 | void logConfigDebugInfo(void) { |
1734 | sds configstring; |
1735 | configstring = getConfigDebugInfo(); |
1736 | serverLogRaw(LL_WARNING|LL_RAW, "\n------ CONFIG DEBUG OUTPUT ------\n" ); |
1737 | serverLogRaw(LL_WARNING|LL_RAW, configstring); |
1738 | sdsfree(configstring); |
1739 | } |
1740 | |
1741 | /* Log modules info. Something we wanna do last since we fear it may crash. */ |
1742 | void logModulesInfo(void) { |
1743 | serverLogRaw(LL_WARNING|LL_RAW, "\n------ MODULES INFO OUTPUT ------\n" ); |
1744 | sds infostring = modulesCollectInfo(sdsempty(), NULL, 1, 0); |
1745 | serverLogRaw(LL_WARNING|LL_RAW, infostring); |
1746 | sdsfree(infostring); |
1747 | } |
1748 | |
1749 | /* Log information about the "current" client, that is, the client that is |
1750 | * currently being served by Redis. May be NULL if Redis is not serving a |
1751 | * client right now. */ |
1752 | void logCurrentClient(void) { |
1753 | if (server.current_client == NULL) return; |
1754 | |
1755 | client *cc = server.current_client; |
1756 | sds client; |
1757 | int j; |
1758 | |
1759 | serverLogRaw(LL_WARNING|LL_RAW, "\n------ CURRENT CLIENT INFO ------\n" ); |
1760 | client = catClientInfoString(sdsempty(),cc); |
1761 | serverLog(LL_WARNING|LL_RAW,"%s\n" , client); |
1762 | sdsfree(client); |
1763 | for (j = 0; j < cc->argc; j++) { |
1764 | robj *decoded; |
1765 | decoded = getDecodedObject(cc->argv[j]); |
1766 | sds repr = sdscatrepr(sdsempty(),decoded->ptr, min(sdslen(decoded->ptr), 128)); |
1767 | serverLog(LL_WARNING|LL_RAW,"argv[%d]: '%s'\n" , j, (char*)repr); |
1768 | sdsfree(repr); |
1769 | decrRefCount(decoded); |
1770 | } |
1771 | /* Check if the first argument, usually a key, is found inside the |
1772 | * selected DB, and if so print info about the associated object. */ |
1773 | if (cc->argc > 1) { |
1774 | robj *val, *key; |
1775 | dictEntry *de; |
1776 | |
1777 | key = getDecodedObject(cc->argv[1]); |
1778 | de = dictFind(cc->db->dict, key->ptr); |
1779 | if (de) { |
1780 | val = dictGetVal(de); |
1781 | serverLog(LL_WARNING,"key '%s' found in DB containing the following object:" , (char*)key->ptr); |
1782 | serverLogObjectDebugInfo(val); |
1783 | } |
1784 | decrRefCount(key); |
1785 | } |
1786 | } |
1787 | |
1788 | #if defined(HAVE_PROC_MAPS) |
1789 | |
1790 | #define MEMTEST_MAX_REGIONS 128 |
1791 | |
1792 | /* A non destructive memory test executed during segfault. */ |
1793 | int memtest_test_linux_anonymous_maps(void) { |
1794 | FILE *fp; |
1795 | char line[1024]; |
1796 | char logbuf[1024]; |
1797 | size_t start_addr, end_addr, size; |
1798 | size_t start_vect[MEMTEST_MAX_REGIONS]; |
1799 | size_t size_vect[MEMTEST_MAX_REGIONS]; |
1800 | int regions = 0, j; |
1801 | |
1802 | int fd = openDirectLogFiledes(); |
1803 | if (!fd) return 0; |
1804 | |
1805 | fp = fopen("/proc/self/maps" ,"r" ); |
1806 | if (!fp) { |
1807 | closeDirectLogFiledes(fd); |
1808 | return 0; |
1809 | } |
1810 | while(fgets(line,sizeof(line),fp) != NULL) { |
1811 | char *start, *end, *p = line; |
1812 | |
1813 | start = p; |
1814 | p = strchr(p,'-'); |
1815 | if (!p) continue; |
1816 | *p++ = '\0'; |
1817 | end = p; |
1818 | p = strchr(p,' '); |
1819 | if (!p) continue; |
1820 | *p++ = '\0'; |
1821 | if (strstr(p,"stack" ) || |
1822 | strstr(p,"vdso" ) || |
1823 | strstr(p,"vsyscall" )) continue; |
1824 | if (!strstr(p,"00:00" )) continue; |
1825 | if (!strstr(p,"rw" )) continue; |
1826 | |
1827 | start_addr = strtoul(start,NULL,16); |
1828 | end_addr = strtoul(end,NULL,16); |
1829 | size = end_addr-start_addr; |
1830 | |
1831 | start_vect[regions] = start_addr; |
1832 | size_vect[regions] = size; |
1833 | snprintf(logbuf,sizeof(logbuf), |
1834 | "*** Preparing to test memory region %lx (%lu bytes)\n" , |
1835 | (unsigned long) start_vect[regions], |
1836 | (unsigned long) size_vect[regions]); |
1837 | if (write(fd,logbuf,strlen(logbuf)) == -1) { /* Nothing to do. */ } |
1838 | regions++; |
1839 | } |
1840 | |
1841 | int errors = 0; |
1842 | for (j = 0; j < regions; j++) { |
1843 | if (write(fd,"." ,1) == -1) { /* Nothing to do. */ } |
1844 | errors += memtest_preserving_test((void*)start_vect[j],size_vect[j],1); |
1845 | if (write(fd, errors ? "E" : "O" ,1) == -1) { /* Nothing to do. */ } |
1846 | } |
1847 | if (write(fd,"\n" ,1) == -1) { /* Nothing to do. */ } |
1848 | |
1849 | /* NOTE: It is very important to close the file descriptor only now |
1850 | * because closing it before may result into unmapping of some memory |
1851 | * region that we are testing. */ |
1852 | fclose(fp); |
1853 | closeDirectLogFiledes(fd); |
1854 | return errors; |
1855 | } |
1856 | #endif /* HAVE_PROC_MAPS */ |
1857 | |
1858 | static void killMainThread(void) { |
1859 | int err; |
1860 | if (pthread_self() != server.main_thread_id && pthread_cancel(server.main_thread_id) == 0) { |
1861 | if ((err = pthread_join(server.main_thread_id,NULL)) != 0) { |
1862 | serverLog(LL_WARNING, "main thread can not be joined: %s" , strerror(err)); |
1863 | } else { |
1864 | serverLog(LL_WARNING, "main thread terminated" ); |
1865 | } |
1866 | } |
1867 | } |
1868 | |
1869 | /* Kill the running threads (other than current) in an unclean way. This function |
1870 | * should be used only when it's critical to stop the threads for some reason. |
1871 | * Currently Redis does this only on crash (for instance on SIGSEGV) in order |
1872 | * to perform a fast memory check without other threads messing with memory. */ |
1873 | void killThreads(void) { |
1874 | killMainThread(); |
1875 | bioKillThreads(); |
1876 | killIOThreads(); |
1877 | } |
1878 | |
1879 | void doFastMemoryTest(void) { |
1880 | #if defined(HAVE_PROC_MAPS) |
1881 | if (server.memcheck_enabled) { |
1882 | /* Test memory */ |
1883 | serverLogRaw(LL_WARNING|LL_RAW, "\n------ FAST MEMORY TEST ------\n" ); |
1884 | killThreads(); |
1885 | if (memtest_test_linux_anonymous_maps()) { |
1886 | serverLogRaw(LL_WARNING|LL_RAW, |
1887 | "!!! MEMORY ERROR DETECTED! Check your memory ASAP !!!\n" ); |
1888 | } else { |
1889 | serverLogRaw(LL_WARNING|LL_RAW, |
1890 | "Fast memory test PASSED, however your memory can still be broken. Please run a memory test for several hours if possible.\n" ); |
1891 | } |
1892 | } |
1893 | #endif /* HAVE_PROC_MAPS */ |
1894 | } |
1895 | |
1896 | /* Scans the (assumed) x86 code starting at addr, for a max of `len` |
1897 | * bytes, searching for E8 (callq) opcodes, and dumping the symbols |
1898 | * and the call offset if they appear to be valid. */ |
1899 | void dumpX86Calls(void *addr, size_t len) { |
1900 | size_t j; |
1901 | unsigned char *p = addr; |
1902 | Dl_info info; |
1903 | /* Hash table to best-effort avoid printing the same symbol |
1904 | * multiple times. */ |
1905 | unsigned long ht[256] = {0}; |
1906 | |
1907 | if (len < 5) return; |
1908 | for (j = 0; j < len-4; j++) { |
1909 | if (p[j] != 0xE8) continue; /* Not an E8 CALL opcode. */ |
1910 | unsigned long target = (unsigned long)addr+j+5; |
1911 | uint32_t tmp; |
1912 | memcpy(&tmp, p+j+1, sizeof(tmp)); |
1913 | target += tmp; |
1914 | if (dladdr((void*)target, &info) != 0 && info.dli_sname != NULL) { |
1915 | if (ht[target&0xff] != target) { |
1916 | printf("Function at 0x%lx is %s\n" ,target,info.dli_sname); |
1917 | ht[target&0xff] = target; |
1918 | } |
1919 | j += 4; /* Skip the 32 bit immediate. */ |
1920 | } |
1921 | } |
1922 | } |
1923 | |
1924 | void dumpCodeAroundEIP(void *eip) { |
1925 | Dl_info info; |
1926 | if (dladdr(eip, &info) != 0) { |
1927 | serverLog(LL_WARNING|LL_RAW, |
1928 | "\n------ DUMPING CODE AROUND EIP ------\n" |
1929 | "Symbol: %s (base: %p)\n" |
1930 | "Module: %s (base %p)\n" |
1931 | "$ xxd -r -p /tmp/dump.hex /tmp/dump.bin\n" |
1932 | "$ objdump --adjust-vma=%p -D -b binary -m i386:x86-64 /tmp/dump.bin\n" |
1933 | "------\n" , |
1934 | info.dli_sname, info.dli_saddr, info.dli_fname, info.dli_fbase, |
1935 | info.dli_saddr); |
1936 | size_t len = (long)eip - (long)info.dli_saddr; |
1937 | unsigned long sz = sysconf(_SC_PAGESIZE); |
1938 | if (len < 1<<13) { /* we don't have functions over 8k (verified) */ |
1939 | /* Find the address of the next page, which is our "safety" |
1940 | * limit when dumping. Then try to dump just 128 bytes more |
1941 | * than EIP if there is room, or stop sooner. */ |
1942 | void *base = (void *)info.dli_saddr; |
1943 | unsigned long next = ((unsigned long)eip + sz) & ~(sz-1); |
1944 | unsigned long end = (unsigned long)eip + 128; |
1945 | if (end > next) end = next; |
1946 | len = end - (unsigned long)base; |
1947 | serverLogHexDump(LL_WARNING, "dump of function" , |
1948 | base, len); |
1949 | dumpX86Calls(base, len); |
1950 | } |
1951 | } |
1952 | } |
1953 | |
1954 | void sigsegvHandler(int sig, siginfo_t *info, void *secret) { |
1955 | UNUSED(secret); |
1956 | UNUSED(info); |
1957 | |
1958 | bugReportStart(); |
1959 | serverLog(LL_WARNING, |
1960 | "Redis %s crashed by signal: %d, si_code: %d" , REDIS_VERSION, sig, info->si_code); |
1961 | if (sig == SIGSEGV || sig == SIGBUS) { |
1962 | serverLog(LL_WARNING, |
1963 | "Accessing address: %p" , (void*)info->si_addr); |
1964 | } |
1965 | if (info->si_code == SI_USER && info->si_pid != -1) { |
1966 | serverLog(LL_WARNING, "Killed by PID: %ld, UID: %d" , (long) info->si_pid, info->si_uid); |
1967 | } |
1968 | |
1969 | #ifdef HAVE_BACKTRACE |
1970 | ucontext_t *uc = (ucontext_t*) secret; |
1971 | void *eip = getMcontextEip(uc); |
1972 | if (eip != NULL) { |
1973 | serverLog(LL_WARNING, |
1974 | "Crashed running the instruction at: %p" , eip); |
1975 | } |
1976 | |
1977 | logStackTrace(getMcontextEip(uc), 1); |
1978 | |
1979 | logRegisters(uc); |
1980 | #endif |
1981 | |
1982 | printCrashReport(); |
1983 | |
1984 | #ifdef HAVE_BACKTRACE |
1985 | if (eip != NULL) |
1986 | dumpCodeAroundEIP(eip); |
1987 | #endif |
1988 | |
1989 | bugReportEnd(1, sig); |
1990 | } |
1991 | |
1992 | void printCrashReport(void) { |
1993 | /* Log INFO and CLIENT LIST */ |
1994 | logServerInfo(); |
1995 | |
1996 | /* Log the current client */ |
1997 | logCurrentClient(); |
1998 | |
1999 | /* Log modules info. Something we wanna do last since we fear it may crash. */ |
2000 | logModulesInfo(); |
2001 | |
2002 | /* Log debug config information, which are some values |
2003 | * which may be useful for debugging crashes. */ |
2004 | logConfigDebugInfo(); |
2005 | |
2006 | /* Run memory test in case the crash was triggered by memory corruption. */ |
2007 | doFastMemoryTest(); |
2008 | } |
2009 | |
2010 | void bugReportEnd(int killViaSignal, int sig) { |
2011 | struct sigaction act; |
2012 | |
2013 | serverLogRaw(LL_WARNING|LL_RAW, |
2014 | "\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n" |
2015 | " Please report the crash by opening an issue on github:\n\n" |
2016 | " http://github.com/redis/redis/issues\n\n" |
2017 | " If a Redis module was involved, please open in the module's repo instead.\n\n" |
2018 | " Suspect RAM error? Use redis-server --test-memory to verify it.\n\n" |
2019 | " Some other issues could be detected by redis-server --check-system\n" |
2020 | ); |
2021 | |
2022 | /* free(messages); Don't call free() with possibly corrupted memory. */ |
2023 | if (server.daemonize && server.supervised == 0 && server.pidfile) unlink(server.pidfile); |
2024 | |
2025 | if (!killViaSignal) { |
2026 | /* To avoid issues with valgrind, we may wanna exit rahter than generate a signal */ |
2027 | if (server.use_exit_on_panic) { |
2028 | /* Using _exit to bypass false leak reports by gcc ASAN */ |
2029 | fflush(stdout); |
2030 | _exit(1); |
2031 | } |
2032 | abort(); |
2033 | } |
2034 | |
2035 | /* Make sure we exit with the right signal at the end. So for instance |
2036 | * the core will be dumped if enabled. */ |
2037 | sigemptyset (&act.sa_mask); |
2038 | act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND; |
2039 | act.sa_handler = SIG_DFL; |
2040 | sigaction (sig, &act, NULL); |
2041 | kill(getpid(),sig); |
2042 | } |
2043 | |
2044 | /* ==================== Logging functions for debugging ===================== */ |
2045 | |
2046 | void serverLogHexDump(int level, char *descr, void *value, size_t len) { |
2047 | char buf[65], *b; |
2048 | unsigned char *v = value; |
2049 | char charset[] = "0123456789abcdef" ; |
2050 | |
2051 | serverLog(level,"%s (hexdump of %zu bytes):" , descr, len); |
2052 | b = buf; |
2053 | while(len) { |
2054 | b[0] = charset[(*v)>>4]; |
2055 | b[1] = charset[(*v)&0xf]; |
2056 | b[2] = '\0'; |
2057 | b += 2; |
2058 | len--; |
2059 | v++; |
2060 | if (b-buf == 64 || len == 0) { |
2061 | serverLogRaw(level|LL_RAW,buf); |
2062 | b = buf; |
2063 | } |
2064 | } |
2065 | serverLogRaw(level|LL_RAW,"\n" ); |
2066 | } |
2067 | |
2068 | /* =========================== Software Watchdog ============================ */ |
2069 | #include <sys/time.h> |
2070 | |
2071 | void watchdogSignalHandler(int sig, siginfo_t *info, void *secret) { |
2072 | #ifdef HAVE_BACKTRACE |
2073 | ucontext_t *uc = (ucontext_t*) secret; |
2074 | #else |
2075 | (void)secret; |
2076 | #endif |
2077 | UNUSED(info); |
2078 | UNUSED(sig); |
2079 | |
2080 | serverLogFromHandler(LL_WARNING,"\n--- WATCHDOG TIMER EXPIRED ---" ); |
2081 | #ifdef HAVE_BACKTRACE |
2082 | logStackTrace(getMcontextEip(uc), 1); |
2083 | #else |
2084 | serverLogFromHandler(LL_WARNING,"Sorry: no support for backtrace()." ); |
2085 | #endif |
2086 | serverLogFromHandler(LL_WARNING,"--------\n" ); |
2087 | } |
2088 | |
2089 | /* Schedule a SIGALRM delivery after the specified period in milliseconds. |
2090 | * If a timer is already scheduled, this function will re-schedule it to the |
2091 | * specified time. If period is 0 the current timer is disabled. */ |
2092 | void watchdogScheduleSignal(int period) { |
2093 | struct itimerval it; |
2094 | |
2095 | /* Will stop the timer if period is 0. */ |
2096 | it.it_value.tv_sec = period/1000; |
2097 | it.it_value.tv_usec = (period%1000)*1000; |
2098 | /* Don't automatically restart. */ |
2099 | it.it_interval.tv_sec = 0; |
2100 | it.it_interval.tv_usec = 0; |
2101 | setitimer(ITIMER_REAL, &it, NULL); |
2102 | } |
2103 | void applyWatchdogPeriod() { |
2104 | struct sigaction act; |
2105 | |
2106 | /* Disable watchdog when period is 0 */ |
2107 | if (server.watchdog_period == 0) { |
2108 | watchdogScheduleSignal(0); /* Stop the current timer. */ |
2109 | |
2110 | /* Set the signal handler to SIG_IGN, this will also remove pending |
2111 | * signals from the queue. */ |
2112 | sigemptyset(&act.sa_mask); |
2113 | act.sa_flags = 0; |
2114 | act.sa_handler = SIG_IGN; |
2115 | sigaction(SIGALRM, &act, NULL); |
2116 | } else { |
2117 | /* Setup the signal handler. */ |
2118 | sigemptyset(&act.sa_mask); |
2119 | act.sa_flags = SA_SIGINFO; |
2120 | act.sa_sigaction = watchdogSignalHandler; |
2121 | sigaction(SIGALRM, &act, NULL); |
2122 | |
2123 | /* If the configured period is smaller than twice the timer period, it is |
2124 | * too short for the software watchdog to work reliably. Fix it now |
2125 | * if needed. */ |
2126 | int min_period = (1000/server.hz)*2; |
2127 | if (server.watchdog_period < min_period) server.watchdog_period = min_period; |
2128 | watchdogScheduleSignal(server.watchdog_period); /* Adjust the current timer. */ |
2129 | } |
2130 | } |
2131 | |
2132 | /* Positive input is sleep time in microseconds. Negative input is fractions |
2133 | * of microseconds, i.e. -10 means 100 nanoseconds. */ |
2134 | void debugDelay(int usec) { |
2135 | /* Since even the shortest sleep results in context switch and system call, |
2136 | * the way we achieve short sleeps is by statistically sleeping less often. */ |
2137 | if (usec < 0) usec = (rand() % -usec) == 0 ? 1: 0; |
2138 | if (usec) usleep(usec); |
2139 | } |
2140 | |