upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gui.cpp38
-rw-r--r--src/main.cpp7
-rw-r--r--src/net.cpp117
-rw-r--r--src/net.h19
4 files changed, 181 insertions, 0 deletions
diff --git a/src/gui.cpp b/src/gui.cpp
index 53cb6db..db6b69e 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -50,6 +50,7 @@ static int64 g_lastWalletRefresh = 0;
50static bool g_showSend = false; 50static bool g_showSend = false;
51static bool g_showOptions = false; 51static bool g_showOptions = false;
52static bool g_showAbout = false; 52static bool g_showAbout = false;
53static bool g_showDiagnostics = false;
53static bool g_needRefresh = true; 54static bool g_needRefresh = true;
54 55
55static char g_sendAddr[128] = {}; 56static char g_sendAddr[128] = {};
@@ -310,6 +311,7 @@ static void DrawMainWindow()
310 ImGui::EndMenu(); 311 ImGui::EndMenu();
311 } 312 }
312 if (ImGui::MenuItem("Options")) g_showOptions = true; 313 if (ImGui::MenuItem("Options")) g_showOptions = true;
314 if (ImGui::MenuItem("Diagnostics")) g_showDiagnostics = true;
313 if (ImGui::MenuItem("About")) g_showAbout = true; 315 if (ImGui::MenuItem("About")) g_showAbout = true;
314 ImGui::EndMenuBar(); 316 ImGui::EndMenuBar();
315 } 317 }
@@ -815,6 +817,41 @@ static void DrawOptionsDialog()
815// --------------------------------------------------------------------------- 817// ---------------------------------------------------------------------------
816// About dialog 818// About dialog
817// --------------------------------------------------------------------------- 819// ---------------------------------------------------------------------------
820// What the node knows about itself, in the words it writes to the log. Same
821// text in both places on purpose: what a user pastes into an issue is then
822// exactly what a developer has already read a hundred times.
823static void DrawDiagnosticsDialog()
824{
825 if (!g_showDiagnostics) return;
826 ImGui::SetNextWindowSize(ImVec2(720.0f, 460.0f), ImGuiCond_FirstUseEver);
827 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
828 ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
829 if (ImGui::Begin("Diagnostics", &g_showDiagnostics, ImGuiWindowFlags_NoCollapse))
830 {
831 // Rebuilt at most once a second: it walks every peer and takes their
832 // send locks, which is not something to do at frame rate.
833 static std::string strReport;
834 static int64 nLastBuilt = 0;
835 if (GetTime() != nLastBuilt)
836 {
837 nLastBuilt = GetTime();
838 strReport = GetDiagnosticsText();
839 }
840
841 if (ImGui::Button("Copy to clipboard", ImVec2(160.0f, 0.0f)))
842 ImGui::SetClipboardText(strReport.c_str());
843 ImGui::SameLine();
844 ImGui::TextDisabled("The same report goes to debug.log every ten minutes.");
845 ImGui::Separator();
846
847 ImGui::BeginChild("##diagtext", ImVec2(0.0f, 0.0f), false,
848 ImGuiWindowFlags_HorizontalScrollbar);
849 ImGui::TextUnformatted(strReport.c_str());
850 ImGui::EndChild();
851 }
852 ImGui::End();
853}
854
818static void DrawAboutDialog() 855static void DrawAboutDialog()
819{ 856{
820 if (!g_showAbout) return; 857 if (!g_showAbout) return;
@@ -921,6 +958,7 @@ int RunGUI(int argc, char* argv[])
921 DrawMainWindow(); 958 DrawMainWindow();
922 DrawSendDialog(); 959 DrawSendDialog();
923 DrawOptionsDialog(); 960 DrawOptionsDialog();
961 DrawDiagnosticsDialog();
924 DrawAboutDialog(); 962 DrawAboutDialog();
925 DrawStatusBar(); 963 DrawStatusBar();
926 964
diff --git a/src/main.cpp b/src/main.cpp
index 5610de9..1a4c9ea 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1494,6 +1494,11 @@ bool CBlock::AcceptBlock()
1494 1494
1495bool ProcessBlock(CNode* pfrom, CBlock* pblock) 1495bool ProcessBlock(CNode* pfrom, CBlock* pblock)
1496{ 1496{
1497 // Counted before any check, so the ratio below is against everything that
1498 // arrived rather than everything that was any good.
1499 if (pfrom)
1500 nBlocksReceived++;
1501
1497 // Check for duplicate 1502 // Check for duplicate
1498 uint256 hash = pblock->GetHash(); 1503 uint256 hash = pblock->GetHash();
1499 if (mapBlockIndex.count(hash)) 1504 if (mapBlockIndex.count(hash))
@@ -1512,6 +1517,8 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
1512 if (!mapBlockIndex.count(pblock->hashPrevBlock)) 1517 if (!mapBlockIndex.count(pblock->hashPrevBlock))
1513 { 1518 {
1514 printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,14).c_str()); 1519 printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,14).c_str());
1520 if (pfrom)
1521 nBlocksWithoutParent++;
1515 mapOrphanBlocks.insert(make_pair(hash, pblock)); 1522 mapOrphanBlocks.insert(make_pair(hash, pblock));
1516 mapOrphanBlocksByPrev.insert(make_pair(pblock->hashPrevBlock, pblock)); 1523 mapOrphanBlocksByPrev.insert(make_pair(pblock->hashPrevBlock, pblock));
1517 1524
diff --git a/src/net.cpp b/src/net.cpp
index 5ef8316..19893ee 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -46,6 +46,96 @@ CCriticalSection cs_mapRelay;
46map<CInv, int64> mapAlreadyAskedFor; 46map<CInv, int64> mapAlreadyAskedFor;
47string strBtfConnect; // .btf peer to keep connected to (from /connectbtf) 47string strBtfConnect; // .btf peer to keep connected to (from /connectbtf)
48 48
49int nPeersWatched = 0;
50int64 nBlocksReceived = 0;
51int64 nBlocksWithoutParent = 0;
52static int64 nNodeStartTime = 0;
53
54// Defined further down, next to the counter it reads. The miner keeps its own
55// live count -- threads bump it on the way in and on the way out -- which is
56// the honest number here: how many are hashing now, not how many were asked to.
57static int MinersRunningCount();
58
59// Seconds rendered the way a person reads them off a screen.
60static string FormatAge(int64 nSeconds)
61{
62 if (nSeconds < 0) return "never";
63 if (nSeconds < 60) return strprintf("%llds", (long long)nSeconds);
64 if (nSeconds < 3600)
65 return strprintf("%lldm%02llds", (long long)(nSeconds / 60), (long long)(nSeconds % 60));
66 return strprintf("%lldh%02lldm", (long long)(nSeconds / 3600), (long long)((nSeconds % 3600) / 60));
67}
68
69string GetDiagnosticsText()
70{
71 int64 nNow = GetTime();
72 string str;
73
74 int nInbound = 0, nHeld = 0;
75 vector<CNode*> vCopy;
76 CRITICAL_BLOCK(cs_vNodes)
77 {
78 vCopy = vNodes;
79 nHeld = (int)vNodes.size();
80 foreach(CNode* pnode, vNodes)
81 if (pnode->fInbound)
82 nInbound++;
83 }
84
85 int nMedian = GetPeerMedianHeight();
86
87 str += "Bitflash node diagnostics\n";
88 str += strprintf(" uptime %s\n",
89 FormatAge(nNodeStartTime ? nNow - nNodeStartTime : -1).c_str());
90 if (nMedian < 0)
91 str += strprintf(" height %d (no peer has said where it is)\n", nBestHeight);
92 else
93 str += strprintf(" height %d (peers report %d, %s)\n",
94 nBestHeight, nMedian,
95 nBestHeight >= nMedian ? "level or ahead"
96 : strprintf("behind by %d", nMedian - nBestHeight).c_str());
97 str += strprintf(" peers held %d (%d inbound, %d outbound)\n",
98 nHeld, nInbound, nHeld - nInbound);
99
100 // The number that would have made the deafness obvious. Anything held but
101 // not watched is a socket this node will never read again.
102 str += strprintf(" peers watched %d of %d that select() can hold%s\n",
103 nPeersWatched, (int)FD_SETSIZE - 1,
104 nHeld > nPeersWatched ? " <-- NOT ALL PEERS ARE BEING READ" : "");
105
106 if (nBlocksReceived > 0)
107 str += strprintf(" blocks received %lld (%lld arrived without a parent, %.1f%%)\n",
108 (long long)nBlocksReceived, (long long)nBlocksWithoutParent,
109 100.0 * nBlocksWithoutParent / nBlocksReceived);
110 else
111 str += " blocks received 0\n";
112
113 int nMining = MinersRunningCount();
114 str += strprintf(" proof of work %s mode%s\n",
115 RandomXFastReady() ? "fast (2 GB dataset)" : "light (256 MB cache)",
116 nMining > 0
117 ? strprintf(", mining on %d thread(s), about %d MB",
118 nMining,
119 (RandomXFastReady() ? 2080 : 256) + 2 * nMining).c_str()
120 : ", not mining");
121
122 str += "\n peer dir height last recv last send unsent\n";
123 foreach(CNode* pnode, vCopy)
124 {
125 int nSendSize = 0;
126 TRY_CRITICAL_BLOCK(pnode->cs_vSend)
127 nSendSize = (int)pnode->vSend.size();
128 str += strprintf(" %-28s %-4s %6d %10s %10s %7d\n",
129 pnode->addr.ToString().substr(0, 28).c_str(),
130 pnode->fInbound ? "in" : "out",
131 pnode->nStartingHeight,
132 FormatAge(pnode->nLastRecv ? nNow - pnode->nLastRecv : -1).c_str(),
133 FormatAge(pnode->nLastSend ? nNow - pnode->nLastSend : -1).c_str(),
134 nSendSize);
135 }
136 return str;
137}
138
49 139
50bool GetMyExternalIP(unsigned int& ipRet) 140bool GetMyExternalIP(unsigned int& ipRet)
51{ 141{
@@ -1058,6 +1148,24 @@ void ThreadSocketHandler2(void* parg)
1058 (int)vNodes.size() - (int)FD_SETSIZE + 1); 1148 (int)vNodes.size() - (int)FD_SETSIZE + 1);
1059 } 1149 }
1060 } 1150 }
1151 nPeersWatched = (int)nWatched - 1; // less the listen socket
1152 }
1153
1154 // Say the state of the node out loud now and then, so a log pulled off
1155 // a machine three weeks later still answers "was it hearing anybody".
1156 {
1157 static int64 nLastReport = 0;
1158 // Wound forward on the first pass so the first report lands about a
1159 // minute in, once peers have had time to connect. A report written
1160 // at second zero says nothing, and waiting ten minutes for the
1161 // first one is ten minutes of a log that cannot answer anything.
1162 if (nLastReport == 0)
1163 nLastReport = GetTime() - 9 * 60;
1164 if (GetTime() - nLastReport > 10 * 60)
1165 {
1166 nLastReport = GetTime();
1167 printf("%s\n", GetDiagnosticsText().c_str());
1168 }
1061 } 1169 }
1062 1170
1063 vfThreadRunning[0] = false; 1171 vfThreadRunning[0] = false;
@@ -1432,6 +1540,14 @@ void ThreadMessageHandler2(void* parg)
1432static CCriticalSection cs_nMinersRunning; 1540static CCriticalSection cs_nMinersRunning;
1433static int nMinersRunning = 0; 1541static int nMinersRunning = 0;
1434 1542
1543static int MinersRunningCount()
1544{
1545 int n = 0;
1546 CRITICAL_BLOCK(cs_nMinersRunning)
1547 n = nMinersRunning;
1548 return n;
1549}
1550
1435void ThreadBitcoinMiner(void* parg) 1551void ThreadBitcoinMiner(void* parg)
1436{ 1552{
1437 int nThreadId = (int)(intptr_t)parg; 1553 int nThreadId = (int)(intptr_t)parg;
@@ -1490,6 +1606,7 @@ int StartMinerThreads()
1490bool StartNode(string& strError) 1606bool StartNode(string& strError)
1491{ 1607{
1492 strError = ""; 1608 strError = "";
1609 nNodeStartTime = GetTime();
1493 1610
1494 // Sockets startup 1611 // Sockets startup
1495 WSADATA wsadata; 1612 WSADATA wsadata;
diff --git a/src/net.h b/src/net.h
index 01f314d..bd4eb77 100644
--- a/src/net.h
+++ b/src/net.h
@@ -102,6 +102,25 @@ static const int BTF_PING_INTERVAL_SECS = 10 * 60;
102static const int BTF_BEHIND_GRACE_SECS = 5 * 60; 102static const int BTF_BEHIND_GRACE_SECS = 5 * 60;
103static const int BTF_BEHIND_WARN_INTERVAL_SECS = 5 * 60; 103static const int BTF_BEHIND_WARN_INTERVAL_SECS = 5 * 60;
104 104
105// --- Self-diagnosis -------------------------------------------------------
106//
107// A node that has stopped working looks exactly like a node with nothing to
108// do. That has cost this project real time more than once: a node deaf to
109// most of its peers, a miner allocating one RandomX dataset per thread, a
110// third of arriving blocks missing their parent -- each was found from
111// outside, by reading the process's sockets or its memory from another
112// machine, because the node itself had no way to say so.
113//
114// These counters exist so it can. Cheap to keep, and they name the failures
115// that actually happened rather than the ones that sound impressive.
116extern int nPeersWatched; // peers in the last select() set
117extern int64 nBlocksReceived; // blocks handed to ProcessBlock
118extern int64 nBlocksWithoutParent; // ...of those, how many arrived orphaned
119
120// One report, rendered the same way for the GUI panel and the log, so what a
121// user pastes into an issue is what a developer already knows how to read.
122string GetDiagnosticsText();
123
105// Defined in main.cpp. Declared here because CNode announces it in the version 124// Defined in main.cpp. Declared here because CNode announces it in the version
106// message, and net.h is included before main.h. 125// message, and net.h is included before main.h.
107extern int nBestHeight; 126extern int nBestHeight;