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