From ec5a0c548b0afbb1bd584b5761bf740460fd20a2 Mon Sep 17 00:00:00 2001 From: Matt Stancliff Date: Tue, 17 Jun 2014 23:05:30 -0400 Subject: [PATCH] Add cached total system memory to INFO output There is no standard cross-platform way of obtaining system memory info, but I found a useful function convering all common platforms. I removed support for uncommon Redis platforms (windows, AIX) and left others intact. For more info, see: http://nadeausoftware.com/articles/2012/09/c_c_tip_how_get_physical_memory_size_system The system memory info is cached on startup, but some systems may be able to change the amount of memory visible to Redis at runtime if Redis is deployed in a VM or container. Also see #1820 --- src/redis.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/redis.h | 2 ++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/redis.c b/src/redis.c index 08a06e4f..be69224c 100644 --- a/src/redis.c +++ b/src/redis.c @@ -53,6 +53,7 @@ #include #include #include +#include /* Our shared "common" objects */ @@ -288,6 +289,7 @@ struct redisCommand redisCommandTable[] = { }; struct evictionPoolEntry *evictionPoolAlloc(void); +static size_t getMemorySize(void); /*============================ Utility functions ============================ */ @@ -1759,6 +1761,7 @@ void initServer(void) { server.clients_waiting_acks = listCreate(); server.get_ack_from_slaves = 0; server.clients_paused = 0; + server.system_memory_size = getMemorySize(); createSharedObjects(); adjustOpenFilesLimit(); @@ -2479,7 +2482,6 @@ void timeCommand(redisClient *c) { addReplyBulkLongLong(c,tv.tv_usec); } - /* Helper function for addReplyCommand() to output flags. */ int addReplyCommandFlag(redisClient *c, struct redisCommand *cmd, int f, char *reply) { if (cmd->flags & f) { @@ -2570,6 +2572,54 @@ void commandCommand(redisClient *c) { } } +/** + * Returns the size of physical memory (RAM) in bytes. + * It looks ugly, but this is the cleanest way to achive cross platform results. + * Cleaned up from: + * http://nadeausoftware.com/articles/2012/09/c_c_tip_how_get_physical_memory_size_system + */ +size_t getMemorySize() { +#if defined(__unix__) || defined(__unix) || defined(unix) || \ + (defined(__APPLE__) && defined(__MACH__)) +#if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64)) + int mib[2]; + mib[0] = CTL_HW; +#if defined(HW_MEMSIZE) + mib[1] = HW_MEMSIZE; /* OSX. --------------------- */ +#elif defined(HW_PHYSMEM64) + mib[1] = HW_PHYSMEM64; /* NetBSD, OpenBSD. --------- */ +#endif + int64_t size = 0; /* 64-bit */ + size_t len = sizeof(size); + if (sysctl( mib, 2, &size, &len, NULL, 0) == 0) + return (size_t)size; + return 0L; /* Failed? */ + +#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE) + /* FreeBSD, Linux, OpenBSD, and Solaris. -------------------- */ + return (size_t)sysconf(_SC_PHYS_PAGES) * (size_t)sysconf(_SC_PAGESIZE); + +#elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM)) + /* DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX. -------- */ + int mib[2]; + mib[0] = CTL_HW; +#if defined(HW_REALMEM) + mib[1] = HW_REALMEM; /* FreeBSD. ----------------- */ +#elif defined(HW_PYSMEM) + mib[1] = HW_PHYSMEM; /* Others. ------------------ */ +#endif + unsigned int size = 0; /* 32-bit */ + size_t len = sizeof(size); + if (sysctl(mib, 2, &size, &len, NULL, 0) == 0) + return (size_t)size; + return 0L; /* Failed? */ +#endif /* sysctl and sysconf variants */ + +#else + return 0L; /* Unknown OS. */ +#endif +} + /* Convert an amount of bytes into a human readable string in the form * of 100B, 2G, 100M, 4K, and so forth. */ void bytesToHuman(char *s, unsigned long long n) { @@ -2698,7 +2748,9 @@ sds genRedisInfoString(char *section) { if (allsections || defsections || !strcasecmp(section,"memory")) { char hmem[64]; char peak_hmem[64]; + char total_system_hmem[64]; size_t zmalloc_used = zmalloc_used_memory(); + size_t total_system_mem = server.system_memory_size; char *evict_policy = maxmemoryToString(); /* Peak memory is updated from time to time by serverCron() so it @@ -2710,6 +2762,8 @@ sds genRedisInfoString(char *section) { bytesToHuman(hmem,zmalloc_used); bytesToHuman(peak_hmem,server.stat_peak_memory); + bytesToHuman(total_system_hmem,total_system_mem); + if (sections++) info = sdscat(info,"\r\n"); info = sdscatprintf(info, "# Memory\r\n" @@ -2718,6 +2772,8 @@ sds genRedisInfoString(char *section) { "used_memory_rss:%zu\r\n" "used_memory_peak:%zu\r\n" "used_memory_peak_human:%s\r\n" + "total_system_memory:%lu\r\n" + "total_system_memory_human:%s\r\n" "used_memory_lua:%lld\r\n" "mem_fragmentation_ratio:%.2f\r\n" "mem_allocator:%s\r\n" @@ -2727,6 +2783,8 @@ sds genRedisInfoString(char *section) { server.resident_set_size, server.stat_peak_memory, peak_hmem, + (unsigned long)total_system_mem, + total_system_hmem, ((long long)lua_gc(server.lua,LUA_GCCOUNT,0))*1024LL, zmalloc_get_fragmentation_ratio(server.resident_set_size), ZMALLOC_LIB, diff --git a/src/redis.h b/src/redis.h index 53a93f00..afed4b88 100644 --- a/src/redis.h +++ b/src/redis.h @@ -908,6 +908,8 @@ struct redisServer { int assert_line; int bug_report_start; /* True if bug report header was already logged. */ int watchdog_period; /* Software watchdog period in ms. 0 = off */ + /* System hardware info */ + size_t system_memory_size; /* Total memory in system as reported by OS */ }; typedef struct pubsubPattern {