|
1 | 1 | use dial9_tokio_telemetry::telemetry::{ |
2 | | - TraceReader, analyze_trace, detect_idle_workers, print_analysis, |
| 2 | + TelemetryEvent, TraceReader, analyze_trace, compute_wake_to_poll_delays, detect_idle_workers, |
| 3 | + print_analysis, |
3 | 4 | }; |
| 5 | +use std::collections::HashMap; |
4 | 6 | use std::env; |
5 | 7 |
|
6 | 8 | fn main() { |
@@ -29,6 +31,127 @@ fn main() { |
29 | 31 |
|
30 | 32 | println!("\n=== Idle Worker Detection ==="); |
31 | 33 | let idle_periods = detect_idle_workers(&events); |
| 34 | + |
| 35 | + let delays = compute_wake_to_poll_delays(&events); |
| 36 | + if !delays.is_empty() { |
| 37 | + let p50 = delays[delays.len() * 50 / 100]; |
| 38 | + let p99 = delays[delays.len() * 99 / 100]; |
| 39 | + let p999 = delays[delays.len() * 999 / 1000]; |
| 40 | + let max = *delays.last().unwrap(); |
| 41 | + println!("\n=== Wake→Poll Delays ({} samples) ===", delays.len()); |
| 42 | + println!( |
| 43 | + " p50: {:.1}µs, p99: {:.1}µs, p99.9: {:.1}µs, max: {:.1}µs", |
| 44 | + p50 as f64 / 1000.0, |
| 45 | + p99 as f64 / 1000.0, |
| 46 | + p999 as f64 / 1000.0, |
| 47 | + max as f64 / 1000.0, |
| 48 | + ); |
| 49 | + } |
| 50 | + |
| 51 | + // Build task_id → spawn_loc_id from PollStart events (more complete than TaskSpawn alone) |
| 52 | + let mut task_locs: HashMap<u32, u16> = HashMap::new(); |
| 53 | + for e in &events { |
| 54 | + if let TelemetryEvent::PollStart { |
| 55 | + task_id, |
| 56 | + spawn_loc_id, |
| 57 | + .. |
| 58 | + } = e |
| 59 | + { |
| 60 | + task_locs |
| 61 | + .entry(task_id.to_u32()) |
| 62 | + .or_insert(spawn_loc_id.as_u16()); |
| 63 | + } |
| 64 | + } |
| 65 | + // Also include TaskSpawn mappings from the reader |
| 66 | + for (task_id, spawn_loc_id) in &reader.task_spawn_locs { |
| 67 | + task_locs |
| 68 | + .entry(task_id.to_u32()) |
| 69 | + .or_insert(spawn_loc_id.as_u16()); |
| 70 | + } |
| 71 | + |
| 72 | + // Count wakes by waker spawn location |
| 73 | + let mut wakes_by_loc: HashMap<Option<&str>, usize> = HashMap::new(); |
| 74 | + let mut resolved = 0usize; |
| 75 | + let mut unresolved = 0usize; |
| 76 | + for e in &events { |
| 77 | + if let TelemetryEvent::WakeEvent { waker_task_id, .. } = e { |
| 78 | + let id = waker_task_id.to_u32(); |
| 79 | + if id == 0 { |
| 80 | + *wakes_by_loc.entry(Some("<non-task context>")).or_default() += 1; |
| 81 | + resolved += 1; |
| 82 | + } else if let Some(loc_id) = task_locs.get(&id) { |
| 83 | + let loc = reader |
| 84 | + .spawn_locations |
| 85 | + .get(&dial9_tokio_telemetry::telemetry::SpawnLocationId::from_u16(*loc_id)); |
| 86 | + *wakes_by_loc.entry(loc.map(|s| s.as_str())).or_default() += 1; |
| 87 | + resolved += 1; |
| 88 | + } else { |
| 89 | + unresolved += 1; |
| 90 | + } |
| 91 | + } |
| 92 | + } |
| 93 | + if resolved + unresolved > 0 { |
| 94 | + println!( |
| 95 | + "\n=== Waker Identity ({} resolved, {} unresolved of {} total tasks in trace) ===", |
| 96 | + resolved, |
| 97 | + unresolved, |
| 98 | + task_locs.len() |
| 99 | + ); |
| 100 | + |
| 101 | + // Debug: show some unresolved waker task IDs vs known task IDs |
| 102 | + let mut unresolved_ids: HashMap<u32, usize> = HashMap::new(); |
| 103 | + for e in &events { |
| 104 | + if let TelemetryEvent::WakeEvent { waker_task_id, .. } = e { |
| 105 | + let id = waker_task_id.to_u32(); |
| 106 | + if id != 0 && !task_locs.contains_key(&id) { |
| 107 | + *unresolved_ids.entry(id).or_default() += 1; |
| 108 | + } |
| 109 | + } |
| 110 | + } |
| 111 | + let mut top_unresolved: Vec<_> = unresolved_ids.into_iter().collect(); |
| 112 | + top_unresolved.sort_by(|a, b| b.1.cmp(&a.1)); |
| 113 | + println!(" Top unresolved waker IDs:"); |
| 114 | + for (id, count) in top_unresolved.iter().take(5) { |
| 115 | + println!(" 0x{:08x} ({}) — {} wakes", id, id, count); |
| 116 | + } |
| 117 | + println!(" Known task IDs (sample):"); |
| 118 | + for (id, loc_id) in task_locs.iter().take(5) { |
| 119 | + let loc = reader |
| 120 | + .spawn_locations |
| 121 | + .get(&dial9_tokio_telemetry::telemetry::SpawnLocationId::from_u16(*loc_id)); |
| 122 | + println!( |
| 123 | + " 0x{:08x} ({}) — {}", |
| 124 | + id, |
| 125 | + id, |
| 126 | + loc.map(|s| s.as_str()).unwrap_or("?") |
| 127 | + ); |
| 128 | + } |
| 129 | + println!( |
| 130 | + " task_spawn_locs from reader: {} entries", |
| 131 | + reader.task_spawn_locs.len() |
| 132 | + ); |
| 133 | + println!(" task_locs from PollStart: {} entries", task_locs.len()); |
| 134 | + // Check: are the unresolved IDs in task_spawn_locs? |
| 135 | + for (id, count) in top_unresolved.iter().take(5) { |
| 136 | + let in_spawn = reader |
| 137 | + .task_spawn_locs |
| 138 | + .contains_key(&dial9_tokio_telemetry::telemetry::TaskId::from_u32(*id)); |
| 139 | + println!( |
| 140 | + " 0x{:08x}: in task_spawn_locs={}, in task_locs={}, wakes={}", |
| 141 | + id, |
| 142 | + in_spawn, |
| 143 | + task_locs.contains_key(id), |
| 144 | + count |
| 145 | + ); |
| 146 | + } |
| 147 | + let mut by_count: Vec<_> = wakes_by_loc.into_iter().collect(); |
| 148 | + by_count.sort_by(|a, b| b.1.cmp(&a.1)); |
| 149 | + for (loc, count) in &by_count { |
| 150 | + let name = loc.unwrap_or("<unknown>"); |
| 151 | + println!(" {:>8} wakes from {}", count, name); |
| 152 | + } |
| 153 | + } |
| 154 | + |
32 | 155 | if idle_periods.is_empty() { |
33 | 156 | println!("No significant idle periods detected with work in queue"); |
34 | 157 | } else { |
|
0 commit comments