wip: per-connection census at kilobyte resolution

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
Martin Vogel
2026-07-25 18:10:42 +02:00
parent bee8658c4d
commit 718037f02d
4 changed files with 55 additions and 24 deletions
+4
View File
@@ -1853,6 +1853,10 @@ static void *runtime_connection_worker(void *opaque) {
* of live blocks). Collecting here hands the pages back while this thread
* still owns them, which is the only point at which that is cheap (#581). */
cbm_mem_collect();
/* Census AFTER the collect: this is the per-connection steady state, so a
* pool that climbs across these lines is holding memory the connection was
* supposed to have given back. */
cbm_mem_census_log("connection.exit");
return NULL;
}
+41
View File
@@ -744,6 +744,12 @@ bool cbm_mem_win_census(cbm_mem_win_census_t *out) {
switch (mbi.Type) {
case MEM_PRIVATE:
out->committed_private += mbi.RegionSize;
if (mbi.RegionSize > out->largest_private_region) {
out->largest_private_region = mbi.RegionSize;
}
if (mbi.RegionSize >= (size_t)CBM_SZ_1K * CBM_SZ_1K) {
out->private_regions_1mb++;
}
break;
case MEM_MAPPED:
out->committed_mapped += mbi.RegionSize;
@@ -791,3 +797,38 @@ bool cbm_mem_win_census(cbm_mem_win_census_t *out) {
return true;
#endif
}
void cbm_mem_census_log(const char *tag) {
if (!cbm_mem_census_enabled()) {
return;
}
cbm_mem_win_census_t census;
if (!cbm_mem_win_census(&census)) {
return;
}
/* Kilobytes throughout: the CRT heap measured 0 at megabyte resolution,
* which reads as "empty" when it may simply be small — a diagnostic that
* rounds its own evidence away is worse than none. */
char priv_kb[CBM_SZ_32];
char crt_kb[CBM_SZ_32];
char busy_kb[CBM_SZ_32];
char mapped_kb[CBM_SZ_32];
char rss_kb[CBM_SZ_32];
char biggest_kb[CBM_SZ_32];
char regions[CBM_SZ_32];
char big_regions[CBM_SZ_32];
char heaps[CBM_SZ_32];
(void)snprintf(priv_kb, sizeof(priv_kb), "%zu", census.committed_private / CBM_SZ_1K);
(void)snprintf(crt_kb, sizeof(crt_kb), "%zu", census.crt_heap_committed / CBM_SZ_1K);
(void)snprintf(busy_kb, sizeof(busy_kb), "%zu", census.crt_heap_busy / CBM_SZ_1K);
(void)snprintf(mapped_kb, sizeof(mapped_kb), "%zu", census.committed_mapped / CBM_SZ_1K);
(void)snprintf(rss_kb, sizeof(rss_kb), "%zu", cbm_mem_rss() / CBM_SZ_1K);
(void)snprintf(biggest_kb, sizeof(biggest_kb), "%zu",
census.largest_private_region / CBM_SZ_1K);
(void)snprintf(regions, sizeof(regions), "%u", census.regions);
(void)snprintf(big_regions, sizeof(big_regions), "%u", census.private_regions_1mb);
(void)snprintf(heaps, sizeof(heaps), "%u", census.heap_walk_ok ? census.crt_heap_count : 0);
cbm_log_info("mem.census", "at", tag ? tag : "?", "priv_kb", priv_kb, "crt_kb", crt_kb,
"crt_busy_kb", busy_kb, "mapped_kb", mapped_kb, "rss_kb", rss_kb, "biggest_kb",
biggest_kb, "regions", regions, "big_regions", big_regions, "heaps", heaps);
}
+9
View File
@@ -233,6 +233,11 @@ typedef struct {
size_t crt_heap_busy; /* bytes in live allocations inside them */
unsigned crt_heap_count;
unsigned regions;
/* The single largest committed private region, and how many are >=1MiB.
* One enormous region means one reservation (allocator metadata, a pagemap)
* rather than many leaked blocks — a different bug with a different fix. */
size_t largest_private_region;
unsigned private_regions_1mb;
bool heap_walk_ok; /* false → crt_* are unknown, not zero */
} cbm_mem_win_census_t;
@@ -240,6 +245,10 @@ typedef struct {
* (non-Windows, or out is NULL). */
bool cbm_mem_win_census(cbm_mem_win_census_t *out);
/* Emit one census line tagged with the call site, so a growth series can be
* read straight out of the daemon log. No-op unless CBM_MEM_CENSUS=1. */
void cbm_mem_census_log(const char *tag);
/* Census logging gate: CBM_MEM_CENSUS=1. Off by default — HeapWalk locks each
* CRT heap, so this is a diagnostic, never a hot-path metric. */
bool cbm_mem_census_enabled(void);
+1 -24
View File
@@ -10453,30 +10453,7 @@ char *cbm_mcp_handle_tool(cbm_mcp_server_t *srv, const char *tool_name, const ch
* rather than inferred from a process total (#581). Emitted after the
* request store is released, which is the point where a well-behaved
* request has given everything back. */
if (cbm_mem_census_enabled()) {
cbm_mem_win_census_t census;
if (cbm_mem_win_census(&census)) {
const size_t mb = (size_t)CBM_SZ_1K * CBM_SZ_1K;
char private_mb[CBM_SZ_32];
char crt_mb[CBM_SZ_32];
char busy_mb[CBM_SZ_32];
char mapped_mb[CBM_SZ_32];
char rss_mb[CBM_SZ_32];
char regions[CBM_SZ_32];
(void)snprintf(private_mb, sizeof(private_mb), "%zu",
census.committed_private / mb);
(void)snprintf(crt_mb, sizeof(crt_mb), "%zu",
census.crt_heap_committed / mb);
(void)snprintf(busy_mb, sizeof(busy_mb), "%zu", census.crt_heap_busy / mb);
(void)snprintf(mapped_mb, sizeof(mapped_mb), "%zu",
census.committed_mapped / mb);
(void)snprintf(rss_mb, sizeof(rss_mb), "%zu", cbm_mem_rss() / mb);
(void)snprintf(regions, sizeof(regions), "%u", census.regions);
cbm_log_info("mem.census", "private_mb", private_mb, "crt_heap_mb", crt_mb,
"crt_busy_mb", busy_mb, "mapped_mb", mapped_mb, "rss_mb", rss_mb,
"regions", regions);
}
}
cbm_mem_census_log("mcp.request");
return result;
}