diff --git a/doc/release-notes.md b/doc/release-notes.md
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -3,3 +3,6 @@
This release includes the following features and fixes:
+
+- The RPC `getrpcinfo` returns runtime details of the RPC server. At the moment
+ it returns the active commands and the corresponding execution time.
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -35,6 +35,32 @@
/* Map of name to timer. */
static std::map> deadlineTimers;
+struct RPCCommandExecutionInfo {
+ std::string method;
+ int64_t start;
+};
+
+struct RPCServerInfo {
+ Mutex mutex;
+ std::list active_commands GUARDED_BY(mutex);
+};
+
+static RPCServerInfo g_rpc_server_info;
+
+struct RPCCommandExecution {
+ std::list::iterator it;
+ explicit RPCCommandExecution(const std::string &method) {
+ LOCK(g_rpc_server_info.mutex);
+ it = g_rpc_server_info.active_commands.insert(
+ g_rpc_server_info.active_commands.cend(),
+ {method, GetTimeMicros()});
+ }
+ ~RPCCommandExecution() {
+ LOCK(g_rpc_server_info.mutex);
+ g_rpc_server_info.active_commands.erase(it);
+ }
+};
+
UniValue RPCServer::ExecuteCommand(Config &config,
const JSONRPCRequest &request) const {
// Return immediately if in warmup
@@ -75,7 +101,6 @@
static struct CRPCSignals {
boost::signals2::signal Started;
boost::signals2::signal Stopped;
- boost::signals2::signal PreCommand;
} g_rpcSignals;
void RPCServerSignals::OnStarted(std::function slot) {
@@ -313,6 +338,29 @@
return GetTime() - GetStartupTime();
}
+static UniValue getrpcinfo(const Config &config,
+ const JSONRPCRequest &request) {
+ if (request.fHelp || request.params.size() > 0) {
+ throw std::runtime_error("getrpcinfo\n"
+ "\nReturns details of the RPC server.\n");
+ }
+
+ LOCK(g_rpc_server_info.mutex);
+ UniValue active_commands(UniValue::VARR);
+ for (const RPCCommandExecutionInfo &info :
+ g_rpc_server_info.active_commands) {
+ UniValue entry(UniValue::VOBJ);
+ entry.pushKV("method", info.method);
+ entry.pushKV("duration", GetTimeMicros() - info.start);
+ active_commands.push_back(entry);
+ }
+
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("active_commands", active_commands);
+
+ return result;
+}
+
/**
* Call Table
*/
@@ -321,6 +369,7 @@
// category name actor (function) argNames
// ------------------- ------------------------ ---------------------- ----------
/* Overall control/query calls */
+ { "control", "getrpcinfo", getrpcinfo, {} },
{ "control", "help", help, {"command"} },
{ "control", "stop", stop, {} },
{ "control", "uptime", uptime, {} },
@@ -515,9 +564,8 @@
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
}
- g_rpcSignals.PreCommand(*pcmd);
-
try {
+ RPCCommandExecution execution(request.strMethod);
// Execute, convert arguments to array if necessary
if (request.params.isObject()) {
return pcmd->call(config,
diff --git a/test/functional/interface_rpc.py b/test/functional/interface_rpc.py
--- a/test/functional/interface_rpc.py
+++ b/test/functional/interface_rpc.py
@@ -5,7 +5,7 @@
"""Tests some generic aspects of the RPC interface."""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
+from test_framework.util import assert_equal, assert_greater_than_or_equal
class RPCInterfaceTest(BitcoinTestFramework):
@@ -13,6 +13,16 @@
self.num_nodes = 1
self.setup_clean_chain = True
+ def test_getrpcinfo(self):
+ self.log.info("Testing getrpcinfo...")
+
+ info = self.nodes[0].getrpcinfo()
+ assert_equal(len(info['active_commands']), 1)
+
+ command = info['active_commands'][0]
+ assert_equal(command['method'], 'getrpcinfo')
+ assert_greater_than_or_equal(command['duration'], 0)
+
def test_batch_request(self):
self.log.info("Testing basic JSON-RPC batch request...")
@@ -40,6 +50,7 @@
assert result_by_id[3]['result'] is not None
def run_test(self):
+ self.test_getrpcinfo()
self.test_batch_request()