trace/
stat.rs

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}