diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/net.cpp | 29 | ||||
| -rw-r--r-- | src/sockcount.h | 23 |
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. | ||
| 150 | inline 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 |