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/net.cpp29
-rw-r--r--src/sockcount.h23
2 files changed, 52 insertions, 0 deletions
diff --git a/src/net.cpp b/src/net.cpp
index 7a13257..4fe5f15 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -66,8 +66,12 @@ static string SockAccountingText()
66 66
67 string str = "\n sockets by where they were created\n"; 67 string str = "\n sockets by where they were created\n";
68 str += " site opened closed live close failed\n"; 68 str += " site opened closed live close failed\n";
69 long long nTotalOpened = 0, nTotalClosed = 0, nTotalFailed = 0;
69 for (int i = 0; i < SOCK_SITES; i++) 70 for (int i = 0; i < SOCK_SITES; i++)
70 { 71 {
72 nTotalOpened += nOpened[i];
73 nTotalClosed += nClosed[i];
74 nTotalFailed += nFailed[i];
71 if (nOpened[i] == 0) 75 if (nOpened[i] == 0)
72 continue; 76 continue;
73 str += strprintf(" %-24s %8lld %8lld %8lld %13lld\n", pszSockSite[i], 77 str += strprintf(" %-24s %8lld %8lld %8lld %13lld\n", pszSockSite[i],
@@ -75,6 +79,31 @@ static string SockAccountingText()
75 } 79 }
76 str += strprintf(" %-24s %8s %8lld %8s %13s\n", "closed but never tagged", 80 str += strprintf(" %-24s %8s %8lld %8s %13s\n", "closed but never tagged",
77 "-", nUntagged, "-", "-"); 81 "-", nUntagged, "-", "-");
82 str += strprintf(" %-24s %8lld %8lld %8lld %13lld\n", "TOTAL",
83 nTotalOpened, nTotalClosed, nTotalOpened - nTotalClosed, nTotalFailed);
84
85 // The live total above is what this program believes it is holding. Compare
86 // it against what the operating system attributes to the process: on
87 // Windows, `Get-NetTCPConnection | ? OwningProcess -eq <pid>`. A gap there
88 // is the leak, and the codes below say what the failed closes hit -- 10038
89 // (WSAENOTSOCK) means the handle was already gone and this close was a
90 // second one, which is a different bug from a descriptor that stayed.
91 if (nTotalFailed > 0)
92 {
93 str += " close failures by code\n";
94 for (int i = 0; i < SOCK_SITES; i++)
95 {
96 if (nFailed[i] == 0)
97 continue;
98 map<int, long long> mapErr;
99 BtfSockCloseErrors(i, mapErr);
100 string strCodes;
101 for (map<int, long long>::const_iterator mi = mapErr.begin(); mi != mapErr.end(); ++mi)
102 strCodes += strprintf("%s%d x%lld", strCodes.empty() ? "" : ", ",
103 mi->first, mi->second);
104 str += strprintf(" %-24s %s\n", pszSockSite[i], strCodes.c_str());
105 }
106 }
78 return str; 107 return str;
79} 108}
80 109
diff --git a/src/sockcount.h b/src/sockcount.h
index 19f0827..a42abb9 100644
--- a/src/sockcount.h
+++ b/src/sockcount.h
@@ -23,6 +23,7 @@
23#ifndef BITFLASH_SOCKCOUNT_H 23#ifndef BITFLASH_SOCKCOUNT_H
24#define BITFLASH_SOCKCOUNT_H 24#define BITFLASH_SOCKCOUNT_H
25 25
26#include <cerrno>
26#include <map> 27#include <map>
27#include <mutex> 28#include <mutex>
28 29
@@ -52,6 +53,13 @@ struct BtfSockAccount
52 long long nOpened[SOCK_SITES]; 53 long long nOpened[SOCK_SITES];
53 long long nClosed[SOCK_SITES]; 54 long long nClosed[SOCK_SITES];
54 long long nCloseFailed[SOCK_SITES]; 55 long long nCloseFailed[SOCK_SITES];
56 // Why the close failed, counted per code per site. Measured on a node with
57 // 3h51m of uptime: the program believed it held 58 sockets and Windows
58 // attributed 112 to the process, 47 of them bound to an ephemeral port and
59 // never connected -- against 55 closes that had returned an error. The
60 // shapes match, and the error code is the piece that says whether the
61 // descriptor is still there or was already gone.
62 std::map<int, long long> mapCloseErr[SOCK_SITES];
55 long long nClosedUntagged; 63 long long nClosedUntagged;
56 BtfSockAccount() : nClosedUntagged(0) 64 BtfSockAccount() : nClosedUntagged(0)
57 { 65 {
@@ -102,8 +110,10 @@ inline int BtfCloseSocket(SOCKET hSocket)
102 } 110 }
103#ifdef _WIN32 111#ifdef _WIN32
104 int nRet = ::closesocket(hSocket); 112 int nRet = ::closesocket(hSocket);
113 int nErr = (nRet != 0) ? WSAGetLastError() : 0;
105#else 114#else
106 int nRet = ::close(hSocket); 115 int nRet = ::close(hSocket);
116 int nErr = (nRet != 0) ? errno : 0;
107#endif 117#endif
108 // A close that fails leaves the descriptor -- and on Windows the ephemeral 118 // A close that fails leaves the descriptor -- and on Windows the ephemeral
109 // port it holds -- in place. Counted because a socket the program believes 119 // port it holds -- in place. Counted because a socket the program believes
@@ -115,6 +125,7 @@ inline int BtfCloseSocket(SOCKET hSocket)
115 BtfSockAccount& a = BtfSockAccounting(); 125 BtfSockAccount& a = BtfSockAccounting();
116 std::lock_guard<std::mutex> lock(a.mtx); 126 std::lock_guard<std::mutex> lock(a.mtx);
117 a.nCloseFailed[nSite]++; 127 a.nCloseFailed[nSite]++;
128 a.mapCloseErr[nSite][nErr]++;
118 } 129 }
119 return nRet; 130 return nRet;
120} 131}
@@ -134,4 +145,16 @@ inline void BtfSockSnapshot(long long* pOpened, long long* pClosed, long long* p
134 *pUntagged = a.nClosedUntagged; 145 *pUntagged = a.nClosedUntagged;
135} 146}
136 147
148// The close-failure codes for one site, newest counts included. Separate call
149// because the table above is fixed-width and this is a list of unknown length.
150inline void BtfSockCloseErrors(int nSite, std::map<int, long long>& mapOut)
151{
152 mapOut.clear();
153 if (nSite < 0 || nSite >= SOCK_SITES)
154 return;
155 BtfSockAccount& a = BtfSockAccounting();
156 std::lock_guard<std::mutex> lock(a.mtx);
157 mapOut = a.mapCloseErr[nSite];
158}
159
137#endif 160#endif