Skip to main content

zx/
debuglog.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Type-safe bindings for Zircon resources.
6
7use crate::{BootInstant, Koid, NullableHandle, Resource, Status, ok, sys};
8use bitflags::bitflags;
9use bstr::BStr;
10
11/// An object representing a Zircon 'debuglog' object.
12///
13/// As essentially a subtype of `NullableHandle`, it can be freely interconverted.
14#[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    /// Create a debug log object that allows access to read from and write to the kernel debug
29    /// logging facility.
30    ///
31    /// Wraps the
32    /// [zx_debuglog_create]((https://fuchsia.dev/fuchsia-src/reference/syscalls/debuglog_create.md)
33    /// syscall.
34    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    /// Write a message to the kernel debug log.
43    ///
44    /// Wraps the
45    /// [zx_debuglog_write]((https://fuchsia.dev/fuchsia-src/reference/syscalls/debuglog_write.md)
46    /// syscall.
47    pub fn write(&self, message: &[u8]) -> Result<(), Status> {
48        // TODO(https://fxbug.dev/42108144): Discussion ongoing over whether debuglog levels are supported, so no
49        // options parameter for now.
50        let status = unsafe {
51            sys::zx_debuglog_write(self.raw_handle(), 0, message.as_ptr(), message.len())
52        };
53        ok(status)
54    }
55
56    /// Read a single log record from the kernel debug log.
57    ///
58    /// The DebugLog object must have been created with DebugLogOpts::READABLE, or this will return
59    /// an error.
60    ///
61    /// Wraps the
62    /// [zx_debuglog_read]((https://fuchsia.dev/fuchsia-src/reference/syscalls/debuglog_read.md)
63    /// syscall.
64    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, /* options are unused, must be 0 */
70                std::ptr::from_mut(&mut record).cast::<u8>(),
71                std::mem::size_of_val(&record),
72            )
73        };
74        // On error, zx_debuglog_read returns a negative value. All other values indicate success.
75        if bytes_written < 0 {
76            Err(Status::from_raw(bytes_written))
77        } else {
78            DebugLogRecord::from_raw(&record)
79        }
80    }
81}
82
83/// A record from the kernel's debuglog.
84#[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    /// Convert a raw debuglog record into this typed wrapper.
98    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    /// Returns the message data for the record.
116    pub fn data(&self) -> &BStr {
117        BStr::new(&self.data[..self.datalen as usize])
118    }
119}
120
121/// The severity a kernel log message can have.
122#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
123pub enum DebugLogSeverity {
124    /// Record was written without a known severity.
125    Unknown,
126    /// Trace records include detailed information about program execution.
127    Trace,
128    /// Debug records include development-facing information about program execution.
129    Debug,
130    /// Info records include general information about program execution. (default)
131    Info,
132    /// Warning records include information about potentially problematic operations.
133    Warn,
134    /// Error records include information about failed operations.
135    Error,
136    /// Fatal records convey information about operations which cause a program's termination.
137    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    // expect_message_in_debuglog will read the last 10000 messages in zircon's debuglog, looking
162    // for a message that equals `sent_msg`. If found, the function returns. If the first 10,000
163    // messages doesn't contain `sent_msg`, it will panic.
164    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        // This test and fuchsia-zircon are different crates, so we need
172        // to use from_raw to convert between the zx handle and this test handle.
173        // See https://fxbug.dev/42173139 for details.
174        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                        // We found our log!
181                        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}