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