use memscope_rs::{
analyzer,
capture::backends::async_tracker::{spawn_tracked, TrackerContext},
global_tracker, init_global_tracking, track, MemScopeResult,
};
use std::collections::HashMap;
use std::time::Instant;
#[derive(Debug)]
struct TaskPayload {
_id: u32,
_name: String,
values: Vec<f64>,
_metadata: HashMap<String, String>,
}
impl TaskPayload {
fn new(id: u32, _name: &str, size: usize) -> Self {
let values: Vec<f64> = (0..size).map(|i| (i as f64).sqrt()).collect();
Self {
_id: id,
_name: String::new(),
values,
_metadata: HashMap::new(),
}
}
}
async fn io_operation(label: &str, size: usize, delay_ms: u64) -> Vec<u8> {
let tracker = global_tracker().unwrap();
let buffer: Vec<u8> = (0..size).map(|i| (i % 256) as u8).collect();
track!(tracker, buffer);
tokio::time::sleep(std::time::Duration::from_millis(delay_ms)).await;
println!(" {label}: read {size} bytes");
buffer
}
async fn worker_task(worker_id: u32, payload_size: usize) -> usize {
let tracker = global_tracker().unwrap();
let _ctx = TrackerContext::capture();
let payload = TaskPayload::new(worker_id, &format!("worker_{worker_id}"), payload_size);
let _ = payload;
let results: Vec<f64> = payload.values.iter().map(|v| v * 2.0).collect();
track!(tracker, results);
let ctx2 = TrackerContext::capture();
println!(
" Worker #{worker_id} task_id={:?} values={} results={} total={}",
ctx2.task_id,
payload_size,
results.len(),
payload_size * 8 + results.len() * 8,
);
payload_size * 8 + results.len() * 8
}
async fn supervisor_task(supervisor_id: u32, num_workers: u32) -> usize {
let _ctx = TrackerContext::capture();
let mut handles = Vec::new();
for w in 0..num_workers {
let handle = spawn_tracked(async move { worker_task(w, 100 + (w as usize) * 50).await });
handles.push(handle);
}
let mut total = 0usize;
for handle in handles {
if let Ok(bytes) = handle.await {
total += bytes;
}
}
println!(" Supervisor #{supervisor_id}: {num_workers} workers completed, {total} total bytes");
total
}
#[tokio::main]
async fn main() -> MemScopeResult<()> {
println!("======================================================================");
println!(" Comprehensive Async Memory Showcase");
println!("======================================================================\n");
let start_time = Instant::now();
init_global_tracking()?;
let tracker = global_tracker()?;
println!("[1/6] Global tracking initialized\n");
println!("[2/6] Capturing tracker context...");
let ctx = TrackerContext::capture();
println!(
" Main thread_id={:?} task_id={:?} tokio_id={:?}\n",
ctx.thread_id, ctx.task_id, ctx.tokio_task_id
);
println!("[3/6] Spawning async tasks...\n");
let mut handles = Vec::new();
handles.push(spawn_tracked(async {
let tracker = global_tracker().unwrap();
let data = vec![0u8; 512];
track!(tracker, data);
println!(" Light task: 512 bytes");
512usize
}));
handles.push(spawn_tracked(async {
let tracker = global_tracker().unwrap();
let mut data = Vec::with_capacity(2000);
for j in 0..2000 {
data.push(format!("payload-{j}-{}", j * j));
}
track!(tracker, data);
let size = data.capacity() * std::mem::size_of::<String>();
println!(" Heavy task: ~{size} bytes (2000 strings)");
size
}));
handles.push(spawn_tracked(async {
let buf = io_operation("IO task", 4096, 15).await;
let sum: u64 = buf.iter().map(|&b| b as u64).sum();
println!(" IO task: checksum={sum}");
buf.len() + 8
}));
handles.push(spawn_tracked(async {
let tracker = global_tracker().unwrap();
let vec_data = vec![1.0f64; 500];
track!(tracker, vec_data);
let string_data = format!("processed {} items via async", 500);
track!(tracker, string_data);
let hash_data: HashMap<u32, String> = (0..100).map(|i| (i, format!("val_{i}"))).collect();
track!(tracker, hash_data);
let total = vec_data.len() * 8 + string_data.len() + hash_data.len() * 32;
println!(" Mixed task: {total} bytes (Vec+f64 + String + HashMap)");
total
}));
handles.push(spawn_tracked(async {
let tracker = global_tracker().unwrap();
let big: Vec<u64> = vec![42u64; 10_000];
track!(tracker, big);
let size = 10_000 * 8;
println!(" Large data task: {size} bytes (10k u64s)");
size
}));
handles.push(spawn_tracked(async { worker_task(101, 200).await }));
handles.push(spawn_tracked(async { worker_task(102, 500).await }));
handles.push(spawn_tracked(async { supervisor_task(1, 3).await }));
let mut grand_total = 0usize;
for (i, handle) in handles.into_iter().enumerate() {
match handle.await {
Ok(bytes) => {
grand_total += bytes;
println!(" handle[{i}] returned {bytes} bytes");
}
Err(e) => println!(" handle[{i}] error: {e}"),
}
}
println!("\n Grand total allocated: ~{grand_total} bytes\n");
println!("[4/6] Async tracker statistics...");
let async_stats = tracker.async_tracker().get_stats();
let profiles = tracker.async_tracker().get_all_profiles();
println!(
" Total tasks: {} Active: {} Allocations: {} Peak mem: {} bytes",
async_stats.total_tasks,
async_stats.active_tasks,
async_stats.total_allocations,
async_stats.peak_memory,
);
println!(" Per-task profiles:");
for p in &profiles {
println!(
" task_id={} name=\"{}\" bytes={} peak={} allocs={} dur={:.1}ms completed={}",
p.task_id,
p.task_name,
p.total_bytes,
p.peak_memory,
p.total_allocations,
p.duration_ns as f64 / 1_000_000.0,
p.is_completed(),
);
}
let zombies = tracker.async_tracker().detect_zombie_tasks();
let (zombie_count, total_tasks) = tracker.async_tracker().zombie_task_stats();
println!("\n Zombie tasks: {zombie_count}/{total_tasks}");
for z in &zombies {
println!(" Zombie task_id={z}");
}
let task_graph = tracker.async_tracker().get_all_profiles();
println!(" Task graph: {} task nodes", task_graph.len());
println!("\n[5/6] Running unified memory analysis...");
let mut az = analyzer(&tracker)?;
let report = az.analyze();
println!(
" Allocations: {} Bytes: {} Peak: {}",
report.stats.allocation_count, report.stats.total_bytes, report.stats.peak_bytes,
);
let leaks = az.detect().leaks();
println!(
" Leaks detected: {} ({leaked} bytes)",
leaks.leak_count,
leaked = leaks.total_leaked_bytes
);
if leaks.leak_count > 0 {
for l in &leaks.leaked_allocations {
println!(
" ptr={:#x} size={} type={:?}",
l.ptr, l.size, l.type_name
);
}
}
let var_rels = az.metrics().summary();
println!(" Relationship types: {}", var_rels.by_type.len());
println!("\n[6/6] Exporting HTML dashboard...");
let output_path = "MemoryAnalysis/async_showcase_new_api";
tracker.export_html(output_path)?;
println!(" ✓ Dashboard exported to {output_path}/dashboard_unified_dashboard.html");
println!(" Open in browser and switch to the task tab to see per-task profiles\n");
let elapsed = start_time.elapsed();
println!("======================================================================");
println!(" Completed in {elapsed:.2?}");
println!(" Total async tasks tracked: {total_tasks}");
println!(" Total tracked bytes: ~{grand_total}");
println!("======================================================================");
Ok(())
}