From 06c75b9a9461fa0eb40deac058575b8fb80e2fc6 Mon Sep 17 00:00:00 2001 From: Seimizu Joukan Date: Fri, 10 Oct 2025 23:17:45 +0900 Subject: [PATCH] Fix age histogram to track allocation lifetime instead of age at allocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The age histogram was incorrectly updating at allocation time (when age=0), causing all allocations to go into the 0-1min bucket and never being decremented on free. Changes: - BPF: Remove histogram update from update_age_statistics() - BPF: Remove histogram initialization at first allocation - BPF: Add histogram update in uprobe_free() to track actual lifetime - Rust: Add unfreed allocations to 30+ min bucket (conservative estimate) The histogram now shows: - Buckets 0-2: Count of allocations freed within those lifetimes - Bucket 3 (30+ min): Freed allocations + all unfreed allocations This provides accurate lifetime distribution and highlights long-lived/leaked memory by conservatively counting unfreed allocations as 30+ minutes old. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- malloc_free/bpf/malloc_free.bpf.c | 16 +++++++++------- malloc_free/malloc_free.rs | 9 +++++++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/malloc_free/bpf/malloc_free.bpf.c b/malloc_free/bpf/malloc_free.bpf.c index 02adeec..6d1ff4f 100644 --- a/malloc_free/bpf/malloc_free.bpf.c +++ b/malloc_free/bpf/malloc_free.bpf.c @@ -281,9 +281,8 @@ static void update_age_statistics(struct malloc_record *record, record->total_unfreed_count++; record->total_age_sum_ns += alloc_timestamp_ns; - // Update age histogram if needed - u32 age_range = calculate_age_histogram_range(alloc_timestamp_ns); - record->age_histogram[age_range]++; + // Histogram will be updated in uprobe_free() when allocation is freed + // (tracking allocation lifetime, not age at allocation time) } // Helper function for updating age statistics on free (Statistics Mode) @@ -479,13 +478,10 @@ static int handle_alloc_return(void *ctx, void *ptr) new->total_unfreed_count = 1; new->total_age_sum_ns = timestamp_ns; - // Initialize histogram + // Initialize histogram (will be populated as allocations are freed) for (int i = 0; i < 4; i++) { new->age_histogram[i] = 0; } - u32 age_range = - calculate_age_histogram_range(timestamp_ns); - new->age_histogram[age_range] = 1; int ret = bpf_map_update_elem(&malloc_records, &pid, new, BPF_ANY); @@ -632,6 +628,12 @@ int BPF_KPROBE(uprobe_free, void *ptr) u64 current_time = bpf_ktime_get_ns(); update_age_statistics_on_free(entry, current_time); + // Update age histogram based on allocation lifetime + // This tracks how long the allocation lived before being freed + u64 alloc_timestamp = event->alloc_timestamp_ns; + u32 age_range = calculate_age_histogram_range(alloc_timestamp); + entry->age_histogram[age_range]++; + bpf_map_update_elem(&malloc_records, &pid, entry, BPF_ANY); } diff --git a/malloc_free/malloc_free.rs b/malloc_free/malloc_free.rs index 35e26eb..b1618d5 100644 --- a/malloc_free/malloc_free.rs +++ b/malloc_free/malloc_free.rs @@ -1206,6 +1206,15 @@ fn process_events( } } } + + // Add currently unfreed allocations to the 30+ minute bucket + // This is a conservative estimate - if they're still unfreed, they're long-lived + if record.total_unfreed_count > 0 { + for _ in 0..record.total_unfreed_count { + histogram.add_allocation(2700, avg_alloc_size); + // 45 minutes as representative + } + } } } }