]> Git Repo - VerusCoin.git/blob - src/metrics.cpp
Merge pull request #1 from jl777/dev
[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 #ifdef _WIN32
20 #include <io.h>
21 #include <windows.h>
22 #else
23 #include <sys/ioctl.h>
24 #endif
25 #include <unistd.h>
26
27 void AtomicTimer::start()
28 {
29     std::unique_lock<std::mutex> lock(mtx);
30     if (threads < 1) {
31         start_time = GetTime();
32     }
33     ++threads;
34 }
35
36 void 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
49 bool AtomicTimer::running()
50 {
51     std::unique_lock<std::mutex> lock(mtx);
52     return threads > 0;
53 }
54
55 uint64_t AtomicTimer::threadCount()
56 {
57     std::unique_lock<std::mutex> lock(mtx);
58     return threads;
59 }
60
61 double 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
72 CCriticalSection cs_metrics;
73
74 boost::synchronized_value<int64_t> nNodeStartTime;
75 boost::synchronized_value<int64_t> nNextRefresh;
76 AtomicCounter transactionsValidated;
77 AtomicCounter ehSolverRuns;
78 AtomicCounter solutionTargetChecks;
79 AtomicCounter minedBlocks;
80 AtomicTimer miningTimer;
81
82 boost::synchronized_value<std::list<uint256>> trackedBlocks;
83
84 boost::synchronized_value<std::list<std::string>> messageBox;
85 boost::synchronized_value<std::string> initMessage;
86 bool loaded = false;
87
88 extern int64_t GetNetworkHashPS(int lookup, int height);
89
90 void TrackMinedBlock(uint256 hash)
91 {
92     LOCK(cs_metrics);
93     minedBlocks.increment();
94     trackedBlocks->push_back(hash);
95 }
96
97 void MarkStartTime()
98 {
99     *nNodeStartTime = GetTime();
100 }
101
102 int64_t GetUptime()
103 {
104     return GetTime() - *nNodeStartTime;
105 }
106
107 double GetLocalSolPS()
108 {
109     return miningTimer.rate(solutionTargetChecks);
110 }
111
112 int EstimateNetHeightInner(int height, int64_t tipmediantime,
113                            int heightLastCheckpoint, int64_t timeLastCheckpoint,
114                            int64_t genesisTime, int64_t targetSpacing)
115 {
116     // We average the target spacing with the observed spacing to the last
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;
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
131 int 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,
138         chainParams.GenesisBlock().nTime,
139         chainParams.GetConsensus().nPowTargetSpacing);
140 }
141
142 void TriggerRefresh()
143 {
144     *nNextRefresh = GetTime();
145     // Ensure that the refresh has started before we return
146     MilliSleep(200);
147 }
148
149 static bool metrics_ThreadSafeMessageBox(const std::string& message,
150                                       const std::string& caption,
151                                       unsigned int style)
152 {
153     // The SECURE flag has no effect in the metrics UI.
154     style &= ~CClientUIInterface::SECURE;
155
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     }
177
178     TriggerRefresh();
179     return false;
180 }
181
182 static 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
187 static void metrics_InitMessage(const std::string& message)
188 {
189     *initMessage = message;
190 }
191
192 void ConnectMetricsScreen()
193 {
194     uiInterface.ThreadSafeMessageBox.disconnect_all_slots();
195     uiInterface.ThreadSafeMessageBox.connect(metrics_ThreadSafeMessageBox);
196     uiInterface.ThreadSafeQuestion.disconnect_all_slots();
197     uiInterface.ThreadSafeQuestion.connect(metrics_ThreadSafeQuestion);
198     uiInterface.InitMessage.disconnect_all_slots();
199     uiInterface.InitMessage.connect(metrics_InitMessage);
200 }
201
202 int printStats(bool mining)
203 {
204     // Number of lines that are always displayed
205     int lines = 4;
206
207     int height;
208     int64_t tipmediantime;
209     size_t connections;
210     int64_t netsolps;
211     {
212         LOCK2(cs_main, cs_vNodes);
213         height = chainActive.Height();
214         tipmediantime = chainActive.LastTip()->GetMedianTimePast();
215         connections = vNodes.size();
216         netsolps = GetNetworkHashPS(120, -1);
217     }
218     auto localsolps = GetLocalSolPS();
219
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     }
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     }
233     std::cout << std::endl;
234
235     return lines;
236 }
237
238 int printMiningStatus(bool mining)
239 {
240 #ifdef ENABLE_MINING
241     // Number of lines that are always displayed
242     int lines = 1;
243
244     if (mining) {
245         auto nThreads = miningTimer.threadCount();
246         if (nThreads > 0) {
247             std::cout << strprintf(_("You are mining with the %s solver on %d threads."),
248                                    GetArg("-equihashsolver", "default"), nThreads) << std::endl;
249         } else {
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             }
262         }
263         lines++;
264     } else {
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;
267         lines += 2;
268     }
269     std::cout << std::endl;
270
271     return lines;
272 #else // ENABLE_MINING
273     return 0;
274 #endif // !ENABLE_MINING
275 }
276
277 int printMetrics(size_t cols, bool mining)
278 {
279     // Number of lines that are always displayed
280     int lines = 3;
281
282     // Calculate uptime
283     int64_t uptime = GetUptime();
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
290     std::string duration;
291     if (days > 0) {
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);
299     }
300     std::string strDuration = strprintf(_("Since starting this node %s ago:"), duration);
301     std::cout << strDuration << std::endl;
302     lines += (strDuration.size() / cols);
303
304     int validatedCount = transactionsValidated.get();
305     if (validatedCount > 1) {
306       std::cout << "- " << strprintf(_("You have validated %d transactions!"), validatedCount) << std::endl;
307     } else if (validatedCount == 1) {
308       std::cout << "- " << _("You have validated a transaction!") << std::endl;
309     } else {
310       std::cout << "- " << _("You have validated no transactions.") << std::endl;
311     }
312
313     if (mining && loaded) {
314         std::cout << "- " << strprintf(_("You have completed %d Equihash solver runs."), ehSolverRuns.get()) << std::endl;
315         lines++;
316
317         int mined = 0;
318         int orphaned = 0;
319         CAmount immature {0};
320         CAmount mature {0};
321         {
322             LOCK2(cs_main, cs_metrics);
323             boost::strict_lock_ptr<std::list<uint256>> u = trackedBlocks.synchronize();
324             auto consensusParams = Params().GetConsensus();
325             auto tipHeight = chainActive.Height();
326
327             // Update orphans and calculate subsidies
328             std::list<uint256>::iterator it = u->begin();
329             while (it != u->end()) {
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                     }
343                     it++;
344                 } else {
345                     it = u->erase(it);
346                 }
347             }
348
349             mined = minedBlocks.get();
350             orphaned = mined - u->size();
351         }
352
353         if (mined > 0) {
354             std::string units = Params().CurrencyUnits();
355             std::cout << "- " << strprintf(_("You have mined %d blocks!"), mined) << std::endl;
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;
363         }
364     }
365     std::cout << std::endl;
366
367     return lines;
368 }
369
370 int printMessageBox(size_t cols)
371 {
372     boost::strict_lock_ptr<std::list<std::string>> u = messageBox.synchronize();
373
374     if (u->size() == 0) {
375         return 0;
376     }
377
378     int lines = 2 + u->size();
379     std::cout << _("Messages:") << std::endl;
380     for (auto it = u->cbegin(); it != u->cend(); ++it) {
381         auto msg = FormatParagraph(*it, cols, 2);
382         std::cout << "- " << msg << std::endl;
383         // Handle newlines and wrapped lines
384         size_t i = 0;
385         size_t j = 0;
386         while (j < msg.size()) {
387             i = msg.find('\n', j);
388             if (i == std::string::npos) {
389                 i = msg.size();
390             } else {
391                 // Newline
392                 lines++;
393             }
394             j = i + 1;
395         }
396     }
397     std::cout << std::endl;
398     return lines;
399 }
400
401 int printInitMessage()
402 {
403     if (loaded) {
404         return 0;
405     }
406
407     std::string msg = *initMessage;
408     std::cout << _("Init message:") << " " << msg << std::endl;
409     std::cout << std::endl;
410
411     if (msg == _("Done loading")) {
412         loaded = true;
413     }
414
415     return 2;
416 }
417
418 void ThreadShowMetricsScreen()
419 {
420     // Make this thread recognisable as the metrics screen thread
421     RenameThread("zcash-metrics-screen");
422
423     // Determine whether we should render a persistent UI or rolling metrics
424     bool isTTY = isatty(STDOUT_FILENO);
425     bool isScreen = GetBoolArg("-metricsui", isTTY);
426     int64_t nRefresh = GetArg("-metricsrefreshtime", isTTY ? 1 : 600);
427
428     if (isScreen) {
429         // Clear screen
430         std::cout << "\e[2J";
431
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;
439
440         // Privacy notice text
441         std::cout << PrivacyInfo();
442         std::cout << std::endl;
443     }
444
445     while (true) {
446         // Number of lines that are always displayed
447         int lines = 1;
448         int cols = 80;
449
450         // Get current window size
451         if (isTTY) {
452 #ifdef _WIN32
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           }
462 #endif
463         }
464
465         if (isScreen) {
466             // Erase below current position
467             std::cout << "\e[J";
468         }
469
470         // Miner status
471 #ifdef ENABLE_MINING
472         bool mining = GetBoolArg("-gen", false);
473 #else
474         bool mining = false;
475 #endif
476
477         if (loaded) {
478             lines += printStats(mining);
479             lines += printMiningStatus(mining);
480         }
481         lines += printMetrics(cols, mining);
482         lines += printMessageBox(cols);
483         lines += printInitMessage();
484
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
490             std::cout << "----------------------------------------" << std::endl;
491         }
492
493         *nNextRefresh = GetTime() + nRefresh;
494         while (GetTime() < *nNextRefresh) {
495             boost::this_thread::interruption_point();
496             MilliSleep(200);
497         }
498
499         if (isScreen) {
500             // Return to the top of the updating section
501             std::cout << "\e[" << lines << "A";
502         }
503     }
504 }
This page took 0.052913 seconds and 4 git commands to generate.