aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWladimir J. van der Laan <laanwj@gmail.com>2019-01-14 18:05:26 +0100
committerWladimir J. van der Laan <laanwj@gmail.com>2019-01-14 18:07:15 +0100
commitcf0c67b62c2037dc9e70ea84ffee3b205a9b1bef (patch)
tree1a8c9ed60f360f883a71d0d6db166b79304e10e7
parent035f349371a5b67922ce92c11ad9aa7178fa04f7 (diff)
parenta0ac15459a0df598e1ee1fd36a3899a129cecaeb (diff)
Merge #14982: rpc: Add getrpcinfo command
a0ac15459a0df598e1ee1fd36a3899a129cecaeb doc: Add getrpcinfo release notes (João Barbosa) 251a91c1bf245b3674c2612149382a0f1e18dc98 qa: Add tests for getrpcinfo (João Barbosa) d0730f5ce475e5a84da7c61fe79bcd6ed24d693e rpc: Add getrpcinfo command (João Barbosa) 068a8fc05f8dbec198bdc3fe46f955d8a5255303 rpc: Track active commands (João Barbosa) bf4383277d6761cc5b7a91975752c08df829af72 rpc: Remove unused PreCommand signal (João Barbosa) Pull request description: The new `getrpcinfo` command exposes details of the RPC interface. The details can be configuration properties or runtime values/stats. This can be particular useful to coordinate concurrent functional tests (see #14958 from where this was extracted). Tree-SHA512: 7292cb6087f4c429973d991aa2b53ffa1327d5a213df7d6ba5fc69b01b2e1a411f6d1609fed9234896293317dab05f65064da48b8f2b4a998eba532591d31882
-rw-r--r--doc/release-notes-14982.md5
-rw-r--r--src/rpc/server.cpp59
-rwxr-xr-xtest/functional/interface_rpc.py13
3 files changed, 73 insertions, 4 deletions
diff --git a/doc/release-notes-14982.md b/doc/release-notes-14982.md
new file mode 100644
index 0000000000..3f0bf8aacd
--- /dev/null
+++ b/doc/release-notes-14982.md
@@ -0,0 +1,5 @@
+New RPCs
+--------
+
+- 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
index e7e047334e..edaf64f3e1 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -31,11 +31,39 @@ static RPCTimerInterface* timerInterface = nullptr;
/* Map of name to timer. */
static std::map<std::string, std::unique_ptr<RPCTimerBase> > deadlineTimers;
+struct RPCCommandExecutionInfo
+{
+ std::string method;
+ int64_t start;
+};
+
+struct RPCServerInfo
+{
+ Mutex mutex;
+ std::list<RPCCommandExecutionInfo> active_commands GUARDED_BY(mutex);
+};
+
+static RPCServerInfo g_rpc_server_info;
+
+struct RPCCommandExecution
+{
+ std::list<RPCCommandExecutionInfo>::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);
+ }
+};
+
static struct CRPCSignals
{
boost::signals2::signal<void ()> Started;
boost::signals2::signal<void ()> Stopped;
- boost::signals2::signal<void (const CRPCCommand&)> PreCommand;
} g_rpcSignals;
void RPCServer::OnStarted(std::function<void ()> slot)
@@ -254,11 +282,37 @@ static UniValue uptime(const JSONRPCRequest& jsonRequest)
return GetTime() - GetStartupTime();
}
+static UniValue getrpcinfo(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() > 0) {
+ throw std::runtime_error(
+ RPCHelpMan{"getrpcinfo",
+ "\nReturns details of the RPC server.\n", {}}
+ .ToString()
+ );
+ }
+
+ 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;
+}
+
// clang-format off
static const CRPCCommand vRPCCommands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
/* Overall control/query calls */
+ { "control", "getrpcinfo", &getrpcinfo, {} },
{ "control", "help", &help, {"command"} },
{ "control", "stop", &stop, {"wait"} },
{ "control", "uptime", &uptime, {} },
@@ -483,10 +537,9 @@ UniValue CRPCTable::execute(const JSONRPCRequest &request) const
if (!pcmd)
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->actor(transformNamedArguments(request, pcmd->argNames));
diff --git a/test/functional/interface_rpc.py b/test/functional/interface_rpc.py
index e3d7b0655d..b6955d4492 100755
--- a/test/functional/interface_rpc.py
+++ b/test/functional/interface_rpc.py
@@ -5,13 +5,23 @@
"""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):
def set_test_params(self):
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...")
@@ -39,6 +49,7 @@ class RPCInterfaceTest(BitcoinTestFramework):
assert result_by_id[3]['result'] is not None
def run_test(self):
+ self.test_getrpcinfo()
self.test_batch_request()