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:
@@ -38,6 +38,21 @@ impl QueryLog {
|
||||
self.entries.push_back(entry);
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.entries.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.entries.is_empty()
|
||||
}
|
||||
|
||||
pub fn heap_bytes(&self) -> usize {
|
||||
self.entries
|
||||
.iter()
|
||||
.map(|e| std::mem::size_of::<QueryLogEntry>() + e.domain.capacity())
|
||||
.sum()
|
||||
}
|
||||
|
||||
pub fn query(&self, filter: &QueryLogFilter) -> Vec<&QueryLogEntry> {
|
||||
self.entries
|
||||
.iter()
|
||||
@@ -77,3 +92,25 @@ pub struct QueryLogFilter {
|
||||
pub since: Option<SystemTime>,
|
||||
pub limit: Option<usize>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn heap_bytes_grows_with_entries() {
|
||||
let mut log = QueryLog::new(100);
|
||||
let empty = log.heap_bytes();
|
||||
log.push(QueryLogEntry {
|
||||
timestamp: SystemTime::now(),
|
||||
src_addr: "127.0.0.1:1234".parse().unwrap(),
|
||||
domain: "example.com".into(),
|
||||
query_type: QueryType::A,
|
||||
path: QueryPath::Forwarded,
|
||||
rescode: ResultCode::NOERROR,
|
||||
latency_us: 500,
|
||||
dnssec: DnssecStatus::Indeterminate,
|
||||
});
|
||||
assert!(log.heap_bytes() > empty);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user