xref: /redis-3.2.3/src/debug.c (revision 07b852d2)
14365e5b2Santirez /*
24365e5b2Santirez  * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
34365e5b2Santirez  * All rights reserved.
44365e5b2Santirez  *
54365e5b2Santirez  * Redistribution and use in source and binary forms, with or without
64365e5b2Santirez  * modification, are permitted provided that the following conditions are met:
74365e5b2Santirez  *
84365e5b2Santirez  *   * Redistributions of source code must retain the above copyright notice,
94365e5b2Santirez  *     this list of conditions and the following disclaimer.
104365e5b2Santirez  *   * Redistributions in binary form must reproduce the above copyright
114365e5b2Santirez  *     notice, this list of conditions and the following disclaimer in the
124365e5b2Santirez  *     documentation and/or other materials provided with the distribution.
134365e5b2Santirez  *   * Neither the name of Redis nor the names of its contributors may be used
144365e5b2Santirez  *     to endorse or promote products derived from this software without
154365e5b2Santirez  *     specific prior written permission.
164365e5b2Santirez  *
174365e5b2Santirez  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
184365e5b2Santirez  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
194365e5b2Santirez  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
204365e5b2Santirez  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
214365e5b2Santirez  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
224365e5b2Santirez  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
234365e5b2Santirez  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
244365e5b2Santirez  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
254365e5b2Santirez  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
264365e5b2Santirez  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
274365e5b2Santirez  * POSSIBILITY OF SUCH DAMAGE.
284365e5b2Santirez  */
294365e5b2Santirez 
30cef054e8Santirez #include "server.h"
31e2641e09Santirez #include "sha1.h"   /* SHA1 is used for DEBUG DIGEST */
322f62c966Santirez #include "crc64.h"
33e2641e09Santirez 
343688d7f3Santirez #include <arpa/inet.h>
35d4d20859Santirez #include <signal.h>
36d4d20859Santirez 
37d4d20859Santirez #ifdef HAVE_BACKTRACE
38d4d20859Santirez #include <execinfo.h>
39d4d20859Santirez #include <ucontext.h>
4011bd247dSantirez #include <fcntl.h>
41d7c7ac4aScharsyam #include "bio.h"
42d4d20859Santirez #endif /* HAVE_BACKTRACE */
433688d7f3Santirez 
443e0e51ddSMatt Stancliff #ifdef __CYGWIN__
453e0e51ddSMatt Stancliff #ifndef SA_ONSTACK
463e0e51ddSMatt Stancliff #define SA_ONSTACK 0x08000000
473e0e51ddSMatt Stancliff #endif
483e0e51ddSMatt Stancliff #endif
493e0e51ddSMatt Stancliff 
50e2641e09Santirez /* ================================= Debugging ============================== */
51e2641e09Santirez 
52e2641e09Santirez /* Compute the sha1 of string at 's' with 'len' bytes long.
539d09ce39Sguiquanz  * The SHA1 is then xored against the string pointed by digest.
54e2641e09Santirez  * Since xor is commutative, this operation is used in order to
55e2641e09Santirez  * "add" digests relative to unordered elements.
56e2641e09Santirez  *
57e2641e09Santirez  * So digest(a,b,c,d) will be the same of digest(b,a,c,d) */
xorDigest(unsigned char * digest,void * ptr,size_t len)58e2641e09Santirez void xorDigest(unsigned char *digest, void *ptr, size_t len) {
59e2641e09Santirez     SHA1_CTX ctx;
60e2641e09Santirez     unsigned char hash[20], *s = ptr;
61e2641e09Santirez     int j;
62e2641e09Santirez 
63e2641e09Santirez     SHA1Init(&ctx);
64e2641e09Santirez     SHA1Update(&ctx,s,len);
65e2641e09Santirez     SHA1Final(hash,&ctx);
66e2641e09Santirez 
67e2641e09Santirez     for (j = 0; j < 20; j++)
68e2641e09Santirez         digest[j] ^= hash[j];
69e2641e09Santirez }
70e2641e09Santirez 
xorObjectDigest(unsigned char * digest,robj * o)71e2641e09Santirez void xorObjectDigest(unsigned char *digest, robj *o) {
72e2641e09Santirez     o = getDecodedObject(o);
73e2641e09Santirez     xorDigest(digest,o->ptr,sdslen(o->ptr));
74e2641e09Santirez     decrRefCount(o);
75e2641e09Santirez }
76e2641e09Santirez 
77e2641e09Santirez /* This function instead of just computing the SHA1 and xoring it
789d09ce39Sguiquanz  * against digest, also perform the digest of "digest" itself and
79e2641e09Santirez  * replace the old value with the new one.
80e2641e09Santirez  *
81e2641e09Santirez  * So the final digest will be:
82e2641e09Santirez  *
83e2641e09Santirez  * digest = SHA1(digest xor SHA1(data))
84e2641e09Santirez  *
85e2641e09Santirez  * This function is used every time we want to preserve the order so
86e2641e09Santirez  * that digest(a,b,c,d) will be different than digest(b,c,d,a)
87e2641e09Santirez  *
88e2641e09Santirez  * Also note that mixdigest("foo") followed by mixdigest("bar")
89e2641e09Santirez  * will lead to a different digest compared to "fo", "obar".
90e2641e09Santirez  */
mixDigest(unsigned char * digest,void * ptr,size_t len)91e2641e09Santirez void mixDigest(unsigned char *digest, void *ptr, size_t len) {
92e2641e09Santirez     SHA1_CTX ctx;
93e2641e09Santirez     char *s = ptr;
94e2641e09Santirez 
95e2641e09Santirez     xorDigest(digest,s,len);
96e2641e09Santirez     SHA1Init(&ctx);
97e2641e09Santirez     SHA1Update(&ctx,digest,20);
98e2641e09Santirez     SHA1Final(digest,&ctx);
99e2641e09Santirez }
100e2641e09Santirez 
mixObjectDigest(unsigned char * digest,robj * o)101e2641e09Santirez void mixObjectDigest(unsigned char *digest, robj *o) {
102e2641e09Santirez     o = getDecodedObject(o);
103e2641e09Santirez     mixDigest(digest,o->ptr,sdslen(o->ptr));
104e2641e09Santirez     decrRefCount(o);
105e2641e09Santirez }
106e2641e09Santirez 
107e2641e09Santirez /* Compute the dataset digest. Since keys, sets elements, hashes elements
108e2641e09Santirez  * are not ordered, we use a trick: every aggregate digest is the xor
109e2641e09Santirez  * of the digests of their elements. This way the order will not change
110e2641e09Santirez  * the result. For list instead we use a feedback entering the output digest
111e2641e09Santirez  * as input in order to ensure that a different ordered list will result in
112e2641e09Santirez  * a different digest. */
computeDatasetDigest(unsigned char * final)113e2641e09Santirez void computeDatasetDigest(unsigned char *final) {
114e2641e09Santirez     unsigned char digest[20];
115e2641e09Santirez     char buf[128];
116e2641e09Santirez     dictIterator *di = NULL;
117e2641e09Santirez     dictEntry *de;
118e2641e09Santirez     int j;
119e2641e09Santirez     uint32_t aux;
120e2641e09Santirez 
121e2641e09Santirez     memset(final,0,20); /* Start with a clean result */
122e2641e09Santirez 
123e2641e09Santirez     for (j = 0; j < server.dbnum; j++) {
124e2641e09Santirez         redisDb *db = server.db+j;
125e2641e09Santirez 
126e2641e09Santirez         if (dictSize(db->dict) == 0) continue;
127e2641e09Santirez         di = dictGetIterator(db->dict);
128e2641e09Santirez 
129e2641e09Santirez         /* hash the DB id, so the same dataset moved in a different
130e2641e09Santirez          * DB will lead to a different digest */
131e2641e09Santirez         aux = htonl(j);
132e2641e09Santirez         mixDigest(final,&aux,sizeof(aux));
133e2641e09Santirez 
134e2641e09Santirez         /* Iterate this DB writing every entry */
135e2641e09Santirez         while((de = dictNext(di)) != NULL) {
136e2641e09Santirez             sds key;
137e2641e09Santirez             robj *keyobj, *o;
1384be855e7Santirez             long long expiretime;
139e2641e09Santirez 
140e2641e09Santirez             memset(digest,0,20); /* This key-val digest */
141c0ba9ebeSantirez             key = dictGetKey(de);
142e2641e09Santirez             keyobj = createStringObject(key,sdslen(key));
143e2641e09Santirez 
144e2641e09Santirez             mixDigest(digest,key,sdslen(key));
145e2641e09Santirez 
146c0ba9ebeSantirez             o = dictGetVal(de);
147e2641e09Santirez 
148e2641e09Santirez             aux = htonl(o->type);
149e2641e09Santirez             mixDigest(digest,&aux,sizeof(aux));
150e2641e09Santirez             expiretime = getExpire(db,keyobj);
151e2641e09Santirez 
152e2641e09Santirez             /* Save the key and associated value */
15314ff5724Santirez             if (o->type == OBJ_STRING) {
154e2641e09Santirez                 mixObjectDigest(digest,o);
15514ff5724Santirez             } else if (o->type == OBJ_LIST) {
15632f80e2fSantirez                 listTypeIterator *li = listTypeInitIterator(o,0,LIST_TAIL);
157e2641e09Santirez                 listTypeEntry entry;
158e2641e09Santirez                 while(listTypeNext(li,&entry)) {
159e2641e09Santirez                     robj *eleobj = listTypeGet(&entry);
160e2641e09Santirez                     mixObjectDigest(digest,eleobj);
161e2641e09Santirez                     decrRefCount(eleobj);
162e2641e09Santirez                 }
163e2641e09Santirez                 listTypeReleaseIterator(li);
16414ff5724Santirez             } else if (o->type == OBJ_SET) {
165cb72d0f1SPieter Noordhuis                 setTypeIterator *si = setTypeInitIterator(o);
1662767f1c0SPieter Noordhuis                 robj *ele;
1671b508da7Santirez                 while((ele = setTypeNextObject(si)) != NULL) {
1682767f1c0SPieter Noordhuis                     xorObjectDigest(digest,ele);
1692767f1c0SPieter Noordhuis                     decrRefCount(ele);
170e2641e09Santirez                 }
1712767f1c0SPieter Noordhuis                 setTypeReleaseIterator(si);
17214ff5724Santirez             } else if (o->type == OBJ_ZSET) {
173dddf5335SPieter Noordhuis                 unsigned char eledigest[20];
174dddf5335SPieter Noordhuis 
17514ff5724Santirez                 if (o->encoding == OBJ_ENCODING_ZIPLIST) {
176dddf5335SPieter Noordhuis                     unsigned char *zl = o->ptr;
177dddf5335SPieter Noordhuis                     unsigned char *eptr, *sptr;
178dddf5335SPieter Noordhuis                     unsigned char *vstr;
179dddf5335SPieter Noordhuis                     unsigned int vlen;
180dddf5335SPieter Noordhuis                     long long vll;
181dddf5335SPieter Noordhuis                     double score;
182dddf5335SPieter Noordhuis 
183dddf5335SPieter Noordhuis                     eptr = ziplistIndex(zl,0);
1842d9e3eb1Santirez                     serverAssert(eptr != NULL);
185dddf5335SPieter Noordhuis                     sptr = ziplistNext(zl,eptr);
1862d9e3eb1Santirez                     serverAssert(sptr != NULL);
187dddf5335SPieter Noordhuis 
188dddf5335SPieter Noordhuis                     while (eptr != NULL) {
1892d9e3eb1Santirez                         serverAssert(ziplistGet(eptr,&vstr,&vlen,&vll));
190dddf5335SPieter Noordhuis                         score = zzlGetScore(sptr);
191dddf5335SPieter Noordhuis 
192dddf5335SPieter Noordhuis                         memset(eledigest,0,20);
193dddf5335SPieter Noordhuis                         if (vstr != NULL) {
194dddf5335SPieter Noordhuis                             mixDigest(eledigest,vstr,vlen);
195dddf5335SPieter Noordhuis                         } else {
196dddf5335SPieter Noordhuis                             ll2string(buf,sizeof(buf),vll);
197dddf5335SPieter Noordhuis                             mixDigest(eledigest,buf,strlen(buf));
198dddf5335SPieter Noordhuis                         }
199dddf5335SPieter Noordhuis 
200dddf5335SPieter Noordhuis                         snprintf(buf,sizeof(buf),"%.17g",score);
201dddf5335SPieter Noordhuis                         mixDigest(eledigest,buf,strlen(buf));
202dddf5335SPieter Noordhuis                         xorDigest(digest,eledigest,20);
203dddf5335SPieter Noordhuis                         zzlNext(zl,&eptr,&sptr);
204dddf5335SPieter Noordhuis                     }
20514ff5724Santirez                 } else if (o->encoding == OBJ_ENCODING_SKIPLIST) {
206e2641e09Santirez                     zset *zs = o->ptr;
207e2641e09Santirez                     dictIterator *di = dictGetIterator(zs->dict);
208e2641e09Santirez                     dictEntry *de;
209e2641e09Santirez 
210e2641e09Santirez                     while((de = dictNext(di)) != NULL) {
211c0ba9ebeSantirez                         robj *eleobj = dictGetKey(de);
212c0ba9ebeSantirez                         double *score = dictGetVal(de);
213e2641e09Santirez 
214e2641e09Santirez                         snprintf(buf,sizeof(buf),"%.17g",*score);
215e2641e09Santirez                         memset(eledigest,0,20);
216e2641e09Santirez                         mixObjectDigest(eledigest,eleobj);
217e2641e09Santirez                         mixDigest(eledigest,buf,strlen(buf));
218e2641e09Santirez                         xorDigest(digest,eledigest,20);
219e2641e09Santirez                     }
220e2641e09Santirez                     dictReleaseIterator(di);
221dddf5335SPieter Noordhuis                 } else {
22232f80e2fSantirez                     serverPanic("Unknown sorted set encoding");
223dddf5335SPieter Noordhuis                 }
22414ff5724Santirez             } else if (o->type == OBJ_HASH) {
225e2641e09Santirez                 hashTypeIterator *hi;
226e2641e09Santirez                 robj *obj;
227e2641e09Santirez 
228e2641e09Santirez                 hi = hashTypeInitIterator(o);
22940eb548aSantirez                 while (hashTypeNext(hi) != C_ERR) {
230e2641e09Santirez                     unsigned char eledigest[20];
231e2641e09Santirez 
232e2641e09Santirez                     memset(eledigest,0,20);
23314ff5724Santirez                     obj = hashTypeCurrentObject(hi,OBJ_HASH_KEY);
234e2641e09Santirez                     mixObjectDigest(eledigest,obj);
235e2641e09Santirez                     decrRefCount(obj);
23614ff5724Santirez                     obj = hashTypeCurrentObject(hi,OBJ_HASH_VALUE);
237e2641e09Santirez                     mixObjectDigest(eledigest,obj);
238e2641e09Santirez                     decrRefCount(obj);
239e2641e09Santirez                     xorDigest(digest,eledigest,20);
240e2641e09Santirez                 }
241e2641e09Santirez                 hashTypeReleaseIterator(hi);
242e2641e09Santirez             } else {
24332f80e2fSantirez                 serverPanic("Unknown object type");
244e2641e09Santirez             }
245e2641e09Santirez             /* If the key has an expire, add it to the mix */
246e2641e09Santirez             if (expiretime != -1) xorDigest(digest,"!!expire!!",10);
247e2641e09Santirez             /* We can finally xor the key-val digest to the final digest */
248e2641e09Santirez             xorDigest(final,digest,20);
249e2641e09Santirez             decrRefCount(keyobj);
250e2641e09Santirez         }
251e2641e09Santirez         dictReleaseIterator(di);
252e2641e09Santirez     }
253e2641e09Santirez }
254e2641e09Santirez 
2553ede6c7aSOran Agra #if defined(USE_JEMALLOC)
inputCatSds(void * result,const char * str)25627937c28SMatt Stancliff void inputCatSds(void *result, const char *str) {
25727937c28SMatt Stancliff     /* result is actually a (sds *), so re-cast it here */
25827937c28SMatt Stancliff     sds *info = (sds *)result;
25927937c28SMatt Stancliff     *info = sdscat(*info, str);
26027937c28SMatt Stancliff }
2613ede6c7aSOran Agra #endif
26227937c28SMatt Stancliff 
debugCommand(client * c)263554bd0e7Santirez void debugCommand(client *c) {
264*07b852d2Santirez     if (c->argc == 1) {
265*07b852d2Santirez         addReplyError(c,"You must specify a subcommand for DEBUG. Try DEBUG HELP for info.");
266*07b852d2Santirez         return;
267*07b852d2Santirez     }
268*07b852d2Santirez 
269*07b852d2Santirez     if (!strcasecmp(c->argv[1]->ptr,"help")) {
270*07b852d2Santirez         void *blenp = addDeferredMultiBulkLength(c);
271*07b852d2Santirez         int blen = 0;
272*07b852d2Santirez         blen++; addReplyStatus(c,
273*07b852d2Santirez         "DEBUG <subcommand> arg arg ... arg. Subcommands:");
274*07b852d2Santirez         blen++; addReplyStatus(c,
275*07b852d2Santirez         "segfault -- Crash the server with sigsegv.");
276*07b852d2Santirez         blen++; addReplyStatus(c,
277*07b852d2Santirez         "restart  -- Graceful restart: save config, db, restart.");
278*07b852d2Santirez         blen++; addReplyStatus(c,
279*07b852d2Santirez         "crash-and-recovery <milliseconds> -- Hard crash and restart after <milliseconds> delay.");
280*07b852d2Santirez         blen++; addReplyStatus(c,
281*07b852d2Santirez         "assert   -- Crash by assertion failed.");
282*07b852d2Santirez         blen++; addReplyStatus(c,
283*07b852d2Santirez         "reload   -- Save the RDB on disk and reload it back in memory.");
284*07b852d2Santirez         blen++; addReplyStatus(c,
285*07b852d2Santirez         "loadaof  -- Flush the AOF buffers on disk and reload the AOF in memory.");
286*07b852d2Santirez         blen++; addReplyStatus(c,
287*07b852d2Santirez         "object <key> -- Show low level info about key and associated value.");
288*07b852d2Santirez         blen++; addReplyStatus(c,
289*07b852d2Santirez         "sdslen <key> -- Show low level SDS string info representing key and value.");
290*07b852d2Santirez         blen++; addReplyStatus(c,
291*07b852d2Santirez         "populate <count> [prefix] -- Create <count> string keys named key:<num>. If a prefix is specified is used instead of the 'key' prefix.");
292*07b852d2Santirez         blen++; addReplyStatus(c,
293*07b852d2Santirez         "digest   -- Outputs an hex signature representing the current DB content.");
294*07b852d2Santirez         blen++; addReplyStatus(c,
295*07b852d2Santirez         "sleep <seconds> -- Stop the server for <seconds>. Decimals allowed.");
296*07b852d2Santirez         blen++; addReplyStatus(c,
297*07b852d2Santirez         "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.");
298*07b852d2Santirez         blen++; addReplyStatus(c,
299*07b852d2Santirez         "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.");
300*07b852d2Santirez         blen++; addReplyStatus(c,
301*07b852d2Santirez         "error <string> -- Return a Redis protocol error with <string> as message. Useful for clients unit tests to simulate Redis errors.");
302*07b852d2Santirez         blen++; addReplyStatus(c,
303*07b852d2Santirez         "structsize -- Return the size of different Redis core C structures.");
304*07b852d2Santirez         blen++; addReplyStatus(c,
305*07b852d2Santirez         "htstats <dbid> -- Return hash table statistics of the specified Redis database.");
306*07b852d2Santirez         blen++; addReplyStatus(c,
307*07b852d2Santirez         "jemalloc info  -- Show internal jemalloc statistics.");
308*07b852d2Santirez         blen++; addReplyStatus(c,
309*07b852d2Santirez         "jemalloc purge -- Force jemalloc to release unused memory.");
310*07b852d2Santirez         setDeferredMultiBulkLength(c,blenp,blen);
311*07b852d2Santirez     } else if (!strcasecmp(c->argv[1]->ptr,"segfault")) {
312e2641e09Santirez         *((char*)-1) = 'x';
313fcb3aef7Santirez     } else if (!strcasecmp(c->argv[1]->ptr,"restart") ||
314fcb3aef7Santirez                !strcasecmp(c->argv[1]->ptr,"crash-and-recover"))
315fcb3aef7Santirez     {
316fcb3aef7Santirez         long long delay = 0;
317fcb3aef7Santirez         if (c->argc >= 3) {
318fcb3aef7Santirez             if (getLongLongFromObjectOrReply(c, c->argv[2], &delay, NULL)
319fcb3aef7Santirez                 != C_OK) return;
320fcb3aef7Santirez             if (delay < 0) delay = 0;
321fcb3aef7Santirez         }
322fcb3aef7Santirez         int flags = !strcasecmp(c->argv[1]->ptr,"restart") ?
323fcb3aef7Santirez             (RESTART_SERVER_GRACEFULLY|RESTART_SERVER_CONFIG_REWRITE) :
324fcb3aef7Santirez              RESTART_SERVER_NONE;
325fcb3aef7Santirez         restartServer(flags,delay);
326fcb3aef7Santirez         addReplyError(c,"failed to restart the server. Check server logs.");
3276fdc6354Santirez     } else if (!strcasecmp(c->argv[1]->ptr,"oom")) {
3286fdc6354Santirez         void *ptr = zmalloc(ULONG_MAX); /* Should trigger an out of memory. */
3296fdc6354Santirez         zfree(ptr);
3306fdc6354Santirez         addReply(c,shared.ok);
331e3e69935Santirez     } else if (!strcasecmp(c->argv[1]->ptr,"assert")) {
332e3e69935Santirez         if (c->argc >= 3) c->argv[2] = tryObjectEncoding(c->argv[2]);
3332d9e3eb1Santirez         serverAssertWithInfo(c,c->argv[0],1 == 2);
334e2641e09Santirez     } else if (!strcasecmp(c->argv[1]->ptr,"reload")) {
33540eb548aSantirez         if (rdbSave(server.rdb_filename) != C_OK) {
336e2641e09Santirez             addReply(c,shared.err);
337e2641e09Santirez             return;
338e2641e09Santirez         }
3392eb781b3Santirez         emptyDb(NULL);
34040eb548aSantirez         if (rdbLoad(server.rdb_filename) != C_OK) {
34180ad7189Santirez             addReplyError(c,"Error trying to load the RDB dump");
342e2641e09Santirez             return;
343e2641e09Santirez         }
34432f80e2fSantirez         serverLog(LL_WARNING,"DB reloaded by DEBUG RELOAD");
345e2641e09Santirez         addReply(c,shared.ok);
346e2641e09Santirez     } else if (!strcasecmp(c->argv[1]->ptr,"loadaof")) {
3474fe431c7Santirez         if (server.aof_state == AOF_ON) flushAppendOnlyFile(1);
3482eb781b3Santirez         emptyDb(NULL);
34940eb548aSantirez         if (loadAppendOnlyFile(server.aof_filename) != C_OK) {
350e2641e09Santirez             addReply(c,shared.err);
351e2641e09Santirez             return;
352e2641e09Santirez         }
353efb60225Santirez         server.dirty = 0; /* Prevent AOF / replication */
35432f80e2fSantirez         serverLog(LL_WARNING,"Append Only File loaded by DEBUG LOADAOF");
355e2641e09Santirez         addReply(c,shared.ok);
356e2641e09Santirez     } else if (!strcasecmp(c->argv[1]->ptr,"object") && c->argc == 3) {
357b39619d8Santirez         dictEntry *de;
358e2641e09Santirez         robj *val;
3595ef64098Santirez         char *strenc;
360e2641e09Santirez 
361b39619d8Santirez         if ((de = dictFind(c->db->dict,c->argv[2]->ptr)) == NULL) {
362e2641e09Santirez             addReply(c,shared.nokeyerr);
363e2641e09Santirez             return;
364e2641e09Santirez         }
365c0ba9ebeSantirez         val = dictGetVal(de);
366e2641e09Santirez         strenc = strEncoding(val->encoding);
3673be00d7eSantirez 
3685127e399SMatt Stancliff         char extra[128] = {0};
36914ff5724Santirez         if (val->encoding == OBJ_ENCODING_QUICKLIST) {
3705127e399SMatt Stancliff             char *nextra = extra;
3715127e399SMatt Stancliff             int remaining = sizeof(extra);
3725127e399SMatt Stancliff             quicklist *ql = val->ptr;
3739e11d079SMatt Stancliff             /* Add number of quicklist nodes */
374abdd1414SMatt Stancliff             int used = snprintf(nextra, remaining, " ql_nodes:%u", ql->len);
3755127e399SMatt Stancliff             nextra += used;
3765127e399SMatt Stancliff             remaining -= used;
3779e11d079SMatt Stancliff             /* Add average quicklist fill factor */
3789e11d079SMatt Stancliff             double avg = (double)ql->count/ql->len;
3799e11d079SMatt Stancliff             used = snprintf(nextra, remaining, " ql_avg_node:%.2f", avg);
3809e11d079SMatt Stancliff             nextra += used;
3819e11d079SMatt Stancliff             remaining -= used;
3829e11d079SMatt Stancliff             /* Add quicklist fill level / max ziplist size */
3839e11d079SMatt Stancliff             used = snprintf(nextra, remaining, " ql_ziplist_max:%d", ql->fill);
3849e11d079SMatt Stancliff             nextra += used;
3859e11d079SMatt Stancliff             remaining -= used;
3869e11d079SMatt Stancliff             /* Add isCompressed? */
3879e11d079SMatt Stancliff             int compressed = ql->compress != 0;
3889e11d079SMatt Stancliff             used = snprintf(nextra, remaining, " ql_compressed:%d", compressed);
3899e11d079SMatt Stancliff             nextra += used;
3909e11d079SMatt Stancliff             remaining -= used;
3919e11d079SMatt Stancliff             /* Add total uncompressed size */
3929e11d079SMatt Stancliff             unsigned long sz = 0;
3939e11d079SMatt Stancliff             for (quicklistNode *node = ql->head; node; node = node->next) {
3949e11d079SMatt Stancliff                 sz += node->sz;
3959e11d079SMatt Stancliff             }
3969e11d079SMatt Stancliff             used = snprintf(nextra, remaining, " ql_uncompressed_size:%lu", sz);
3979e11d079SMatt Stancliff             nextra += used;
3989e11d079SMatt Stancliff             remaining -= used;
3995127e399SMatt Stancliff         }
4005127e399SMatt Stancliff 
4013ab20376SPieter Noordhuis         addReplyStatusFormat(c,
4023ab20376SPieter Noordhuis             "Value at:%p refcount:%d "
403f7043604SMatt Stancliff             "encoding:%s serializedlength:%zu "
4045127e399SMatt Stancliff             "lru:%d lru_seconds_idle:%llu%s",
405e2641e09Santirez             (void*)val, val->refcount,
406f7043604SMatt Stancliff             strenc, rdbSavedObjectLen(val),
4075127e399SMatt Stancliff             val->lru, estimateObjectIdleTime(val)/1000, extra);
408e2180334Santirez     } else if (!strcasecmp(c->argv[1]->ptr,"sdslen") && c->argc == 3) {
409e2180334Santirez         dictEntry *de;
410e2180334Santirez         robj *val;
411e2180334Santirez         sds key;
412e2180334Santirez 
413e2180334Santirez         if ((de = dictFind(c->db->dict,c->argv[2]->ptr)) == NULL) {
414e2180334Santirez             addReply(c,shared.nokeyerr);
415e2180334Santirez             return;
416e2180334Santirez         }
417e2180334Santirez         val = dictGetVal(de);
418e2180334Santirez         key = dictGetKey(de);
419e2180334Santirez 
42014ff5724Santirez         if (val->type != OBJ_STRING || !sdsEncodedObject(val)) {
421e2180334Santirez             addReplyError(c,"Not an sds encoded string.");
422e2180334Santirez         } else {
423e2180334Santirez             addReplyStatusFormat(c,
424e2180334Santirez                 "key_sds_len:%lld, key_sds_avail:%lld, "
425e2180334Santirez                 "val_sds_len:%lld, val_sds_avail:%lld",
426e2180334Santirez                 (long long) sdslen(key),
427e2180334Santirez                 (long long) sdsavail(key),
428e2180334Santirez                 (long long) sdslen(val->ptr),
429e2180334Santirez                 (long long) sdsavail(val->ptr));
430e2180334Santirez         }
431d4222e6bSantirez     } else if (!strcasecmp(c->argv[1]->ptr,"populate") &&
432d4222e6bSantirez                (c->argc == 3 || c->argc == 4)) {
433e2641e09Santirez         long keys, j;
434e2641e09Santirez         robj *key, *val;
435e2641e09Santirez         char buf[128];
436e2641e09Santirez 
43740eb548aSantirez         if (getLongFromObjectOrReply(c, c->argv[2], &keys, NULL) != C_OK)
438e2641e09Santirez             return;
43972ff0334Santirez         dictExpand(c->db->dict,keys);
440e2641e09Santirez         for (j = 0; j < keys; j++) {
441d4222e6bSantirez             snprintf(buf,sizeof(buf),"%s:%lu",
442591b69c7Santirez                 (c->argc == 3) ? "key" : (char*)c->argv[3]->ptr, j);
443e2641e09Santirez             key = createStringObject(buf,strlen(buf));
44406e76bc3Santirez             if (lookupKeyWrite(c->db,key) != NULL) {
445e2641e09Santirez                 decrRefCount(key);
446e2641e09Santirez                 continue;
447e2641e09Santirez             }
448e2641e09Santirez             snprintf(buf,sizeof(buf),"value:%lu",j);
449e2641e09Santirez             val = createStringObject(buf,strlen(buf));
450e2641e09Santirez             dbAdd(c->db,key,val);
4513ede6c7aSOran Agra             signalModifiedKey(c->db,key);
452e2641e09Santirez             decrRefCount(key);
453e2641e09Santirez         }
454e2641e09Santirez         addReply(c,shared.ok);
455e2641e09Santirez     } else if (!strcasecmp(c->argv[1]->ptr,"digest") && c->argc == 2) {
456e2641e09Santirez         unsigned char digest[20];
4573ab20376SPieter Noordhuis         sds d = sdsempty();
458e2641e09Santirez         int j;
459e2641e09Santirez 
460e2641e09Santirez         computeDatasetDigest(digest);
461e2641e09Santirez         for (j = 0; j < 20; j++)
462e2641e09Santirez             d = sdscatprintf(d, "%02x",digest[j]);
4633ab20376SPieter Noordhuis         addReplyStatus(c,d);
4643ab20376SPieter Noordhuis         sdsfree(d);
465404345d8Santirez     } else if (!strcasecmp(c->argv[1]->ptr,"sleep") && c->argc == 3) {
466404345d8Santirez         double dtime = strtod(c->argv[2]->ptr,NULL);
467404345d8Santirez         long long utime = dtime*1000000;
468ae828676SYAMAMOTO Takashi         struct timespec tv;
469404345d8Santirez 
470ae828676SYAMAMOTO Takashi         tv.tv_sec = utime / 1000000;
471ae828676SYAMAMOTO Takashi         tv.tv_nsec = (utime % 1000000) * 1000;
472ae828676SYAMAMOTO Takashi         nanosleep(&tv, NULL);
473404345d8Santirez         addReply(c,shared.ok);
47432a83c82Santirez     } else if (!strcasecmp(c->argv[1]->ptr,"set-active-expire") &&
47532a83c82Santirez                c->argc == 3)
47632a83c82Santirez     {
47732a83c82Santirez         server.active_expire_enabled = atoi(c->argv[2]->ptr);
47832a83c82Santirez         addReply(c,shared.ok);
4798695d960Santirez     } else if (!strcasecmp(c->argv[1]->ptr,"lua-always-replicate-commands") &&
4808695d960Santirez                c->argc == 3)
4818695d960Santirez     {
4828695d960Santirez         server.lua_always_replicate_commands = atoi(c->argv[2]->ptr);
4838695d960Santirez         addReply(c,shared.ok);
4848eae54aaSantirez     } else if (!strcasecmp(c->argv[1]->ptr,"error") && c->argc == 3) {
4858eae54aaSantirez         sds errstr = sdsnewlen("-",1);
4868eae54aaSantirez 
4878eae54aaSantirez         errstr = sdscatsds(errstr,c->argv[2]->ptr);
4888eae54aaSantirez         errstr = sdsmapchars(errstr,"\n\r","  ",2); /* no newlines in errors. */
4898eae54aaSantirez         errstr = sdscatlen(errstr,"\r\n",2);
4908eae54aaSantirez         addReplySds(c,errstr);
4917885e126Santirez     } else if (!strcasecmp(c->argv[1]->ptr,"structsize") && c->argc == 2) {
4927885e126Santirez         sds sizes = sdsempty();
4937885e126Santirez         sizes = sdscatprintf(sizes,"bits:%d ",(sizeof(void*) == 8)?64:32);
4947885e126Santirez         sizes = sdscatprintf(sizes,"robj:%d ",(int)sizeof(robj));
4957885e126Santirez         sizes = sdscatprintf(sizes,"dictentry:%d ",(int)sizeof(dictEntry));
4963da97ea6Santirez         sizes = sdscatprintf(sizes,"sdshdr5:%d ",(int)sizeof(struct sdshdr5));
497f15df8baSOran Agra         sizes = sdscatprintf(sizes,"sdshdr8:%d ",(int)sizeof(struct sdshdr8));
498f15df8baSOran Agra         sizes = sdscatprintf(sizes,"sdshdr16:%d ",(int)sizeof(struct sdshdr16));
499f15df8baSOran Agra         sizes = sdscatprintf(sizes,"sdshdr32:%d ",(int)sizeof(struct sdshdr32));
500f15df8baSOran Agra         sizes = sdscatprintf(sizes,"sdshdr64:%d ",(int)sizeof(struct sdshdr64));
5017885e126Santirez         addReplyBulkSds(c,sizes);
5020f64080dSantirez     } else if (!strcasecmp(c->argv[1]->ptr,"htstats") && c->argc == 3) {
5030f64080dSantirez         long dbid;
5040f64080dSantirez         sds stats = sdsempty();
5050f64080dSantirez         char buf[4096];
5060f64080dSantirez 
50740eb548aSantirez         if (getLongFromObjectOrReply(c, c->argv[2], &dbid, NULL) != C_OK)
5080f64080dSantirez             return;
5090f64080dSantirez         if (dbid < 0 || dbid >= server.dbnum) {
5100f64080dSantirez             addReplyError(c,"Out of range database");
5110f64080dSantirez             return;
5120f64080dSantirez         }
5130f64080dSantirez 
5140f64080dSantirez         stats = sdscatprintf(stats,"[Dictionary HT]\n");
5150f64080dSantirez         dictGetStats(buf,sizeof(buf),server.db[dbid].dict);
5160f64080dSantirez         stats = sdscat(stats,buf);
5170f64080dSantirez 
5180f64080dSantirez         stats = sdscatprintf(stats,"[Expires HT]\n");
5190f64080dSantirez         dictGetStats(buf,sizeof(buf),server.db[dbid].expires);
5200f64080dSantirez         stats = sdscat(stats,buf);
5210f64080dSantirez 
5220f64080dSantirez         addReplyBulkSds(c,stats);
52327937c28SMatt Stancliff     } else if (!strcasecmp(c->argv[1]->ptr,"jemalloc") && c->argc == 3) {
52427937c28SMatt Stancliff #if defined(USE_JEMALLOC)
52527937c28SMatt Stancliff         if (!strcasecmp(c->argv[2]->ptr, "info")) {
52627937c28SMatt Stancliff             sds info = sdsempty();
52727937c28SMatt Stancliff             je_malloc_stats_print(inputCatSds, &info, NULL);
52827937c28SMatt Stancliff             addReplyBulkSds(c, info);
5293ede6c7aSOran Agra         } else if (!strcasecmp(c->argv[2]->ptr, "purge")) {
5303ede6c7aSOran Agra             char tmp[32];
5313ede6c7aSOran Agra             unsigned narenas = 0;
5323ede6c7aSOran Agra             size_t sz = sizeof(unsigned);
5333ede6c7aSOran Agra             if (!je_mallctl("arenas.narenas", &narenas, &sz, NULL, 0)) {
5343ede6c7aSOran Agra                 sprintf(tmp, "arena.%d.purge", narenas);
5353ede6c7aSOran Agra                 if (!je_mallctl(tmp, NULL, 0, NULL, 0)) {
5363ede6c7aSOran Agra                     addReply(c, shared.ok);
5373ede6c7aSOran Agra                     return;
5383ede6c7aSOran Agra                 }
5393ede6c7aSOran Agra             }
5403ede6c7aSOran Agra             addReplyError(c, "Error purging dirty pages");
54127937c28SMatt Stancliff         } else {
5423ede6c7aSOran Agra             addReplyErrorFormat(c, "Valid jemalloc debug fields: info, purge");
54327937c28SMatt Stancliff         }
54427937c28SMatt Stancliff #else
54527937c28SMatt Stancliff         addReplyErrorFormat(c, "jemalloc support not available");
54627937c28SMatt Stancliff #endif
547e2641e09Santirez     } else {
5483816e7bdSantirez         addReplyErrorFormat(c, "Unknown DEBUG subcommand or wrong number of arguments for '%s'",
54932a83c82Santirez             (char*)c->argv[1]->ptr);
550e2641e09Santirez     }
551e2641e09Santirez }
552e2641e09Santirez 
553d4d20859Santirez /* =========================== Crash handling  ============================== */
554d4d20859Santirez 
_serverAssert(char * estr,char * file,int line)5552d9e3eb1Santirez void _serverAssert(char *estr, char *file, int line) {
556fa5af017Santirez     bugReportStart();
55732f80e2fSantirez     serverLog(LL_WARNING,"=== ASSERTION FAILED ===");
55832f80e2fSantirez     serverLog(LL_WARNING,"==> %s:%d '%s' is not true",file,line,estr);
559e2641e09Santirez #ifdef HAVE_BACKTRACE
560fa5af017Santirez     server.assert_failed = estr;
561fa5af017Santirez     server.assert_file = file;
562fa5af017Santirez     server.assert_line = line;
56332f80e2fSantirez     serverLog(LL_WARNING,"(forcing SIGSEGV to print the bug report.)");
564e2641e09Santirez #endif
56557be4775SPieter Noordhuis     *((char*)-1) = 'x';
566e2641e09Santirez }
567e2641e09Santirez 
_serverAssertPrintClientInfo(client * c)5682d9e3eb1Santirez void _serverAssertPrintClientInfo(client *c) {
569e3e69935Santirez     int j;
570e3e69935Santirez 
571fa5af017Santirez     bugReportStart();
57232f80e2fSantirez     serverLog(LL_WARNING,"=== ASSERTION FAILED CLIENT CONTEXT ===");
57332f80e2fSantirez     serverLog(LL_WARNING,"client->flags = %d", c->flags);
57432f80e2fSantirez     serverLog(LL_WARNING,"client->fd = %d", c->fd);
57532f80e2fSantirez     serverLog(LL_WARNING,"client->argc = %d", c->argc);
576e3e69935Santirez     for (j=0; j < c->argc; j++) {
577e3e69935Santirez         char buf[128];
578e3e69935Santirez         char *arg;
579e3e69935Santirez 
58014ff5724Santirez         if (c->argv[j]->type == OBJ_STRING && sdsEncodedObject(c->argv[j])) {
581e3e69935Santirez             arg = (char*) c->argv[j]->ptr;
582e3e69935Santirez         } else {
5834916d205Santirez             snprintf(buf,sizeof(buf),"Object type: %u, encoding: %u",
584e3e69935Santirez                 c->argv[j]->type, c->argv[j]->encoding);
585e3e69935Santirez             arg = buf;
586e3e69935Santirez         }
58732f80e2fSantirez         serverLog(LL_WARNING,"client->argv[%d] = \"%s\" (refcount: %d)",
588e3e69935Santirez             j, arg, c->argv[j]->refcount);
589e3e69935Santirez     }
590e3e69935Santirez }
591bab205f7Santirez 
serverLogObjectDebugInfo(robj * o)592424fe9afSantirez void serverLogObjectDebugInfo(robj *o) {
59332f80e2fSantirez     serverLog(LL_WARNING,"Object type: %d", o->type);
59432f80e2fSantirez     serverLog(LL_WARNING,"Object encoding: %d", o->encoding);
59532f80e2fSantirez     serverLog(LL_WARNING,"Object refcount: %d", o->refcount);
59614ff5724Santirez     if (o->type == OBJ_STRING && sdsEncodedObject(o)) {
59732f80e2fSantirez         serverLog(LL_WARNING,"Object raw string len: %zu", sdslen(o->ptr));
598cf71b821Santirez         if (sdslen(o->ptr) < 4096) {
599cf71b821Santirez             sds repr = sdscatrepr(sdsempty(),o->ptr,sdslen(o->ptr));
60032f80e2fSantirez             serverLog(LL_WARNING,"Object raw string content: %s", repr);
601cf71b821Santirez             sdsfree(repr);
602cf71b821Santirez         }
60314ff5724Santirez     } else if (o->type == OBJ_LIST) {
60432f80e2fSantirez         serverLog(LL_WARNING,"List length: %d", (int) listTypeLength(o));
60514ff5724Santirez     } else if (o->type == OBJ_SET) {
60632f80e2fSantirez         serverLog(LL_WARNING,"Set size: %d", (int) setTypeSize(o));
60714ff5724Santirez     } else if (o->type == OBJ_HASH) {
60832f80e2fSantirez         serverLog(LL_WARNING,"Hash size: %d", (int) hashTypeLength(o));
60914ff5724Santirez     } else if (o->type == OBJ_ZSET) {
61032f80e2fSantirez         serverLog(LL_WARNING,"Sorted set size: %d", (int) zsetLength(o));
61114ff5724Santirez         if (o->encoding == OBJ_ENCODING_SKIPLIST)
61232f80e2fSantirez             serverLog(LL_WARNING,"Skiplist level: %d", (int) ((zset*)o->ptr)->zsl->level);
613bab205f7Santirez     }
614e3e69935Santirez }
615e3e69935Santirez 
_serverAssertPrintObject(robj * o)6162d9e3eb1Santirez void _serverAssertPrintObject(robj *o) {
61700010fa9Santirez     bugReportStart();
61832f80e2fSantirez     serverLog(LL_WARNING,"=== ASSERTION FAILED OBJECT CONTEXT ===");
619424fe9afSantirez     serverLogObjectDebugInfo(o);
62000010fa9Santirez }
62100010fa9Santirez 
_serverAssertWithInfo(client * c,robj * o,char * estr,char * file,int line)6222d9e3eb1Santirez void _serverAssertWithInfo(client *c, robj *o, char *estr, char *file, int line) {
6232d9e3eb1Santirez     if (c) _serverAssertPrintClientInfo(c);
6242d9e3eb1Santirez     if (o) _serverAssertPrintObject(o);
6252d9e3eb1Santirez     _serverAssert(estr,file,line);
626e3e69935Santirez }
627e3e69935Santirez 
_serverPanic(char * msg,char * file,int line)62832f80e2fSantirez void _serverPanic(char *msg, char *file, int line) {
629fa5af017Santirez     bugReportStart();
63032f80e2fSantirez     serverLog(LL_WARNING,"------------------------------------------------");
63132f80e2fSantirez     serverLog(LL_WARNING,"!!! Software Failure. Press left mouse button to continue");
63232f80e2fSantirez     serverLog(LL_WARNING,"Guru Meditation: %s #%s:%d",msg,file,line);
633e2641e09Santirez #ifdef HAVE_BACKTRACE
63432f80e2fSantirez     serverLog(LL_WARNING,"(forcing SIGSEGV in order to print the stack trace)");
63557be4775SPieter Noordhuis #endif
63632f80e2fSantirez     serverLog(LL_WARNING,"------------------------------------------------");
637e2641e09Santirez     *((char*)-1) = 'x';
638e2641e09Santirez }
639d4d20859Santirez 
bugReportStart(void)640ac834d23Santirez void bugReportStart(void) {
641ac834d23Santirez     if (server.bug_report_start == 0) {
642f034a075Santirez         serverLogRaw(LL_WARNING|LL_RAW,
643f034a075Santirez         "\n\n=== REDIS BUG REPORT START: Cut & paste starting from here ===\n");
644ac834d23Santirez         server.bug_report_start = 1;
645ac834d23Santirez     }
646ac834d23Santirez }
647ac834d23Santirez 
648d4d20859Santirez #ifdef HAVE_BACKTRACE
getMcontextEip(ucontext_t * uc)649d4d20859Santirez static void *getMcontextEip(ucontext_t *uc) {
650a66a4963Santirez #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
651a66a4963Santirez     /* OSX < 10.6 */
652a66a4963Santirez     #if defined(__x86_64__)
653d4d20859Santirez     return (void*) uc->uc_mcontext->__ss.__rip;
654a66a4963Santirez     #elif defined(__i386__)
655d4d20859Santirez     return (void*) uc->uc_mcontext->__ss.__eip;
656d4d20859Santirez     #else
657d4d20859Santirez     return (void*) uc->uc_mcontext->__ss.__srr0;
658d4d20859Santirez     #endif
659d4d20859Santirez #elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
660a66a4963Santirez     /* OSX >= 10.6 */
661d4d20859Santirez     #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
662d4d20859Santirez     return (void*) uc->uc_mcontext->__ss.__rip;
663d4d20859Santirez     #else
664d4d20859Santirez     return (void*) uc->uc_mcontext->__ss.__eip;
665d4d20859Santirez     #endif
666a66a4963Santirez #elif defined(__linux__)
667a66a4963Santirez     /* Linux */
668a66a4963Santirez     #if defined(__i386__)
669d4d20859Santirez     return (void*) uc->uc_mcontext.gregs[14]; /* Linux 32 */
670d4d20859Santirez     #elif defined(__X86_64__) || defined(__x86_64__)
671d4d20859Santirez     return (void*) uc->uc_mcontext.gregs[16]; /* Linux 64 */
672d4d20859Santirez     #elif defined(__ia64__) /* Linux IA64 */
673d4d20859Santirez     return (void*) uc->uc_mcontext.sc_ip;
674a66a4963Santirez     #endif
675d4d20859Santirez #else
676d4d20859Santirez     return NULL;
677d4d20859Santirez #endif
678d4d20859Santirez }
679d4d20859Santirez 
logStackContent(void ** sp)680d4d20859Santirez void logStackContent(void **sp) {
681d4d20859Santirez     int i;
682d4d20859Santirez     for (i = 15; i >= 0; i--) {
683f9b5ca29Santirez         unsigned long addr = (unsigned long) sp+i;
684f9b5ca29Santirez         unsigned long val = (unsigned long) sp[i];
685f9b5ca29Santirez 
686447ebf3bSantirez         if (sizeof(long) == 4)
68732f80e2fSantirez             serverLog(LL_WARNING, "(%08lx) -> %08lx", addr, val);
688447ebf3bSantirez         else
68932f80e2fSantirez             serverLog(LL_WARNING, "(%016lx) -> %016lx", addr, val);
690d4d20859Santirez     }
691d4d20859Santirez }
692d4d20859Santirez 
logRegisters(ucontext_t * uc)693d4d20859Santirez void logRegisters(ucontext_t *uc) {
694f034a075Santirez     serverLog(LL_WARNING|LL_RAW, "\n------ REGISTERS ------\n");
695a66a4963Santirez 
696a66a4963Santirez /* OSX */
697d4d20859Santirez #if defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
698a66a4963Santirez   /* OSX AMD64 */
699d4d20859Santirez     #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
70032f80e2fSantirez     serverLog(LL_WARNING,
701d4d20859Santirez     "\n"
702447ebf3bSantirez     "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n"
703447ebf3bSantirez     "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n"
704447ebf3bSantirez     "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n"
705447ebf3bSantirez     "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n"
706447ebf3bSantirez     "RIP:%016lx EFL:%016lx\nCS :%016lx FS:%016lx  GS:%016lx",
707f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__rax,
708f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__rbx,
709f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__rcx,
710f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__rdx,
711f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__rdi,
712f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__rsi,
713f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__rbp,
714f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__rsp,
715f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__r8,
716f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__r9,
717f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__r10,
718f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__r11,
719f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__r12,
720f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__r13,
721f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__r14,
722f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__r15,
723f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__rip,
724f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__rflags,
725f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__cs,
726f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__fs,
727f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__gs
728d4d20859Santirez     );
729d4d20859Santirez     logStackContent((void**)uc->uc_mcontext->__ss.__rsp);
730d4d20859Santirez     #else
731a66a4963Santirez     /* OSX x86 */
73232f80e2fSantirez     serverLog(LL_WARNING,
733d4d20859Santirez     "\n"
734447ebf3bSantirez     "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n"
735447ebf3bSantirez     "EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n"
736447ebf3bSantirez     "SS:%08lx  EFL:%08lx EIP:%08lx CS :%08lx\n"
737447ebf3bSantirez     "DS:%08lx  ES:%08lx  FS :%08lx GS :%08lx",
738f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__eax,
739f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__ebx,
740f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__ecx,
741f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__edx,
742f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__edi,
743f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__esi,
744f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__ebp,
745f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__esp,
746f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__ss,
747f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__eflags,
748f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__eip,
749f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__cs,
750f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__ds,
751f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__es,
752f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__fs,
753f9b5ca29Santirez         (unsigned long) uc->uc_mcontext->__ss.__gs
754d4d20859Santirez     );
755d4d20859Santirez     logStackContent((void**)uc->uc_mcontext->__ss.__esp);
756d4d20859Santirez     #endif
757a66a4963Santirez /* Linux */
758a66a4963Santirez #elif defined(__linux__)
759a66a4963Santirez     /* Linux x86 */
760a66a4963Santirez     #if defined(__i386__)
76132f80e2fSantirez     serverLog(LL_WARNING,
762632da605Santirez     "\n"
763447ebf3bSantirez     "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n"
764447ebf3bSantirez     "EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n"
765447ebf3bSantirez     "SS :%08lx EFL:%08lx EIP:%08lx CS:%08lx\n"
766447ebf3bSantirez     "DS :%08lx ES :%08lx FS :%08lx GS:%08lx",
767f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[11],
768f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[8],
769f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[10],
770f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[9],
771f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[4],
772f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[5],
773f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[6],
774f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[7],
775f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[18],
776f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[17],
777f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[14],
778f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[15],
779f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[3],
780f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[2],
781f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[1],
782f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[0]
783632da605Santirez     );
784632da605Santirez     logStackContent((void**)uc->uc_mcontext.gregs[7]);
785d4d20859Santirez     #elif defined(__X86_64__) || defined(__x86_64__)
786a66a4963Santirez     /* Linux AMD64 */
78732f80e2fSantirez     serverLog(LL_WARNING,
788eea8c7a4Santirez     "\n"
789447ebf3bSantirez     "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n"
790447ebf3bSantirez     "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n"
791447ebf3bSantirez     "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n"
792447ebf3bSantirez     "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n"
793447ebf3bSantirez     "RIP:%016lx EFL:%016lx\nCSGSFS:%016lx",
794f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[13],
795f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[11],
796f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[14],
797f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[12],
798f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[8],
799f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[9],
800f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[10],
801f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[15],
802f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[0],
803f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[1],
804f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[2],
805f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[3],
806f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[4],
807f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[5],
808f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[6],
809f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[7],
810f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[16],
811f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[17],
812f9b5ca29Santirez         (unsigned long) uc->uc_mcontext.gregs[18]
813eea8c7a4Santirez     );
814eea8c7a4Santirez     logStackContent((void**)uc->uc_mcontext.gregs[15]);
815a66a4963Santirez     #endif
816d4d20859Santirez #else
81732f80e2fSantirez     serverLog(LL_WARNING,
818d4d20859Santirez         "  Dumping of registers not supported for this OS/arch");
819d4d20859Santirez #endif
820d4d20859Santirez }
821d4d20859Santirez 
822fc00042eSantirez /* Return a file descriptor to write directly to the Redis log with the
823fc00042eSantirez  * write(2) syscall, that can be used in critical sections of the code
824fc00042eSantirez  * where the rest of Redis can't be trusted (for example during the memory
825fc00042eSantirez  * test) or when an API call requires a raw fd.
826fc00042eSantirez  *
827fc00042eSantirez  * Close it with closeDirectLogFiledes(). */
openDirectLogFiledes(void)828fc00042eSantirez int openDirectLogFiledes(void) {
829fc00042eSantirez     int log_to_stdout = server.logfile[0] == '\0';
830fc00042eSantirez     int fd = log_to_stdout ?
831fc00042eSantirez         STDOUT_FILENO :
832fc00042eSantirez         open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644);
833fc00042eSantirez     return fd;
834fc00042eSantirez }
835fc00042eSantirez 
836fc00042eSantirez /* Used to close what closeDirectLogFiledes() returns. */
closeDirectLogFiledes(int fd)837fc00042eSantirez void closeDirectLogFiledes(int fd) {
838fc00042eSantirez     int log_to_stdout = server.logfile[0] == '\0';
839fc00042eSantirez     if (!log_to_stdout) close(fd);
840fc00042eSantirez }
841fc00042eSantirez 
84211bd247dSantirez /* Logs the stack trace using the backtrace() call. This function is designed
84311bd247dSantirez  * to be called from signal handlers safely. */
logStackTrace(ucontext_t * uc)84411bd247dSantirez void logStackTrace(ucontext_t *uc) {
845f034a075Santirez     void *trace[101];
846fc00042eSantirez     int trace_size = 0, fd = openDirectLogFiledes();
84711bd247dSantirez 
848fc00042eSantirez     if (fd == -1) return; /* If we can't log there is anything to do. */
849d4d20859Santirez 
850d4d20859Santirez     /* Generate the stack trace */
851f034a075Santirez     trace_size = backtrace(trace+1, 100);
852d4d20859Santirez 
853f034a075Santirez     if (getMcontextEip(uc) != NULL) {
854f034a075Santirez         char *msg1 = "EIP:\n";
855f034a075Santirez         char *msg2 = "\nBacktrace:\n";
856ef92f90dSantirez         if (write(fd,msg1,strlen(msg1)) == -1) {/* Avoid warning. */};
857f034a075Santirez         trace[0] = getMcontextEip(uc);
858f034a075Santirez         backtrace_symbols_fd(trace, 1, fd);
859ef92f90dSantirez         if (write(fd,msg2,strlen(msg2)) == -1) {/* Avoid warning. */};
860f034a075Santirez     }
86111bd247dSantirez 
86211bd247dSantirez     /* Write symbols to log file */
863f034a075Santirez     backtrace_symbols_fd(trace+1, trace_size, fd);
86411bd247dSantirez 
86511bd247dSantirez     /* Cleanup */
866fc00042eSantirez     closeDirectLogFiledes(fd);
867a3238704Santirez }
868d4d20859Santirez 
869a3238704Santirez /* Log information about the "current" client, that is, the client that is
870a3238704Santirez  * currently being served by Redis. May be NULL if Redis is not serving a
871a3238704Santirez  * client right now. */
logCurrentClient(void)872a3238704Santirez void logCurrentClient(void) {
873a3238704Santirez     if (server.current_client == NULL) return;
874d4d20859Santirez 
875554bd0e7Santirez     client *cc = server.current_client;
876d4d20859Santirez     sds client;
877d4d20859Santirez     int j;
878d4d20859Santirez 
879f034a075Santirez     serverLogRaw(LL_WARNING|LL_RAW, "\n------ CURRENT CLIENT INFO ------\n");
8800bcc7cb4Santirez     client = catClientInfoString(sdsempty(),cc);
881f034a075Santirez     serverLog(LL_WARNING|LL_RAW,"%s\n", client);
882a3238704Santirez     sdsfree(client);
883d4d20859Santirez     for (j = 0; j < cc->argc; j++) {
884d4d20859Santirez         robj *decoded;
885d4d20859Santirez 
886d4d20859Santirez         decoded = getDecodedObject(cc->argv[j]);
887f034a075Santirez         serverLog(LL_WARNING|LL_RAW,"argv[%d]: '%s'\n", j,
888f034a075Santirez             (char*)decoded->ptr);
889d4d20859Santirez         decrRefCount(decoded);
890d4d20859Santirez     }
891d4d20859Santirez     /* Check if the first argument, usually a key, is found inside the
892d4d20859Santirez      * selected DB, and if so print info about the associated object. */
893d4d20859Santirez     if (cc->argc >= 1) {
894d4d20859Santirez         robj *val, *key;
895d4d20859Santirez         dictEntry *de;
896d4d20859Santirez 
897d4d20859Santirez         key = getDecodedObject(cc->argv[1]);
898d4d20859Santirez         de = dictFind(cc->db->dict, key->ptr);
899d4d20859Santirez         if (de) {
900d4d20859Santirez             val = dictGetVal(de);
90132f80e2fSantirez             serverLog(LL_WARNING,"key '%s' found in DB containing the following object:", (char*)key->ptr);
902424fe9afSantirez             serverLogObjectDebugInfo(val);
903d4d20859Santirez         }
904d4d20859Santirez         decrRefCount(key);
905d4d20859Santirez     }
906d4d20859Santirez }
907d4d20859Santirez 
9085a9e3f58Santirez #if defined(HAVE_PROC_MAPS)
909fc00042eSantirez 
910b1b602a9Santirez #define MEMTEST_MAX_REGIONS 128
9115a9e3f58Santirez 
912fc00042eSantirez /* A non destructive memory test executed during segfauls. */
memtest_test_linux_anonymous_maps(void)9135a9e3f58Santirez int memtest_test_linux_anonymous_maps(void) {
914fc00042eSantirez     FILE *fp;
9155a9e3f58Santirez     char line[1024];
916fc00042eSantirez     char logbuf[1024];
9175a9e3f58Santirez     size_t start_addr, end_addr, size;
918b1b602a9Santirez     size_t start_vect[MEMTEST_MAX_REGIONS];
919b1b602a9Santirez     size_t size_vect[MEMTEST_MAX_REGIONS];
920b1b602a9Santirez     int regions = 0, j;
9215a9e3f58Santirez 
922fc00042eSantirez     int fd = openDirectLogFiledes();
923fc00042eSantirez     if (!fd) return 0;
924fc00042eSantirez 
925fc00042eSantirez     fp = fopen("/proc/self/maps","r");
926fc00042eSantirez     if (!fp) return 0;
9275a9e3f58Santirez     while(fgets(line,sizeof(line),fp) != NULL) {
9285a9e3f58Santirez         char *start, *end, *p = line;
9295a9e3f58Santirez 
9305a9e3f58Santirez         start = p;
9315a9e3f58Santirez         p = strchr(p,'-');
9325a9e3f58Santirez         if (!p) continue;
9335a9e3f58Santirez         *p++ = '\0';
9345a9e3f58Santirez         end = p;
9355a9e3f58Santirez         p = strchr(p,' ');
9365a9e3f58Santirez         if (!p) continue;
9375a9e3f58Santirez         *p++ = '\0';
9385a9e3f58Santirez         if (strstr(p,"stack") ||
9395a9e3f58Santirez             strstr(p,"vdso") ||
9405a9e3f58Santirez             strstr(p,"vsyscall")) continue;
9415a9e3f58Santirez         if (!strstr(p,"00:00")) continue;
9425a9e3f58Santirez         if (!strstr(p,"rw")) continue;
9435a9e3f58Santirez 
9445a9e3f58Santirez         start_addr = strtoul(start,NULL,16);
9455a9e3f58Santirez         end_addr = strtoul(end,NULL,16);
9465a9e3f58Santirez         size = end_addr-start_addr;
947b1b602a9Santirez 
948b1b602a9Santirez         start_vect[regions] = start_addr;
949b1b602a9Santirez         size_vect[regions] = size;
950fc00042eSantirez         snprintf(logbuf,sizeof(logbuf),
951fc00042eSantirez             "*** Preparing to test memory region %lx (%lu bytes)\n",
952fc00042eSantirez                 (unsigned long) start_vect[regions],
9537404b958Santirez                 (unsigned long) size_vect[regions]);
954fc00042eSantirez         if (write(fd,logbuf,strlen(logbuf)) == -1) { /* Nothing to do. */ }
955b1b602a9Santirez         regions++;
956b1b602a9Santirez     }
957b1b602a9Santirez 
958fc00042eSantirez     int errors = 0;
959b1b602a9Santirez     for (j = 0; j < regions; j++) {
960fc00042eSantirez         if (write(fd,".",1) == -1) { /* Nothing to do. */ }
961fc00042eSantirez         errors += memtest_preserving_test((void*)start_vect[j],size_vect[j],1);
962fc00042eSantirez         if (write(fd, errors ? "E" : "O",1) == -1) { /* Nothing to do. */ }
963b1b602a9Santirez     }
964fc00042eSantirez     if (write(fd,"\n",1) == -1) { /* Nothing to do. */ }
965b1b602a9Santirez 
966b1b602a9Santirez     /* NOTE: It is very important to close the file descriptor only now
967b1b602a9Santirez      * because closing it before may result into unmapping of some memory
968b1b602a9Santirez      * region that we are testing. */
9695a9e3f58Santirez     fclose(fp);
970fc00042eSantirez     closeDirectLogFiledes(fd);
971fc00042eSantirez     return errors;
9725a9e3f58Santirez }
9735a9e3f58Santirez #endif
9745a9e3f58Santirez 
sigsegvHandler(int sig,siginfo_t * info,void * secret)975a3238704Santirez void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
976a3238704Santirez     ucontext_t *uc = (ucontext_t*) secret;
977f034a075Santirez     void *eip = getMcontextEip(uc);
97811bd247dSantirez     sds infostring, clients;
979a3238704Santirez     struct sigaction act;
98032f80e2fSantirez     UNUSED(info);
981a3238704Santirez 
982a3238704Santirez     bugReportStart();
98332f80e2fSantirez     serverLog(LL_WARNING,
984a3238704Santirez         "Redis %s crashed by signal: %d", REDIS_VERSION, sig);
985f034a075Santirez     if (eip != NULL) {
986d5b55bdfSantirez         serverLog(LL_WARNING,
987f034a075Santirez         "Crashed running the instuction at: %p", eip);
988f034a075Santirez     }
989f034a075Santirez     if (sig == SIGSEGV || sig == SIGBUS) {
990f034a075Santirez         serverLog(LL_WARNING,
991f034a075Santirez         "Accessing address: %p", (void*)info->si_addr);
992d5b55bdfSantirez     }
99332f80e2fSantirez     serverLog(LL_WARNING,
994a3238704Santirez         "Failed assertion: %s (%s:%d)", server.assert_failed,
995a3238704Santirez                         server.assert_file, server.assert_line);
996a3238704Santirez 
997a3238704Santirez     /* Log the stack trace */
998f034a075Santirez     serverLogRaw(LL_WARNING|LL_RAW, "\n------ STACK TRACE ------\n");
99911bd247dSantirez     logStackTrace(uc);
1000a3238704Santirez 
1001a3238704Santirez     /* Log INFO and CLIENT LIST */
1002f034a075Santirez     serverLogRaw(LL_WARNING|LL_RAW, "\n------ INFO OUTPUT ------\n");
1003a3238704Santirez     infostring = genRedisInfoString("all");
1004a3238704Santirez     infostring = sdscatprintf(infostring, "hash_init_value: %u\n",
1005a3238704Santirez         dictGetHashFunctionSeed());
1006f034a075Santirez     serverLogRaw(LL_WARNING|LL_RAW, infostring);
1007f034a075Santirez     serverLogRaw(LL_WARNING|LL_RAW, "\n------ CLIENT LIST OUTPUT ------\n");
1008a3238704Santirez     clients = getAllClientsInfoString();
1009f034a075Santirez     serverLogRaw(LL_WARNING|LL_RAW, clients);
1010a3238704Santirez     sdsfree(infostring);
1011a3238704Santirez     sdsfree(clients);
1012a3238704Santirez 
1013a3238704Santirez     /* Log the current client */
1014a3238704Santirez     logCurrentClient();
1015a3238704Santirez 
1016d4d20859Santirez     /* Log dump of processor registers */
1017d4d20859Santirez     logRegisters(uc);
1018d4d20859Santirez 
10195a9e3f58Santirez #if defined(HAVE_PROC_MAPS)
10205a9e3f58Santirez     /* Test memory */
1021f034a075Santirez     serverLogRaw(LL_WARNING|LL_RAW, "\n------ FAST MEMORY TEST ------\n");
102275369917Santirez     bioKillThreads();
10235a9e3f58Santirez     if (memtest_test_linux_anonymous_maps()) {
1024fc00042eSantirez         serverLogRaw(LL_WARNING|LL_RAW,
10255a9e3f58Santirez             "!!! MEMORY ERROR DETECTED! Check your memory ASAP !!!");
10265a9e3f58Santirez     } else {
1027fc00042eSantirez         serverLogRaw(LL_WARNING|LL_RAW,
10285a9e3f58Santirez             "Fast memory test PASSED, however your memory can still be broken. Please run a memory test for several hours if possible.");
10295a9e3f58Santirez     }
10305a9e3f58Santirez #endif
10315a9e3f58Santirez 
1032f034a075Santirez     serverLogRaw(LL_WARNING|LL_RAW,
1033d4d20859Santirez "\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n"
1034678df675SMatt Stancliff "       Please report the crash by opening an issue on github:\n\n"
1035d4d20859Santirez "           http://github.com/antirez/redis/issues\n\n"
1036b187c591SJan-Erik Rediger "  Suspect RAM error? Use redis-server --test-memory to verify it.\n\n"
1037d4d20859Santirez );
1038d4d20859Santirez     /* free(messages); Don't call free() with possibly corrupted memory. */
1039f0858634Santirez     if (server.daemonize && server.supervised == 0) unlink(server.pidfile);
1040d4d20859Santirez 
1041d4d20859Santirez     /* Make sure we exit with the right signal at the end. So for instance
1042d4d20859Santirez      * the core will be dumped if enabled. */
1043d4d20859Santirez     sigemptyset (&act.sa_mask);
1044d4d20859Santirez     act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND;
1045d4d20859Santirez     act.sa_handler = SIG_DFL;
1046d4d20859Santirez     sigaction (sig, &act, NULL);
1047d4d20859Santirez     kill(getpid(),sig);
1048d4d20859Santirez }
1049d4d20859Santirez #endif /* HAVE_BACKTRACE */
105039bd025cSantirez 
1051ee789e15Santirez /* ==================== Logging functions for debugging ===================== */
1052ee789e15Santirez 
serverLogHexDump(int level,char * descr,void * value,size_t len)1053424fe9afSantirez void serverLogHexDump(int level, char *descr, void *value, size_t len) {
1054ee789e15Santirez     char buf[65], *b;
1055ee789e15Santirez     unsigned char *v = value;
1056ee789e15Santirez     char charset[] = "0123456789abcdef";
1057ee789e15Santirez 
1058424fe9afSantirez     serverLog(level,"%s (hexdump):", descr);
1059ee789e15Santirez     b = buf;
1060ee789e15Santirez     while(len) {
1061ee789e15Santirez         b[0] = charset[(*v)>>4];
1062ee789e15Santirez         b[1] = charset[(*v)&0xf];
1063ee789e15Santirez         b[2] = '\0';
1064ee789e15Santirez         b += 2;
1065ee789e15Santirez         len--;
1066ee789e15Santirez         v++;
1067ee789e15Santirez         if (b-buf == 64 || len == 0) {
106832f80e2fSantirez             serverLogRaw(level|LL_RAW,buf);
1069ee789e15Santirez             b = buf;
1070ee789e15Santirez         }
1071ee789e15Santirez     }
107232f80e2fSantirez     serverLogRaw(level|LL_RAW,"\n");
1073ee789e15Santirez }
1074ee789e15Santirez 
107539bd025cSantirez /* =========================== Software Watchdog ============================ */
107639bd025cSantirez #include <sys/time.h>
107739bd025cSantirez 
watchdogSignalHandler(int sig,siginfo_t * info,void * secret)107839bd025cSantirez void watchdogSignalHandler(int sig, siginfo_t *info, void *secret) {
1079a66a4963Santirez #ifdef HAVE_BACKTRACE
108039bd025cSantirez     ucontext_t *uc = (ucontext_t*) secret;
1081a66a4963Santirez #endif
108232f80e2fSantirez     UNUSED(info);
108332f80e2fSantirez     UNUSED(sig);
108439bd025cSantirez 
108532f80e2fSantirez     serverLogFromHandler(LL_WARNING,"\n--- WATCHDOG TIMER EXPIRED ---");
108639bd025cSantirez #ifdef HAVE_BACKTRACE
108711bd247dSantirez     logStackTrace(uc);
108823c0cdd2Santirez #else
108932f80e2fSantirez     serverLogFromHandler(LL_WARNING,"Sorry: no support for backtrace().");
109039bd025cSantirez #endif
109132f80e2fSantirez     serverLogFromHandler(LL_WARNING,"--------\n");
109239bd025cSantirez }
109339bd025cSantirez 
109439bd025cSantirez /* Schedule a SIGALRM delivery after the specified period in milliseconds.
109539bd025cSantirez  * If a timer is already scheduled, this function will re-schedule it to the
109639bd025cSantirez  * specified time. If period is 0 the current timer is disabled. */
watchdogScheduleSignal(int period)109739bd025cSantirez void watchdogScheduleSignal(int period) {
109839bd025cSantirez     struct itimerval it;
109939bd025cSantirez 
110039bd025cSantirez     /* Will stop the timer if period is 0. */
110139bd025cSantirez     it.it_value.tv_sec = period/1000;
1102a354da9aSantirez     it.it_value.tv_usec = (period%1000)*1000;
110339bd025cSantirez     /* Don't automatically restart. */
110439bd025cSantirez     it.it_interval.tv_sec = 0;
110539bd025cSantirez     it.it_interval.tv_usec = 0;
110639bd025cSantirez     setitimer(ITIMER_REAL, &it, NULL);
110739bd025cSantirez }
110839bd025cSantirez 
1109504e5072SMatt Arsenault /* Enable the software watchdog with the specified period in milliseconds. */
enableWatchdog(int period)111039bd025cSantirez void enableWatchdog(int period) {
111161daf891Santirez     int min_period;
111261daf891Santirez 
111339bd025cSantirez     if (server.watchdog_period == 0) {
111439bd025cSantirez         struct sigaction act;
111539bd025cSantirez 
111639bd025cSantirez         /* Watchdog was actually disabled, so we have to setup the signal
111739bd025cSantirez          * handler. */
111839bd025cSantirez         sigemptyset(&act.sa_mask);
11197a0c5fdfSMatt Stancliff         act.sa_flags = SA_ONSTACK | SA_SIGINFO;
112039bd025cSantirez         act.sa_sigaction = watchdogSignalHandler;
112139bd025cSantirez         sigaction(SIGALRM, &act, NULL);
112239bd025cSantirez     }
112361daf891Santirez     /* If the configured period is smaller than twice the timer period, it is
112461daf891Santirez      * too short for the software watchdog to work reliably. Fix it now
112561daf891Santirez      * if needed. */
1126f1481d4aSantirez     min_period = (1000/server.hz)*2;
112761daf891Santirez     if (period < min_period) period = min_period;
112839bd025cSantirez     watchdogScheduleSignal(period); /* Adjust the current timer. */
112939bd025cSantirez     server.watchdog_period = period;
113039bd025cSantirez }
113139bd025cSantirez 
113239bd025cSantirez /* Disable the software watchdog. */
disableWatchdog(void)113339bd025cSantirez void disableWatchdog(void) {
113439bd025cSantirez     struct sigaction act;
113539bd025cSantirez     if (server.watchdog_period == 0) return; /* Already disabled. */
113639bd025cSantirez     watchdogScheduleSignal(0); /* Stop the current timer. */
113739bd025cSantirez 
113839bd025cSantirez     /* Set the signal handler to SIG_IGN, this will also remove pending
113939bd025cSantirez      * signals from the queue. */
114039bd025cSantirez     sigemptyset(&act.sa_mask);
114139bd025cSantirez     act.sa_flags = 0;
114239bd025cSantirez     act.sa_handler = SIG_IGN;
114339bd025cSantirez     sigaction(SIGALRM, &act, NULL);
114439bd025cSantirez     server.watchdog_period = 0;
114539bd025cSantirez }
1146