Skip to content

Commit cf0c67b

Browse files
committed
Merge #14982: rpc: Add getrpcinfo command
a0ac154 doc: Add getrpcinfo release notes (João Barbosa) 251a91c qa: Add tests for getrpcinfo (João Barbosa) d0730f5 rpc: Add getrpcinfo command (João Barbosa) 068a8fc rpc: Track active commands (João Barbosa) bf43832 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
2 parents 035f349 + a0ac154 commit cf0c67b

File tree

3 files changed

+73
-4
lines changed

3 files changed

+73
-4
lines changed

doc/release-notes-14982.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
New RPCs
2+
--------
3+
4+
- The RPC `getrpcinfo` returns runtime details of the RPC server. At the moment
5+
it returns the active commands and the corresponding execution time.

src/rpc/server.cpp

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,39 @@ static RPCTimerInterface* timerInterface = nullptr;
3131
/* Map of name to timer. */
3232
static std::map<std::string, std::unique_ptr<RPCTimerBase> > deadlineTimers;
3333

34+
struct RPCCommandExecutionInfo
35+
{
36+
std::string method;
37+
int64_t start;
38+
};
39+
40+
struct RPCServerInfo
41+
{
42+
Mutex mutex;
43+
std::list<RPCCommandExecutionInfo> active_commands GUARDED_BY(mutex);
44+
};
45+
46+
static RPCServerInfo g_rpc_server_info;
47+
48+
struct RPCCommandExecution
49+
{
50+
std::list<RPCCommandExecutionInfo>::iterator it;
51+
explicit RPCCommandExecution(const std::string& method)
52+
{
53+
LOCK(g_rpc_server_info.mutex);
54+
it = g_rpc_server_info.active_commands.insert(g_rpc_server_info.active_commands.cend(), {method, GetTimeMicros()});
55+
}
56+
~RPCCommandExecution()
57+
{
58+
LOCK(g_rpc_server_info.mutex);
59+
g_rpc_server_info.active_commands.erase(it);
60+
}
61+
};
62+
3463
static struct CRPCSignals
3564
{
3665
boost::signals2::signal<void ()> Started;
3766
boost::signals2::signal<void ()> Stopped;
38-
boost::signals2::signal<void (const CRPCCommand&)> PreCommand;
3967
} g_rpcSignals;
4068

4169
void RPCServer::OnStarted(std::function<void ()> slot)
@@ -254,11 +282,37 @@ static UniValue uptime(const JSONRPCRequest& jsonRequest)
254282
return GetTime() - GetStartupTime();
255283
}
256284

285+
static UniValue getrpcinfo(const JSONRPCRequest& request)
286+
{
287+
if (request.fHelp || request.params.size() > 0) {
288+
throw std::runtime_error(
289+
RPCHelpMan{"getrpcinfo",
290+
"\nReturns details of the RPC server.\n", {}}
291+
.ToString()
292+
);
293+
}
294+
295+
LOCK(g_rpc_server_info.mutex);
296+
UniValue active_commands(UniValue::VARR);
297+
for (const RPCCommandExecutionInfo& info : g_rpc_server_info.active_commands) {
298+
UniValue entry(UniValue::VOBJ);
299+
entry.pushKV("method", info.method);
300+
entry.pushKV("duration", GetTimeMicros() - info.start);
301+
active_commands.push_back(entry);
302+
}
303+
304+
UniValue result(UniValue::VOBJ);
305+
result.pushKV("active_commands", active_commands);
306+
307+
return result;
308+
}
309+
257310
// clang-format off
258311
static const CRPCCommand vRPCCommands[] =
259312
{ // category name actor (function) argNames
260313
// --------------------- ------------------------ ----------------------- ----------
261314
/* Overall control/query calls */
315+
{ "control", "getrpcinfo", &getrpcinfo, {} },
262316
{ "control", "help", &help, {"command"} },
263317
{ "control", "stop", &stop, {"wait"} },
264318
{ "control", "uptime", &uptime, {} },
@@ -483,10 +537,9 @@ UniValue CRPCTable::execute(const JSONRPCRequest &request) const
483537
if (!pcmd)
484538
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
485539

486-
g_rpcSignals.PreCommand(*pcmd);
487-
488540
try
489541
{
542+
RPCCommandExecution execution(request.strMethod);
490543
// Execute, convert arguments to array if necessary
491544
if (request.params.isObject()) {
492545
return pcmd->actor(transformNamedArguments(request, pcmd->argNames));

test/functional/interface_rpc.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,23 @@
55
"""Tests some generic aspects of the RPC interface."""
66

77
from test_framework.test_framework import BitcoinTestFramework
8-
from test_framework.util import assert_equal
8+
from test_framework.util import assert_equal, assert_greater_than_or_equal
99

1010
class RPCInterfaceTest(BitcoinTestFramework):
1111
def set_test_params(self):
1212
self.num_nodes = 1
1313
self.setup_clean_chain = True
1414

15+
def test_getrpcinfo(self):
16+
self.log.info("Testing getrpcinfo...")
17+
18+
info = self.nodes[0].getrpcinfo()
19+
assert_equal(len(info['active_commands']), 1)
20+
21+
command = info['active_commands'][0]
22+
assert_equal(command['method'], 'getrpcinfo')
23+
assert_greater_than_or_equal(command['duration'], 0)
24+
1525
def test_batch_request(self):
1626
self.log.info("Testing basic JSON-RPC batch request...")
1727

@@ -39,6 +49,7 @@ def test_batch_request(self):
3949
assert result_by_id[3]['result'] is not None
4050

4151
def run_test(self):
52+
self.test_getrpcinfo()
4253
self.test_batch_request()
4354

4455

0 commit comments

Comments
 (0)