Skip to main content

debuglog_rs/
debuglog.rs

1// Copyright 2026 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7#![no_std]
8
9use object_constants_rs as object_constants;
10use zx_status::Status;
11
12pub use debuglog_types::{ZX_LOG_FLAGS_MASK, dlog_header_t, dlog_record_t};
13pub const DLOG_MAX_RECORD: usize = debuglog_types::DLOG_MAX_RECORD as usize;
14pub const DLOG_MAX_DATA: usize = debuglog_types::DLOG_MAX_DATA as usize;
15
16#[allow(improper_ctypes)]
17unsafe extern "C" {
18    fn cpp_dlog_shutdown(deadline: platform_rs::InstantMono) -> i32;
19    fn cpp_dlog_write(severity: u32, flags: u32, ptr: *const u8, len: usize) -> i32;
20    fn cpp_dlog_reader_init(
21        reader: *mut DlogReaderStorage,
22        notify: unsafe extern "C" fn(*mut core::ffi::c_void),
23        cookie: *mut core::ffi::c_void,
24    );
25    fn cpp_dlog_reader_disconnect(reader: *mut DlogReaderStorage);
26    fn cpp_dlog_reader_read(
27        reader: *mut DlogReaderStorage,
28        flags: u32,
29        record: *mut dlog_record_t,
30        actual: *mut usize,
31    ) -> i32;
32}
33
34/// Shutdown the debuglog subsystem.
35///
36/// Blocks, waiting up to |deadline|, for dlog threads to terminate.
37pub fn dlog_shutdown(deadline: platform_rs::InstantMono) -> Result<(), Status> {
38    // SAFETY: FFI call to C++ dlog shutdown.
39    Status::ok(unsafe { cpp_dlog_shutdown(deadline) })
40}
41
42/// Writes `bytes` to debuglog.
43///
44/// Returns `Status::BAD_STATE` if the dlog has already started shutting down.
45pub fn dlog_write(severity: u32, flags: u32, bytes: &[u8]) -> Result<(), Status> {
46    // SAFETY: `bytes.as_ptr()` points to `bytes.len()` initialized bytes.
47    Status::ok(unsafe { cpp_dlog_write(severity, flags, bytes.as_ptr(), bytes.len()) })
48}
49
50#[repr(C, align(8))]
51pub struct DlogReaderStorage {
52    inner: zr::OpaqueBytes<{ object_constants::kDlogReaderStorageSize }>,
53    _pinned: core::marker::PhantomPinned,
54}
55
56zr::static_assert_size_and_align!(
57    DlogReaderStorage,
58    object_constants::kDlogReaderStorageSize,
59    object_constants::kDlogReaderStorageAlign,
60);
61
62impl DlogReaderStorage {
63    /// Creates a `PinInit` initializer for `DlogReaderStorage`.
64    ///
65    /// Since `DlogReader`s typically capture containing objects via `cookie`, they
66    /// use 2-phase initialization to avoid races in the construction of the
67    /// `DlogReader` and the containing object.
68    ///
69    /// # Safety
70    ///
71    /// The caller must ensure `cookie` is valid for `notify`.
72    pub unsafe fn init(
73        flags: u32,
74        notify: unsafe extern "C" fn(*mut core::ffi::c_void),
75        cookie: *mut core::ffi::c_void,
76    ) -> impl pin_init::PinInit<Self, core::convert::Infallible> {
77        // SAFETY: `slot` is valid memory for `DlogReaderStorage`.
78        unsafe {
79            pin_init::pin_init_from_closure(move |slot: *mut Self| {
80                let reader = &mut *slot;
81                *reader = Self {
82                    inner: zr::OpaqueBytes::new([0; object_constants::kDlogReaderStorageSize]),
83                    _pinned: core::marker::PhantomPinned,
84                };
85                if (flags & zx_types::ZX_LOG_FLAG_READABLE) != 0 {
86                    // SAFETY: `reader` is pinned and points to valid storage for DlogReader.
87                    cpp_dlog_reader_init(reader, notify, cookie);
88                }
89                Ok(())
90            })
91        }
92    }
93
94    /// Safely disconnects DlogReader.
95    ///
96    /// DlogReaders must be manually stopped via `disconnect` to avoid reentrancy
97    /// issues in the DlogReader and its containing object.
98    ///
99    /// This method must be called before the `DlogReaderStorage` is dropped if it
100    /// was initialized.
101    pub fn disconnect(self: core::pin::Pin<&mut Self>) {
102        // SAFETY: `self` is pinned and initialized.
103        unsafe { cpp_dlog_reader_disconnect(self.get_unchecked_mut()) }
104    }
105
106    /// Reads one record from debuglog.
107    ///
108    /// Upon success, returns `Status::OK` and sets `actual` to the record's size.
109    /// Returns `Status::SHOULD_WAIT` if there are no records available to read.
110    pub fn read(
111        self: core::pin::Pin<&mut Self>,
112        flags: u32,
113        record: &mut dlog_record_t,
114        actual: &mut usize,
115    ) -> Status {
116        // SAFETY: `self` is pinned and initialized, `record` and `actual` are valid.
117        Status::from_raw(unsafe {
118            cpp_dlog_reader_read(self.get_unchecked_mut(), flags, record, actual)
119        })
120    }
121}