diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/gui.cpp | 38 | ||||
| -rw-r--r-- | src/main.cpp | 7 | ||||
| -rw-r--r-- | src/net.cpp | 117 | ||||
| -rw-r--r-- | src/net.h | 19 |
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; | |||
| 50 | static bool g_showSend = false; | 50 | static bool g_showSend = false; |
| 51 | static bool g_showOptions = false; | 51 | static bool g_showOptions = false; |
| 52 | static bool g_showAbout = false; | 52 | static bool g_showAbout = false; |
| 53 | static bool g_showDiagnostics = false; | ||
| 53 | static bool g_needRefresh = true; | 54 | static bool g_needRefresh = true; |
| 54 | 55 | ||
| 55 | static char g_sendAddr[128] = {}; | 56 | static 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. | ||
| 823 | static 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 | |||
| 818 | static void DrawAboutDialog() | 855 | static 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 | ||
| 1495 | bool ProcessBlock(CNode* pfrom, CBlock* pblock) | 1495 | bool 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; | |||
| 46 | map<CInv, int64> mapAlreadyAskedFor; | 46 | map<CInv, int64> mapAlreadyAskedFor; |
| 47 | string strBtfConnect; // .btf peer to keep connected to (from /connectbtf) | 47 | string strBtfConnect; // .btf peer to keep connected to (from /connectbtf) |
| 48 | 48 | ||
| 49 | int nPeersWatched = 0; | ||
| 50 | int64 nBlocksReceived = 0; | ||
| 51 | int64 nBlocksWithoutParent = 0; | ||
| 52 | static 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. | ||
| 57 | static int MinersRunningCount(); | ||
| 58 | |||
| 59 | // Seconds rendered the way a person reads them off a screen. | ||
| 60 | static 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 | |||
| 69 | string 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 | ||
| 50 | bool GetMyExternalIP(unsigned int& ipRet) | 140 | bool 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) | |||
| 1432 | static CCriticalSection cs_nMinersRunning; | 1540 | static CCriticalSection cs_nMinersRunning; |
| 1433 | static int nMinersRunning = 0; | 1541 | static int nMinersRunning = 0; |
| 1434 | 1542 | ||
| 1543 | static int MinersRunningCount() | ||
| 1544 | { | ||
| 1545 | int n = 0; | ||
| 1546 | CRITICAL_BLOCK(cs_nMinersRunning) | ||
| 1547 | n = nMinersRunning; | ||
| 1548 | return n; | ||
| 1549 | } | ||
| 1550 | |||
| 1435 | void ThreadBitcoinMiner(void* parg) | 1551 | void 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() | |||
| 1490 | bool StartNode(string& strError) | 1606 | bool 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; |
| @@ -102,6 +102,25 @@ static const int BTF_PING_INTERVAL_SECS = 10 * 60; | |||
| 102 | static const int BTF_BEHIND_GRACE_SECS = 5 * 60; | 102 | static const int BTF_BEHIND_GRACE_SECS = 5 * 60; |
| 103 | static const int BTF_BEHIND_WARN_INTERVAL_SECS = 5 * 60; | 103 | static 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. | ||
| 116 | extern int nPeersWatched; // peers in the last select() set | ||
| 117 | extern int64 nBlocksReceived; // blocks handed to ProcessBlock | ||
| 118 | extern 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. | ||
| 122 | string 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. |
| 107 | extern int nBestHeight; | 126 | extern int nBestHeight; |