241 lines
6.4 KiB
Rust
241 lines
6.4 KiB
Rust
|
//! Utilities for debug information logging
|
||
|
|
||
|
use alloc::{
|
||
|
string::{String, ToString},
|
||
|
sync::Arc,
|
||
|
};
|
||
|
use core::{fmt, str::FromStr};
|
||
|
use ring::RingLoggerSink;
|
||
|
|
||
|
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
|
||
|
use sink::DEBUG_SINKS;
|
||
|
use yggdrasil_abi::error::Error;
|
||
|
|
||
|
use crate::{
|
||
|
fs::sysfs::{
|
||
|
attribute::{StringAttribute, StringAttributeOps},
|
||
|
object::KObject,
|
||
|
},
|
||
|
task::{process::Process, thread::Thread},
|
||
|
};
|
||
|
|
||
|
mod panic;
|
||
|
mod ring;
|
||
|
mod sink;
|
||
|
|
||
|
pub use panic::{panic_log, PanicLoggerSink};
|
||
|
pub use ring::add_kernel_log_file;
|
||
|
pub use sink::{add_early_sink, add_serial_sink, add_sink, DebugSink};
|
||
|
|
||
|
static DEBUG_LOCK: IrqSafeSpinlock<()> = IrqSafeSpinlock::new(());
|
||
|
|
||
|
struct KernelLoggerSink;
|
||
|
|
||
|
/// Defines the severity of the message
|
||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||
|
pub enum LogLevel {
|
||
|
/// Very verbose low-level debugging information
|
||
|
Trace,
|
||
|
/// Debugging and verbose information
|
||
|
Debug,
|
||
|
/// General information about transitions in the system state
|
||
|
Info,
|
||
|
/// Non-critical abnormalities or notices
|
||
|
Warning,
|
||
|
/// Failures of non-essential components
|
||
|
Error,
|
||
|
/// Irrecoverable errors which result in kernel panic
|
||
|
Fatal,
|
||
|
}
|
||
|
|
||
|
impl LogLevel {
|
||
|
fn log_prefix(self) -> &'static str {
|
||
|
match self {
|
||
|
LogLevel::Trace => "",
|
||
|
LogLevel::Debug => "",
|
||
|
LogLevel::Info => "\x1b[36m\x1b[1m",
|
||
|
LogLevel::Warning => "\x1b[33m\x1b[1m",
|
||
|
LogLevel::Error => "\x1b[31m\x1b[1m",
|
||
|
LogLevel::Fatal => "\x1b[38;2;255;0;0m\x1b[1m",
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn log_suffix(self) -> &'static str {
|
||
|
match self {
|
||
|
LogLevel::Trace => "",
|
||
|
LogLevel::Debug => "",
|
||
|
LogLevel::Info => "\x1b[0m",
|
||
|
LogLevel::Warning => "\x1b[0m",
|
||
|
LogLevel::Error => "\x1b[0m",
|
||
|
LogLevel::Fatal => "\x1b[0m",
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl From<log::Level> for LogLevel {
|
||
|
fn from(value: log::Level) -> Self {
|
||
|
match value {
|
||
|
log::Level::Trace => Self::Trace,
|
||
|
log::Level::Debug => Self::Debug,
|
||
|
log::Level::Info => Self::Info,
|
||
|
log::Level::Warn => Self::Warning,
|
||
|
log::Level::Error => Self::Error,
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl log::Log for KernelLoggerSink {
|
||
|
fn enabled(&self, metadata: &log::Metadata) -> bool {
|
||
|
metadata.target() != "io"
|
||
|
}
|
||
|
|
||
|
fn log(&self, record: &log::Record) {
|
||
|
if !self.enabled(record.metadata()) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
RingLoggerSink.log(record);
|
||
|
|
||
|
let _guard = DEBUG_LOCK.lock();
|
||
|
for sink in DEBUG_SINKS.read().iter() {
|
||
|
if sink.enabled(record.metadata()) {
|
||
|
sink.log(record);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn flush(&self) {}
|
||
|
}
|
||
|
|
||
|
static LOGGER: KernelLoggerSink = KernelLoggerSink;
|
||
|
|
||
|
fn make_sysfs_sink_object(index: usize) -> Arc<KObject<usize>> {
|
||
|
struct Level;
|
||
|
|
||
|
impl StringAttributeOps for Level {
|
||
|
type Data = usize;
|
||
|
const NAME: &'static str = "level";
|
||
|
const LIMIT: usize = 16;
|
||
|
const WRITEABLE: bool = true;
|
||
|
|
||
|
fn read(state: &Self::Data) -> Result<String, Error> {
|
||
|
let sinks = DEBUG_SINKS.read();
|
||
|
let sink = sinks.get(*state).ok_or(Error::InvalidFile)?;
|
||
|
Ok(sink.level().to_string())
|
||
|
}
|
||
|
|
||
|
fn write(state: &Self::Data, value: &str) -> Result<(), Error> {
|
||
|
let level = LogLevel::from_str(value)?;
|
||
|
let mut sinks = DEBUG_SINKS.write();
|
||
|
let sink = sinks.get_mut(*state).ok_or(Error::InvalidFile)?;
|
||
|
sink.set_level(level);
|
||
|
Ok(())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
let object = KObject::new(index);
|
||
|
|
||
|
object.add_attribute(StringAttribute::from(Level)).ok();
|
||
|
|
||
|
object
|
||
|
}
|
||
|
|
||
|
/// Print a trace message coming from a process
|
||
|
pub fn program_trace(process: &Process, _thread: &Thread, message: &str) {
|
||
|
log::debug!(
|
||
|
target: ":program",
|
||
|
"{} ({}) {message}\n",
|
||
|
process.name,
|
||
|
process.id,
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/// Initializes kernel logging output. Prior to this call, no log entries are recorded.
|
||
|
pub fn init_logger() {
|
||
|
static LOGGER_SET_UP: OneTimeInit<()> = OneTimeInit::new();
|
||
|
|
||
|
LOGGER_SET_UP.or_init_with(|| {
|
||
|
log::set_logger(&LOGGER)
|
||
|
.map(|_| log::set_max_level(log::LevelFilter::Trace))
|
||
|
.ok();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/// Resets the debugging terminal by clearing it
|
||
|
pub fn init() {
|
||
|
init_logger();
|
||
|
}
|
||
|
|
||
|
/// Print a hex dump into the kernel log with given level
|
||
|
pub fn hex_dump(level: log::Level, bytes: &[u8], start_address: u64, with_chars: bool) {
|
||
|
if bytes.is_empty() {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
let line_width = 16;
|
||
|
let address_width = (start_address + bytes.len() as u64).ilog10() as usize + 1;
|
||
|
|
||
|
for i in (0..bytes.len()).step_by(line_width) {
|
||
|
log::log!(target: ":raw", level, "{:0width$X}: ", start_address + i as u64, width = address_width);
|
||
|
|
||
|
for j in 0..line_width {
|
||
|
if i + j < bytes.len() {
|
||
|
log::log!(target: ":raw", level, "{:02X}", bytes[i + j]);
|
||
|
} else {
|
||
|
log::log!(target: ":raw", level, " ");
|
||
|
}
|
||
|
|
||
|
if j % 2 != 0 {
|
||
|
log::log!(target: ":raw", level, " ");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if with_chars {
|
||
|
for j in 0..line_width {
|
||
|
if i + j >= bytes.len() {
|
||
|
break;
|
||
|
}
|
||
|
let ch = if bytes[i + j].is_ascii_graphic() {
|
||
|
bytes[i + j]
|
||
|
} else {
|
||
|
b'.'
|
||
|
};
|
||
|
log::log!(target: ":raw", level, "{}", ch as char);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
log::log!(target: ":raw", level, "\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl fmt::Display for LogLevel {
|
||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||
|
let level = match self {
|
||
|
Self::Trace => "trace",
|
||
|
Self::Debug => "debug",
|
||
|
Self::Info => "info",
|
||
|
Self::Warning => "warn",
|
||
|
Self::Error => "error",
|
||
|
Self::Fatal => "fatal",
|
||
|
};
|
||
|
|
||
|
f.write_str(level)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl FromStr for LogLevel {
|
||
|
type Err = Error;
|
||
|
|
||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||
|
match s {
|
||
|
"trace" | "t" => Ok(Self::Trace),
|
||
|
"debug" | "d" => Ok(Self::Debug),
|
||
|
"info" | "i" => Ok(Self::Info),
|
||
|
"warn" | "w" => Ok(Self::Warning),
|
||
|
"error" | "e" => Ok(Self::Error),
|
||
|
"fatal" | "f" => Ok(Self::Fatal),
|
||
|
_ => Err(Error::InvalidArgument),
|
||
|
}
|
||
|
}
|
||
|
}
|