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