"Fossies" - the Fresh Open Source Software Archive

Member "redis-6.0.8/src/debug.c" (10 Sep 2020, 69136 Bytes) of package /linux/misc/redis-6.0.8.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "debug.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 6.0.7_vs_6.0.8.

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