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