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