archivist_lib/logs/
debuglog.rs1use crate::identity::ComponentIdentity;
8use crate::logs::error::LogsError;
9use crate::logs::stored_message::StoredMessage;
10use fidl::prelude::*;
11use fidl_fuchsia_boot::ReadOnlyLogMarker;
12use fuchsia_async as fasync;
13use fuchsia_component::client::connect_to_protocol;
14use futures::stream::{Stream, unfold};
15use moniker::ExtendedMoniker;
16use std::future::Future;
17use std::sync::{Arc, LazyLock};
18
19const KERNEL_URL: &str = "fuchsia-boot://kernel";
20pub static KERNEL_IDENTITY: LazyLock<Arc<ComponentIdentity>> = LazyLock::new(|| {
21 Arc::new(ComponentIdentity::new(ExtendedMoniker::parse_str("./klog").unwrap(), KERNEL_URL))
22});
23
24pub trait DebugLog {
25 fn read(&self) -> Result<zx::DebugLogRecord, zx::Status>;
28
29 fn ready_signal(&self) -> impl Future<Output = Result<(), zx::Status>> + Send;
31}
32
33pub struct KernelDebugLog {
34 debuglogger: zx::DebugLog,
35}
36
37impl DebugLog for KernelDebugLog {
38 fn read(&self) -> Result<zx::DebugLogRecord, zx::Status> {
39 self.debuglogger.read()
40 }
41
42 async fn ready_signal(&self) -> Result<(), zx::Status> {
43 fasync::OnSignals::new(&self.debuglogger, zx::Signals::LOG_READABLE).await?;
44 Ok(())
45 }
46}
47
48impl KernelDebugLog {
49 pub async fn new() -> Result<Self, LogsError> {
51 let boot_log = connect_to_protocol::<ReadOnlyLogMarker>().map_err(|source| {
52 LogsError::ConnectingToService { protocol: ReadOnlyLogMarker::PROTOCOL_NAME, source }
53 })?;
54 let debuglogger =
55 boot_log.get().await.map_err(|source| LogsError::RetrievingDebugLog { source })?;
56 Ok(KernelDebugLog { debuglogger })
57 }
58}
59
60pub struct DebugLogBridge<K: DebugLog> {
61 debug_log: K,
62 next_sequence_id: u64,
63}
64
65impl<K: DebugLog> DebugLogBridge<K> {
66 pub fn create(debug_log: K) -> Self {
67 DebugLogBridge { debug_log, next_sequence_id: 0 }
68 }
69
70 fn read_log(&mut self) -> Result<StoredMessage, zx::Status> {
71 let record = self.debug_log.read()?;
72 let dropped = record.sequence - self.next_sequence_id;
73 self.next_sequence_id = record.sequence + 1;
74 Ok(StoredMessage::from_debuglog(record, dropped))
75 }
76
77 pub fn existing_logs(&mut self) -> Result<Vec<StoredMessage>, zx::Status> {
78 let mut result = vec![];
79 loop {
80 match self.read_log() {
81 Err(zx::Status::SHOULD_WAIT) => break,
82 Err(err) => return Err(err),
83 Ok(log) => result.push(log),
84 }
85 }
86 Ok(result)
87 }
88
89 pub fn listen(self) -> impl Stream<Item = Result<StoredMessage, zx::Status>> {
90 unfold((true, self), move |(mut is_readable, mut klogger)| async move {
91 loop {
92 if !is_readable {
93 if let Err(e) = klogger.debug_log.ready_signal().await {
94 break Some((Err(e), (is_readable, klogger)));
95 }
96 }
97 is_readable = true;
98 match klogger.read_log() {
99 Err(zx::Status::SHOULD_WAIT) => {
100 is_readable = false;
101 continue;
102 }
103 x => break Some((x, (is_readable, klogger))),
104 }
105 }
106 })
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113 use crate::logs::testing::*;
114 use diagnostics_data::{BuilderArgs, LogsDataBuilder, Severity};
115 use futures::stream::{StreamExt, TryStreamExt};
116
117 #[fuchsia::test]
118 fn logger_existing_logs_test() {
119 let debug_log = TestDebugLog::default();
120 let klog = TestDebugEntry::new("test log".as_bytes());
121 debug_log.enqueue_read_entry(&klog);
122 debug_log.enqueue_read_fail(zx::Status::SHOULD_WAIT);
123 let mut log_bridge = DebugLogBridge::create(debug_log);
124
125 assert_eq!(
126 log_bridge
127 .existing_logs()
128 .unwrap()
129 .into_iter()
130 .map(|m| m.parse(&KERNEL_IDENTITY).unwrap())
131 .collect::<Vec<_>>(),
132 vec![
133 LogsDataBuilder::new(BuilderArgs {
134 timestamp: klog.record.timestamp,
135 component_url: Some(KERNEL_IDENTITY.url.clone()),
136 moniker: KERNEL_IDENTITY.moniker.clone(),
137 severity: Severity::Info,
138 })
139 .set_pid(klog.record.pid.raw_koid())
140 .set_tid(klog.record.tid.raw_koid())
141 .add_tag("klog")
142 .set_message("test log".to_string())
143 .build()
144 ]
145 );
146
147 let debug_log = TestDebugLog::default();
149 let malformed_klog = TestDebugEntry::new(b"\x80");
151 debug_log.enqueue_read_entry(&malformed_klog);
152
153 debug_log.enqueue_read_fail(zx::Status::SHOULD_WAIT);
154 let mut log_bridge = DebugLogBridge::create(debug_log);
155 assert!(!log_bridge.existing_logs().unwrap().is_empty());
156 }
157
158 #[fasync::run_until_stalled(test)]
159 async fn logger_keep_listening_after_exhausting_initial_contents_test() {
160 let debug_log = TestDebugLog::default();
161 debug_log.enqueue_read_entry(&TestDebugEntry::new("test log".as_bytes()));
162 debug_log.enqueue_read_fail(zx::Status::SHOULD_WAIT);
163 debug_log.enqueue_read_entry(&TestDebugEntry::new("second test log".as_bytes()));
164 let log_bridge = DebugLogBridge::create(debug_log);
165 let mut log_stream =
166 Box::pin(log_bridge.listen()).map(|r| r.unwrap().parse(&KERNEL_IDENTITY));
167 let log_message = log_stream.try_next().await.unwrap().unwrap();
168 assert_eq!(log_message.msg().unwrap(), "test log");
169 let log_message = log_stream.try_next().await.unwrap().unwrap();
170 assert_eq!(log_message.msg().unwrap(), "second test log");
171
172 let debug_log = TestDebugLog::default();
174 let malformed_klog = TestDebugEntry::new(b"\x80");
176 debug_log.enqueue_read_entry(&malformed_klog);
177
178 debug_log.enqueue_read_entry(&TestDebugEntry::new("test log".as_bytes()));
179 let log_bridge = DebugLogBridge::create(debug_log);
180 let mut log_stream = Box::pin(log_bridge.listen());
181 let log_message =
182 log_stream.try_next().await.unwrap().unwrap().parse(&KERNEL_IDENTITY).unwrap();
183 assert_eq!(log_message.msg().unwrap(), "�");
184 }
185
186 #[fasync::run_until_stalled(test)]
187 async fn severity_parsed_from_log() {
188 let debug_log = TestDebugLog::default();
189 debug_log.enqueue_read_entry(&TestDebugEntry::new("ERROR: first log".as_bytes()));
190 debug_log.enqueue_read_entry(&TestDebugEntry::new("first log error".as_bytes()));
192 debug_log.enqueue_read_entry(&TestDebugEntry::new("WARNING: second log".as_bytes()));
193 debug_log.enqueue_read_entry(&TestDebugEntry::new("INFO: third log".as_bytes()));
194 debug_log.enqueue_read_entry(&TestDebugEntry::new("fourth log".as_bytes()));
195 debug_log.enqueue_read_entry(&TestDebugEntry::new_with_severity(
196 "ERROR: severity takes precedence over msg when not info".as_bytes(),
197 zx::DebugLogSeverity::Warn,
198 ));
199 let long_padding = (0..100).map(|_| "\u{10FF}").collect::<String>();
206 let long_log = format!("{long_padding}ERROR: fifth log");
207 debug_log.enqueue_read_entry(&TestDebugEntry::new(long_log.as_bytes()));
208
209 let log_bridge = DebugLogBridge::create(debug_log);
210 let mut log_stream =
211 Box::pin(log_bridge.listen()).map(|r| r.unwrap().parse(&KERNEL_IDENTITY));
212
213 let log_message = log_stream.try_next().await.unwrap().unwrap();
214 assert_eq!(log_message.msg().unwrap(), "ERROR: first log");
215 assert_eq!(log_message.metadata.severity, Severity::Error);
216
217 let log_message = log_stream.try_next().await.unwrap().unwrap();
218 assert_eq!(log_message.msg().unwrap(), "first log error");
219 assert_eq!(log_message.metadata.severity, Severity::Info);
220
221 let log_message = log_stream.try_next().await.unwrap().unwrap();
222 assert_eq!(log_message.msg().unwrap(), "WARNING: second log");
223 assert_eq!(log_message.metadata.severity, Severity::Warn);
224
225 let log_message = log_stream.try_next().await.unwrap().unwrap();
226 assert_eq!(log_message.msg().unwrap(), "INFO: third log");
227 assert_eq!(log_message.metadata.severity, Severity::Info);
228
229 let log_message = log_stream.try_next().await.unwrap().unwrap();
230 assert_eq!(log_message.msg().unwrap(), "fourth log");
231 assert_eq!(log_message.metadata.severity, Severity::Info);
232
233 let log_message = log_stream.try_next().await.unwrap().unwrap();
234 assert_eq!(
235 log_message.msg().unwrap(),
236 "ERROR: severity takes precedence over msg when not info"
237 );
238 assert_eq!(log_message.metadata.severity, Severity::Warn);
239
240 }
249}