1use std::collections::{BTreeMap, HashMap};
2
3use ndarray_stats::QuantileExt;
4use twizzler_abi::{
5 syscall::ThreadControl,
6 thread::ExecutionState,
7 trace::{
8 CONTEXT_INVALIDATION, CONTEXT_SHOOTDOWN, ContextFaultEvent, FaultFlags, SyscallEntryEvent,
9 THREAD_SAMPLE, THREAD_SYSCALL_ENTRY, ThreadSamplingEvent, TraceKind,
10 },
11};
12
13use crate::tracer::TracingState;
14
15struct PfEvent {
16 data: ContextFaultEvent,
17}
18
19pub fn stat(state: TracingState) {
20 let data = state.data();
21
22 let mut pfs = Vec::new();
23 for entry in data.filter(|p| p.0.kind == TraceKind::Context) {
24 if let Some(data) = entry
25 .1
26 .and_then(|data| data.try_cast::<ContextFaultEvent>(entry.0.event))
27 {
28 let pfe = PfEvent { data: data.data };
29 pfs.push(pfe);
30 }
31 }
32
33 if pfs.len() > 0 {
34 let durations = pfs
35 .iter()
36 .map(|p| p.data.processing_time.as_nanos() as f64)
37 .collect::<ndarray::Array1<_>>();
38
39 let mean = durations.mean().unwrap();
40 let _max = durations.max().unwrap();
41 let _min = durations.min().unwrap();
42 let stddev = durations.std(1.);
43 let total = durations.sum() / 1_000_000_000.;
44
45 println!(
46 "{} pages faults, costing {}s, mean = {:5.5}us, stddev = {:5.5}us",
47 pfs.len(),
48 total,
49 mean / 1000.,
50 stddev / 1000.
51 );
52
53 let num_pager = pfs
54 .iter()
55 .filter(|p| p.data.flags.contains(FaultFlags::PAGER))
56 .count();
57 let num_large = pfs
58 .iter()
59 .filter(|p| p.data.flags.contains(FaultFlags::LARGE))
60 .count();
61 println!("{} used large pages, {} used pager", num_large, num_pager);
62
63 let mut map = HashMap::<_, usize>::new();
64 for pf in pfs {
65 *map.entry(pf.data.obj).or_default() += 1;
66 }
67
68 let mut coll = map.into_iter().collect::<Vec<_>>();
69 coll.sort_by_key(|c| c.1);
70
71 let mut banner = false;
72 for (k, v) in coll.iter().rev() {
73 if !banner {
74 banner = true;
75 println!(" OBJECT COUNT")
76 }
77 println!("{:>37x} {:10}", k.raw(), v);
78 }
79 }
80 let tlbs = state
81 .data()
82 .filter(|p| {
83 p.0.kind == TraceKind::Context
84 && p.0.event & (CONTEXT_INVALIDATION | CONTEXT_SHOOTDOWN) != 0
85 })
86 .collect::<Vec<_>>();
87
88 if tlbs.len() > 0 {
89 let invalidations = tlbs
90 .iter()
91 .filter(|t| t.0.event & CONTEXT_INVALIDATION != 0)
92 .count();
93 let shootdowns = tlbs
94 .iter()
95 .filter(|t| t.0.event & CONTEXT_SHOOTDOWN != 0)
96 .count();
97
98 println!(
99 "collected {} TLB events: {} invalidations, {} shootdowns",
100 tlbs.len(),
101 invalidations,
102 shootdowns
103 );
104 }
105
106 let syscalls = state
107 .data()
108 .filter(|p| p.0.kind == TraceKind::Thread && p.0.event & THREAD_SYSCALL_ENTRY != 0)
109 .collect::<Vec<_>>();
110
111 if syscalls.len() > 0 {
112 let mut map = BTreeMap::<_, BTreeMap<u64, (Option<String>, usize)>>::new();
113
114 for syscall in &syscalls {
115 if let Some(data) = syscall
116 .1
117 .and_then(|data| data.try_cast::<SyscallEntryEvent>(THREAD_SYSCALL_ENTRY))
118 {
119 let entry = match data.data.num {
120 twizzler_abi::syscall::Syscall::ThreadCtrl => map
121 .entry(data.data.num)
122 .or_default()
123 .entry(data.data.args[2])
124 .or_insert_with(|| {
125 (
126 ThreadControl::try_from(data.data.args[2])
127 .ok()
128 .map(|x| format!("{:?}", x)),
129 0,
130 )
131 }),
132 twizzler_abi::syscall::Syscall::ObjectCtrl => map
133 .entry(data.data.num)
134 .or_default()
135 .entry(data.data.args[2])
136 .or_insert_with(|| {
137 (
138 match data.data.args[2] {
139 0 => Some("CreateCommit".to_string()),
140 1 => Some("Delete".to_string()),
141 2 => Some("Sync".to_string()),
142 3 => Some("Preload".to_string()),
143 _ => Some("???".to_string()),
144 },
145 0,
146 )
147 }),
148 twizzler_abi::syscall::Syscall::MapCtrl => map
149 .entry(data.data.num)
150 .or_default()
151 .entry(data.data.args[2])
152 .or_insert_with(|| {
153 (
154 match data.data.args[2] {
155 0 => Some("Sync".to_string()),
156 1 => Some("Discard".to_string()),
157 2 => Some("Invalidate".to_string()),
158 3 => Some("Update".to_string()),
159 _ => Some("???".to_string()),
160 },
161 0,
162 )
163 }),
164 _ => {
165 let entry = map.entry(data.data.num).or_default().entry(0).or_default();
166 entry
167 }
168 };
169 *(&mut entry.1) += 1usize;
170 }
171 }
172
173 println!("collected {} syscalls", syscalls.len(),);
174
175 let mut coll = map.into_iter().collect::<Vec<_>>();
176 coll.sort_by_key(|c| c.1.values().fold(0, |a, v| a + v.1));
177
178 let mut banner = false;
179 for (k, v) in coll.iter().rev() {
180 if !banner {
181 banner = true;
182 println!(" SYSCALL SUBTYPE COUNT")
183 }
184 let sys = format!("{:?}", k);
185
186 let mut coll = v.values().collect::<Vec<_>>();
187 coll.sort_by_key(|c| c.1);
188 for v in coll.iter().rev() {
189 println!(
190 " {:>20} {:>20} {:7}",
191 sys,
192 match v.0 {
193 Some(ref st) => st.as_str(),
194 None => "",
195 },
196 v.1
197 );
198 }
199 }
200 }
201
202 let samples = state
203 .data()
204 .filter_map(|p| {
205 if p.0.kind == TraceKind::Thread && p.0.event & THREAD_SAMPLE != 0 {
206 Some((
207 p.0,
208 p.1.and_then(|d| d.try_cast::<ThreadSamplingEvent>(THREAD_SAMPLE))
209 .map(|d| d.data)?,
210 ))
211 } else {
212 None
213 }
214 })
215 .collect::<Vec<_>>();
216
217 if samples.len() > 0 {
218 println!("collected {} samples", samples.len());
219
220 let mut map = HashMap::<_, usize>::new();
221 for (_head, sample) in samples {
222 if sample.state == ExecutionState::Running {
223 *map.entry(sample.ip).or_default() += 1usize;
224 }
225 }
226
227 let mut coll = map.into_iter().collect::<Vec<_>>();
228 coll.sort_by_key(|x| x.1);
229
230 let mut banner = false;
231 for (ip, count) in coll {
232 if count > 1 {
233 if !banner {
234 banner = true;
235 println!("PROGRAM COUNTER ADDRESS COUNT")
236 }
237 println!(" {:18x} {:7}", ip, count);
238 }
239 }
240 }
241}