diff --git a/src/object.c b/src/object.c index cb5d1818..3b627e90 100644 --- a/src/object.c +++ b/src/object.c @@ -867,7 +867,107 @@ void memoryCommand(client *c) { usage += sdsAllocSize(c->argv[1]->ptr); usage += sizeof(dictEntry); addReplyLongLong(c,usage); + } else if (!strcasecmp(c->argv[1]->ptr,"overhead") && c->argc == 2) { + int j; + size_t mem_total = 0; + size_t mem = 0; + size_t zmalloc_used = zmalloc_used_memory(); + + int toplevel_keys = 8; + void *tlk = addDeferredMultiBulkLength(c); + + addReplyBulkCString(c,"total.allocated"); + addReplyLongLong(c,zmalloc_used); + + addReplyBulkCString(c,"startup.allocated"); + addReplyLongLong(c,server.initial_memory_usage); + mem_total += server.initial_memory_usage; + + mem = 0; + if (server.repl_backlog) + mem += zmalloc_size(server.repl_backlog); + addReplyBulkCString(c,"replication.backlog"); + addReplyLongLong(c,mem); + mem_total += mem; + + mem = 0; + if (listLength(server.slaves)) { + listIter li; + listNode *ln; + + listRewind(server.slaves,&li); + while((ln = listNext(&li))) { + client *client = listNodeValue(ln); + mem += getClientOutputBufferMemoryUsage(client); + mem += sdsAllocSize(client->querybuf); + mem += sizeof(client); + } + } + addReplyBulkCString(c,"clients.slaves"); + addReplyLongLong(c,mem); + mem_total+=mem; + + mem = 0; + if (listLength(server.clients)) { + listIter li; + listNode *ln; + + listRewind(server.clients,&li); + while((ln = listNext(&li))) { + client *client = listNodeValue(ln); + if (client->flags & CLIENT_SLAVE) + continue; + mem += getClientOutputBufferMemoryUsage(client); + mem += sdsAllocSize(client->querybuf); + mem += sizeof(client); + } + } + addReplyBulkCString(c,"clients.normal"); + addReplyLongLong(c,mem); + mem_total+=mem; + + mem = 0; + if (server.aof_state != AOF_OFF) { + mem += sdslen(server.aof_buf); + mem += aofRewriteBufferSize(); + } + addReplyBulkCString(c,"aof.buffer"); + addReplyLongLong(c,mem); + mem_total+=mem; + + for (j = 0; j < server.dbnum; j++) { + redisDb *db = server.db+j; + long long keyscount = dictSize(db->dict); + if (keyscount==0) continue; + + char dbname[32]; + toplevel_keys++; + snprintf(dbname,sizeof(dbname),"db.%d",j); + addReplyBulkCString(c,dbname); + addReplyMultiBulkLen(c,4); + + mem = dictSize(db->dict) * sizeof(dictEntry) + + dictSlots(db->dict) * sizeof(dictEntry*) + + dictSize(db->dict) * sizeof(robj); + addReplyBulkCString(c,"overhead.hashtable.main"); + addReplyLongLong(c,mem); + mem_total+=mem; + + mem = dictSize(db->expires) * sizeof(dictEntry) + + dictSlots(db->expires) * sizeof(dictEntry*); + addReplyBulkCString(c,"overhead.hashtable.expires"); + addReplyLongLong(c,mem); + mem_total+=mem; + } + + addReplyBulkCString(c,"overhead.total"); + addReplyLongLong(c,mem_total); + + addReplyBulkCString(c,"dataset"); + addReplyLongLong(c,zmalloc_used - mem_total); + + setDeferredMultiBulkLength(c,tlk,toplevel_keys*2); } else { - addReplyError(c,"Syntax error. Try MEMORY usage "); + addReplyError(c,"Syntax error. Try MEMORY [usage ] | [overhead]"); } } diff --git a/src/server.c b/src/server.c index d143bf0c..91c17f65 100644 --- a/src/server.c +++ b/src/server.c @@ -274,7 +274,7 @@ struct redisCommand redisCommandTable[] = { {"readwrite",readwriteCommand,1,"F",0,NULL,0,0,0,0,0}, {"dump",dumpCommand,2,"r",0,NULL,1,1,1,0,0}, {"object",objectCommand,3,"r",0,NULL,2,2,2,0,0}, - {"memory",memoryCommand,3,"r",0,NULL,0,0,0,0,0}, + {"memory",memoryCommand,-2,"r",0,NULL,0,0,0,0,0}, {"client",clientCommand,-2,"as",0,NULL,0,0,0,0,0}, {"eval",evalCommand,-3,"s",0,evalGetKeys,0,0,0,0,0}, {"evalsha",evalShaCommand,-3,"s",0,evalGetKeys,0,0,0,0,0}, @@ -1860,6 +1860,7 @@ void initServer(void) { slowlogInit(); latencyMonitorInit(); bioInit(); + server.initial_memory_usage = zmalloc_used_memory(); } /* Populates the Redis Command Table starting from the hard coded list diff --git a/src/server.h b/src/server.h index 28198910..0d104425 100644 --- a/src/server.h +++ b/src/server.h @@ -801,6 +801,7 @@ struct redisServer { int cronloops; /* Number of times the cron function run */ char runid[CONFIG_RUN_ID_SIZE+1]; /* ID always different at every exec. */ int sentinel_mode; /* True if this instance is a Sentinel. */ + size_t initial_memory_usage; /* Bytes used after initialization. */ /* Modules */ dict *moduleapi; /* Exported APIs dictionary for modules. */ list *loadmodule_queue; /* List of modules to load at startup. */