281 lines
7.3 KiB
Rust
281 lines
7.3 KiB
Rust
//! Utilities for debug information logging
|
|
|
|
use alloc::{
|
|
string::{String, ToString},
|
|
sync::Arc,
|
|
};
|
|
use core::{
|
|
fmt,
|
|
str::FromStr,
|
|
sync::atomic::{AtomicBool, AtomicU32, Ordering},
|
|
};
|
|
use ring::RingLoggerSink;
|
|
|
|
use libk_util::OneTimeInit;
|
|
use sink::DEBUG_SINKS;
|
|
use yggdrasil_abi::error::Error;
|
|
|
|
use crate::{
|
|
arch::Cpu,
|
|
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, disable_early_sinks, DebugSink};
|
|
|
|
static DEBUG_LOCK: AtomicU32 = AtomicU32::new(u32::MAX);
|
|
static MUTE_DEBUG: AtomicBool = AtomicBool::new(false);
|
|
|
|
pub struct MuteGuard(bool);
|
|
|
|
impl MuteGuard {
|
|
pub fn acquire() -> Self {
|
|
let muted = MUTE_DEBUG.swap(true, Ordering::Acquire);
|
|
Self(muted)
|
|
}
|
|
}
|
|
|
|
impl Drop for MuteGuard {
|
|
fn drop(&mut self) {
|
|
MUTE_DEBUG.store(self.0, Ordering::Release);
|
|
}
|
|
}
|
|
|
|
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,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn lock_debug() -> bool {
|
|
let cpu = Cpu::try_local().map_or(0, |cpu| cpu.id());
|
|
loop {
|
|
match DEBUG_LOCK.compare_exchange(u32::MAX, cpu, Ordering::Acquire, Ordering::Relaxed) {
|
|
Ok(_) => return true,
|
|
Err(x) if x == cpu => return false,
|
|
_ => core::hint::spin_loop(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl log::Log for KernelLoggerSink {
|
|
fn enabled(&self, metadata: &log::Metadata) -> bool {
|
|
metadata.target() != "io"
|
|
}
|
|
|
|
fn log(&self, record: &log::Record) {
|
|
if MUTE_DEBUG.load(Ordering::Acquire) {
|
|
return;
|
|
}
|
|
|
|
if !self.enabled(record.metadata()) {
|
|
return;
|
|
}
|
|
|
|
RingLoggerSink.log(record);
|
|
|
|
if !lock_debug() {
|
|
return;
|
|
}
|
|
for sink in DEBUG_SINKS.read().iter() {
|
|
if sink.enabled(record.metadata()) {
|
|
sink.log(record);
|
|
}
|
|
}
|
|
DEBUG_LOCK.store(u32::MAX, Ordering::Release);
|
|
}
|
|
|
|
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, level: log::Level, message: &str) {
|
|
log::log!(
|
|
target: ":program",
|
|
level,
|
|
"{} ({}) {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),
|
|
}
|
|
}
|
|
}
|