diff --git a/src/debug.c b/src/debug.c index 94c3c26582..b1b54b5e19 100644 --- a/src/debug.c +++ b/src/debug.c @@ -413,9 +413,9 @@ void debugCommand(client *c) { " Create a memory leak of the input string.", "LOG ", " Write to the server log.", -"HTSTATS ", +"HTSTATS [full]", " Return hash table statistics of the specified Redis database.", -"HTSTATS-KEY ", +"HTSTATS-KEY [full]", " Like HTSTATS but for the hash table stored at 's value.", "LOADAOF", " Flush the AOF buffers on disk and reload the AOF in memory.", @@ -883,10 +883,11 @@ NULL sizes = sdscatprintf(sizes,"sdshdr32:%d ",(int)sizeof(struct sdshdr32)); sizes = sdscatprintf(sizes,"sdshdr64:%d ",(int)sizeof(struct sdshdr64)); addReplyBulkSds(c,sizes); - } else if (!strcasecmp(c->argv[1]->ptr,"htstats") && c->argc == 3) { + } else if (!strcasecmp(c->argv[1]->ptr,"htstats") && c->argc >= 3) { long dbid; sds stats = sdsempty(); char buf[4096]; + int full = 0; if (getLongFromObjectOrReply(c, c->argv[2], &dbid, NULL) != C_OK) { sdsfree(stats); @@ -897,20 +898,26 @@ NULL addReplyError(c,"Out of range database"); return; } + if (c->argc >= 4 && !strcasecmp(c->argv[3]->ptr,"full")) + full = 1; stats = sdscatprintf(stats,"[Dictionary HT]\n"); - dictGetStats(buf,sizeof(buf),server.db[dbid].dict); + dictGetStats(buf,sizeof(buf),server.db[dbid].dict,full); stats = sdscat(stats,buf); stats = sdscatprintf(stats,"[Expires HT]\n"); - dictGetStats(buf,sizeof(buf),server.db[dbid].expires); + dictGetStats(buf,sizeof(buf),server.db[dbid].expires,full); stats = sdscat(stats,buf); addReplyVerbatim(c,stats,sdslen(stats),"txt"); sdsfree(stats); - } else if (!strcasecmp(c->argv[1]->ptr,"htstats-key") && c->argc == 3) { + } else if (!strcasecmp(c->argv[1]->ptr,"htstats-key") && c->argc >= 3) { robj *o; dict *ht = NULL; + int full = 0; + + if (c->argc >= 4 && !strcasecmp(c->argv[3]->ptr,"full")) + full = 1; if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nokeyerr)) == NULL) return; @@ -933,7 +940,7 @@ NULL "represented using an hash table"); } else { char buf[4096]; - dictGetStats(buf,sizeof(buf),ht); + dictGetStats(buf,sizeof(buf),ht,full); addReplyVerbatim(c,buf,strlen(buf),"txt"); } } else if (!strcasecmp(c->argv[1]->ptr,"change-repl-id") && c->argc == 2) { diff --git a/src/dict.c b/src/dict.c index b3d7fa4060..1d9934b4b3 100644 --- a/src/dict.c +++ b/src/dict.c @@ -1502,7 +1502,7 @@ dictEntry *dictFindEntryByPtrAndHash(dict *d, const void *oldptr, uint64_t hash) /* ------------------------------- Debugging ---------------------------------*/ #define DICT_STATS_VECTLEN 50 -size_t _dictGetStatsHt(char *buf, size_t bufsize, dict *d, int htidx) { +size_t _dictGetStatsHt(char *buf, size_t bufsize, dict *d, int htidx, int full) { unsigned long i, slots = 0, chainlen, maxchainlen = 0; unsigned long totchainlen = 0; unsigned long clvector[DICT_STATS_VECTLEN]; @@ -1513,6 +1513,20 @@ size_t _dictGetStatsHt(char *buf, size_t bufsize, dict *d, int htidx) { "No stats available for empty dictionaries\n"); } + if (!full) { + l += snprintf(buf+l,bufsize-l, + "Hash table %d stats (%s):\n" + " table size: %lu\n" + " number of elements: %lu\n", + htidx, (htidx == 0) ? "main hash table" : "rehashing target", + DICTHT_SIZE(d->ht_size_exp[htidx]), d->ht_used[htidx]); + + /* Make sure there is a NULL term at the end. */ + buf[bufsize-1] = '\0'; + /* Unlike snprintf(), return the number of characters actually written. */ + return strlen(buf); + } + /* Compute stats. */ for (i = 0; i < DICT_STATS_VECTLEN; i++) clvector[i] = 0; for (i = 0; i < DICTHT_SIZE(d->ht_size_exp[htidx]); i++) { @@ -1557,24 +1571,25 @@ size_t _dictGetStatsHt(char *buf, size_t bufsize, dict *d, int htidx) { i, clvector[i], ((float)clvector[i]/DICTHT_SIZE(d->ht_size_exp[htidx]))*100); } + /* Make sure there is a NULL term at the end. */ + buf[bufsize-1] = '\0'; /* Unlike snprintf(), return the number of characters actually written. */ - if (bufsize) buf[bufsize-1] = '\0'; return strlen(buf); } -void dictGetStats(char *buf, size_t bufsize, dict *d) { +void dictGetStats(char *buf, size_t bufsize, dict *d, int full) { size_t l; char *orig_buf = buf; size_t orig_bufsize = bufsize; - l = _dictGetStatsHt(buf,bufsize,d,0); - buf += l; - bufsize -= l; - if (dictIsRehashing(d) && bufsize > 0) { - _dictGetStatsHt(buf,bufsize,d,1); + l = _dictGetStatsHt(buf,bufsize,d,0,full); + if (dictIsRehashing(d) && bufsize > l) { + buf += l; + bufsize -= l; + _dictGetStatsHt(buf,bufsize,d,1,full); } /* Make sure there is a NULL term at the end. */ - if (orig_bufsize) orig_buf[orig_bufsize-1] = '\0'; + orig_buf[orig_bufsize-1] = '\0'; } /* ------------------------------- Benchmark ---------------------------------*/ diff --git a/src/dict.h b/src/dict.h index 06d4bff78a..e96cd44eb1 100644 --- a/src/dict.h +++ b/src/dict.h @@ -210,7 +210,7 @@ void dictReleaseIterator(dictIterator *iter); dictEntry *dictGetRandomKey(dict *d); dictEntry *dictGetFairRandomKey(dict *d); unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count); -void dictGetStats(char *buf, size_t bufsize, dict *d); +void dictGetStats(char *buf, size_t bufsize, dict *d, int full); uint64_t dictGenHashFunction(const void *key, size_t len); uint64_t dictGenCaseHashFunction(const unsigned char *buf, size_t len); void dictEmpty(dict *d, void(callback)(dict*));