diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -733,6 +733,14 @@ [ 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([ int foo_def( void ) __attribute__((visibility("default"))); diff --git a/doc/release-notes.md b/doc/release-notes.md --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -6,4 +6,5 @@ - Remove support for Qt4 - Upgrade reproducible build to us Qt 5.9.6 - Improve SHA256 performance using SSE4.1, AVX2 and/or SHA if available. + - Add a mode argument to the `getmemoryinfo` RPC call to query `malloc_info` from the system if available. diff --git a/src/config/CMakeLists.txt b/src/config/CMakeLists.txt --- a/src/config/CMakeLists.txt +++ b/src/config/CMakeLists.txt @@ -108,6 +108,7 @@ # Memory management capabilities check_symbol_exists(M_ARENA_MAX "malloc.h" HAVE_MALLOPT_ARENA_MAX) +check_symbol_exists(malloc_info "malloc.h" HAVE_MALLOC_INFO) # Various system libraries check_symbol_exists(strnlen "string.h" HAVE_DECL_STRNLEN) diff --git a/src/config/bitcoin-config.h.cmake.in b/src/config/bitcoin-config.h.cmake.in --- a/src/config/bitcoin-config.h.cmake.in +++ b/src/config/bitcoin-config.h.cmake.in @@ -43,6 +43,7 @@ #cmakedefine HAVE_DECL___BUILTIN_CLZLL 1 #cmakedefine HAVE_MALLOPT_ARENA_MAX 1 +#cmakedefine HAVE_MALLOC_INFO 1 #cmakedefine HAVE_DECL_STRNLEN 1 #cmakedefine HAVE_DECL_DAEMON 1 diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -26,6 +26,9 @@ #include #include +#ifdef HAVE_MALLOC_INFO +#include +#endif /** * @note Do not add or change anything in the information returned by this @@ -578,16 +581,41 @@ 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 + static UniValue getmemoryinfo(const Config &config, 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" @@ -604,14 +632,31 @@ " \"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); + } } static UniValue echo(const Config &config, const JSONRPCRequest &request) { @@ -633,12 +678,11 @@ // 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"} }, { "util", "signmessagewithprivkey", signmessagewithprivkey, true, {"privkey","message"} }, - /* Not shown in help */ { "hidden", "setmocktime", setmocktime, true, {"timestamp"}}, { "hidden", "echo", echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},