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 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 unsafe { __monitor_ready() };
56 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 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 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 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
215static 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}