1use std::fmt::Write;
5
6use crate::model::Trace;
7use crate::record::{LoopEvent, Origin, Record};
8
9fn ms(us: u64) -> String {
10 format!("{:>10.3}", us as f64 / 1000.0)
11}
12
13pub fn dump(trace: &Trace) -> String {
16 let mut out = String::new();
17 let n = |id| trace.name(id);
18 if let Some(s) = &trace.session {
19 let _ = writeln!(
20 out,
21 "session kui {} · {} · {} · {:.0} Hz ({:.2} ms) · @{} · wall {}",
22 s.kui, s.app, s.platform, s.refresh_hz, s.budget_ms, s.scale, s.wall_ms
23 );
24 }
25 for (id, name) in &trace.tracks {
26 let _ = writeln!(out, "track {} {}", id.0, n(*name));
27 }
28 for r in &trace.records {
29 let line = match r {
30 Record::FrameBegin { t, frame, causes } => format!(
31 "{} frame+ #{frame} {}",
32 ms(*t),
33 causes.iter().map(|c| n(*c)).collect::<Vec<_>>().join(",")
34 ),
35 Record::Holder {
36 frame,
37 what,
38 name,
39 node,
40 } => format!(
41 "{:>10} holder #{frame} {} {}{}",
42 "",
43 n(*what),
44 n(*name),
45 if *node != 0 {
46 format!(" node {node:016x}")
47 } else {
48 String::new()
49 }
50 ),
51 Record::FrameEnd {
52 t,
53 frame,
54 unchanged,
55 nodes,
56 } => format!(
57 "{} frame- #{frame} {nodes} nodes{}",
58 ms(*t),
59 match unchanged {
60 Some(true) => " unchanged",
61 Some(false) => "",
62 None => " (untraced)",
63 }
64 ),
65 Record::Present {
66 t,
67 frame,
68 render,
69 wait,
70 } => format!(
71 "{} present #{frame} render {:.3} wait {:.3}",
72 ms(*t),
73 *render as f64 / 1000.0,
74 *wait as f64 / 1000.0
75 ),
76 Record::Span(s) => format!(
77 "{} span {} [{}] {} {:.3} ms{}{}{}{}",
78 ms(s.start),
79 trace.track_name(s.track),
80 match s.origin {
81 Origin::Kui => "kui".to_string(),
82 Origin::Host => "host".to_string(),
83 Origin::Extension(ns) => n(ns).to_string(),
84 },
85 n(s.name),
86 s.dur as f64 / 1000.0,
87 s.cpu
88 .map_or(String::new(), |c| format!(" cpu {:.3}", c as f64 / 1000.0)),
89 if s.parent != 0 {
90 format!(" in {}", s.parent)
91 } else {
92 String::new()
93 },
94 if s.node != 0 {
95 format!(" node {:016x}", s.node)
96 } else {
97 String::new()
98 },
99 if s.cut { " cut" } else { "" }
100 ),
101 Record::Input {
102 t,
103 kind,
104 os_t,
105 count,
106 until,
107 } => format!(
108 "{} input {}{}{}",
109 ms(*t),
110 n(*kind),
111 if *count > 1 {
112 format!(" ×{count} until {}", ms(*until).trim())
113 } else {
114 String::new()
115 },
116 os_t.map_or(String::new(), |o| format!(" os {}", ms(o).trim()))
117 ),
118 Record::Event { t, kind, node } => {
119 format!("{} event {} node {node:016x}", ms(*t), n(*kind))
120 }
121 Record::Wake { t, name } => format!("{} wake {}", ms(*t), n(*name)),
122 Record::Request { t, why, at } => {
123 format!("{} request {} at {}", ms(*t), n(*why), n(*at))
124 }
125 Record::Loop { t, event } => match event {
126 LoopEvent::Sleep { until } => format!(
127 "{} loop sleep{}",
128 ms(*t),
129 until.map_or(String::new(), |u| format!(" until {}", ms(u).trim()))
130 ),
131 LoopEvent::Woke { by } => format!("{} loop woke {}", ms(*t), n(*by)),
132 LoopEvent::Os { kind } => format!("{} loop os {}", ms(*t), n(*kind)),
133 },
134 Record::Counter {
135 t,
136 frame,
137 name,
138 value,
139 } => format!("{} counter #{frame} {} {value}", ms(*t), n(*name)),
140 Record::Gauge { t, name, value } => {
141 format!("{} gauge {} {value}", ms(*t), n(*name))
142 }
143 Record::Mark { t, track, name } => {
144 format!("{} mark {} {}", ms(*t), trace.track_name(*track), n(*name))
145 }
146 Record::Note { t, text } => format!("{} note {text}", ms(*t)),
147 Record::Placement {
148 t,
149 node,
150 component,
151 answer,
152 why,
153 } => format!(
154 "{} placed {} {}{} node {node:016x}",
155 ms(*t),
156 n(*component),
157 n(*answer),
158 match n(*why) {
159 "" => String::new(),
160 w => format!(" ({w})"),
161 }
162 ),
163 Record::Session(_) | Record::Name { .. } | Record::Track { .. } => continue,
164 };
165 out.push_str(&line);
166 out.push('\n');
167 }
168 out
169}