blob: 76215d8c290c8ff24bbe0284254b9cc5c8df9a4a [file] [log] [blame] [edit]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Type-safe bindings for Zircon resources.
use crate::{
ok, sys, AsHandleRef, BootInstant, Handle, HandleBased, HandleRef, Koid, Resource, Status,
};
use bitflags::bitflags;
use bstr::BStr;
/// An object representing a Zircon 'debuglog' object.
///
/// As essentially a subtype of `Handle`, it can be freely interconverted.
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct DebugLog(Handle);
impl_handle_based!(DebugLog);
bitflags! {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DebugLogOpts: u32 {
const READABLE = sys::ZX_LOG_FLAG_READABLE;
}
}
impl DebugLog {
/// Create a debug log object that allows access to read from and write to the kernel debug
/// logging facility.
///
/// Wraps the
/// [zx_debuglog_create]((https://fuchsia.dev/fuchsia-src/reference/syscalls/debuglog_create.md)
/// syscall.
pub fn create(resource: &Resource, opts: DebugLogOpts) -> Result<DebugLog, Status> {
let mut handle = 0;
let status =
unsafe { sys::zx_debuglog_create(resource.raw_handle(), opts.bits(), &mut handle) };
ok(status)?;
unsafe { Ok(DebugLog::from(Handle::from_raw(handle))) }
}
/// Write a message to the kernel debug log.
///
/// Wraps the
/// [zx_debuglog_write]((https://fuchsia.dev/fuchsia-src/reference/syscalls/debuglog_write.md)
/// syscall.
pub fn write(&self, message: &[u8]) -> Result<(), Status> {
// TODO(https://fxbug.dev/42108144): Discussion ongoing over whether debuglog levels are supported, so no
// options parameter for now.
let status = unsafe {
sys::zx_debuglog_write(self.raw_handle(), 0, message.as_ptr(), message.len())
};
ok(status)
}
/// Read a single log record from the kernel debug log.
///
/// The DebugLog object must have been created with DebugLogOpts::READABLE, or this will return
/// an error.
///
/// Wraps the
/// [zx_debuglog_read]((https://fuchsia.dev/fuchsia-src/reference/syscalls/debuglog_read.md)
/// syscall.
pub fn read(&self) -> Result<DebugLogRecord, Status> {
let mut record = sys::zx_log_record_t::default();
let bytes_written = unsafe {
sys::zx_debuglog_read(
self.raw_handle(),
0, /* options are unused, must be 0 */
std::ptr::from_mut(&mut record).cast::<u8>(),
std::mem::size_of_val(&record),
)
};
// On error, zx_debuglog_read returns a negative value. All other values indicate success.
if bytes_written < 0 {
Err(Status::from_raw(bytes_written))
} else {
DebugLogRecord::from_raw(&record)
}
}
}
/// A record from the kernel's debuglog.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct DebugLogRecord {
pub sequence: u64,
pub timestamp: BootInstant,
pub severity: DebugLogSeverity,
pub pid: Koid,
pub tid: Koid,
pub flags: u8,
data: [u8; sys::ZX_LOG_RECORD_DATA_MAX],
datalen: u16,
}
impl DebugLogRecord {
/// Convert a raw debuglog record into this typed wrapper.
pub fn from_raw(raw: &sys::zx_log_record_t) -> Result<Self, Status> {
if raw.datalen <= sys::ZX_LOG_RECORD_DATA_MAX as u16 {
Ok(Self {
timestamp: BootInstant::from_nanos(raw.timestamp),
sequence: raw.sequence,
severity: DebugLogSeverity::from_raw(raw.severity),
pid: Koid::from_raw(raw.pid),
tid: Koid::from_raw(raw.tid),
flags: raw.flags,
data: raw.data,
datalen: raw.datalen,
})
} else {
Err(Status::INTERNAL)
}
}
/// Returns the message data for the record.
pub fn data(&self) -> &BStr {
BStr::new(&self.data[..self.datalen as usize])
}
}
/// The severity a kernel log message can have.
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum DebugLogSeverity {
/// Record was written without a known severity.
Unknown,
/// Trace records include detailed information about program execution.
Trace,
/// Debug records include development-facing information about program execution.
Debug,
/// Info records include general information about program execution. (default)
Info,
/// Warning records include information about potentially problematic operations.
Warn,
/// Error records include information about failed operations.
Error,
/// Fatal records convey information about operations which cause a program's termination.
Fatal,
}
impl DebugLogSeverity {
fn from_raw(raw: u8) -> Self {
match raw {
sys::DEBUGLOG_TRACE => Self::Trace,
sys::DEBUGLOG_DEBUG => Self::Debug,
sys::DEBUGLOG_INFO => Self::Info,
sys::DEBUGLOG_WARNING => Self::Warn,
sys::DEBUGLOG_ERROR => Self::Error,
sys::DEBUGLOG_FATAL => Self::Fatal,
_ => Self::Unknown,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{cprng_draw, Instant, Signals};
use fidl_fuchsia_kernel as fkernel;
use fuchsia_component::client::connect_channel_to_protocol;
// expect_message_in_debuglog will read the last 10000 messages in zircon's debuglog, looking
// for a message that equals `sent_msg`. If found, the function returns. If the first 10,000
// messages doesn't contain `sent_msg`, it will panic.
fn expect_message_in_debuglog(sent_msg: String) {
use zx::{Channel, HandleBased};
let (client_end, server_end) = Channel::create();
connect_channel_to_protocol::<fkernel::DebuglogResourceMarker>(server_end).unwrap();
let service = fkernel::DebuglogResourceSynchronousProxy::new(client_end);
let resource =
service.get(zx::MonotonicInstant::INFINITE).expect("couldn't get debuglog resource");
// This test and fuchsia-zircon are different crates, so we need
// to use from_raw to convert between the zx handle and this test handle.
// See https://fxbug.dev/42173139 for details.
let resource = unsafe { Resource::from(Handle::from_raw(resource.into_raw())) };
let debuglog = DebugLog::create(&resource, DebugLogOpts::READABLE).unwrap();
for _ in 0..10000 {
match debuglog.read() {
Ok(record) => {
if record.data() == sent_msg.as_bytes() {
// We found our log!
return;
}
}
Err(status) if status == Status::SHOULD_WAIT => {
debuglog
.wait_handle(Signals::LOG_READABLE, Instant::INFINITE)
.expect("Failed to wait for log readable");
continue;
}
Err(status) => {
panic!("Unexpected error from zx_debuglog_read: {}", status);
}
}
}
panic!("first 10000 log messages didn't include the one we sent!");
}
#[test]
fn read_from_nonreadable() {
let resource = Resource::from(Handle::invalid());
let debuglog = DebugLog::create(&resource, DebugLogOpts::empty()).unwrap();
assert!(debuglog.read().err() == Some(Status::ACCESS_DENIED));
}
#[test]
fn write_and_read_back() {
let mut bytes = [0; 8];
cprng_draw(&mut bytes);
let message = format!("log message {:?}", bytes);
let resource = Resource::from(Handle::invalid());
let debuglog = DebugLog::create(&resource, DebugLogOpts::empty()).unwrap();
debuglog.write(message.as_bytes()).unwrap();
expect_message_in_debuglog(message);
}
}