monitor/
main.rs

1#![feature(thread_local)]
2#![feature(iterator_try_collect)]
3#![feature(linkage)]
4
5use std::alloc::GlobalAlloc;
6
7use dynlink::context::NewCompartmentFlags;
8use miette::IntoDiagnostic;
9use monitor_api::{CompartmentFlags, CompartmentHandle, CompartmentLoader};
10use tracing::{debug, info, warn, Level};
11use tracing_subscriber::FmtSubscriber;
12use twizzler_abi::{object::NULLPAGE_SIZE, simple_mutex::Mutex};
13use twizzler_rt_abi::object::MapFlags;
14
15mod dlengine;
16mod gates;
17pub mod init;
18mod mon;
19mod upcall;
20
21pub use monitor_api::MappedObjectAddrs;
22
23extern crate dynlink;
24extern crate twizzler_runtime;
25
26extern "C-unwind" {
27    fn __monitor_ready();
28}
29
30pub fn main() {
31    // For early init, if something breaks, we really want to see everything...
32    std::env::set_var("RUST_BACKTRACE", "full");
33    let subscriber = FmtSubscriber::builder()
34        .with_max_level(Level::INFO)
35        .with_target(false)
36        .without_time()
37        .finish();
38
39    tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
40
41    miette::set_hook(Box::new(|_| {
42        Box::new(miette::NarratableReportHandler::new().with_cause_chain())
43    }))
44    .unwrap();
45
46    debug!("monitor entered, discovering dynlink context");
47    let init =
48        init::bootstrap_dynlink_context().expect("failed to discover initial dynlink context");
49
50    let mon = mon::Monitor::new(init);
51    mon::set_monitor(mon);
52
53    debug!("ok, starting monitor proper");
54    // Safety: the monitor is ready, and so we can set our runtime as ready to use the monitor.
55    unsafe { __monitor_ready() };
56    // Had to wait till now to be able to spawn threads.
57    mon::get_monitor().start_background_threads();
58
59    std::env::set_var("RUST_BACKTRACE", "1");
60    unsafe {
61        twizzler_rt_abi::bindings::twz_rt_set_upcall_handler(Some(
62            crate::upcall::upcall_monitor_handler_entry,
63        ))
64    };
65
66    let main_thread = std::thread::spawn(monitor_init);
67    let _r = main_thread.join().unwrap().map_err(|e| {
68        tracing::error!("{:?}", e);
69    });
70    warn!("monitor main thread exited");
71}
72
73fn monitor_init() -> miette::Result<()> {
74    // If we have monitor tests to run, do so.
75    if let Some(ki_name) = dlengine::get_kernel_init_info()
76        .names()
77        .iter()
78        .find(|iname| iname.name() == "monitor_test_info")
79    {
80        info!("starting monitor tests [{}]", ki_name.name());
81        // Read the monitor test binary name.
82        const MAX_NAMELEN: usize = 0x1000;
83        let info =
84            twizzler_rt_abi::object::twz_rt_map_object(ki_name.id(), MapFlags::READ).unwrap();
85        let test_name_slice =
86            unsafe { core::slice::from_raw_parts(info.start().add(NULLPAGE_SIZE), MAX_NAMELEN) };
87        let first_null = test_name_slice
88            .iter()
89            .position(|x| *x == 0)
90            .unwrap_or(MAX_NAMELEN - 1);
91        let test_name = String::from_utf8_lossy(&test_name_slice[0..first_null]);
92        debug!("monitor test binary: {}", test_name);
93        let Some(mt_id) = dlengine::get_kernel_init_info()
94            .names()
95            .iter()
96            .find(|iname| iname.name() == "montest")
97        else {
98            panic!("failed to find montest binary");
99        };
100
101        if let Some(_ki_name) = dlengine::get_kernel_init_info()
102            .names()
103            .iter()
104            .find(|iname| iname.name() == test_name)
105        {
106            // Load and wait for tests to complete
107            let comp: CompartmentHandle = CompartmentLoader::new(
108                "montest",
109                test_name,
110                mt_id.id(),
111                NewCompartmentFlags::empty(),
112            )
113            .args(&["montest", "--test-threads=1"])
114            .load()
115            .into_diagnostic()?;
116            let mut flags = comp.info().flags;
117            while !flags.contains(CompartmentFlags::EXITED) {
118                flags = comp.wait(flags);
119            }
120        } else {
121            tracing::error!("failed to start monitor tests: {}", ki_name.name());
122        }
123    }
124
125    info!("monitor early init completed, starting init",);
126    let mut args = vec!["init".to_string()];
127    for arg in std::env::args() {
128        args.push(arg);
129    }
130    let Some(init_id) = dlengine::get_kernel_init_info()
131        .names()
132        .iter()
133        .find(|iname| iname.name() == "init")
134    else {
135        panic!("failed to find init");
136    };
137    let comp: CompartmentHandle =
138        CompartmentLoader::new("init", "init", init_id.id(), NewCompartmentFlags::empty())
139            .args(&args)
140            .load()
141            .into_diagnostic()?;
142    let mut flags = comp.info().flags;
143    while !flags.contains(CompartmentFlags::EXITED) {
144        flags = comp.wait(flags);
145    }
146
147    tracing::warn!("init exited");
148
149    Ok(())
150}
151
152struct MonAlloc {
153    track: Mutex<Track>,
154}
155
156struct Track {
157    ips: [usize; 4096],
158    count: [usize; 4096],
159    idx: usize,
160}
161
162#[allow(dead_code)]
163impl Track {
164    const fn new() -> Self {
165        Self {
166            ips: [0; 4096],
167            count: [0; 4096],
168            idx: 0,
169        }
170    }
171
172    fn insert(&mut self, ip: *mut u8) {
173        let addr = ip.addr() + 1;
174        let existing = self.ips.iter().position(|i| *i == addr);
175        if let Some(existing) = existing {
176            self.count[existing] += 1;
177        } else if self.idx < 4096 {
178            self.ips[self.idx] = addr;
179            self.count[self.idx] = 1;
180            self.idx += 1;
181        } else {
182            twizzler_abi::klog_println!("dropping ip {:x}", addr);
183        }
184    }
185
186    fn reset(&mut self) {
187        self.idx = 0;
188        self.ips.fill(0);
189        self.count.fill(0);
190    }
191
192    fn print(&self) {
193        for pair in self.ips[0..self.idx]
194            .iter()
195            .zip(self.count[0..self.idx].iter())
196        {
197            twizzler_abi::klog_println!("==> {:x} {}", pair.0, pair.1);
198        }
199    }
200}
201
202use twizzler_rt_abi::alloc::AllocFlags;
203unsafe impl GlobalAlloc for MonAlloc {
204    #[track_caller]
205    unsafe fn alloc(&self, layout: std::alloc::Layout) -> *mut u8 {
206        twizzler_rt_abi::alloc::twz_rt_malloc(layout, AllocFlags::empty())
207            .unwrap_or(core::ptr::null_mut())
208    }
209
210    unsafe fn dealloc(&self, ptr: *mut u8, layout: std::alloc::Layout) {
211        twizzler_rt_abi::alloc::twz_rt_dealloc(ptr, layout, AllocFlags::empty());
212    }
213}
214
215//#[global_allocator]
216static MA: MonAlloc = MonAlloc {
217    track: Mutex::new(Track::new()),
218};
219
220pub fn print_alloc_stats() {
221    MA.track.lock().print();
222}
223
224pub fn reset_alloc_stats() {
225    MA.track.lock().reset();
226}