1#![feature(error_reporter)]
2
3use std::sync::atomic::AtomicU64;
4
5use clap::Parser;
6use miette::IntoDiagnostic;
7use monitor_api::{CompartmentLoader, NewCompartmentFlags};
8use tracer::{BaseWrap, TracingState};
9use tracing::Level;
10use twizzler::object::ObjectBuilder;
11use twizzler_abi::{
12 syscall::TraceSpec,
13 trace::{
14 CONTEXT_FAULT, CONTEXT_INVALIDATION, CONTEXT_SHOOTDOWN, KERNEL_ALLOC, RUNTIME_ALLOC,
15 THREAD_BLOCK, THREAD_CONTEXT_SWITCH, THREAD_MIGRATE, THREAD_RESUME, THREAD_SAMPLE,
16 THREAD_SYSCALL_EXIT, TraceBase, TraceEntryFlags, TraceFlags, TraceKind,
17 },
18};
19
20pub mod stat;
21pub mod tracer;
22
23#[derive(Debug, Clone, clap::Subcommand)]
24pub enum Subcommand {
25 Stat,
26}
27
28#[derive(clap::Args, Clone, Debug)]
29pub struct RunCli {
30 #[arg(long, short, help = "Sample threads.")]
31 pub sample: bool,
32 #[arg(trailing_var_arg = true, allow_hyphen_values = true, hide = true)]
33 pub cmdline: Vec<String>,
34}
35
36#[derive(clap::Parser, Clone, Debug)]
37pub struct Cli {
38 #[clap(subcommand)]
39 pub cmd: Option<Subcommand>,
40 #[clap(long, short, help = "List of events to traces, one per flag.")]
41 pub events: Vec<String>,
42 #[clap(flatten)]
43 pub prog: RunCli,
44}
45
46fn main() -> miette::Result<()> {
47 tracing::subscriber::set_global_default(
48 tracing_subscriber::fmt()
49 .without_time()
50 .with_max_level(Level::DEBUG)
51 .finish(),
52 )
53 .unwrap();
54
55 let cli = Cli::try_parse().into_diagnostic()?;
56
57 let state = run_trace_program(&cli)?;
58
59 match cli.cmd {
60 None | Some(Subcommand::Stat) => {
61 stat::stat(state);
62 }
63 }
64
65 Ok(())
66}
67
68fn run_trace_program(cli: &Cli) -> miette::Result<TracingState> {
69 let name = &cli.prog.cmdline[0];
70 let compname = format!("trace-{}", name);
71
72 let mut rt_trace = None;
73 if cli
74 .events
75 .iter()
76 .find(|s| s.as_str() == "rt" || s.as_str() == "runtime")
77 .is_some()
78 {
79 let obj = ObjectBuilder::default()
80 .build(BaseWrap(TraceBase {
81 end: AtomicU64::new(0),
82 start: 0,
83 }))
84 .into_diagnostic()?;
85 rt_trace = Some(obj);
86 }
87 let mut comp = CompartmentLoader::new(&compname, name, NewCompartmentFlags::DEBUG);
88 comp.args(&cli.prog.cmdline);
89
90 if let Some(ref rt_trace) = rt_trace {
91 let mut env = std::env::vars().collect::<Vec<_>>();
92
93 env.push((
94 "TWZRT_TRACE_OBJECT".to_string(),
95 format!("{:x}", rt_trace.id()),
96 ));
97 comp.env(env.into_iter().map(|(k, v)| format!("{}={}", k, v)));
98 }
99 let comp = comp.load().into_diagnostic()?;
100
101 tracing::info!("compartment {} loaded, starting tracing monitor", compname);
102
103 let info = comp.info();
104
105 let mut specs: Vec<_> = cli
106 .events
107 .iter()
108 .map(|event| match event.as_str() {
109 "page-faults" | "pf" | "faults" | "page-fault" => TraceSpec {
110 kind: TraceKind::Context,
111 flags: TraceFlags::empty(),
112 enable_events: CONTEXT_FAULT,
113 disable_events: 0,
114 sctx: Some(info.id),
115 mctx: None,
116 thread: None,
117 cpuid: None,
118 extra: 0.into(),
119 },
120 "tlb" | "tlb-shootdowns" | "tlb-shootdown" | "shootdown" => TraceSpec {
121 kind: TraceKind::Context,
122 flags: TraceFlags::empty(),
123 enable_events: CONTEXT_SHOOTDOWN | CONTEXT_INVALIDATION,
124 disable_events: 0,
125 sctx: Some(info.id),
126 mctx: None,
127 thread: None,
128 cpuid: None,
129 extra: 0.into(),
130 },
131 "sys" | "syscall" | "syscalls" => TraceSpec {
132 kind: TraceKind::Thread,
133 flags: TraceFlags::empty(),
134 enable_events: THREAD_SYSCALL_EXIT,
135 disable_events: 0,
136 sctx: Some(info.id),
137 mctx: None,
138 thread: None,
139 cpuid: None,
140 extra: 0.into(),
141 },
142 "th" | "thread" | "thread-stats" => TraceSpec {
143 kind: TraceKind::Thread,
144 flags: TraceFlags::empty(),
145 enable_events: THREAD_BLOCK
146 | THREAD_RESUME
147 | THREAD_MIGRATE
148 | THREAD_CONTEXT_SWITCH,
149 disable_events: 0,
150 sctx: Some(info.id),
151 mctx: None,
152 thread: None,
153 cpuid: None,
154 extra: 0.into(),
155 },
156 "rt" | "runtime" => TraceSpec {
157 kind: TraceKind::Runtime,
158 flags: TraceFlags::empty(),
159 enable_events: RUNTIME_ALLOC,
160 disable_events: 0,
161 sctx: Some(info.id),
162 mctx: None,
163 thread: None,
164 cpuid: None,
165 extra: 0.into(),
166 },
167 "kalloc" => TraceSpec {
168 kind: TraceKind::Kernel,
169 flags: TraceFlags::empty(),
170 enable_events: KERNEL_ALLOC,
171 disable_events: 0,
172 sctx: Some(info.id),
173 mctx: None,
174 thread: None,
175 cpuid: None,
176 extra: 0.into(),
177 },
178 _ => panic!("unknown event type: {}", event),
179 })
180 .collect();
181
182 if cli.prog.sample {
183 specs.push(TraceSpec {
184 kind: TraceKind::Thread,
185 flags: TraceFlags::empty(),
186 enable_events: THREAD_SAMPLE,
187 disable_events: 0,
188 sctx: Some(info.id),
189 mctx: None,
190 thread: None,
191 cpuid: None,
192 extra: 0.into(),
193 })
194 }
195
196 let state = tracer::start(cli, comp, specs, rt_trace)?;
197
198 tracing::info!(
199 "disconnected {}: {} bytes of trace data, woke up {} times",
200 compname,
201 state.kernel_source.total + state.user_source.as_ref().map_or(0, |us| us.total),
202 state.nr_wakes
203 );
204
205 let dropped = state
206 .data()
207 .filter(|d| d.0.flags.contains(TraceEntryFlags::DROPPED))
208 .count();
209 tracing::info!(
210 "counted {} events, {} dropped flags",
211 state.data().count(),
212 dropped
213 );
214
215 Ok(state)
216}