feat: add memory footprint to /stats and dashboard (#26)
* feat: add memory footprint to /stats and dashboard Per-structure heap estimation (cache, blocklist, query log, SRTT, overrides) with process RSS via mach_task_basic_info / sysconf. Dashboard gets a 6th stat card and a sidebar breakdown panel with stacked bar visualization. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use phys_footprint on macOS to match Activity Monitor Switch from MACH_TASK_BASIC_INFO (resident_size) to TASK_VM_INFO (phys_footprint) which matches Activity Monitor's Memory column. Also: capacity-aware heap estimation, entry counts in memory payload, heap_bytes tests for all stores. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: remove redundant fields and fix naming in memory stats Remove duplicate entry counts from MemoryStats (already in parent StatsResponse), rename process_rss_bytes to process_memory_bytes to match macOS phys_footprint semantics, drop restating comments. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -183,6 +183,15 @@ impl BlocklistStore {
|
||||
self.allowlist.iter().cloned().collect()
|
||||
}
|
||||
|
||||
pub fn heap_bytes(&self) -> usize {
|
||||
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_heap: usize = self.domains.iter().map(|d| d.capacity()).sum();
|
||||
let allow_table = self.allowlist.capacity() * per_slot_overhead;
|
||||
let allow_heap: usize = self.allowlist.iter().map(|d| d.capacity()).sum();
|
||||
domains_table + domains_heap + allow_table + allow_heap
|
||||
}
|
||||
|
||||
pub fn stats(&self) -> BlocklistStats {
|
||||
BlocklistStats {
|
||||
enabled: self.is_enabled(),
|
||||
@@ -234,6 +243,23 @@ pub fn parse_blocklist(text: &str) -> HashSet<String> {
|
||||
domains
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn heap_bytes_grows_with_domains() {
|
||||
let mut store = BlocklistStore::new();
|
||||
let empty = store.heap_bytes();
|
||||
let domains: HashSet<String> = ["example.com", "example.org", "test.net"]
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
store.swap_domains(domains, vec![]);
|
||||
assert!(store.heap_bytes() > empty);
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn download_blocklists(lists: &[String]) -> Vec<(String, String)> {
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(30))
|
||||
|
||||
Reference in New Issue
Block a user