Auto merge of #4623 - str4d:metrics-locking, r=str4d

metrics: Collect general stats before clearing screen

This prevents the metrics screen from flashing if locks are being held
by long-running processes, specifically cs_main during block validation.

We split up locking on cs_main and cs_vNodes to make obtaining the locks
easier, at the expense of potentially having slightly out-of-sync
statistics (which doesn't really matter, as all we are fetching from the
latter lock is the number of connected peers).
This commit is contained in:
Homu 2020-07-29 09:33:07 +00:00
commit 24f3bcf275
1 changed files with 56 additions and 21 deletions

View File

@ -14,6 +14,7 @@
#include "utilmoneystr.h"
#include "utilstrencodings.h"
#include <boost/optional.hpp>
#include <boost/range/irange.hpp>
#include <boost/thread.hpp>
#include <boost/thread/synchronized_value.hpp>
@ -284,43 +285,71 @@ boost::optional<int64_t> SecondsLeftToNextEpoch(const Consensus::Params& params,
}
}
int printStats(bool isScreen, bool mining)
{
// Number of lines that are always displayed
int lines = 5;
struct MetricsStats {
int height;
int64_t currentHeadersHeight;
int64_t currentHeadersTime;
size_t connections;
int64_t netsolps;
const Consensus::Params& params = Params().GetConsensus();
};
MetricsStats loadStats()
{
int height;
int64_t currentHeadersHeight;
int64_t currentHeadersTime;
size_t connections;
int64_t netsolps;
{
LOCK2(cs_main, cs_vNodes);
LOCK(cs_main);
height = chainActive.Height();
currentHeadersHeight = pindexBestHeader ? pindexBestHeader->nHeight: -1;
currentHeadersTime = pindexBestHeader ? pindexBestHeader->nTime : 0;
connections = vNodes.size();
netsolps = GetNetworkHashPS(120, -1);
}
{
LOCK(cs_vNodes);
connections = vNodes.size();
}
return MetricsStats {
height,
currentHeadersHeight,
currentHeadersTime,
connections,
netsolps
};
}
int printStats(MetricsStats stats, bool isScreen, bool mining)
{
// Number of lines that are always displayed
int lines = 5;
const Consensus::Params& params = Params().GetConsensus();
auto localsolps = GetLocalSolPS();
if (IsInitialBlockDownload(Params())) {
if (fReindex) {
int downloadPercent = nSizeReindexed * 100 / nFullSizeToReindex;
std::cout << " " << _("Reindexing blocks") << " | " << DisplaySize(nSizeReindexed) << " / " << DisplaySize(nFullSizeToReindex) << " (" << downloadPercent << "%, " << height << " " << _("blocks") << ")" << std::endl;
std::cout << " " << _("Reindexing blocks") << " | "
<< DisplaySize(nSizeReindexed) << " / " << DisplaySize(nFullSizeToReindex)
<< " (" << downloadPercent << "%, " << stats.height << " " << _("blocks") << ")" << std::endl;
} else {
int nHeaders = currentHeadersHeight;
int nHeaders = stats.currentHeadersHeight;
if (nHeaders < 0)
nHeaders = 0;
int netheight = currentHeadersHeight == -1 || currentHeadersTime == 0 ?
0 : EstimateNetHeight(params, currentHeadersHeight, currentHeadersTime);
int netheight = stats.currentHeadersHeight == -1 || stats.currentHeadersTime == 0 ?
0 : EstimateNetHeight(params, stats.currentHeadersHeight, stats.currentHeadersTime);
if (netheight < nHeaders)
netheight = nHeaders;
if (netheight <= 0)
netheight = 1;
int downloadPercent = height * 100 / netheight;
std::cout << " " << _("Downloading blocks") << " | " << height << " (" << nHeaders << " " << _("headers") << ") / ~" << netheight << " (" << downloadPercent << "%)" << std::endl;
int downloadPercent = stats.height * 100 / netheight;
std::cout << " " << _("Downloading blocks") << " | "
<< stats.height << " (" << nHeaders << " " << _("headers") << ") / ~" << netheight
<< " (" << downloadPercent << "%)" << std::endl;
if (isScreen) {
// Draw 50-character progress bar, which will fit into a 79-character line.
@ -344,14 +373,14 @@ int printStats(bool isScreen, bool mining)
}
}
} else {
std::cout << " " << _("Block height") << " | " << height << std::endl;
std::cout << " " << _("Block height") << " | " << stats.height << std::endl;
}
auto secondsLeft = SecondsLeftToNextEpoch(params, height);
auto secondsLeft = SecondsLeftToNextEpoch(params, stats.height);
std::string strUpgradeTime;
if (secondsLeft) {
auto nextHeight = NextActivationHeight(height, params).value();
auto nextBranch = NextEpoch(height, params).value();
auto nextHeight = NextActivationHeight(stats.height, params).value();
auto nextBranch = NextEpoch(stats.height, params).value();
strUpgradeTime = strprintf(_("%s at block height %d, in around %s"),
NetworkUpgradeInfo[nextBranch].strName, nextHeight, DisplayDuration(secondsLeft.value(), DurationFormat::REDUCED));
}
@ -359,8 +388,8 @@ int printStats(bool isScreen, bool mining)
strUpgradeTime = "Unknown";
}
std::cout << " " << _("Next upgrade") << " | " << strUpgradeTime << std::endl;
std::cout << " " << _("Connections") << " | " << connections << std::endl;
std::cout << " " << _("Network solution rate") << " | " << DisplayHashRate(netsolps) << std::endl;
std::cout << " " << _("Connections") << " | " << stats.connections << std::endl;
std::cout << " " << _("Network solution rate") << " | " << DisplayHashRate(stats.netsolps) << std::endl;
if (mining && miningTimer.running()) {
std::cout << " " << _("Local solution rate") << " | " << DisplayHashRate(localsolps) << std::endl;
lines++;
@ -611,6 +640,12 @@ void ThreadShowMetricsScreen()
#endif
}
// Lock and fetch stats before erasing the screen, in case we block.
boost::optional<MetricsStats> metricsStats;
if (loaded) {
metricsStats = loadStats();
}
if (isScreen) {
// Erase below current position
std::cout << "\e[J";
@ -624,7 +659,7 @@ void ThreadShowMetricsScreen()
#endif
if (loaded) {
lines += printStats(isScreen, mining);
lines += printStats(metricsStats.value(), isScreen, mining);
lines += printMiningStatus(mining);
}
lines += printMetrics(cols, mining);