#![allow(deprecated)]
use dial9::analysis::analysis_events::{CpuSampleSource, Dial9Event, WorkerId};
use dial9::cpu::CpuProfilingConfig;
use dial9::format::Decoder;
use dial9::{Dial9HandleTokioExt, RecorderPerfExt, TokioAttachOptions};
use dial9::{DiskBuffer, recorder};
use std::time::Duration;
fn burn_cpu(duration: Duration) {
let start = std::time::Instant::now();
let mut x: u64 = 1;
while start.elapsed() < duration {
for _ in 0..1000 {
x = x.wrapping_mul(6364136223846793005).wrapping_add(1);
}
std::hint::black_box(x);
}
}
async fn cpu_heavy_task(id: usize) {
for _ in 0..5 {
burn_cpu(Duration::from_millis(20));
tokio::task::yield_now().await;
}
eprintln!("Task {id} done");
}
fn main() {
let trace_dir = "cpu_profile_trace";
let segment_path = "cpu_profile_trace/trace.0.bin";
let writer = DiskBuffer::builder()
.base_path(trace_dir)
.max_file_size(1024 * 1024 * 20) .max_total_size(1024 * 1024 * 100) .build()
.unwrap();
let recorder = recorder(writer)
.with_cpu_profiling(CpuProfilingConfig::default())
.build();
let mut builder = tokio::runtime::Builder::new_multi_thread();
builder.enable_all().worker_threads(4);
let rt = recorder
.handle()
.attach_tokio_runtime(
builder,
TokioAttachOptions::builder()
.task_tracking_enabled(true)
.build(),
)
.expect("build tokio runtime");
eprintln!("Running workload with CPU profiling at 99 Hz...");
rt.block_on(async {
let tasks: Vec<_> = (0..200).map(|i| tokio::spawn(cpu_heavy_task(i))).collect();
for task in tasks {
let _ = task.await;
}
tokio::time::sleep(Duration::from_millis(500)).await;
});
eprintln!("Waiting for background worker to symbolize trace (up to 30s)...");
drop(rt);
recorder.graceful_shutdown(Duration::from_secs(30));
eprintln!("\n=== Reading trace from {segment_path} ===");
let data = std::fs::read(segment_path).unwrap();
let mut decoder = Decoder::new(&data).unwrap();
let mut cpu_samples = 0usize;
let mut polls = 0usize;
let mut samples_by_worker: std::collections::HashMap<WorkerId, usize> =
std::collections::HashMap::new();
decoder
.for_each_event(|raw| {
let ev: Dial9Event = raw.deserialize().expect("deserialize");
match &ev {
Dial9Event::CpuSampleEvent(e) if e.source == CpuSampleSource::CpuProfile => {
cpu_samples += 1;
*samples_by_worker.entry(e.worker_id).or_default() += 1;
if cpu_samples <= 10 {
eprintln!(
" CpuSample: worker={} t={}ns source={:?} frames={}",
e.worker_id,
e.timestamp_ns,
e.source,
e.callchain.len()
);
for (i, addr) in e.callchain.iter().take(8).enumerate() {
eprintln!(" [{i}] {addr:#x}");
}
}
}
Dial9Event::PollStartEvent(_) => polls += 1,
_ => {}
}
})
.unwrap();
eprintln!("\nPoll starts: {polls}");
eprintln!("CPU samples: {cpu_samples}");
for (worker, count) in &samples_by_worker {
eprintln!(" worker {worker}: {count} samples");
}
if cpu_samples == 0 {
eprintln!("\nNo CPU samples collected! Check:");
eprintln!(" - perf_event_paranoid: cat /proc/sys/kernel/perf_event_paranoid");
eprintln!(" - frame pointers: RUSTFLAGS=\"-C force-frame-pointers=yes\"");
}
}