| // Copyright 2026 The Fuchsia Authors |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #![no_std] |
| |
| use object_constants_rs as object_constants; |
| use zx_status::Status; |
| |
| pub use debuglog_types::{ZX_LOG_FLAGS_MASK, dlog_header_t, dlog_record_t}; |
| pub const DLOG_MAX_RECORD: usize = debuglog_types::DLOG_MAX_RECORD as usize; |
| pub const DLOG_MAX_DATA: usize = debuglog_types::DLOG_MAX_DATA as usize; |
| |
| #[allow(improper_ctypes)] |
| unsafe extern "C" { |
| fn cpp_dlog_shutdown(deadline: platform_rs::InstantMono) -> i32; |
| fn cpp_dlog_write(severity: u32, flags: u32, ptr: *const u8, len: usize) -> i32; |
| fn cpp_dlog_reader_init( |
| reader: *mut DlogReaderStorage, |
| notify: unsafe extern "C" fn(*mut core::ffi::c_void), |
| cookie: *mut core::ffi::c_void, |
| ); |
| fn cpp_dlog_reader_disconnect(reader: *mut DlogReaderStorage); |
| fn cpp_dlog_reader_read( |
| reader: *mut DlogReaderStorage, |
| flags: u32, |
| record: *mut dlog_record_t, |
| actual: *mut usize, |
| ) -> i32; |
| } |
| |
| /// Shutdown the debuglog subsystem. |
| /// |
| /// Blocks, waiting up to |deadline|, for dlog threads to terminate. |
| pub fn dlog_shutdown(deadline: platform_rs::InstantMono) -> Result<(), Status> { |
| // SAFETY: FFI call to C++ dlog shutdown. |
| Status::ok(unsafe { cpp_dlog_shutdown(deadline) }) |
| } |
| |
| /// Writes `bytes` to debuglog. |
| /// |
| /// Returns `Status::BAD_STATE` if the dlog has already started shutting down. |
| pub fn dlog_write(severity: u32, flags: u32, bytes: &[u8]) -> Result<(), Status> { |
| // SAFETY: `bytes.as_ptr()` points to `bytes.len()` initialized bytes. |
| Status::ok(unsafe { cpp_dlog_write(severity, flags, bytes.as_ptr(), bytes.len()) }) |
| } |
| |
| #[repr(C, align(8))] |
| pub struct DlogReaderStorage { |
| inner: zr::OpaqueBytes<{ object_constants::kDlogReaderStorageSize }>, |
| _pinned: core::marker::PhantomPinned, |
| } |
| |
| zr::static_assert_size_and_align!( |
| DlogReaderStorage, |
| object_constants::kDlogReaderStorageSize, |
| object_constants::kDlogReaderStorageAlign, |
| ); |
| |
| impl DlogReaderStorage { |
| /// Creates a `PinInit` initializer for `DlogReaderStorage`. |
| /// |
| /// Since `DlogReader`s typically capture containing objects via `cookie`, they |
| /// use 2-phase initialization to avoid races in the construction of the |
| /// `DlogReader` and the containing object. |
| /// |
| /// # Safety |
| /// |
| /// The caller must ensure `cookie` is valid for `notify`. |
| pub unsafe fn init( |
| flags: u32, |
| notify: unsafe extern "C" fn(*mut core::ffi::c_void), |
| cookie: *mut core::ffi::c_void, |
| ) -> impl pin_init::PinInit<Self, core::convert::Infallible> { |
| // SAFETY: `slot` is valid memory for `DlogReaderStorage`. |
| unsafe { |
| pin_init::pin_init_from_closure(move |slot: *mut Self| { |
| let reader = &mut *slot; |
| *reader = Self { |
| inner: zr::OpaqueBytes::new([0; object_constants::kDlogReaderStorageSize]), |
| _pinned: core::marker::PhantomPinned, |
| }; |
| if (flags & zx_types::ZX_LOG_FLAG_READABLE) != 0 { |
| // SAFETY: `reader` is pinned and points to valid storage for DlogReader. |
| cpp_dlog_reader_init(reader, notify, cookie); |
| } |
| Ok(()) |
| }) |
| } |
| } |
| |
| /// Safely disconnects DlogReader. |
| /// |
| /// DlogReaders must be manually stopped via `disconnect` to avoid reentrancy |
| /// issues in the DlogReader and its containing object. |
| /// |
| /// This method must be called before the `DlogReaderStorage` is dropped if it |
| /// was initialized. |
| pub fn disconnect(self: core::pin::Pin<&mut Self>) { |
| // SAFETY: `self` is pinned and initialized. |
| unsafe { cpp_dlog_reader_disconnect(self.get_unchecked_mut()) } |
| } |
| |
| /// Reads one record from debuglog. |
| /// |
| /// Upon success, returns `Status::OK` and sets `actual` to the record's size. |
| /// Returns `Status::SHOULD_WAIT` if there are no records available to read. |
| pub fn read( |
| self: core::pin::Pin<&mut Self>, |
| flags: u32, |
| record: &mut dlog_record_t, |
| actual: &mut usize, |
| ) -> Status { |
| // SAFETY: `self` is pinned and initialized, `record` and `actual` are valid. |
| Status::from_raw(unsafe { |
| cpp_dlog_reader_read(self.get_unchecked_mut(), flags, record, actual) |
| }) |
| } |
| } |