feat: add memory footprint to /stats and dashboard #26

Merged
razvandimescu merged 3 commits from feat/memory-footprint into main 2026-04-01 14:09:44 +08:00
7 changed files with 10 additions and 20 deletions
Showing only changes of commit 987e3aeeae - Show all commits

View File

@@ -814,15 +814,15 @@ function renderMemory(mem, stats) {
if (!mem) return; if (!mem) return;
// Stat card // Stat card
document.getElementById('memoryRss').textContent = formatBytes(mem.process_rss_bytes); document.getElementById('memoryRss').textContent = formatBytes(mem.process_memory_bytes);
document.getElementById('memorySub').textContent = 'est. ' + formatBytes(mem.total_estimated_bytes); document.getElementById('memorySub').textContent = 'est. ' + formatBytes(mem.total_estimated_bytes);
const entryCounts = { const entryCounts = {
cache: mem.cache_entries, cache: stats.cache.entries,
blocklist: mem.blocklist_entries, blocklist: stats.blocking.domains_loaded,
query_log: mem.query_log_entries, query_log: mem.query_log_entries,
srtt: mem.srtt_entries, srtt: mem.srtt_entries,
overrides: mem.overrides_entries, overrides: stats.overrides.active,
}; };
// Sidebar panel // Sidebar panel
@@ -852,7 +852,7 @@ function renderMemory(mem, stats) {
${rows} ${rows}
<div class="memory-rss"> <div class="memory-rss">
<span>Process Footprint</span> <span>Process Footprint</span>
<span>${formatBytes(mem.process_rss_bytes)}</span> <span>${formatBytes(mem.process_memory_bytes)}</span>
</div> </div>
`; `;
} }

View File

@@ -214,17 +214,14 @@ struct BlockingStatsResponse {
#[derive(Serialize)] #[derive(Serialize)]
struct MemoryStats { struct MemoryStats {
cache_bytes: usize, cache_bytes: usize,
cache_entries: usize,
blocklist_bytes: usize, blocklist_bytes: usize,
blocklist_entries: usize,
query_log_bytes: usize, query_log_bytes: usize,
query_log_entries: usize, query_log_entries: usize,
srtt_bytes: usize, srtt_bytes: usize,
srtt_entries: usize, srtt_entries: usize,
overrides_bytes: usize, overrides_bytes: usize,
overrides_entries: usize,
total_estimated_bytes: usize, total_estimated_bytes: usize,
process_rss_bytes: usize, process_memory_bytes: usize,
} }
#[derive(Serialize)] #[derive(Serialize)]
@@ -556,17 +553,14 @@ async fn stats(State(ctx): State<Arc<ServerCtx>>) -> Json<StatsResponse> {
}, },
memory: MemoryStats { memory: MemoryStats {
cache_bytes, cache_bytes,
cache_entries: cache_len,
blocklist_bytes, blocklist_bytes,
blocklist_entries: bl_stats.domains_loaded,
query_log_bytes, query_log_bytes,
query_log_entries, query_log_entries,
srtt_bytes, srtt_bytes,
srtt_entries, srtt_entries,
overrides_bytes, overrides_bytes,
overrides_entries: override_count,
total_estimated_bytes: total_estimated, total_estimated_bytes: total_estimated,
process_rss_bytes: crate::stats::process_rss_bytes(), process_memory_bytes: crate::stats::process_memory_bytes(),
}, },
}) })
} }

View File

@@ -184,7 +184,6 @@ impl BlocklistStore {
} }
pub fn heap_bytes(&self) -> usize { pub fn heap_bytes(&self) -> usize {
// HashSet<String> stores (hash, String) per slot + 1 control byte
let per_slot_overhead = std::mem::size_of::<u64>() + std::mem::size_of::<String>() + 1; let per_slot_overhead = std::mem::size_of::<u64>() + std::mem::size_of::<String>() + 1;
let domains_table = self.domains.capacity() * per_slot_overhead; let domains_table = self.domains.capacity() * per_slot_overhead;
let domains_heap: usize = self.domains.iter().map(|d| d.capacity()).sum(); let domains_heap: usize = self.domains.iter().map(|d| d.capacity()).sum();

View File

@@ -143,7 +143,6 @@ impl DnsCache {
} }
pub fn heap_bytes(&self) -> usize { pub fn heap_bytes(&self) -> usize {
// Outer HashMap<String, HashMap>: (hash, String, HashMap) per slot + control byte
let outer_slot = std::mem::size_of::<u64>() let outer_slot = std::mem::size_of::<u64>()
+ std::mem::size_of::<String>() + std::mem::size_of::<String>()
+ std::mem::size_of::<HashMap<QueryType, CacheEntry>>() + std::mem::size_of::<HashMap<QueryType, CacheEntry>>()
@@ -151,7 +150,6 @@ impl DnsCache {
let mut total = self.entries.capacity() * outer_slot; let mut total = self.entries.capacity() * outer_slot;
for (domain, type_map) in &self.entries { for (domain, type_map) in &self.entries {
total += domain.capacity(); total += domain.capacity();
// Inner HashMap<QueryType, CacheEntry>: (hash, QueryType, CacheEntry) per slot + control byte
let inner_slot = std::mem::size_of::<u64>() let inner_slot = std::mem::size_of::<u64>()
+ std::mem::size_of::<QueryType>() + std::mem::size_of::<QueryType>()
+ std::mem::size_of::<CacheEntry>() + std::mem::size_of::<CacheEntry>()

View File

@@ -118,7 +118,6 @@ impl OverrideStore {
} }
pub fn heap_bytes(&self) -> usize { pub fn heap_bytes(&self) -> usize {
// HashMap<String, OverrideEntry>: (hash, String, OverrideEntry) per slot + control byte
let per_slot = std::mem::size_of::<u64>() let per_slot = std::mem::size_of::<u64>()
+ std::mem::size_of::<String>() + std::mem::size_of::<String>()
+ std::mem::size_of::<OverrideEntry>() + std::mem::size_of::<OverrideEntry>()

View File

@@ -101,7 +101,6 @@ impl SrttCache {
} }
pub fn heap_bytes(&self) -> usize { pub fn heap_bytes(&self) -> usize {
// HashMap stores (hash, key, value) per slot + 1 control byte
let per_slot = std::mem::size_of::<u64>() let per_slot = std::mem::size_of::<u64>()
+ std::mem::size_of::<IpAddr>() + std::mem::size_of::<IpAddr>()
+ std::mem::size_of::<SrttEntry>() + std::mem::size_of::<SrttEntry>()

View File

@@ -1,7 +1,8 @@
use std::time::Instant; use std::time::Instant;
/// Returns the process resident set size in bytes, or 0 if unavailable. /// Returns the process memory footprint in bytes, or 0 if unavailable.
pub fn process_rss_bytes() -> usize { /// macOS: phys_footprint (matches Activity Monitor). Linux: RSS from /proc/self/statm.
pub fn process_memory_bytes() -> usize {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
{ {
macos_rss() macos_rss()