trace/
main.rs

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}