1#![feature(error_reporter)]
2
3use clap::Parser;
4use miette::IntoDiagnostic;
5use monitor_api::{CompartmentLoader, NewCompartmentFlags};
6use tracer::TracingState;
7use tracing::Level;
8use twizzler_abi::{
9 syscall::TraceSpec,
10 trace::{
11 CONTEXT_FAULT, CONTEXT_INVALIDATION, CONTEXT_SHOOTDOWN, THREAD_SAMPLE,
12 THREAD_SYSCALL_ENTRY, TraceFlags, TraceKind,
13 },
14};
15
16pub mod stat;
17pub mod tracer;
18
19#[derive(Debug, Clone, clap::Subcommand)]
20pub enum Subcommand {
21 Stat,
22}
23
24#[derive(clap::Args, Clone, Debug)]
25pub struct RunCli {
26 #[arg(long, short, help = "Sample threads.")]
27 pub sample: bool,
28 #[arg(trailing_var_arg = true, allow_hyphen_values = true, hide = true)]
29 pub cmdline: Vec<String>,
30}
31
32#[derive(clap::Parser, Clone, Debug)]
33pub struct Cli {
34 #[clap(subcommand)]
35 pub cmd: Option<Subcommand>,
36 #[clap(long, short, help = "List of events to traces, one per flag.")]
37 pub events: Vec<String>,
38 #[clap(flatten)]
39 pub prog: RunCli,
40}
41
42fn main() -> miette::Result<()> {
43 tracing::subscriber::set_global_default(
44 tracing_subscriber::fmt()
45 .without_time()
46 .with_max_level(Level::DEBUG)
47 .finish(),
48 )
49 .unwrap();
50
51 let cli = Cli::try_parse().into_diagnostic()?;
52
53 let state = run_trace_program(&cli)?;
54
55 match cli.cmd {
56 None | Some(Subcommand::Stat) => {
57 stat::stat(state);
58 }
59 }
60
61 Ok(())
62}
63
64fn run_trace_program(cli: &Cli) -> miette::Result<TracingState> {
65 let name = &cli.prog.cmdline[0];
66 let compname = format!("trace-{}", name);
67
68 let mut comp = CompartmentLoader::new(&compname, name, NewCompartmentFlags::DEBUG);
69 comp.args(&cli.prog.cmdline);
70 let comp = comp.load().into_diagnostic()?;
71
72 tracing::info!("compartment {} loaded, starting tracing monitor", compname);
73
74 let info = comp.info();
75
76 let mut specs: Vec<_> = cli
77 .events
78 .iter()
79 .map(|event| match event.as_str() {
80 "page-faults" | "pf" | "faults" | "page-fault" => TraceSpec {
81 kind: TraceKind::Context,
82 flags: TraceFlags::empty(),
83 enable_events: CONTEXT_FAULT,
84 disable_events: 0,
85 sctx: Some(info.id),
86 mctx: None,
87 thread: None,
88 cpuid: None,
89 extra: 0.into(),
90 },
91 "tlb" | "tlb-shootdowns" | "tlb-shootdown" | "shootdown" => TraceSpec {
92 kind: TraceKind::Context,
93 flags: TraceFlags::empty(),
94 enable_events: CONTEXT_SHOOTDOWN | CONTEXT_INVALIDATION,
95 disable_events: 0,
96 sctx: Some(info.id),
97 mctx: None,
98 thread: None,
99 cpuid: None,
100 extra: 0.into(),
101 },
102 "sys" | "syscall" | "syscalls" => TraceSpec {
103 kind: TraceKind::Thread,
104 flags: TraceFlags::empty(),
105 enable_events: THREAD_SYSCALL_ENTRY,
106 disable_events: 0,
107 sctx: Some(info.id),
108 mctx: None,
109 thread: None,
110 cpuid: None,
111 extra: 0.into(),
112 },
113 _ => panic!("unknown event type: {}", event),
114 })
115 .collect();
116
117 if cli.prog.sample {
118 specs.push(TraceSpec {
119 kind: TraceKind::Thread,
120 flags: TraceFlags::empty(),
121 enable_events: THREAD_SAMPLE,
122 disable_events: 0,
123 sctx: Some(info.id),
124 mctx: None,
125 thread: None,
126 cpuid: None,
127 extra: 0.into(),
128 })
129 }
130
131 let state = tracer::start(cli, comp, specs)?;
132
133 tracing::info!(
134 "disconnected {}: {} bytes of trace data",
135 compname,
136 state.total
137 );
138
139 tracing::info!("counted {} events", state.data().count());
140
141 Ok(state)
142}