use crate::event_store::event::{MemoryEvent, MemoryEventType};
use crate::event_store::EventStore;
use std::sync::Arc;
pub struct TimelineQuery {
event_store: Arc<EventStore>,
}
impl TimelineQuery {
pub fn new(event_store: Arc<EventStore>) -> Self {
Self { event_store }
}
pub fn get_events_in_range(&self, start: u64, end: u64) -> Vec<MemoryEvent> {
self.event_store
.snapshot()
.into_iter()
.filter(|e| e.timestamp >= start && e.timestamp < end)
.collect()
}
pub fn get_allocations_in_range(&self, start: u64, end: u64) -> Vec<MemoryEvent> {
self.get_events_in_range(start, end)
.into_iter()
.filter(|e| e.event_type == MemoryEventType::Allocate)
.collect()
}
pub fn get_deallocations_in_range(&self, start: u64, end: u64) -> Vec<MemoryEvent> {
self.get_events_in_range(start, end)
.into_iter()
.filter(|e| e.event_type == MemoryEventType::Deallocate)
.collect()
}
pub fn get_thread_events_in_range(
&self,
thread_id: u64,
start: u64,
end: u64,
) -> Vec<MemoryEvent> {
self.get_events_in_range(start, end)
.into_iter()
.filter(|e| e.thread_id == thread_id)
.collect()
}
pub fn get_memory_usage_over_time(
&self,
start: u64,
end: u64,
interval_ms: u64,
) -> Vec<(u64, usize)> {
let mut result = Vec::new();
let interval_ns = interval_ms * 1_000_000;
let mut all_events: Vec<(u64, MemoryEvent)> = Vec::new();
for event in self.get_events_in_range(start, end) {
all_events.push((event.timestamp, event));
}
all_events.sort_by_key(|(ts, _)| *ts);
let mut current = start;
let mut event_idx = 0;
let mut running_memory: usize = 0;
while current < end {
while event_idx < all_events.len() {
let (ts, event) = &all_events[event_idx];
if *ts > current {
break;
}
match &event.event_type {
MemoryEventType::Allocate => {
running_memory += event.size;
}
MemoryEventType::Deallocate => {
running_memory = running_memory.saturating_sub(event.size);
}
MemoryEventType::Reallocate => {
let old_size = event.old_size.unwrap_or(0);
running_memory = running_memory
.saturating_sub(old_size)
.saturating_add(event.size);
}
_ => {}
}
event_idx += 1;
}
result.push((current, running_memory));
current += interval_ns;
}
result
}
pub fn get_peak_memory_in_range(&self, start: u64, end: u64) -> usize {
let mut all_events: Vec<MemoryEvent> = Vec::new();
for event in self.get_events_in_range(start, end) {
all_events.push(event);
}
all_events.sort_by_key(|e| e.timestamp);
let mut running_memory: usize = 0;
let mut peak_memory: usize = 0;
for event in all_events {
match &event.event_type {
MemoryEventType::Allocate => {
running_memory += event.size;
}
MemoryEventType::Deallocate => {
running_memory = running_memory.saturating_sub(event.size);
}
MemoryEventType::Reallocate => {
let old_size = event.old_size.unwrap_or(0);
running_memory = running_memory
.saturating_sub(old_size)
.saturating_add(event.size);
}
_ => {}
}
peak_memory = peak_memory.max(running_memory);
}
peak_memory
}
pub fn get_event_rate(&self, start: u64, end: u64) -> f64 {
let events = self.get_events_in_range(start, end);
let duration_ns = end.saturating_sub(start) as f64;
if duration_ns > 0.0 {
(events.len() as f64) / (duration_ns / 1_000_000_000.0)
} else {
0.0
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_timeline_query_creation() {
let event_store = Arc::new(EventStore::new());
let query = TimelineQuery::new(event_store);
let events = query.get_events_in_range(0, 1000);
assert!(events.is_empty());
}
#[test]
fn test_get_events_in_range() {
let event_store = Arc::new(EventStore::new());
let event1 = MemoryEvent::allocate(0x1000, 1024, 123);
event_store.record(event1);
let event2 = MemoryEvent::deallocate(0x1000, 1024, 456);
event_store.record(event2);
let query = TimelineQuery::new(event_store);
let events = query.get_events_in_range(0, u64::MAX);
assert_eq!(events.len(), 2);
}
#[test]
fn test_memory_usage_over_time() {
let event_store = Arc::new(EventStore::new());
let event = MemoryEvent::allocate(0x1000, 1024, 123);
event_store.record(event);
let query = TimelineQuery::new(event_store);
let usage = query.get_memory_usage_over_time(0, 1000, 100);
assert!(!usage.is_empty());
}
}