diff --git a/src/redis.c b/src/redis.c index a7760e7b7087504cdd2281ba1a6fe261347f23db..52d3649a9b2ab78b67f5c73f5d242bf988d15aaf 100644 --- a/src/redis.c +++ b/src/redis.c @@ -174,7 +174,7 @@ struct redisCommand readonlyCommandTable[] = { {"flushdb",flushdbCommand,1,0,NULL,0,0,0,0}, {"flushall",flushallCommand,1,0,NULL,0,0,0,0}, {"sort",sortCommand,-2,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, - {"info",infoCommand,1,0,NULL,0,0,0,0}, + {"info",infoCommand,-1,0,NULL,0,0,0,0}, {"monitor",monitorCommand,1,0,NULL,0,0,0,0}, {"ttl",ttlCommand,2,0,NULL,1,1,1,0}, {"persist",persistCommand,2,0,NULL,1,1,1,0}, @@ -1156,204 +1156,277 @@ void bytesToHuman(char *s, unsigned long long n) { /* Create the string returned by the INFO command. This is decoupled * by the INFO command itself as we need to report the same information * on memory corruption problems. */ -sds genRedisInfoString(void) { - sds info; +sds genRedisInfoString(char *section) { + sds info = sdsempty(); time_t uptime = time(NULL)-server.stat_starttime; int j, numcommands; char hmem[64]; struct rusage self_ru, c_ru; unsigned long lol, bib; + int allsections = 0, defsections = 0; + int sections = 0; + + if (section) { + allsections = strcasecmp(section,"all") == 0; + allsections = strcasecmp(section,"default") == 0; + } getrusage(RUSAGE_SELF, &self_ru); getrusage(RUSAGE_CHILDREN, &c_ru); getClientsMaxBuffers(&lol,&bib); - bytesToHuman(hmem,zmalloc_used_memory()); - info = sdscatprintf(sdsempty(), - "redis_version:%s\r\n" - "redis_git_sha1:%s\r\n" - "redis_git_dirty:%d\r\n" - "arch_bits:%s\r\n" - "multiplexing_api:%s\r\n" - "process_id:%ld\r\n" - "uptime_in_seconds:%ld\r\n" - "uptime_in_days:%ld\r\n" - "lru_clock:%ld\r\n" - "used_cpu_sys:%.2f\r\n" - "used_cpu_user:%.2f\r\n" - "used_cpu_sys_childrens:%.2f\r\n" - "used_cpu_user_childrens:%.2f\r\n" - "connected_clients:%d\r\n" - "connected_slaves:%d\r\n" - "client_longest_output_list:%lu\r\n" - "client_biggest_input_buf:%lu\r\n" - "blocked_clients:%d\r\n" - "used_memory:%zu\r\n" - "used_memory_human:%s\r\n" - "used_memory_rss:%zu\r\n" - "mem_fragmentation_ratio:%.2f\r\n" - "use_tcmalloc:%d\r\n" - "loading:%d\r\n" - "aof_enabled:%d\r\n" - "changes_since_last_save:%lld\r\n" - "bgsave_in_progress:%d\r\n" - "last_save_time:%ld\r\n" - "bgrewriteaof_in_progress:%d\r\n" - "total_connections_received:%lld\r\n" - "total_commands_processed:%lld\r\n" - "expired_keys:%lld\r\n" - "evicted_keys:%lld\r\n" - "keyspace_hits:%lld\r\n" - "keyspace_misses:%lld\r\n" - "hash_max_zipmap_entries:%zu\r\n" - "hash_max_zipmap_value:%zu\r\n" - "pubsub_channels:%ld\r\n" - "pubsub_patterns:%u\r\n" - "ds_enabled:%d\r\n" - "role:%s\r\n" - ,REDIS_VERSION, - redisGitSHA1(), - strtol(redisGitDirty(),NULL,10) > 0, - (sizeof(long) == 8) ? "64" : "32", - aeGetApiName(), - (long) getpid(), - uptime, - uptime/(3600*24), - (unsigned long) server.lruclock, - (float)self_ru.ru_utime.tv_sec+(float)self_ru.ru_utime.tv_usec/1000000, - (float)self_ru.ru_stime.tv_sec+(float)self_ru.ru_stime.tv_usec/1000000, - (float)c_ru.ru_utime.tv_sec+(float)c_ru.ru_utime.tv_usec/1000000, - (float)c_ru.ru_stime.tv_sec+(float)c_ru.ru_stime.tv_usec/1000000, - listLength(server.clients)-listLength(server.slaves), - listLength(server.slaves), - lol, bib, - server.bpop_blocked_clients, - zmalloc_used_memory(), - hmem, - zmalloc_get_rss(), - zmalloc_get_fragmentation_ratio(), -#ifdef USE_TCMALLOC - 1, -#else - 0, -#endif - server.loading, - server.appendonly, - server.dirty, - server.bgsavechildpid != -1 || server.bgsavethread != (pthread_t) -1, - server.lastsave, - server.bgrewritechildpid != -1, - server.stat_numconnections, - server.stat_numcommands, - server.stat_expiredkeys, - server.stat_evictedkeys, - server.stat_keyspace_hits, - server.stat_keyspace_misses, - server.hash_max_zipmap_entries, - server.hash_max_zipmap_value, - dictSize(server.pubsub_channels), - listLength(server.pubsub_patterns), - server.ds_enabled != 0, - server.masterhost == NULL ? "master" : "slave" - ); - if (server.masterhost) { + + /* Server */ + if (allsections || defsections || !strcasecmp(section,"server")) { + if (sections++) info = sdscat(info,"\r\n"); info = sdscatprintf(info, - "master_host:%s\r\n" - "master_port:%d\r\n" - "master_link_status:%s\r\n" - "master_last_io_seconds_ago:%d\r\n" - "master_sync_in_progress:%d\r\n" - ,server.masterhost, - server.masterport, - (server.replstate == REDIS_REPL_CONNECTED) ? - "up" : "down", - server.master ? ((int)(time(NULL)-server.master->lastinteraction)) : -1, - server.replstate == REDIS_REPL_TRANSFER - ); - - if (server.replstate == REDIS_REPL_TRANSFER) { - info = sdscatprintf(info, - "master_sync_left_bytes:%ld\r\n" - "master_sync_last_io_seconds_ago:%d\r\n" - ,(long)server.repl_transfer_left, - (int)(time(NULL)-server.repl_transfer_lastio) + "# Server\r\n" + "redis_version:%s\r\n" + "redis_git_sha1:%s\r\n" + "redis_git_dirty:%d\r\n" + "arch_bits:%s\r\n" + "multiplexing_api:%s\r\n" + "process_id:%ld\r\n" + "tcp_port:%d\r\n" + "uptime_in_seconds:%ld\r\n" + "uptime_in_days:%ld\r\n" + "lru_clock:%ld\r\n", + REDIS_VERSION, + redisGitSHA1(), + strtol(redisGitDirty(),NULL,10) > 0, + (sizeof(long) == 8) ? "64" : "32", + aeGetApiName(), + (long) getpid(), + server.port, + uptime, + uptime/(3600*24), + (unsigned long) server.lruclock); + } + + /* Clients */ + if (allsections || defsections || !strcasecmp(section,"clients")) { + if (sections++) info = sdscat(info,"\r\n"); + info = sdscatprintf(info, + "# Clients\r\n" + "connected_clients:%d\r\n" + "client_longest_output_list:%lu\r\n" + "client_biggest_input_buf:%lu\r\n" + "blocked_clients:%d\r\n", + listLength(server.clients)-listLength(server.slaves), + lol, bib, + server.bpop_blocked_clients); + } + + /* Memory */ + if (allsections || defsections || !strcasecmp(section,"memory")) { + if (sections++) info = sdscat(info,"\r\n"); + info = sdscatprintf(info, + "# Memory\r\n" + "used_memory:%zu\r\n" + "used_memory_human:%s\r\n" + "used_memory_rss:%zu\r\n" + "mem_fragmentation_ratio:%.2f\r\n" + "use_tcmalloc:%d\r\n", + zmalloc_used_memory(), + hmem, + zmalloc_get_rss(), + zmalloc_get_fragmentation_ratio(), + #ifdef USE_TCMALLOC + 1 + #else + 0 + #endif ); + info = sdscat(info,"allocation_stats:"); + for (j = 0; j <= ZMALLOC_MAX_ALLOC_STAT; j++) { + size_t count = zmalloc_allocations_for_size(j); + if (count) { + if (info[sdslen(info)-1] != ':') info = sdscatlen(info,",",1); + info = sdscatprintf(info,"%s%d=%zu", + (j == ZMALLOC_MAX_ALLOC_STAT) ? ">=" : "", + j,count); + } } } - if (server.ds_enabled) { - lockThreadedIO(); + + /* Persistence */ + if (allsections || defsections || !strcasecmp(section,"persistence")) { + if (sections++) info = sdscat(info,"\r\n"); info = sdscatprintf(info, - "cache_max_memory:%llu\r\n" - "cache_blocked_clients:%lu\r\n" - ,(unsigned long long) server.cache_max_memory, - (unsigned long) server.cache_blocked_clients - ); - unlockThreadedIO(); + "# Persistence\r\n" + "loading:%d\r\n" + "aof_enabled:%d\r\n" + "changes_since_last_save:%lld\r\n" + "bgsave_in_progress:%d\r\n" + "last_save_time:%ld\r\n" + "bgrewriteaof_in_progress:%d\r\n", + server.loading, + server.appendonly, + server.dirty, + server.bgsavechildpid != -1 || + server.bgsavethread != (pthread_t) -1, + server.lastsave, + server.bgrewritechildpid != -1); + + if (server.loading) { + double perc; + time_t eta, elapsed; + off_t remaining_bytes = server.loading_total_bytes- + server.loading_loaded_bytes; + + perc = ((double)server.loading_loaded_bytes / + server.loading_total_bytes) * 100; + + elapsed = time(NULL)-server.loading_start_time; + if (elapsed == 0) { + eta = 1; /* A fake 1 second figure if we don't have + enough info */ + } else { + eta = (elapsed*remaining_bytes)/server.loading_loaded_bytes; + } + + info = sdscatprintf(info, + "loading_start_time:%ld\r\n" + "loading_total_bytes:%llu\r\n" + "loading_loaded_bytes:%llu\r\n" + "loading_loaded_perc:%.2f\r\n" + "loading_eta_seconds:%ld\r\n" + ,(unsigned long) server.loading_start_time, + (unsigned long long) server.loading_total_bytes, + (unsigned long long) server.loading_loaded_bytes, + perc, + eta + ); + } } - if (server.loading) { - double perc; - time_t eta, elapsed; - off_t remaining_bytes = server.loading_total_bytes- - server.loading_loaded_bytes; - - perc = ((double)server.loading_loaded_bytes / - server.loading_total_bytes) * 100; - - elapsed = time(NULL)-server.loading_start_time; - if (elapsed == 0) { - eta = 1; /* A fake 1 second figure if we don't have enough info */ - } else { - eta = (elapsed*remaining_bytes)/server.loading_loaded_bytes; + + /* Diskstore */ + if (allsections || defsections || !strcasecmp(section,"diskstore")) { + if (sections++) info = sdscat(info,"\r\n"); + info = sdscatprintf(info, + "# Diskstore\r\n" + "ds_enabled:%d\r\n", + server.ds_enabled != 0); + if (server.ds_enabled) { + lockThreadedIO(); + info = sdscatprintf(info, + "cache_max_memory:%llu\r\n" + "cache_blocked_clients:%lu\r\n" + ,(unsigned long long) server.cache_max_memory, + (unsigned long) server.cache_blocked_clients + ); + unlockThreadedIO(); } + } + /* Stats */ + if (allsections || defsections || !strcasecmp(section,"stats")) { + if (sections++) info = sdscat(info,"\r\n"); info = sdscatprintf(info, - "loading_start_time:%ld\r\n" - "loading_total_bytes:%llu\r\n" - "loading_loaded_bytes:%llu\r\n" - "loading_loaded_perc:%.2f\r\n" - "loading_eta_seconds:%ld\r\n" - ,(unsigned long) server.loading_start_time, - (unsigned long long) server.loading_total_bytes, - (unsigned long long) server.loading_loaded_bytes, - perc, - eta - ); + "# Stats\r\n" + "total_connections_received:%lld\r\n" + "total_commands_processed:%lld\r\n" + "expired_keys:%lld\r\n" + "evicted_keys:%lld\r\n" + "keyspace_hits:%lld\r\n" + "keyspace_misses:%lld\r\n" + "pubsub_channels:%ld\r\n" + "pubsub_patterns:%u\r\n", + server.stat_numconnections, + server.stat_numcommands, + server.stat_expiredkeys, + server.stat_evictedkeys, + server.stat_keyspace_hits, + server.stat_keyspace_misses, + dictSize(server.pubsub_channels), + listLength(server.pubsub_patterns)); } - info = sdscat(info,"allocation_stats:"); - for (j = 0; j <= ZMALLOC_MAX_ALLOC_STAT; j++) { - size_t count = zmalloc_allocations_for_size(j); - if (count) { - if (info[sdslen(info)-1] != ':') info = sdscatlen(info,",",1); - info = sdscatprintf(info,"%s%d=%zu", - (j == ZMALLOC_MAX_ALLOC_STAT) ? ">=" : "", - j,count); + /* Replication */ + if (allsections || defsections || !strcasecmp(section,"replication")) { + if (sections++) info = sdscat(info,"\r\n"); + info = sdscatprintf(info, + "# Replication\r\n" + "role:%s\r\n", + server.masterhost == NULL ? "master" : "slave"); + if (server.masterhost) { + info = sdscatprintf(info, + "master_host:%s\r\n" + "master_port:%d\r\n" + "master_link_status:%s\r\n" + "master_last_io_seconds_ago:%d\r\n" + "master_sync_in_progress:%d\r\n" + ,server.masterhost, + server.masterport, + (server.replstate == REDIS_REPL_CONNECTED) ? + "up" : "down", + server.master ? + ((int)(time(NULL)-server.master->lastinteraction)) : -1, + server.replstate == REDIS_REPL_TRANSFER + ); + + if (server.replstate == REDIS_REPL_TRANSFER) { + info = sdscatprintf(info, + "master_sync_left_bytes:%ld\r\n" + "master_sync_last_io_seconds_ago:%d\r\n" + ,(long)server.repl_transfer_left, + (int)(time(NULL)-server.repl_transfer_lastio) + ); + } } + info = sdscatprintf(info, + "connected_slaves:%d\r\n", + listLength(server.slaves)); } - info = sdscat(info,"\r\n"); - numcommands = sizeof(readonlyCommandTable)/sizeof(struct redisCommand); - for (j = 0; j < numcommands; j++) { - struct redisCommand *c = readonlyCommandTable+j; - info = sdscatprintf(info,"command_%s:microseconds:%lld\r\n", - c->name, c->microseconds); + /* Profiling */ + if (allsections || defsections || !strcasecmp(section,"profiling")) { + if (sections++) info = sdscat(info,"\r\n"); + info = sdscatprintf(info, + "# Profiling\r\n" + "used_cpu_sys:%.2f\r\n" + "used_cpu_user:%.2f\r\n" + "used_cpu_sys_childrens:%.2f\r\n" + "used_cpu_user_childrens:%.2f\r\n", + (float)self_ru.ru_utime.tv_sec+(float)self_ru.ru_utime.tv_usec/1000000, + (float)self_ru.ru_stime.tv_sec+(float)self_ru.ru_stime.tv_usec/1000000, + (float)c_ru.ru_utime.tv_sec+(float)c_ru.ru_utime.tv_usec/1000000, + (float)c_ru.ru_stime.tv_sec+(float)c_ru.ru_stime.tv_usec/1000000); + + numcommands = sizeof(readonlyCommandTable)/sizeof(struct redisCommand); + for (j = 0; j < numcommands; j++) { + struct redisCommand *c = readonlyCommandTable+j; + info = sdscatprintf(info,"used_time_cmd_%s:%lld\r\n", + c->name, c->microseconds); + } } - for (j = 0; j < server.dbnum; j++) { - long long keys, vkeys; + /* Key space */ + if (allsections || defsections || !strcasecmp(section,"keyspace")) { + if (sections++) info = sdscat(info,"\r\n"); + info = sdscatprintf(info, "# Keyspace\r\n"); + for (j = 0; j < server.dbnum; j++) { + long long keys, vkeys; - keys = dictSize(server.db[j].dict); - vkeys = dictSize(server.db[j].expires); - if (keys || vkeys) { - info = sdscatprintf(info, "db%d:keys=%lld,expires=%lld\r\n", - j, keys, vkeys); + keys = dictSize(server.db[j].dict); + vkeys = dictSize(server.db[j].expires); + if (keys || vkeys) { + info = sdscatprintf(info, "db%d:keys=%lld,expires=%lld\r\n", + j, keys, vkeys); + } } } return info; } void infoCommand(redisClient *c) { - sds info = genRedisInfoString(); + char *section = c->argc == 2 ? c->argv[1]->ptr : "default"; + + if (c->argc > 2) { + addReply(c,shared.syntaxerr); + return; + } + sds info = genRedisInfoString(section); addReplySds(c,sdscatprintf(sdsempty(),"$%lu\r\n", (unsigned long)sdslen(info))); addReplySds(c,info); @@ -1619,7 +1692,7 @@ void segvHandler(int sig, siginfo_t *info, void *secret) { redisLog(REDIS_WARNING, "======= Ooops! Redis %s got signal: -%d- =======", REDIS_VERSION, sig); - infostring = genRedisInfoString(); + infostring = genRedisInfoString("all"); redisLog(REDIS_WARNING, "%s",infostring); /* It's not safe to sdsfree() the returned string under memory * corruption conditions. Let it leak as we are going to abort */