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