+/* ============================ Maxmemory directive ======================== */
+
+/* This function gets called when 'maxmemory' is set on the config file to limit
+ * the max memory used by the server, and we are out of memory.
+ * This function will try to, in order:
+ *
+ * - Free objects from the free list
+ * - Try to remove keys with an EXPIRE set
+ *
+ * It is not possible to free enough memory to reach used-memory < maxmemory
+ * the server will start refusing commands that will enlarge even more the
+ * memory usage.
+ */
+static void freeMemoryIfNeeded(void) {
+ while (server.maxmemory && zmalloc_used_memory() > server.maxmemory) {
+ if (listLength(server.objfreelist)) {
+ robj *o;
+
+ listNode *head = listFirst(server.objfreelist);
+ o = listNodeValue(head);
+ listDelNode(server.objfreelist,head);
+ zfree(o);
+ } else {
+ int j, k, freed = 0;
+
+ for (j = 0; j < server.dbnum; j++) {
+ int minttl = -1;
+ robj *minkey = NULL;
+ struct dictEntry *de;
+
+ if (dictSize(server.db[j].expires)) {
+ freed = 1;
+ /* From a sample of three keys drop the one nearest to
+ * the natural expire */
+ for (k = 0; k < 3; k++) {
+ time_t t;
+
+ de = dictGetRandomKey(server.db[j].expires);
+ t = (time_t) dictGetEntryVal(de);
+ if (minttl == -1 || t < minttl) {
+ minkey = dictGetEntryKey(de);
+ minttl = t;
+ }
+ }
+ deleteKey(server.db+j,minkey);
+ }
+ }
+ if (!freed) return; /* nothing to free... */
+ }
+ }
+}
+
+/* ================================= Debugging ============================== */
+
+static void debugCommand(redisClient *c) {
+ if (!strcasecmp(c->argv[1]->ptr,"segfault")) {
+ *((char*)-1) = 'x';
+ } else if (!strcasecmp(c->argv[1]->ptr,"object") && c->argc == 3) {
+ dictEntry *de = dictFind(c->db->dict,c->argv[2]);
+ robj *key, *val;
+
+ if (!de) {
+ addReply(c,shared.nokeyerr);
+ return;
+ }
+ key = dictGetEntryKey(de);
+ val = dictGetEntryVal(de);
+ addReplySds(c,sdscatprintf(sdsempty(),
+ "+Key at:%p refcount:%d, value at:%p refcount:%d encoding:%d\r\n",
+ key, key->refcount, val, val->refcount, val->encoding));
+ } else {
+ addReplySds(c,sdsnew(
+ "-ERR Syntax error, try DEBUG [SEGFAULT|OBJECT <key>]\r\n"));
+ }
+}
+
+#ifdef HAVE_BACKTRACE
+static struct redisFunctionSym symsTable[] = {
+{"compareStringObjects", (unsigned long)compareStringObjects},
+{"isStringRepresentableAsLong", (unsigned long)isStringRepresentableAsLong},
+{"dictEncObjKeyCompare", (unsigned long)dictEncObjKeyCompare},
+{"dictEncObjHash", (unsigned long)dictEncObjHash},
+{"incrDecrCommand", (unsigned long)incrDecrCommand},
+{"freeStringObject", (unsigned long)freeStringObject},
+{"freeListObject", (unsigned long)freeListObject},
+{"freeSetObject", (unsigned long)freeSetObject},
+{"decrRefCount", (unsigned long)decrRefCount},
+{"createObject", (unsigned long)createObject},
+{"freeClient", (unsigned long)freeClient},
+{"rdbLoad", (unsigned long)rdbLoad},
+{"rdbSaveStringObject", (unsigned long)rdbSaveStringObject},
+{"rdbSaveStringObjectRaw", (unsigned long)rdbSaveStringObjectRaw},
+{"addReply", (unsigned long)addReply},
+{"addReplySds", (unsigned long)addReplySds},
+{"incrRefCount", (unsigned long)incrRefCount},
+{"rdbSaveBackground", (unsigned long)rdbSaveBackground},
+{"createStringObject", (unsigned long)createStringObject},
+{"replicationFeedSlaves", (unsigned long)replicationFeedSlaves},
+{"syncWithMaster", (unsigned long)syncWithMaster},
+{"tryObjectSharing", (unsigned long)tryObjectSharing},
+{"tryObjectEncoding", (unsigned long)tryObjectEncoding},
+{"getDecodedObject", (unsigned long)getDecodedObject},
+{"removeExpire", (unsigned long)removeExpire},
+{"expireIfNeeded", (unsigned long)expireIfNeeded},
+{"deleteIfVolatile", (unsigned long)deleteIfVolatile},
+{"deleteKey", (unsigned long)deleteKey},
+{"getExpire", (unsigned long)getExpire},
+{"setExpire", (unsigned long)setExpire},
+{"updateSlavesWaitingBgsave", (unsigned long)updateSlavesWaitingBgsave},
+{"freeMemoryIfNeeded", (unsigned long)freeMemoryIfNeeded},
+{"authCommand", (unsigned long)authCommand},
+{"pingCommand", (unsigned long)pingCommand},
+{"echoCommand", (unsigned long)echoCommand},
+{"setCommand", (unsigned long)setCommand},
+{"setnxCommand", (unsigned long)setnxCommand},
+{"getCommand", (unsigned long)getCommand},
+{"delCommand", (unsigned long)delCommand},
+{"existsCommand", (unsigned long)existsCommand},
+{"incrCommand", (unsigned long)incrCommand},
+{"decrCommand", (unsigned long)decrCommand},
+{"incrbyCommand", (unsigned long)incrbyCommand},
+{"decrbyCommand", (unsigned long)decrbyCommand},
+{"selectCommand", (unsigned long)selectCommand},
+{"randomkeyCommand", (unsigned long)randomkeyCommand},
+{"keysCommand", (unsigned long)keysCommand},
+{"dbsizeCommand", (unsigned long)dbsizeCommand},
+{"lastsaveCommand", (unsigned long)lastsaveCommand},
+{"saveCommand", (unsigned long)saveCommand},
+{"bgsaveCommand", (unsigned long)bgsaveCommand},
+{"shutdownCommand", (unsigned long)shutdownCommand},
+{"moveCommand", (unsigned long)moveCommand},
+{"renameCommand", (unsigned long)renameCommand},
+{"renamenxCommand", (unsigned long)renamenxCommand},
+{"lpushCommand", (unsigned long)lpushCommand},
+{"rpushCommand", (unsigned long)rpushCommand},
+{"lpopCommand", (unsigned long)lpopCommand},
+{"rpopCommand", (unsigned long)rpopCommand},
+{"llenCommand", (unsigned long)llenCommand},
+{"lindexCommand", (unsigned long)lindexCommand},
+{"lrangeCommand", (unsigned long)lrangeCommand},
+{"ltrimCommand", (unsigned long)ltrimCommand},
+{"typeCommand", (unsigned long)typeCommand},
+{"lsetCommand", (unsigned long)lsetCommand},
+{"saddCommand", (unsigned long)saddCommand},
+{"sremCommand", (unsigned long)sremCommand},
+{"smoveCommand", (unsigned long)smoveCommand},
+{"sismemberCommand", (unsigned long)sismemberCommand},
+{"scardCommand", (unsigned long)scardCommand},
+{"spopCommand", (unsigned long)spopCommand},
+{"srandmemberCommand", (unsigned long)srandmemberCommand},
+{"sinterCommand", (unsigned long)sinterCommand},
+{"sinterstoreCommand", (unsigned long)sinterstoreCommand},
+{"sunionCommand", (unsigned long)sunionCommand},
+{"sunionstoreCommand", (unsigned long)sunionstoreCommand},
+{"sdiffCommand", (unsigned long)sdiffCommand},
+{"sdiffstoreCommand", (unsigned long)sdiffstoreCommand},
+{"syncCommand", (unsigned long)syncCommand},
+{"flushdbCommand", (unsigned long)flushdbCommand},
+{"flushallCommand", (unsigned long)flushallCommand},
+{"sortCommand", (unsigned long)sortCommand},
+{"lremCommand", (unsigned long)lremCommand},
+{"infoCommand", (unsigned long)infoCommand},
+{"mgetCommand", (unsigned long)mgetCommand},
+{"monitorCommand", (unsigned long)monitorCommand},
+{"expireCommand", (unsigned long)expireCommand},
+{"expireatCommand", (unsigned long)expireatCommand},
+{"getsetCommand", (unsigned long)getsetCommand},
+{"ttlCommand", (unsigned long)ttlCommand},
+{"slaveofCommand", (unsigned long)slaveofCommand},
+{"debugCommand", (unsigned long)debugCommand},
+{"processCommand", (unsigned long)processCommand},
+{"setupSigSegvAction", (unsigned long)setupSigSegvAction},
+{"readQueryFromClient", (unsigned long)readQueryFromClient},
+{"rdbRemoveTempFile", (unsigned long)rdbRemoveTempFile},
+{"msetGenericCommand", (unsigned long)msetGenericCommand},
+{"msetCommand", (unsigned long)msetCommand},
+{"msetnxCommand", (unsigned long)msetnxCommand},
+{"zslCreateNode", (unsigned long)zslCreateNode},
+{"zslCreate", (unsigned long)zslCreate},
+{"zslFreeNode",(unsigned long)zslFreeNode},
+{"zslFree",(unsigned long)zslFree},
+{"zslRandomLevel",(unsigned long)zslRandomLevel},
+{"zslInsert",(unsigned long)zslInsert},
+{"zslDelete",(unsigned long)zslDelete},
+{"createZsetObject",(unsigned long)createZsetObject},
+{"zaddCommand",(unsigned long)zaddCommand},
+{"zrangeGenericCommand",(unsigned long)zrangeGenericCommand},
+{"zrangeCommand",(unsigned long)zrangeCommand},
+{"zrevrangeCommand",(unsigned long)zrevrangeCommand},
+{"zremCommand",(unsigned long)zremCommand},
+{"rdbSaveDoubleValue",(unsigned long)rdbSaveDoubleValue},
+{"rdbLoadDoubleValue",(unsigned long)rdbLoadDoubleValue},
+{NULL,0}
+};
+
+/* This function try to convert a pointer into a function name. It's used in
+ * oreder to provide a backtrace under segmentation fault that's able to
+ * display functions declared as static (otherwise the backtrace is useless). */
+static char *findFuncName(void *pointer, unsigned long *offset){
+ int i, ret = -1;
+ unsigned long off, minoff = 0;
+
+ /* Try to match against the Symbol with the smallest offset */
+ for (i=0; symsTable[i].pointer; i++) {
+ unsigned long lp = (unsigned long) pointer;
+
+ if (lp != (unsigned long)-1 && lp >= symsTable[i].pointer) {
+ off=lp-symsTable[i].pointer;
+ if (ret < 0 || off < minoff) {
+ minoff=off;
+ ret=i;
+ }
+ }
+ }
+ if (ret == -1) return NULL;
+ *offset = minoff;
+ return symsTable[ret].name;
+}
+
+static void *getMcontextEip(ucontext_t *uc) {
+#if defined(__FreeBSD__)
+ return (void*) uc->uc_mcontext.mc_eip;
+#elif defined(__dietlibc__)
+ return (void*) uc->uc_mcontext.eip;
+#elif defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
+ return (void*) uc->uc_mcontext->__ss.__eip;
+#elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
+ #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
+ return (void*) uc->uc_mcontext->__ss.__rip;
+ #else
+ return (void*) uc->uc_mcontext->__ss.__eip;
+ #endif
+#elif defined(__i386__) || defined(__X86_64__) /* Linux x86 */
+ return (void*) uc->uc_mcontext.gregs[REG_EIP];
+#elif defined(__ia64__) /* Linux IA64 */
+ return (void*) uc->uc_mcontext.sc_ip;
+#else
+ return NULL;
+#endif
+}
+
+static void segvHandler(int sig, siginfo_t *info, void *secret) {
+ void *trace[100];
+ char **messages = NULL;
+ int i, trace_size = 0;
+ unsigned long offset=0;
+ time_t uptime = time(NULL)-server.stat_starttime;
+ ucontext_t *uc = (ucontext_t*) secret;
+ REDIS_NOTUSED(info);
+
+ redisLog(REDIS_WARNING,
+ "======= Ooops! Redis %s got signal: -%d- =======", REDIS_VERSION, sig);
+ redisLog(REDIS_WARNING, "%s", sdscatprintf(sdsempty(),
+ "redis_version:%s; "
+ "uptime_in_seconds:%d; "
+ "connected_clients:%d; "
+ "connected_slaves:%d; "
+ "used_memory:%zu; "
+ "changes_since_last_save:%lld; "
+ "bgsave_in_progress:%d; "
+ "last_save_time:%d; "
+ "total_connections_received:%lld; "
+ "total_commands_processed:%lld; "
+ "role:%s;"
+ ,REDIS_VERSION,
+ uptime,
+ listLength(server.clients)-listLength(server.slaves),
+ listLength(server.slaves),
+ server.usedmemory,
+ server.dirty,
+ server.bgsaveinprogress,
+ server.lastsave,
+ server.stat_numconnections,
+ server.stat_numcommands,
+ server.masterhost == NULL ? "master" : "slave"
+ ));
+
+ trace_size = backtrace(trace, 100);
+ /* overwrite sigaction with caller's address */
+ if (getMcontextEip(uc) != NULL) {
+ trace[1] = getMcontextEip(uc);
+ }
+ messages = backtrace_symbols(trace, trace_size);
+
+ for (i=1; i<trace_size; ++i) {
+ char *fn = findFuncName(trace[i], &offset), *p;
+
+ p = strchr(messages[i],'+');
+ if (!fn || (p && ((unsigned long)strtol(p+1,NULL,10)) < offset)) {
+ redisLog(REDIS_WARNING,"%s", messages[i]);
+ } else {
+ redisLog(REDIS_WARNING,"%d redis-server %p %s + %d", i, trace[i], fn, (unsigned int)offset);
+ }
+ }
+ free(messages);
+ exit(0);
+}
+
+static void setupSigSegvAction(void) {
+ struct sigaction act;
+
+ sigemptyset (&act.sa_mask);
+ /* When the SA_SIGINFO flag is set in sa_flags then sa_sigaction
+ * is used. Otherwise, sa_handler is used */
+ act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
+ act.sa_sigaction = segvHandler;
+ sigaction (SIGSEGV, &act, NULL);
+ sigaction (SIGBUS, &act, NULL);
+ sigaction (SIGFPE, &act, NULL);
+ sigaction (SIGILL, &act, NULL);
+ sigaction (SIGBUS, &act, NULL);
+ return;
+}
+#else /* HAVE_BACKTRACE */
+static void setupSigSegvAction(void) {
+}
+#endif /* HAVE_BACKTRACE */
+