From e141aa4ba604ff22c68454112501c166d3e892c9 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 20 Mar 2017 10:09:01 +0100 Subject: [PATCH] Add mallocinfo mode to `getmemoryinfo` RPC This adds a mode argument to `getmemoryinfo`. By default the output will remain the same. However if a mode argument of `mallocinfo` is provided the result of glibc `malloc_info` (if available) will be returned as a string, as-is. This is useful for tracking heap usage over time or troubleshooting memory fragmentation issues. --- configure.ac | 7 +++++++ src/rpc/misc.cpp | 54 +++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/configure.ac b/configure.ac index 220fc62f8e..41634e035b 100644 --- a/configure.ac +++ b/configure.ac @@ -566,6 +566,13 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [ AC_MSG_RESULT(no)] ) +dnl Check for malloc_info (for memory statistics information in getmemoryinfo) +AC_MSG_CHECKING(for getmemoryinfo) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[ int f = malloc_info(0, NULL); ]])], + [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_MALLOC_INFO, 1,[Define this symbol if you have malloc_info]) ], + [ AC_MSG_RESULT(no)] +) AC_MSG_CHECKING([for visibility attribute]) AC_LINK_IFELSE([AC_LANG_SOURCE([ diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 2a8f95b615..de1bbe62e5 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -20,6 +20,9 @@ #endif #include +#ifdef HAVE_MALLOC_INFO +#include +#endif #include @@ -485,16 +488,39 @@ static UniValue RPCLockedMemoryInfo() return obj; } +#ifdef HAVE_MALLOC_INFO +static std::string RPCMallocInfo() +{ + char *ptr = nullptr; + size_t size = 0; + FILE *f = open_memstream(&ptr, &size); + if (f) { + malloc_info(0, f); + fclose(f); + if (ptr) { + std::string rv(ptr, size); + free(ptr); + return rv; + } + } + return ""; +} +#endif + UniValue getmemoryinfo(const JSONRPCRequest& request) { /* Please, avoid using the word "pool" here in the RPC interface or help, * as users will undoubtedly confuse it with the other "memory pool" */ - if (request.fHelp || request.params.size() != 0) + if (request.fHelp || request.params.size() > 1) throw std::runtime_error( - "getmemoryinfo\n" + "getmemoryinfo (\"mode\")\n" "Returns an object containing information about memory usage.\n" - "\nResult:\n" + "Arguments:\n" + "1. \"mode\" determines what kind of information is returned. This argument is optional, the default mode is \"stats\".\n" + " - \"stats\" returns general statistics about memory usage in the daemon.\n" + " - \"mallocinfo\" returns an XML string describing low-level heap state (only available if compiled with glibc 2.10+).\n" + "\nResult (mode \"stats\"):\n" "{\n" " \"locked\": { (json object) Information about locked memory manager\n" " \"used\": xxxxx, (numeric) Number of bytes used\n" @@ -505,13 +531,27 @@ UniValue getmemoryinfo(const JSONRPCRequest& request) " \"chunks_free\": xxxxx, (numeric) Number unused chunks\n" " }\n" "}\n" + "\nResult (mode \"mallocinfo\"):\n" + "\"...\"\n" "\nExamples:\n" + HelpExampleCli("getmemoryinfo", "") + HelpExampleRpc("getmemoryinfo", "") ); - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("locked", RPCLockedMemoryInfo())); - return obj; + + std::string mode = (request.params.size() < 1 || request.params[0].isNull()) ? "stats" : request.params[0].get_str(); + if (mode == "stats") { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("locked", RPCLockedMemoryInfo())); + return obj; + } else if (mode == "mallocinfo") { +#ifdef HAVE_MALLOC_INFO + return RPCMallocInfo(); +#else + throw JSONRPCError(RPC_INVALID_PARAMETER, "mallocinfo is only available when compiled with glibc 2.10+"); +#endif + } else { + throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown mode " + mode); + } } UniValue echo(const JSONRPCRequest& request) @@ -531,7 +571,7 @@ static const CRPCCommand commands[] = { // category name actor (function) okSafeMode // --------------------- ------------------------ ----------------------- ---------- { "control", "getinfo", &getinfo, true, {} }, /* uses wallet if enabled */ - { "control", "getmemoryinfo", &getmemoryinfo, true, {} }, + { "control", "getmemoryinfo", &getmemoryinfo, true, {"mode"} }, { "util", "validateaddress", &validateaddress, true, {"address"} }, /* uses wallet if enabled */ { "util", "createmultisig", &createmultisig, true, {"nrequired","keys"} }, { "util", "verifymessage", &verifymessage, true, {"address","signature","message"} },