1use crate::{BootInstant, Koid, NullableHandle, Resource, Status, ok, sys};
8use bitflags::bitflags;
9use bstr::BStr;
10
11#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
15#[repr(transparent)]
16pub struct DebugLog(NullableHandle);
17impl_handle_based!(DebugLog);
18
19bitflags! {
20 #[repr(transparent)]
21 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
22 pub struct DebugLogOpts: u32 {
23 const READABLE = sys::ZX_LOG_FLAG_READABLE;
24 }
25}
26
27impl DebugLog {
28 pub fn create(resource: &Resource, opts: DebugLogOpts) -> Result<DebugLog, Status> {
35 let mut handle = 0;
36 let status =
37 unsafe { sys::zx_debuglog_create(resource.raw_handle(), opts.bits(), &mut handle) };
38 ok(status)?;
39 unsafe { Ok(DebugLog::from(NullableHandle::from_raw(handle))) }
40 }
41
42 pub fn write(&self, message: &[u8]) -> Result<(), Status> {
48 let status = unsafe {
51 sys::zx_debuglog_write(self.raw_handle(), 0, message.as_ptr(), message.len())
52 };
53 ok(status)
54 }
55
56 pub fn read(&self) -> Result<DebugLogRecord, Status> {
65 let mut record = sys::zx_log_record_t::default();
66 let bytes_written = unsafe {
67 sys::zx_debuglog_read(
68 self.raw_handle(),
69 0, std::ptr::from_mut(&mut record).cast::<u8>(),
71 std::mem::size_of_val(&record),
72 )
73 };
74 if bytes_written < 0 {
76 Err(Status::from_raw(bytes_written))
77 } else {
78 DebugLogRecord::from_raw(&record)
79 }
80 }
81}
82
83#[derive(Debug, Copy, Clone, Eq, PartialEq)]
85pub struct DebugLogRecord {
86 pub sequence: u64,
87 pub timestamp: BootInstant,
88 pub severity: DebugLogSeverity,
89 pub pid: Koid,
90 pub tid: Koid,
91 pub flags: u8,
92 data: [u8; sys::ZX_LOG_RECORD_DATA_MAX],
93 datalen: u16,
94}
95
96impl DebugLogRecord {
97 pub fn from_raw(raw: &sys::zx_log_record_t) -> Result<Self, Status> {
99 if raw.datalen <= sys::ZX_LOG_RECORD_DATA_MAX as u16 {
100 Ok(Self {
101 timestamp: BootInstant::from_nanos(raw.timestamp),
102 sequence: raw.sequence,
103 severity: DebugLogSeverity::from_raw(raw.severity),
104 pid: Koid::from_raw(raw.pid),
105 tid: Koid::from_raw(raw.tid),
106 flags: raw.flags,
107 data: raw.data,
108 datalen: raw.datalen,
109 })
110 } else {
111 Err(Status::INTERNAL)
112 }
113 }
114
115 pub fn data(&self) -> &BStr {
117 BStr::new(&self.data[..self.datalen as usize])
118 }
119}
120
121#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
123pub enum DebugLogSeverity {
124 Unknown,
126 Trace,
128 Debug,
130 Info,
132 Warn,
134 Error,
136 Fatal,
138}
139
140impl DebugLogSeverity {
141 fn from_raw(raw: u8) -> Self {
142 match raw {
143 sys::DEBUGLOG_TRACE => Self::Trace,
144 sys::DEBUGLOG_DEBUG => Self::Debug,
145 sys::DEBUGLOG_INFO => Self::Info,
146 sys::DEBUGLOG_WARNING => Self::Warn,
147 sys::DEBUGLOG_ERROR => Self::Error,
148 sys::DEBUGLOG_FATAL => Self::Fatal,
149 _ => Self::Unknown,
150 }
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use crate::{Instant, Signals, cprng_draw};
158 use fidl_fuchsia_kernel as fkernel;
159 use fuchsia_component::client::connect_channel_to_protocol;
160
161 fn expect_message_in_debuglog(sent_msg: String) {
165 use zx::Channel;
166 let (client_end, server_end) = Channel::create();
167 connect_channel_to_protocol::<fkernel::DebuglogResourceMarker>(server_end).unwrap();
168 let service = fkernel::DebuglogResourceSynchronousProxy::new(client_end);
169 let resource =
170 service.get(zx::MonotonicInstant::INFINITE).expect("couldn't get debuglog resource");
171 let resource = unsafe { Resource::from(NullableHandle::from_raw(resource.into_raw())) };
175 let debuglog = DebugLog::create(&resource, DebugLogOpts::READABLE).unwrap();
176 for _ in 0..10000 {
177 match debuglog.read() {
178 Ok(record) => {
179 if record.data() == sent_msg.as_bytes() {
180 return;
182 }
183 }
184 Err(status) if status == Status::SHOULD_WAIT => {
185 debuglog
186 .wait_one(Signals::LOG_READABLE, Instant::INFINITE)
187 .expect("Failed to wait for log readable");
188 continue;
189 }
190 Err(status) => {
191 panic!("Unexpected error from zx_debuglog_read: {}", status);
192 }
193 }
194 }
195 panic!("first 10000 log messages didn't include the one we sent!");
196 }
197
198 #[test]
199 fn read_from_nonreadable() {
200 let resource = Resource::from(NullableHandle::invalid());
201 let debuglog = DebugLog::create(&resource, DebugLogOpts::empty()).unwrap();
202 assert!(debuglog.read().err() == Some(Status::ACCESS_DENIED));
203 }
204
205 #[test]
206 fn write_and_read_back() {
207 let mut bytes = [0; 8];
208 cprng_draw(&mut bytes);
209 let message = format!("log message {:?}", bytes);
210
211 let resource = Resource::from(NullableHandle::invalid());
212 let debuglog = DebugLog::create(&resource, DebugLogOpts::empty()).unwrap();
213 debuglog.write(message.as_bytes()).unwrap();
214 expect_message_in_debuglog(message);
215 }
216}