241 lines
6.4 KiB
Rust
Raw Normal View History

2025-02-09 14:30:39 +02:00
//! 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),
}
}
}