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
88 let id =
89 twizzler_rt_abi::fd::twz_rt_resolve_name(Default::default(), name).into_diagnostic()?;
90 let mut comp = CompartmentLoader::new(&compname, name, id, NewCompartmentFlags::DEBUG);
91 comp.args(&cli.prog.cmdline);
92
93 if let Some(ref rt_trace) = rt_trace {
94 let mut env = std::env::vars().collect::<Vec<_>>();
95
96 env.push((
97 "TWZRT_TRACE_OBJECT".to_string(),
98 format!("{:x}", rt_trace.id()),
99 ));
100 comp.env(env.into_iter().map(|(k, v)| format!("{}={}", k, v)));
101 }
102 let comp = comp.load().into_diagnostic()?;
103
104 tracing::info!("compartment {} loaded, starting tracing monitor", compname);
105
106 let info = comp.info();
107
108 let mut specs: Vec<_> = cli
109 .events
110 .iter()
111 .map(|event| match event.as_str() {
112 "page-faults" | "pf" | "faults" | "page-fault" => TraceSpec {
113 kind: TraceKind::Context,
114 flags: TraceFlags::empty(),
115 enable_events: CONTEXT_FAULT,
116 disable_events: 0,
117 sctx: Some(info.id),
118 mctx: None,
119 thread: None,
120 cpuid: None,
121 extra: 0.into(),
122 },
123 "tlb" | "tlb-shootdowns" | "tlb-shootdown" | "shootdown" => TraceSpec {
124 kind: TraceKind::Context,
125 flags: TraceFlags::empty(),
126 enable_events: CONTEXT_SHOOTDOWN | CONTEXT_INVALIDATION,
127 disable_events: 0,
128 sctx: Some(info.id),
129 mctx: None,
130 thread: None,
131 cpuid: None,
132 extra: 0.into(),
133 },
134 "sys" | "syscall" | "syscalls" => TraceSpec {
135 kind: TraceKind::Thread,
136 flags: TraceFlags::empty(),
137 enable_events: THREAD_SYSCALL_EXIT,
138 disable_events: 0,
139 sctx: Some(info.id),
140 mctx: None,
141 thread: None,
142 cpuid: None,
143 extra: 0.into(),
144 },
145 "th" | "thread" | "thread-stats" => TraceSpec {
146 kind: TraceKind::Thread,
147 flags: TraceFlags::empty(),
148 enable_events: THREAD_BLOCK
149 | THREAD_RESUME
150 | THREAD_MIGRATE
151 | THREAD_CONTEXT_SWITCH,
152 disable_events: 0,
153 sctx: Some(info.id),
154 mctx: None,
155 thread: None,
156 cpuid: None,
157 extra: 0.into(),
158 },
159 "rt" | "runtime" => TraceSpec {
160 kind: TraceKind::Runtime,
161 flags: TraceFlags::empty(),
162 enable_events: RUNTIME_ALLOC,
163 disable_events: 0,
164 sctx: Some(info.id),
165 mctx: None,
166 thread: None,
167 cpuid: None,
168 extra: 0.into(),
169 },
170 "kalloc" => TraceSpec {
171 kind: TraceKind::Kernel,
172 flags: TraceFlags::empty(),
173 enable_events: KERNEL_ALLOC,
174 disable_events: 0,
175 sctx: Some(info.id),
176 mctx: None,
177 thread: None,
178 cpuid: None,
179 extra: 0.into(),
180 },
181 _ => panic!("unknown event type: {}", event),
182 })
183 .collect();
184
185 if cli.prog.sample {
186 specs.push(TraceSpec {
187 kind: TraceKind::Thread,
188 flags: TraceFlags::empty(),
189 enable_events: THREAD_SAMPLE,
190 disable_events: 0,
191 sctx: Some(info.id),
192 mctx: None,
193 thread: None,
194 cpuid: None,
195 extra: 0.into(),
196 })
197 }
198
199 let state = tracer::start(cli, comp, specs, rt_trace)?;
200
201 tracing::info!(
202 "disconnected {}: {} bytes of trace data, woke up {} times",
203 compname,
204 state.kernel_source.total + state.user_source.as_ref().map_or(0, |us| us.total),
205 state.nr_wakes
206 );
207
208 let dropped = state
209 .data()
210 .filter(|d| d.0.flags.contains(TraceEntryFlags::DROPPED))
211 .count();
212 tracing::info!(
213 "counted {} events, {} dropped flags",
214 state.data().count(),
215 dropped
216 );
217
218 Ok(state)
219}