diff --git a/src/debug.c b/src/debug.c index b8dcf648..2acba149 100644 --- a/src/debug.c +++ b/src/debug.c @@ -425,6 +425,27 @@ void debugCommand(redisClient *c) { sizes = sdscatprintf(sizes,"dictentry:%d ", (int)sizeof(dictEntry)); sizes = sdscatprintf(sizes,"sdshdr:%d", (int)sizeof(struct sdshdr)); addReplyBulkSds(c,sizes); + } else if (!strcasecmp(c->argv[1]->ptr,"htstats") && c->argc == 3) { + long dbid; + sds stats = sdsempty(); + char buf[4096]; + + if (getLongFromObjectOrReply(c, c->argv[2], &dbid, NULL) != REDIS_OK) + return; + if (dbid < 0 || dbid >= server.dbnum) { + addReplyError(c,"Out of range database"); + return; + } + + stats = sdscatprintf(stats,"[Dictionary HT]\n"); + dictGetStats(buf,sizeof(buf),server.db[dbid].dict); + stats = sdscat(stats,buf); + + stats = sdscatprintf(stats,"[Expires HT]\n"); + dictGetStats(buf,sizeof(buf),server.db[dbid].expires); + stats = sdscat(stats,buf); + + addReplyBulkSds(c,stats); } else if (!strcasecmp(c->argv[1]->ptr,"jemalloc") && c->argc == 3) { #if defined(USE_JEMALLOC) if (!strcasecmp(c->argv[2]->ptr, "info")) { diff --git a/src/dict.c b/src/dict.c index f728d381..06826275 100644 --- a/src/dict.c +++ b/src/dict.c @@ -1002,24 +1002,21 @@ void dictDisableResize(void) { dict_can_resize = 0; } -#if 0 - -/* The following is code that we don't use for Redis currently, but that is part -of the library. */ - -/* ----------------------- Debugging ------------------------*/ +/* ------------------------------- Debugging ---------------------------------*/ #define DICT_STATS_VECTLEN 50 -static void _dictPrintStatsHt(dictht *ht) { +size_t _dictGetStatsHt(char *buf, size_t bufsize, dictht *ht, int tableid) { unsigned long i, slots = 0, chainlen, maxchainlen = 0; unsigned long totchainlen = 0; unsigned long clvector[DICT_STATS_VECTLEN]; + size_t l = 0; if (ht->used == 0) { - printf("No stats available for empty dictionaries\n"); - return; + return snprintf(buf,bufsize, + "No stats available for empty dictionaries\n"); } + /* Compute stats. */ for (i = 0; i < DICT_STATS_VECTLEN; i++) clvector[i] = 0; for (i = 0; i < ht->size; i++) { dictEntry *he; @@ -1040,89 +1037,46 @@ static void _dictPrintStatsHt(dictht *ht) { if (chainlen > maxchainlen) maxchainlen = chainlen; totchainlen += chainlen; } - printf("Hash table stats:\n"); - printf(" table size: %ld\n", ht->size); - printf(" number of elements: %ld\n", ht->used); - printf(" different slots: %ld\n", slots); - printf(" max chain length: %ld\n", maxchainlen); - printf(" avg chain length (counted): %.02f\n", (float)totchainlen/slots); - printf(" avg chain length (computed): %.02f\n", (float)ht->used/slots); - printf(" Chain length distribution:\n"); + + /* Generate human readable stats. */ + l += snprintf(buf+l,bufsize-l, + "Hash table %d stats (%s):\n" + " table size: %ld\n" + " number of elements: %ld\n" + " different slots: %ld\n" + " max chain length: %ld\n" + " avg chain length (counted): %.02f\n" + " avg chain length (computed): %.02f\n" + " Chain length distribution:\n", + tableid, (tableid == 0) ? "main hash table" : "rehashing target", + ht->size, ht->used, slots, maxchainlen, + (float)totchainlen/slots, (float)ht->used/slots); + for (i = 0; i < DICT_STATS_VECTLEN-1; i++) { if (clvector[i] == 0) continue; - printf(" %s%ld: %ld (%.02f%%)\n",(i == DICT_STATS_VECTLEN-1)?">= ":"", i, clvector[i], ((float)clvector[i]/ht->size)*100); + if (l >= bufsize) break; + l += snprintf(buf+l,bufsize-l, + " %s%ld: %ld (%.02f%%)\n", + (i == DICT_STATS_VECTLEN-1)?">= ":"", + i, clvector[i], ((float)clvector[i]/ht->size)*100); } + + /* Unlike snprintf(), teturn the number of characters actually written. */ + if (bufsize) buf[bufsize-1] = '\0'; + return strlen(buf); } -void dictPrintStats(dict *d) { - _dictPrintStatsHt(&d->ht[0]); - if (dictIsRehashing(d)) { - printf("-- Rehashing into ht[1]:\n"); - _dictPrintStatsHt(&d->ht[1]); +void dictGetStats(char *buf, size_t bufsize, dict *d) { + size_t l; + char *orig_buf = buf; + size_t orig_bufsize = bufsize; + + l = _dictGetStatsHt(buf,bufsize,&d->ht[0],0); + buf += l; + bufsize -= l; + if (dictIsRehashing(d) && bufsize > 0) { + _dictGetStatsHt(buf,bufsize,&d->ht[1],1); } + /* Make sure there is a NULL term at the end. */ + if (orig_bufsize) orig_buf[orig_bufsize-1] = '\0'; } - -/* ----------------------- StringCopy Hash Table Type ------------------------*/ - -static unsigned int _dictStringCopyHTHashFunction(const void *key) -{ - return dictGenHashFunction(key, strlen(key)); -} - -static void *_dictStringDup(void *privdata, const void *key) -{ - int len = strlen(key); - char *copy = zmalloc(len+1); - DICT_NOTUSED(privdata); - - memcpy(copy, key, len); - copy[len] = '\0'; - return copy; -} - -static int _dictStringCopyHTKeyCompare(void *privdata, const void *key1, - const void *key2) -{ - DICT_NOTUSED(privdata); - - return strcmp(key1, key2) == 0; -} - -static void _dictStringDestructor(void *privdata, void *key) -{ - DICT_NOTUSED(privdata); - - zfree(key); -} - -dictType dictTypeHeapStringCopyKey = { - _dictStringCopyHTHashFunction, /* hash function */ - _dictStringDup, /* key dup */ - NULL, /* val dup */ - _dictStringCopyHTKeyCompare, /* key compare */ - _dictStringDestructor, /* key destructor */ - NULL /* val destructor */ -}; - -/* This is like StringCopy but does not auto-duplicate the key. - * It's used for intepreter's shared strings. */ -dictType dictTypeHeapStrings = { - _dictStringCopyHTHashFunction, /* hash function */ - NULL, /* key dup */ - NULL, /* val dup */ - _dictStringCopyHTKeyCompare, /* key compare */ - _dictStringDestructor, /* key destructor */ - NULL /* val destructor */ -}; - -/* This is like StringCopy but also automatically handle dynamic - * allocated C strings as values. */ -dictType dictTypeHeapStringCopyKeyValue = { - _dictStringCopyHTHashFunction, /* hash function */ - _dictStringDup, /* key dup */ - _dictStringDup, /* val dup */ - _dictStringCopyHTKeyCompare, /* key compare */ - _dictStringDestructor, /* key destructor */ - _dictStringDestructor, /* val destructor */ -}; -#endif diff --git a/src/dict.h b/src/dict.h index 014d1821..e31daee2 100644 --- a/src/dict.h +++ b/src/dict.h @@ -165,7 +165,7 @@ dictEntry *dictNext(dictIterator *iter); void dictReleaseIterator(dictIterator *iter); dictEntry *dictGetRandomKey(dict *d); unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count); -void dictPrintStats(dict *d); +void dictGetStats(char *buf, size_t bufsize, dict *d); unsigned int dictGenHashFunction(const void *key, int len); unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len); void dictEmpty(dict *d, void(callback)(void*)); diff --git a/src/redis-cli.c b/src/redis-cli.c index 251e42fa..acf7c98b 100644 --- a/src/redis-cli.c +++ b/src/redis-cli.c @@ -644,9 +644,9 @@ static int cliSendCommand(int argc, char **argv, int repeat) { output_raw = 0; if (!strcasecmp(command,"info") || - (argc == 3 && !strcasecmp(command,"debug") && - (!strcasecmp(argv[1],"jemalloc") && - !strcasecmp(argv[2],"info"))) || + (argc >= 2 && !strcasecmp(command,"debug") && + (!strcasecmp(argv[1],"jemalloc") || + !strcasecmp(argv[1],"htstats"))) || (argc == 2 && !strcasecmp(command,"cluster") && (!strcasecmp(argv[1],"nodes") || !strcasecmp(argv[1],"info"))) ||