Skip to content

Commit

Permalink
Auto merge of #1375 - str4d:1331-node-metrics, r=daira
Browse files Browse the repository at this point in the history
Add node metrics screen

Continuation of #1336
Closes #1331
  • Loading branch information
zkbot committed Oct 23, 2016
2 parents 026c3f7 + 02a4ace commit a294b26
Show file tree
Hide file tree
Showing 9 changed files with 323 additions and 12 deletions.
1 change: 1 addition & 0 deletions qa/rpc-tests/test_framework/util.py
Expand Up @@ -65,6 +65,7 @@ def initialize_datadir(dirname, n):
os.makedirs(datadir)
with open(os.path.join(datadir, "zcash.conf"), 'w') as f:
f.write("regtest=1\n");
f.write("showmetrics=0\n");
f.write("rpcuser=rt\n");
f.write("rpcpassword=rt\n");
f.write("port="+str(p2p_port(n))+"\n");
Expand Down
2 changes: 2 additions & 0 deletions src/Makefile.am
Expand Up @@ -120,6 +120,7 @@ BITCOIN_CORE_H = \
main.h \
memusage.h \
merkleblock.h \
metrics.h \
miner.h \
mruset.h \
net.h \
Expand Down Expand Up @@ -205,6 +206,7 @@ libbitcoin_server_a_SOURCES = \
leveldbwrapper.cpp \
main.cpp \
merkleblock.cpp \
metrics.cpp \
miner.cpp \
net.cpp \
noui.cpp \
Expand Down
7 changes: 7 additions & 0 deletions src/init.cpp
Expand Up @@ -16,6 +16,7 @@
#include "consensus/validation.h"
#include "key.h"
#include "main.h"
#include "metrics.h"
#include "miner.h"
#include "net.h"
#include "rpcserver.h"
Expand Down Expand Up @@ -975,6 +976,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
CScheduler::Function serviceLoop = boost::bind(&CScheduler::serviceQueue, &scheduler);
threadGroup.create_thread(boost::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop));

if (GetBoolArg("-showmetrics", true) && !fPrintToConsole && !GetBoolArg("-daemon", false)) {
// Start the persistent metrics interface
ConnectMetricsScreen();
threadGroup.create_thread(&ThreadShowMetricsScreen);
}

// Initialize Zcash circuit parameters
ZC_LoadParams();
// These must be disabled for now, they are buggy and we probably don't
Expand Down
6 changes: 6 additions & 0 deletions src/main.cpp
Expand Up @@ -16,6 +16,7 @@
#include "consensus/validation.h"
#include "init.h"
#include "merkleblock.h"
#include "metrics.h"
#include "net.h"
#include "pow.h"
#include "txdb.h"
Expand Down Expand Up @@ -833,6 +834,11 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in

bool CheckTransaction(const CTransaction& tx, CValidationState &state)
{
// Don't count coinbase transactions because mining skews the count
if (!tx.IsCoinBase()) {
transactionsValidated.increment();
}

if (!CheckTransactionWithoutProofVerification(tx, state)) {
return false;
} else {
Expand Down
216 changes: 216 additions & 0 deletions src/metrics.cpp
@@ -0,0 +1,216 @@
// Copyright (c) 2016 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "metrics.h"

#include "chainparams.h"
#include "ui_interface.h"
#include "util.h"
#include "utiltime.h"

#include <boost/thread.hpp>
#include <boost/thread/synchronized_value.hpp>
#include <string>
#include <sys/ioctl.h>

AtomicCounter transactionsValidated;
AtomicCounter ehSolverRuns;
AtomicCounter solutionTargetChecks;
AtomicCounter minedBlocks;

boost::synchronized_value<std::list<std::string>> messageBox;
boost::synchronized_value<std::string> initMessage;
bool loaded = false;

static bool metrics_ThreadSafeMessageBox(const std::string& message,
const std::string& caption,
unsigned int style)
{
std::string strCaption;
// Check for usage of predefined caption
switch (style) {
case CClientUIInterface::MSG_ERROR:
strCaption += _("Error");
break;
case CClientUIInterface::MSG_WARNING:
strCaption += _("Warning");
break;
case CClientUIInterface::MSG_INFORMATION:
strCaption += _("Information");
break;
default:
strCaption += caption; // Use supplied caption (can be empty)
}

boost::strict_lock_ptr<std::list<std::string>> u = messageBox.synchronize();
u->push_back(strCaption + ": " + message);
if (u->size() > 5) {
u->pop_back();
}
}

static void metrics_InitMessage(const std::string& message)
{
*initMessage = message;
}

void ConnectMetricsScreen()
{
uiInterface.ThreadSafeMessageBox.disconnect_all_slots();
uiInterface.ThreadSafeMessageBox.connect(metrics_ThreadSafeMessageBox);
uiInterface.InitMessage.disconnect_all_slots();
uiInterface.InitMessage.connect(metrics_InitMessage);
}

void printMiningStatus(bool mining)
{
if (mining) {
int nThreads = GetArg("-genproclimit", 1);
if (nThreads < 0) {
// In regtest threads defaults to 1
if (Params().DefaultMinerThreads())
nThreads = Params().DefaultMinerThreads();
else
nThreads = boost::thread::hardware_concurrency();
}
std::cout << strprintf(_("You are running %d mining threads."), nThreads) << std::endl;
} else {
std::cout << _("You are currently not mining.") << std::endl;
std::cout << _("To enable mining, add 'gen=1' to your zcash.conf and restart.") << std::endl;
}
std::cout << std::endl;
}

int printMetrics(size_t cols, int64_t nStart, bool mining)
{
// Number of lines that are always displayed
int lines = 3;

// Calculate uptime
int64_t uptime = GetTime() - nStart;
int days = uptime / (24 * 60 * 60);
int hours = (uptime - (days * 24 * 60 * 60)) / (60 * 60);
int minutes = (uptime - (((days * 24) + hours) * 60 * 60)) / 60;
int seconds = uptime - (((((days * 24) + hours) * 60) + minutes) * 60);

// Display uptime
std::string duration;
if (days > 0) {
duration = strprintf(_("%d days, %d hours, %d minutes, %d seconds"), days, hours, minutes, seconds);
} else if (hours > 0) {
duration = strprintf(_("%d hours, %d minutes, %d seconds"), hours, minutes, seconds);
} else if (minutes > 0) {
duration = strprintf(_("%d minutes, %d seconds"), minutes, seconds);
} else {
duration = strprintf(_("%d seconds"), seconds);
}
std::string strDuration = strprintf(_("Since starting this node %s ago:"), duration);
std::cout << strDuration << std::endl;
lines += (strDuration.size() / cols);

std::cout << "- " << strprintf(_("You have validated %d transactions!"), transactionsValidated.get()) << std::endl;

if (mining) {
double solps = uptime > 0 ? (double)solutionTargetChecks.get() / uptime : 0;
std::string strSolps = strprintf("%.4f Sol/s", solps);
std::cout << "- " << strprintf(_("You have contributed %s on average to the network solution rate."), strSolps) << std::endl;
std::cout << "- " << strprintf(_("You have completed %d Equihash solver runs."), ehSolverRuns.get()) << std::endl;
lines += 2;

int mined = minedBlocks.get();
if (mined > 0) {
std::cout << "- " << strprintf(_("You have mined %d blocks!"), mined) << std::endl;
lines++;
}
}
std::cout << std::endl;

return lines;
}

int printMessageBox(size_t cols)
{
boost::strict_lock_ptr<std::list<std::string>> u = messageBox.synchronize();

if (u->size() == 0) {
return 0;
}

int lines = 2 + u->size();
std::cout << _("Messages:") << std::endl;
for (auto it = u->cbegin(); it != u->cend(); ++it) {
std::cout << *it << std::endl;
// Handle wrapped lines
lines += (it->size() / cols);
}
std::cout << std::endl;
return lines;
}

int printInitMessage()
{
if (loaded) {
return 0;
}

std::string msg = *initMessage;
std::cout << _("Init message:") << " " << msg << std::endl;
std::cout << std::endl;

if (msg == _("Done loading")) {
loaded = true;
}

return 2;
}

void ThreadShowMetricsScreen()
{
// Make this thread recognisable as the metrics screen thread
RenameThread("zcash-metrics-screen");

// Clear screen
std::cout << "\e[2J";

// Print art
std::cout << METRICS_ART << std::endl;
std::cout << std::endl;

// Thank you text
std::cout << _("Thank you for running a Zcash node!") << std::endl;
std::cout << _("You're helping to strengthen the network and contributing to a social good :)") << std::endl;
std::cout << std::endl;

// Miner status
bool mining = GetBoolArg("-gen", false);
printMiningStatus(mining);

// Count uptime
int64_t nStart = GetTime();

while (true) {
// Number of lines that are always displayed
int lines = 1;

// Get current window size
struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);

// Erase below current position
std::cout << "\e[J";

lines += printMetrics(w.ws_col, nStart, mining);
lines += printMessageBox(w.ws_col);
lines += printInitMessage();

// Explain how to exit
std::cout << "[" << _("Press Ctrl+C to exit") << "] [" << _("Set 'showmetrics=0' to hide") << "]" << std::endl;;

boost::this_thread::interruption_point();
MilliSleep(1000);

// Return to the top of the updating section
std::cout << "\e[" << lines << "A";
}
}
62 changes: 62 additions & 0 deletions src/metrics.h
@@ -0,0 +1,62 @@
// Copyright (c) 2016 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <atomic>
#include <string>

struct AtomicCounter {
std::atomic<int> value;

AtomicCounter() : value {0} { }

void increment(){
++value;
}

void decrement(){
--value;
}

int get(){
return value.load();
}
};

extern AtomicCounter transactionsValidated;
extern AtomicCounter ehSolverRuns;
extern AtomicCounter solutionTargetChecks;
extern AtomicCounter minedBlocks;

void ConnectMetricsScreen();
void ThreadShowMetricsScreen();

/**
* Heart image: https://commons.wikimedia.org/wiki/File:Heart_coraz%C3%B3n.svg
* License: CC BY-SA 3.0
*
* Rendering options:
* Zcash: img2txt -W 40 -H 20 -f utf8 -d none -g 0.7 Z-yellow.orange-logo.png
* Heart: img2txt -W 40 -H 20 -f utf8 -d none 2000px-Heart_corazón.svg.png
*/
const std::string METRICS_ART =
"   \n"
"   \n"
"  :88SX@888@@X8:  8; %X X% ;8 \n"
"  %%Xt%tt%SSSSS:XXXt@@  X :: :: X \n"
"  @S;;tt%%%t ;;::XXXXSX  % SS % \n"
"  .t:::;;%8888 88888tXXXX8;  S S \n"
"  .%...:::8 8::XXX%;  X X \n"
"  8888...:t888888X 8t;;::XX8   8 8 \n"
" %888888...:::;:8  :Xttt;;;::X@    \n"
" 888888888...:St 8:%%tttt;;;:X  X X \n"
" 88888888888S8  :%;ttt%%tttt;;X  8 8 \n"
" %888888888%t 8S:;;;tt%%%ttt;8  : : \n"
"  8t8888888  S8888888Stt%%%t@   :: :: \n"
"  .@tt888@ 8;;ttt@;  t t \n"
"  .8ttt8@SSSSS SXXXX%:;;;X;  8 8 \n"
"  X8ttt8888% %88...::X8   X. .X \n"
"  %8@tt88;8888%8888%8X   :; ;: \n"
"  :@888@XXX@888:  tt \n"
"   \n"
"   ";

0 comments on commit a294b26

Please sign in to comment.