1use std::fmt::Write;
6use std::os::fd::AsFd;
7use std::sync::LazyLock;
8use zx::{self as zx, AsHandleRef, ObjectType};
9
10static LOGGER: LazyLock<KernelLogger> = LazyLock::new(KernelLogger::new);
11
12pub struct KernelLogger {
14 debuglog: zx::DebugLog,
15}
16
17impl KernelLogger {
18 fn new() -> KernelLogger {
20 let debuglog = fdio::clone_fd(std::io::stdout().as_fd()).expect("get handle from stdout");
21 assert_eq!(debuglog.basic_info().unwrap().object_type, ObjectType::DEBUGLOG);
22 KernelLogger { debuglog: debuglog.into() }
23 }
24
25 pub fn init() {
30 log::set_logger(&*LOGGER).expect("set logger must succeed");
31 log::set_max_level(log::LevelFilter::Info);
32 let previous_hook = std::panic::take_hook();
33 std::panic::set_hook(Box::new(move |info| {
34 log::error!("PANIC {info}");
35 previous_hook(info);
36 }));
37 }
38}
39
40impl log::Log for KernelLogger {
41 fn enabled(&self, metadata: &log::Metadata<'_>) -> bool {
42 metadata.level() <= log::Level::Info
44 }
45
46 fn log(&self, record: &log::Record<'_>) {
47 if self.enabled(record.metadata()) {
48 let msg_buffer = format!("{}", record.args());
49 let mut visitor = StringVisitor(msg_buffer);
50 let _ = record.key_values().visit(&mut visitor);
51 let msg_buffer = visitor.0;
52
53 let msg_prefix = format!("[component_manager] {}: ", record.level());
54
55 let mut msg = msg_buffer.as_str();
57 while msg.len() > 0 {
58 let mut split_point = if let Some(newline_pos) = msg.find('\n') {
61 newline_pos + 1
62 } else {
63 msg.len()
64 };
65 split_point =
66 std::cmp::min(split_point, zx::sys::ZX_LOG_RECORD_DATA_MAX - msg_prefix.len());
67
68 while !msg.is_char_boundary(split_point) {
71 split_point -= 1;
72 }
73
74 let mut msg_to_write = format!("{}{}", msg_prefix, &msg[..split_point]);
78
79 if msg_to_write.chars().last() == Some('\n') {
81 msg_to_write.truncate(msg_to_write.len() - 1);
82 }
83
84 if let Err(_) = self.debuglog.write(msg_to_write.as_bytes()) {
85 }
91 msg = &msg[split_point..];
92 }
93 }
94 }
95
96 fn flush(&self) {}
97}
98
99struct StringVisitor(String);
100
101impl log::kv::VisitSource<'_> for StringVisitor {
102 fn visit_pair(
103 &mut self,
104 key: log::kv::Key<'_>,
105 value: log::kv::Value<'_>,
106 ) -> Result<(), log::kv::Error> {
107 value.visit(StringValueVisitor { buf: &mut self.0, key: key.as_str() })
108 }
109}
110
111struct StringValueVisitor<'a> {
112 buf: &'a mut String,
113 key: &'a str,
114}
115
116impl log::kv::VisitValue<'_> for StringValueVisitor<'_> {
117 fn visit_any(&mut self, value: log::kv::Value<'_>) -> Result<(), log::kv::Error> {
118 write!(self.buf, " {}={}", self.key, value).expect("writing into strings does not fail");
119 Ok(())
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use anyhow::Context;
127 use fidl_fuchsia_boot as fboot;
128 use fuchsia_component::client::connect_channel_to_protocol;
129 use log::{error, info, warn};
130 use rand::Rng;
131 use std::panic;
132 use zx::HandleBased;
133
134 const MAX_INFO_LINE_LEN: usize =
135 zx::sys::ZX_LOG_RECORD_DATA_MAX - "[component_manager] INFO: ".len();
136
137 fn get_readonlylog() -> zx::DebugLog {
138 let (client_end, server_end) = zx::Channel::create();
139 connect_channel_to_protocol::<fboot::ReadOnlyLogMarker>(server_end).unwrap();
140 let service = fboot::ReadOnlyLogSynchronousProxy::new(client_end);
141 let log = service.get(zx::MonotonicInstant::INFINITE).expect("couldn't get read only log");
142 log
143 }
144
145 fn expect_message_in_debuglog(sent_msg: String) {
149 let debuglog = get_readonlylog();
150 for _ in 0..10000 {
151 match debuglog.read() {
152 Ok(record) => {
153 let log = record.data().to_string();
154 if log.starts_with(&sent_msg) {
155 return;
157 }
158 }
159 Err(status) if status == zx::Status::SHOULD_WAIT => {
160 debuglog
161 .wait_handle(zx::Signals::LOG_READABLE, zx::MonotonicInstant::INFINITE)
162 .expect("Failed to wait for log readable");
163 continue;
164 }
165 Err(status) => {
166 panic!("Unexpected error from zx_debuglog_read: {}", status);
167 }
168 }
169 }
170 panic!("first 10000 log messages didn't include the one we sent!");
171 }
172
173 fn init() {
177 const STDOUT_FD: i32 = 1;
178
179 let resource = zx::Resource::from(zx::Handle::invalid());
180 let debuglog = zx::DebugLog::create(&resource, zx::DebugLogOpts::empty())
181 .context("Failed to create debuglog object")
182 .unwrap();
183 fdio::bind_to_fd(debuglog.into_handle(), STDOUT_FD).unwrap();
184
185 KernelLogger::init();
186 }
187
188 fn make_str_with_len(prefix: &str, len: usize) -> String {
189 let mut rng = rand::thread_rng();
190 let mut res = format!("{}{}{}", prefix, rng.gen::<u64>().to_string(), "a".repeat(len));
191 res.truncate(len);
192 res
193 }
194
195 #[test]
196 fn log_info_test() {
197 let mut rng = rand::thread_rng();
198 let logged_value: u64 = rng.gen();
199
200 init();
201 info!("log_test {}", logged_value);
202
203 expect_message_in_debuglog(format!("[component_manager] INFO: log_test {}", logged_value));
204 }
205
206 #[test]
207 fn log_info_newline_test() {
208 let mut rng = rand::thread_rng();
209 let logged_value1: u64 = rng.gen();
210 let logged_value2: u64 = rng.gen();
211
212 init();
213 info!("log_test1 {}\nlog_test2 {}", logged_value1, logged_value2);
214
215 expect_message_in_debuglog(format!(
216 "[component_manager] INFO: log_test1 {}",
217 logged_value1
218 ));
219 expect_message_in_debuglog(format!(
220 "[component_manager] INFO: log_test2 {}",
221 logged_value2
222 ));
223 }
224
225 #[test]
226 fn log_many_newlines_test() {
227 let mut rng = rand::thread_rng();
228 let logged_value1: u64 = rng.gen();
229 let logged_value2: u64 = rng.gen();
230
231 init();
232 info!("\n\nmnl_log_test1 {}\n\nmnl_log_test2 {}\n\n", logged_value1, logged_value2);
233
234 expect_message_in_debuglog(format!(
235 "[component_manager] INFO: mnl_log_test1 {}",
236 logged_value1
237 ));
238 expect_message_in_debuglog(format!(
239 "[component_manager] INFO: mnl_log_test2 {}",
240 logged_value2
241 ));
242 }
243
244 #[test]
245 fn log_one_very_long_line_test() {
246 let line1: String = make_str_with_len("line1:", MAX_INFO_LINE_LEN);
247 let line2: String = make_str_with_len("line2:", MAX_INFO_LINE_LEN);
248 let line3: String = make_str_with_len("line3:", MAX_INFO_LINE_LEN);
249
250 init();
251 info!("{}{}{}", line1, line2, line3);
252
253 expect_message_in_debuglog(format!("[component_manager] INFO: {}", line1));
254 expect_message_in_debuglog(format!("[component_manager] INFO: {}", line2));
255 expect_message_in_debuglog(format!("[component_manager] INFO: {}", line3));
256 }
257
258 #[test]
259 fn log_line_that_would_be_split_without_newline_test() {
260 let line1: String = make_str_with_len("line1:", 128);
261 let line2: String = make_str_with_len("line2:", 128);
262
263 init();
264 info!("{}\n{}", line1, line2);
265
266 expect_message_in_debuglog(format!("[component_manager] INFO: {}", line1));
267 expect_message_in_debuglog(format!("[component_manager] INFO: {}", line2));
268 }
269
270 #[test]
271 fn log_overly_long_line_with_newline_test() {
272 let line1: String = make_str_with_len("line1:", MAX_INFO_LINE_LEN);
273 let line1part2: String = make_str_with_len("line1part2:", 80);
274 let line2: String = make_str_with_len("line2:", 80);
275
276 init();
277 info!("{}{}\n{}", line1, line1part2, line2);
278
279 expect_message_in_debuglog(format!("[component_manager] INFO: {}", line1));
280 expect_message_in_debuglog(format!("[component_manager] INFO: {}", line1part2));
281 expect_message_in_debuglog(format!("[component_manager] INFO: {}", line2));
282 }
283
284 #[test]
285 fn log_pathological_utf8_data() {
286 let line1: String = make_str_with_len("emojiline1:", MAX_INFO_LINE_LEN - 1);
289
290 init();
291 info!("{}😈", line1);
292
293 expect_message_in_debuglog(format!("[component_manager] INFO: {}", line1));
294 expect_message_in_debuglog(format!("[component_manager] INFO: 😈"));
295 }
296
297 #[test]
298 fn log_warn_test() {
299 let mut rng = rand::thread_rng();
300 let logged_value: u64 = rng.gen();
301
302 init();
303 warn!("log_test {}", logged_value);
304
305 expect_message_in_debuglog(format!("[component_manager] WARN: log_test {}", logged_value));
306 }
307
308 #[test]
309 fn log_error_test() {
310 let mut rng = rand::thread_rng();
311 let logged_value: u64 = rng.gen();
312
313 init();
314 error!("log_test {}", logged_value);
315
316 expect_message_in_debuglog(format!("[component_manager] ERROR: log_test {}", logged_value));
317 }
318
319 #[test]
320 #[should_panic(expected = "panic_test")]
321 #[cfg_attr(feature = "variant_asan", ignore)]
323 #[cfg_attr(feature = "variant_hwasan", ignore)]
324 fn log_panic_test() {
325 let mut rng = rand::thread_rng();
326 let logged_value: u64 = rng.gen();
327
328 let old_hook = panic::take_hook();
329 panic::set_hook(Box::new(move |info| {
330 old_hook(info);
333 expect_message_in_debuglog(format!("[component_manager] PANIC: panicked at"));
334 expect_message_in_debuglog(format!("panic_test {logged_value}"));
335 }));
336
337 init();
338 panic!("panic_test {}", logged_value);
339 }
340}