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
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}