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