From 3b48ff8be71f587809cd5843bb77de29a489f133 Mon Sep 17 00:00:00 2001 From: bunnie Date: Mon, 26 Sep 2022 11:36:49 +0800 Subject: [PATCH] initial cut at splitting out the lib section of log-server --- api/log-server/Cargo.toml | 22 +++++++ api/log-server/src/api.rs | 76 ++++++++++++++++++++++++ api/log-server/src/cursor.rs | 43 ++++++++++++++ api/log-server/src/lib.rs | 110 +++++++++++++++++++++++++++++++++++ 4 files changed, 251 insertions(+) create mode 100644 api/log-server/Cargo.toml create mode 100644 api/log-server/src/api.rs create mode 100644 api/log-server/src/cursor.rs create mode 100644 api/log-server/src/lib.rs diff --git a/api/log-server/Cargo.toml b/api/log-server/Cargo.toml new file mode 100644 index 000000000..b90238a7c --- /dev/null +++ b/api/log-server/Cargo.toml @@ -0,0 +1,22 @@ +[package] +authors = ["Sean Cross "] +description = "Log server API" +edition = "2018" +name = "xousapi-log-server" +version = "0.1.0" +license = "MIT OR Apache-2.0" +repository = "https://github.com/betrusted-io/xous-core/" +homepage = "https://betrusted.io/xous-book/" + +# Dependency versions enforced by Cargo.lock. +[dependencies] +log = "0.4.14" +num-derive = {version = "0.3.3", default-features = false} +num-traits = {version = "0.2.14", default-features = false} +xous = "0.9.8" +xous-ipc = "0.9.7" + +[features] +debugprint = [] # adding this allocates the UART for debugging the logger +logging = [] # adding this allocates the hardware UART for console interactions +default = ["logging"] diff --git a/api/log-server/src/api.rs b/api/log-server/src/api.rs new file mode 100644 index 000000000..e6bd5896b --- /dev/null +++ b/api/log-server/src/api.rs @@ -0,0 +1,76 @@ +#[repr(C, align(4096))] +pub struct LogRecord { + pub file_length: u32, + pub file: [u8; 128], + pub line: Option, + pub module_length: u32, + pub module: [u8; 128], + pub level: u32, + pub args_length: u32, + pub args: [u8; 3820], +} + +impl Default for LogRecord { + fn default() -> Self { + LogRecord { file_length: 0, file: [0u8; 128], line: None, module_length: 0, module: [0u8; 128], level: 0, args_length: 0, args: [0u8; 3820] } + } +} + +#[derive(Debug, PartialEq, num_derive::FromPrimitive, num_derive::ToPrimitive)] +pub enum Opcode { + /// A `LogRecord` message, delivering structured log output + LogRecord = 0, + + /// A `&[u8]` destined for stdout + StandardOutput = 1, + + /// A `&[u8]` destined for stderr + StandardError = 2, + + /// A `xous::StringBuffer` containing this program's name + ProgramName = 3, + + /// A panic occurred, and a panic log is forthcoming + PanicStarted = 1000, + + /// Log messages of varying size + PanicMessage0 = 1100, + PanicMessage1 = 1101, + PanicMessage2 = 1102, + PanicMessage3 = 1103, + PanicMessage4 = 1104, + PanicMessage5 = 1105, + PanicMessage6 = 1106, + PanicMessage7 = 1107, + PanicMessage8 = 1108, + PanicMessage9 = 1109, + PanicMessage10 = 1110, + PanicMessage11 = 1111, + PanicMessage12 = 1112, + PanicMessage13 = 1113, + PanicMessage14 = 1114, + PanicMessage15 = 1115, + PanicMessage16 = 1116, + PanicMessage17 = 1117, + PanicMessage18 = 1118, + PanicMessage19 = 1119, + PanicMessage20 = 1120, + PanicMessage21 = 1121, + PanicMessage22 = 1122, + PanicMessage23 = 1123, + PanicMessage24 = 1124, + PanicMessage25 = 1125, + PanicMessage26 = 1126, + PanicMessage27 = 1127, + PanicMessage28 = 1128, + PanicMessage29 = 1129, + PanicMessage30 = 1130, + PanicMessage31 = 1131, + PanicMessage32 = 1132, + + /// End of a panic + PanicFinished = 1200, + + /// Enable receiving messages when the system is resumed from sleep. + EnableRx = 2000, +} diff --git a/api/log-server/src/cursor.rs b/api/log-server/src/cursor.rs new file mode 100644 index 000000000..0d5f660db --- /dev/null +++ b/api/log-server/src/cursor.rs @@ -0,0 +1,43 @@ +/// no_std replacement for Cursor. + +pub struct BufferWrapper<'a> { + buf: &'a mut [u8], + offset: usize, +} + +impl<'a> BufferWrapper<'a> { + pub fn new(buf: &'a mut [u8]) -> Self { + BufferWrapper { + buf: buf, + offset: 0, + } + } + pub fn len(&self) -> usize { + self.offset + } +} + +impl<'a> core::fmt::Write for BufferWrapper<'a> { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + let bytes = s.as_bytes(); + + // Skip over already-copied data + let remainder = &mut self.buf[self.offset..]; + + // Check if there is space remaining (return error instead of panicking) + if remainder.len() < bytes.len() { + return Err(core::fmt::Error); + } + + // Make the two slices the same length + let remainder = &mut remainder[..bytes.len()]; + + // Copy + remainder.copy_from_slice(bytes); + + // Update offset to avoid overwriting + self.offset += bytes.len(); + + Ok(()) + } +} diff --git a/api/log-server/src/lib.rs b/api/log-server/src/lib.rs new file mode 100644 index 000000000..7748c54b0 --- /dev/null +++ b/api/log-server/src/lib.rs @@ -0,0 +1,110 @@ +#![cfg_attr(target_os = "none", no_std)] +use core::fmt::Write; +use core::sync::atomic::{AtomicU32, Ordering}; +use num_traits::ToPrimitive; + +pub mod api; +mod cursor; + +#[derive(Debug)] +pub enum LogError { + LoggerExists, + NoConnection, +} + +struct XousLogger; +static XOUS_LOGGER: XousLogger = XousLogger {}; +static XOUS_LOGGER_CONNECTION: AtomicU32 = AtomicU32::new(0); + +impl XousLogger { + fn log_impl(&self, record: &log::Record) { + let mut log_record = api::LogRecord::default(); + assert_eq!(core::mem::size_of::(), 4096); + + // A "line" of 0 is the same as "None" for our purposes here. + log_record.line = core::num::NonZeroU32::new(record.line().unwrap_or_default()); + + log_record.level = record.level() as u32; + + let file = record.file().unwrap_or_default().as_bytes(); + log_record.file_length = file.len().min(file.len()) as u32; + for (dest, src) in log_record.file.iter_mut().zip(file) { + *dest = *src; + } + + let module = record.module_path().unwrap_or_default().as_bytes(); + log_record.module_length = module.len().min(module.len()) as u32; + for (dest, src) in log_record.module.iter_mut().zip(module) { + *dest = *src; + } + + // Serialize the text to our record buffer + let mut wrapper = cursor::BufferWrapper::new(&mut log_record.args); + write!(wrapper, "{}", record.args()).ok(); // truncate if error + log_record.args_length = wrapper.len().min(log_record.args.len()) as u32; + + let buf = unsafe { + xous::MemoryRange::new( + &log_record as *const api::LogRecord as usize, + core::mem::size_of::(), + ) + .unwrap() + }; + + xous::send_message( + XOUS_LOGGER_CONNECTION.load(Ordering::Relaxed), + xous::Message::new_lend( + crate::api::Opcode::LogRecord.to_usize().unwrap(), + buf, + None, + None, + ), + ) + .unwrap(); + } + + fn resume(&self) { + xous::send_message( + XOUS_LOGGER_CONNECTION.load(Ordering::Relaxed), + xous::Message::new_scalar(2000, 0, 0, 0, 0), // logger is one of the few servers that uses special, non-encoded message IDs. + ) + .expect("couldn't send resume message to the logger implementation"); + } +} + +impl log::Log for XousLogger { + fn enabled(&self, _metadata: &log::Metadata) -> bool { + true + } + + fn log(&self, record: &log::Record) { + XOUS_LOGGER.log_impl(record); + } + + fn flush(&self) {} +} + +pub fn init() -> Result<(), LogError> { + XOUS_LOGGER_CONNECTION.store( + xous::try_connect(xous::SID::from_bytes(b"xous-log-server ").unwrap()) + .or(Err(LogError::NoConnection))?, + Ordering::Relaxed, + ); + log::set_logger(&XOUS_LOGGER).map_err(|_| LogError::LoggerExists)?; + log::set_max_level(log::LevelFilter::Info); + Ok(()) +} + +pub fn init_wait() -> Result<(), ()> { + XOUS_LOGGER_CONNECTION.store( + xous::connect(xous::SID::from_bytes(b"xous-log-server ").unwrap()).or(Err(()))?, + Ordering::Relaxed, + ); + log::set_logger(&XOUS_LOGGER).or(Err(()))?; + log::set_max_level(log::LevelFilter::Info); + Ok(()) +} + +pub fn resume() { + XOUS_LOGGER.resume(); +}