blob: e0a546e1637ea33cb245972b809d7466dfea92aa [file] [log] [blame]
// 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::{AsHandleRef, HandleBased, HandleRef, Handle, Status};
use crate::ok;
use bitflags::bitflags;
use fuchsia_zircon_sys as sys;
/// An object representing a Zircon 'debuglog' object.
///
/// As essentially a subtype of `Handle`, it can be freely interconverted.
#[derive(Debug, Eq, PartialEq)]
#[repr(transparent)]
pub struct DebugLog(Handle);
impl_handle_based!(DebugLog);
bitflags! {
#[repr(transparent)]
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.googlesource.com/fuchsia/+/master/zircon/docs/syscalls/debuglog_create.md)
/// syscall.
pub fn create(opts: DebugLogOpts) -> Result<DebugLog, Status> {
// TODO(ZX-2184): Once the resource required for zx_debuglog_create is defined, add it as a
// parameter to this call.
let resource = sys::ZX_HANDLE_INVALID;
let mut handle = 0;
let status = unsafe {
sys::zx_debuglog_create(resource, 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.googlesource.com/fuchsia/+/master/zircon/docs/syscalls/debuglog_write.md)
/// syscall.
pub fn write(&self, message: &[u8]) -> Result<(), Status> {
// TODO(ZX-3187): 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.googlesource.com/fuchsia/+/master/zircon/docs/syscalls/debuglog_read.md)
/// syscall.
// TODO(ZX-3187): Return a safe wrapper type for zx_log_record_t rather than raw bytes
// depending on resolution.
pub fn read(&self, record: &mut Vec<u8>) -> Result<(), Status> {
let mut buf = [0; sys::ZX_LOG_RECORD_MAX];
// zx_debuglog_read options appear to be unused.
// zx_debuglog_read returns either an error status or, on success, the actual size of bytes
// read into the buffer.
let status_or_actual = unsafe {
sys::zx_debuglog_read(self.raw_handle(), 0, buf.as_mut_ptr(), buf.len())
};
let actual = Status::ioctl_ok(status_or_actual)? as usize;
record.clear();
record.extend_from_slice(&buf[0..actual]);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{cprng_draw, Signals, Time};
// 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) {
let debuglog = DebugLog::create(DebugLogOpts::READABLE).unwrap();
let mut record = Vec::with_capacity(sys::ZX_LOG_RECORD_MAX);
for _ in 0..10000 {
match debuglog.read(&mut record) {
Ok(()) => {
// TODO(ZX-3187): Manually unpack log record until DebugLog::read returns
// an wrapper type.
let mut len_bytes = [0; 2];
len_bytes.copy_from_slice(&record[4..6]);
let data_len = u16::from_le_bytes(len_bytes) as usize;
let log = &record[32..(32 + data_len)];
if log == sent_msg.as_bytes() {
// We found our log!
return;
}
}
Err(status) if status == Status::SHOULD_WAIT => {
debuglog
.wait_handle(Signals::LOG_READABLE, Time::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 mut data = vec![];
let debuglog = DebugLog::create(DebugLogOpts::empty()).unwrap();
assert!(debuglog.read(&mut data).err() == Some(Status::ACCESS_DENIED));
}
#[test]
fn write_and_read_back() {
let mut bytes = [0; 8];
cprng_draw(&mut bytes).unwrap();
let rand = u64::from_ne_bytes(bytes);
let message = format!("log message {}", rand);
let debuglog = DebugLog::create(DebugLogOpts::empty()).unwrap();
debuglog.write(message.as_bytes()).unwrap();
expect_message_in_debuglog(message);
}
}