Merge branch 'dev' into jl777
[VerusCoin.git] / src / metrics.cpp
CommitLineData
a6df7ab5
JG
1// Copyright (c) 2016 The Zcash developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include "metrics.h"
6
7#include "chainparams.h"
047aec1e 8#include "checkpoints.h"
d793f94b 9#include "main.h"
4ace963c 10#include "ui_interface.h"
a6df7ab5
JG
11#include "util.h"
12#include "utiltime.h"
d793f94b 13#include "utilmoneystr.h"
b4f861d1 14#include "utilstrencodings.h"
a6df7ab5
JG
15
16#include <boost/thread.hpp>
4ace963c
JG
17#include <boost/thread/synchronized_value.hpp>
18#include <string>
9d365796 19#ifdef _WIN32
20#include <io.h>
21#include <windows.h>
22#else
db853f8a 23#include <sys/ioctl.h>
9d365796 24#endif
1da44b34 25#include <unistd.h>
a6df7ab5 26
73bf85b4
JG
27void AtomicTimer::start()
28{
29 std::unique_lock<std::mutex> lock(mtx);
30 if (threads < 1) {
31 start_time = GetTime();
32 }
33 ++threads;
34}
35
36void AtomicTimer::stop()
37{
38 std::unique_lock<std::mutex> lock(mtx);
39 // Ignore excess calls to stop()
40 if (threads > 0) {
41 --threads;
42 if (threads < 1) {
43 int64_t time_span = GetTime() - start_time;
44 total_time += time_span;
45 }
46 }
47}
48
49bool AtomicTimer::running()
50{
51 std::unique_lock<std::mutex> lock(mtx);
52 return threads > 0;
53}
54
0d0265fd
JG
55uint64_t AtomicTimer::threadCount()
56{
57 std::unique_lock<std::mutex> lock(mtx);
58 return threads;
59}
60
73bf85b4
JG
61double AtomicTimer::rate(const AtomicCounter& count)
62{
63 std::unique_lock<std::mutex> lock(mtx);
64 int64_t duration = total_time;
65 if (threads > 0) {
66 // Timer is running, so get the latest count
67 duration += GetTime() - start_time;
68 }
69 return duration > 0 ? (double)count.get() / duration : 0;
70}
71
cb709831
JG
72CCriticalSection cs_metrics;
73
000499ae 74boost::synchronized_value<int64_t> nNodeStartTime;
83561c9c 75boost::synchronized_value<int64_t> nNextRefresh;
a6df7ab5
JG
76AtomicCounter transactionsValidated;
77AtomicCounter ehSolverRuns;
e7d59bbc 78AtomicCounter solutionTargetChecks;
a6df7ab5 79AtomicCounter minedBlocks;
07be8f7e 80AtomicTimer miningTimer;
a6df7ab5 81
d793f94b
JG
82boost::synchronized_value<std::list<uint256>> trackedBlocks;
83
4ace963c
JG
84boost::synchronized_value<std::list<std::string>> messageBox;
85boost::synchronized_value<std::string> initMessage;
86bool loaded = false;
87
eb5b582e
JG
88extern int64_t GetNetworkHashPS(int lookup, int height);
89
d793f94b
JG
90void TrackMinedBlock(uint256 hash)
91{
cb709831 92 LOCK(cs_metrics);
d793f94b
JG
93 minedBlocks.increment();
94 trackedBlocks->push_back(hash);
95}
96
000499ae
JG
97void MarkStartTime()
98{
99 *nNodeStartTime = GetTime();
100}
101
102int64_t GetUptime()
103{
104 return GetTime() - *nNodeStartTime;
105}
106
000499ae
JG
107double GetLocalSolPS()
108{
07be8f7e 109 return miningTimer.rate(solutionTargetChecks);
000499ae
JG
110}
111
047aec1e
JG
112int EstimateNetHeightInner(int height, int64_t tipmediantime,
113 int heightLastCheckpoint, int64_t timeLastCheckpoint,
92bfde0e 114 int64_t genesisTime, int64_t targetSpacing)
047aec1e
JG
115{
116 // We average the target spacing with the observed spacing to the last
92bfde0e
JG
117 // checkpoint (either from below or above depending on the current height),
118 // and use that to estimate the current network height.
119 int medianHeight = height > CBlockIndex::nMedianTimeSpan ?
120 height - (1 + ((CBlockIndex::nMedianTimeSpan - 1) / 2)) :
121 height / 2;
122 double checkpointSpacing = medianHeight > heightLastCheckpoint ?
123 (double (tipmediantime - timeLastCheckpoint)) / (medianHeight - heightLastCheckpoint) :
124 (double (timeLastCheckpoint - genesisTime)) / heightLastCheckpoint;
047aec1e
JG
125 double averageSpacing = (targetSpacing + checkpointSpacing) / 2;
126 int netheight = medianHeight + ((GetTime() - tipmediantime) / averageSpacing);
127 // Round to nearest ten to reduce noise
128 return ((netheight + 5) / 10) * 10;
129}
130
131int EstimateNetHeight(int height, int64_t tipmediantime, CChainParams chainParams)
132{
133 auto checkpointData = chainParams.Checkpoints();
134 return EstimateNetHeightInner(
135 height, tipmediantime,
136 Checkpoints::GetTotalBlocksEstimate(checkpointData),
137 checkpointData.nTimeLastCheckpoint,
92bfde0e 138 chainParams.GenesisBlock().nTime,
047aec1e
JG
139 chainParams.GetConsensus().nPowTargetSpacing);
140}
141
83561c9c
JG
142void TriggerRefresh()
143{
144 *nNextRefresh = GetTime();
145 // Ensure that the refresh has started before we return
146 MilliSleep(200);
147}
148
4ace963c
JG
149static bool metrics_ThreadSafeMessageBox(const std::string& message,
150 const std::string& caption,
151 unsigned int style)
152{
e698459e
JG
153 // The SECURE flag has no effect in the metrics UI.
154 style &= ~CClientUIInterface::SECURE;
155
4ace963c
JG
156 std::string strCaption;
157 // Check for usage of predefined caption
158 switch (style) {
159 case CClientUIInterface::MSG_ERROR:
160 strCaption += _("Error");
161 break;
162 case CClientUIInterface::MSG_WARNING:
163 strCaption += _("Warning");
164 break;
165 case CClientUIInterface::MSG_INFORMATION:
166 strCaption += _("Information");
167 break;
168 default:
169 strCaption += caption; // Use supplied caption (can be empty)
170 }
171
172 boost::strict_lock_ptr<std::list<std::string>> u = messageBox.synchronize();
173 u->push_back(strCaption + ": " + message);
174 if (u->size() > 5) {
175 u->pop_back();
176 }
83561c9c
JG
177
178 TriggerRefresh();
179 return false;
4ace963c
JG
180}
181
0cd769fb
JG
182static bool metrics_ThreadSafeQuestion(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style)
183{
184 return metrics_ThreadSafeMessageBox(message, caption, style);
185}
186
4ace963c
JG
187static void metrics_InitMessage(const std::string& message)
188{
189 *initMessage = message;
190}
191
192void ConnectMetricsScreen()
193{
194 uiInterface.ThreadSafeMessageBox.disconnect_all_slots();
195 uiInterface.ThreadSafeMessageBox.connect(metrics_ThreadSafeMessageBox);
0cd769fb
JG
196 uiInterface.ThreadSafeQuestion.disconnect_all_slots();
197 uiInterface.ThreadSafeQuestion.connect(metrics_ThreadSafeQuestion);
4ace963c
JG
198 uiInterface.InitMessage.disconnect_all_slots();
199 uiInterface.InitMessage.connect(metrics_InitMessage);
200}
201
dd20d046 202int printStats(bool mining)
eb5b582e 203{
dd20d046
JG
204 // Number of lines that are always displayed
205 int lines = 4;
206
207 int height;
047aec1e 208 int64_t tipmediantime;
dd20d046
JG
209 size_t connections;
210 int64_t netsolps;
211 {
212 LOCK2(cs_main, cs_vNodes);
213 height = chainActive.Height();
047aec1e 214 tipmediantime = chainActive.Tip()->GetMedianTimePast();
dd20d046
JG
215 connections = vNodes.size();
216 netsolps = GetNetworkHashPS(120, -1);
217 }
218 auto localsolps = GetLocalSolPS();
eb5b582e 219
047aec1e
JG
220 if (IsInitialBlockDownload()) {
221 int netheight = EstimateNetHeight(height, tipmediantime, Params());
222 int downloadPercent = height * 100 / netheight;
223 std::cout << " " << _("Downloading blocks") << " | " << height << " / ~" << netheight << " (" << downloadPercent << "%)" << std::endl;
224 } else {
225 std::cout << " " << _("Block height") << " | " << height << std::endl;
226 }
dd20d046
JG
227 std::cout << " " << _("Connections") << " | " << connections << std::endl;
228 std::cout << " " << _("Network solution rate") << " | " << netsolps << " Sol/s" << std::endl;
229 if (mining && miningTimer.running()) {
230 std::cout << " " << _("Local solution rate") << " | " << strprintf("%.4f Sol/s", localsolps) << std::endl;
231 lines++;
232 }
eb5b582e
JG
233 std::cout << std::endl;
234
dd20d046 235 return lines;
eb5b582e
JG
236}
237
848c89cd 238int printMiningStatus(bool mining)
d30273f9 239{
2cc0a252 240#ifdef ENABLE_MINING
848c89cd
JG
241 // Number of lines that are always displayed
242 int lines = 1;
243
d30273f9 244 if (mining) {
0d0265fd
JG
245 auto nThreads = miningTimer.threadCount();
246 if (nThreads > 0) {
af370021
JG
247 std::cout << strprintf(_("You are mining with the %s solver on %d threads."),
248 GetArg("-equihashsolver", "default"), nThreads) << std::endl;
249 } else {
493d8d81
JG
250 bool fvNodesEmpty;
251 {
252 LOCK(cs_vNodes);
253 fvNodesEmpty = vNodes.empty();
254 }
255 if (fvNodesEmpty) {
256 std::cout << _("Mining is paused while waiting for connections.") << std::endl;
257 } else if (IsInitialBlockDownload()) {
258 std::cout << _("Mining is paused while downloading blocks.") << std::endl;
259 } else {
260 std::cout << _("Mining is paused (a JoinSplit may be in progress).") << std::endl;
261 }
af370021 262 }
848c89cd 263 lines++;
d30273f9 264 } else {
0ddd6d1c
JG
265 std::cout << _("You are currently not mining.") << std::endl;
266 std::cout << _("To enable mining, add 'gen=1' to your zcash.conf and restart.") << std::endl;
848c89cd 267 lines += 2;
d30273f9
JG
268 }
269 std::cout << std::endl;
848c89cd
JG
270
271 return lines;
2cc0a252
JG
272#else // ENABLE_MINING
273 return 0;
274#endif // !ENABLE_MINING
d30273f9
JG
275}
276
000499ae 277int printMetrics(size_t cols, bool mining)
d30273f9
JG
278{
279 // Number of lines that are always displayed
280 int lines = 3;
281
282 // Calculate uptime
000499ae 283 int64_t uptime = GetUptime();
d30273f9
JG
284 int days = uptime / (24 * 60 * 60);
285 int hours = (uptime - (days * 24 * 60 * 60)) / (60 * 60);
286 int minutes = (uptime - (((days * 24) + hours) * 60 * 60)) / 60;
287 int seconds = uptime - (((((days * 24) + hours) * 60) + minutes) * 60);
288
289 // Display uptime
0ddd6d1c 290 std::string duration;
d30273f9 291 if (days > 0) {
0ddd6d1c
JG
292 duration = strprintf(_("%d days, %d hours, %d minutes, %d seconds"), days, hours, minutes, seconds);
293 } else if (hours > 0) {
294 duration = strprintf(_("%d hours, %d minutes, %d seconds"), hours, minutes, seconds);
295 } else if (minutes > 0) {
296 duration = strprintf(_("%d minutes, %d seconds"), minutes, seconds);
297 } else {
298 duration = strprintf(_("%d seconds"), seconds);
d30273f9 299 }
db853f8a
JG
300 std::string strDuration = strprintf(_("Since starting this node %s ago:"), duration);
301 std::cout << strDuration << std::endl;
302 lines += (strDuration.size() / cols);
d30273f9 303
1c8d5c40 304 int validatedCount = transactionsValidated.get();
ba6fc72b
S
305 if (validatedCount > 1) {
306 std::cout << "- " << strprintf(_("You have validated %d transactions!"), validatedCount) << std::endl;
307 } else if (validatedCount == 1) {
1c8d5c40 308 std::cout << "- " << _("You have validated a transaction!") << std::endl;
ba6fc72b 309 } else {
1c8d5c40 310 std::cout << "- " << _("You have validated no transactions.") << std::endl;
ba6fc72b 311 }
d30273f9 312
24f10266 313 if (mining && loaded) {
0ddd6d1c 314 std::cout << "- " << strprintf(_("You have completed %d Equihash solver runs."), ehSolverRuns.get()) << std::endl;
dd20d046 315 lines++;
d30273f9 316
cb709831
JG
317 int mined = 0;
318 int orphaned = 0;
22ee0efe
JG
319 CAmount immature {0};
320 CAmount mature {0};
cb709831
JG
321 {
322 LOCK2(cs_main, cs_metrics);
d793f94b
JG
323 boost::strict_lock_ptr<std::list<uint256>> u = trackedBlocks.synchronize();
324 auto consensusParams = Params().GetConsensus();
325 auto tipHeight = chainActive.Height();
d793f94b
JG
326
327 // Update orphans and calculate subsidies
73a43918
JG
328 std::list<uint256>::iterator it = u->begin();
329 while (it != u->end()) {
d793f94b
JG
330 auto hash = *it;
331 if (mapBlockIndex.count(hash) > 0 &&
332 chainActive.Contains(mapBlockIndex[hash])) {
333 int height = mapBlockIndex[hash]->nHeight;
334 CAmount subsidy = GetBlockSubsidy(height, consensusParams);
335 if ((height > 0) && (height <= consensusParams.GetLastFoundersRewardBlockHeight())) {
336 subsidy -= subsidy/5;
337 }
338 if (std::max(0, COINBASE_MATURITY - (tipHeight - height)) > 0) {
339 immature += subsidy;
340 } else {
341 mature += subsidy;
342 }
73a43918 343 it++;
d793f94b
JG
344 } else {
345 it = u->erase(it);
346 }
347 }
d793f94b 348
cb709831
JG
349 mined = minedBlocks.get();
350 orphaned = mined - u->size();
351 }
352
353 if (mined > 0) {
22ee0efe 354 std::string units = Params().CurrencyUnits();
0ddd6d1c 355 std::cout << "- " << strprintf(_("You have mined %d blocks!"), mined) << std::endl;
d793f94b
JG
356 std::cout << " "
357 << strprintf(_("Orphaned: %d blocks, Immature: %u %s, Mature: %u %s"),
358 orphaned,
359 FormatMoney(immature), units,
360 FormatMoney(mature), units)
361 << std::endl;
362 lines += 2;
d30273f9
JG
363 }
364 }
365 std::cout << std::endl;
366
367 return lines;
368}
369
db853f8a 370int printMessageBox(size_t cols)
4ace963c
JG
371{
372 boost::strict_lock_ptr<std::list<std::string>> u = messageBox.synchronize();
373
374 if (u->size() == 0) {
375 return 0;
376 }
377
db853f8a 378 int lines = 2 + u->size();
0ddd6d1c 379 std::cout << _("Messages:") << std::endl;
4ace963c 380 for (auto it = u->cbegin(); it != u->cend(); ++it) {
b4f861d1
JG
381 auto msg = FormatParagraph(*it, cols, 2);
382 std::cout << "- " << msg << std::endl;
b6e439b2
JG
383 // Handle newlines and wrapped lines
384 size_t i = 0;
385 size_t j = 0;
b4f861d1
JG
386 while (j < msg.size()) {
387 i = msg.find('\n', j);
b6e439b2 388 if (i == std::string::npos) {
b4f861d1 389 i = msg.size();
b6e439b2
JG
390 } else {
391 // Newline
392 lines++;
393 }
b6e439b2
JG
394 j = i + 1;
395 }
4ace963c 396 }
d30273f9 397 std::cout << std::endl;
db853f8a 398 return lines;
4ace963c
JG
399}
400
401int printInitMessage()
402{
403 if (loaded) {
404 return 0;
405 }
406
407 std::string msg = *initMessage;
0ddd6d1c 408 std::cout << _("Init message:") << " " << msg << std::endl;
d30273f9 409 std::cout << std::endl;
4ace963c 410
0ddd6d1c 411 if (msg == _("Done loading")) {
4ace963c
JG
412 loaded = true;
413 }
414
415 return 2;
416}
417
a6df7ab5
JG
418void ThreadShowMetricsScreen()
419{
420 // Make this thread recognisable as the metrics screen thread
421 RenameThread("zcash-metrics-screen");
422
3c024773 423 // Determine whether we should render a persistent UI or rolling metrics
83ccbf6b
JG
424 bool isTTY = isatty(STDOUT_FILENO);
425 bool isScreen = GetBoolArg("-metricsui", isTTY);
426 int64_t nRefresh = GetArg("-metricsrefreshtime", isTTY ? 1 : 600);
a6df7ab5 427
3c024773
JG
428 if (isScreen) {
429 // Clear screen
430 std::cout << "\e[2J";
a6df7ab5 431
3c024773
JG
432 // Print art
433 std::cout << METRICS_ART << std::endl;
434 std::cout << std::endl;
435
436 // Thank you text
437 std::cout << _("Thank you for running a Zcash node!") << std::endl;
438 std::cout << _("You're helping to strengthen the network and contributing to a social good :)") << std::endl;
54c0a281 439
f0d1accb
DH
440 // Privacy notice text
441 std::cout << PrivacyInfo();
3c024773
JG
442 std::cout << std::endl;
443 }
a6df7ab5 444
a6df7ab5 445 while (true) {
4ace963c 446 // Number of lines that are always displayed
d30273f9 447 int lines = 1;
c0876672 448 int cols = 80;
a6df7ab5 449
db853f8a 450 // Get current window size
83ccbf6b 451 if (isTTY) {
9cb1ec9c 452#ifdef _WIN32
9d365796 453 CONSOLE_SCREEN_BUFFER_INFO csbi;
454 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
455 cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
456 #else
457 struct winsize w;
458 w.ws_col = 0;
459 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1 && w.ws_col != 0) {
460 cols = w.ws_col;
461 }
9cb1ec9c 462#endif
c0876672 463 }
db853f8a 464
3c024773
JG
465 if (isScreen) {
466 // Erase below current position
467 std::cout << "\e[J";
468 }
a6df7ab5 469
848c89cd 470 // Miner status
2cc0a252 471#ifdef ENABLE_MINING
848c89cd 472 bool mining = GetBoolArg("-gen", false);
2cc0a252
JG
473#else
474 bool mining = false;
475#endif
848c89cd 476
eb5b582e 477 if (loaded) {
dd20d046 478 lines += printStats(mining);
af370021 479 lines += printMiningStatus(mining);
eb5b582e 480 }
000499ae 481 lines += printMetrics(cols, mining);
c0876672 482 lines += printMessageBox(cols);
4ace963c
JG
483 lines += printInitMessage();
484
3c024773
JG
485 if (isScreen) {
486 // Explain how to exit
487 std::cout << "[" << _("Press Ctrl+C to exit") << "] [" << _("Set 'showmetrics=0' to hide") << "]" << std::endl;
488 } else {
489 // Print delineator
e2752e1e 490 std::cout << "----------------------------------------" << std::endl;
3c024773 491 }
a6df7ab5 492
83561c9c
JG
493 *nNextRefresh = GetTime() + nRefresh;
494 while (GetTime() < *nNextRefresh) {
83ccbf6b
JG
495 boost::this_thread::interruption_point();
496 MilliSleep(200);
497 }
a6df7ab5 498
3c024773
JG
499 if (isScreen) {
500 // Return to the top of the updating section
501 std::cout << "\e[" << lines << "A";
502 }
a6df7ab5
JG
503 }
504}
This page took 0.26428 seconds and 4 git commands to generate.