rpc: make uptime monotonic across NTP jumps
Compute `uptime` from `SteadyClock` so it is unaffected by system time changes after startup.
Derive GUI startup time by subtracting the monotonic uptime from the wall clock time.
Add a functional test covering a large `setmocktime` jump.
Co-authored-by: MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz>
This commit is contained in:
5 files changed
+15
-13
No files matched your search
@@ -37,9 +37,6 @@
|
|||||||
|
|
||||||
using util::ReplaceAll;
|
using util::ReplaceAll;
|
||||||
|
|
||||||
// Application startup time (used for uptime calculation)
|
|
||||||
const int64_t nStartupTime = GetTime();
|
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
std::string ShellEscape(const std::string& arg)
|
std::string ShellEscape(const std::string& arg)
|
||||||
{
|
{
|
||||||
@@ -130,8 +127,8 @@ std::optional<size_t> GetTotalRAM()
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtain the application startup time (used for uptime calculation)
|
SteadyClock::duration GetUptime()
|
||||||
int64_t GetStartupTime()
|
|
||||||
{
|
{
|
||||||
return nStartupTime;
|
static const auto g_startup_time{SteadyClock::now()};
|
||||||
|
return SteadyClock::now() - g_startup_time;
|
||||||
}
|
}
|
||||||
+4
-2
@@ -7,13 +7,15 @@
|
|||||||
#define BITCOIN_COMMON_SYSTEM_H
|
#define BITCOIN_COMMON_SYSTEM_H
|
||||||
|
|
||||||
#include <bitcoin-build-config.h> // IWYU pragma: keep
|
#include <bitcoin-build-config.h> // IWYU pragma: keep
|
||||||
|
#include <util/time.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
// Application startup time (used for uptime calculation)
|
/// Monotonic uptime (not affected by system time changes).
|
||||||
int64_t GetStartupTime();
|
SteadyClock::duration GetUptime();
|
||||||
|
|
||||||
void SetupEnvironment();
|
void SetupEnvironment();
|
||||||
[[nodiscard]] bool SetupNetworking();
|
[[nodiscard]] bool SetupNetworking();
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ bool ClientModel::isReleaseVersion() const
|
|||||||
|
|
||||||
QString ClientModel::formatClientStartupTime() const
|
QString ClientModel::formatClientStartupTime() const
|
||||||
{
|
{
|
||||||
return QDateTime::fromSecsSinceEpoch(GetStartupTime()).toString();
|
return QDateTime::currentDateTime().addSecs(-TicksSeconds(GetUptime())).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ClientModel::dataDir() const
|
QString ClientModel::dataDir() const
|
||||||
|
|||||||
+1
-1
@@ -184,7 +184,7 @@ static RPCHelpMan uptime()
|
|||||||
},
|
},
|
||||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||||
{
|
{
|
||||||
return GetTime() - GetStartupTime();
|
return TicksSeconds(GetUptime());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,9 +26,12 @@ class UptimeTest(BitcoinTestFramework):
|
|||||||
assert_raises_rpc_error(-8, "Mocktime must be in the range [0, 9223372036], not -1.", self.nodes[0].setmocktime, -1)
|
assert_raises_rpc_error(-8, "Mocktime must be in the range [0, 9223372036], not -1.", self.nodes[0].setmocktime, -1)
|
||||||
|
|
||||||
def _test_uptime(self):
|
def _test_uptime(self):
|
||||||
wait_time = 10
|
wait_time = 20_000
|
||||||
self.nodes[0].setmocktime(int(time.time() + wait_time))
|
uptime_before = self.nodes[0].uptime()
|
||||||
assert self.nodes[0].uptime() >= wait_time
|
self.nodes[0].setmocktime(int(time.time()) + wait_time)
|
||||||
|
uptime_after = self.nodes[0].uptime()
|
||||||
|
self.nodes[0].setmocktime(0)
|
||||||
|
assert uptime_after - uptime_before < wait_time, "uptime should not jump with wall clock"
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
Reference in new issue
Block a user