实现内核日志系统 (#489)

* 实现写日志和读取日志,并且能够在用户态下执行dmesg命令查看日志

* 通过klogctl实现dmesg

* 改用ConstGenericRingBuffer作内核缓冲区

* 更改缓冲区容量

* 将能够输出到控制台的日志级别改为日志级别枚举类,使用SpinLock控制KMSG,使用枚举类定义SYSLOG_ACTION,将do_syslog系统调用接口放在syscall.rs

* fix warning

* 完善do_syslog注释

* 将KMSG接入kinfo、kdebug等

* fix warning

* 修复显示的秒数不正确,·以及无法通过CI的问题
This commit is contained in:
Jomo
2024-01-24 16:13:15 +08:00
committed by GitHub
parent d46c6d2794
commit 8d72b68da9
27 changed files with 763 additions and 26 deletions

View File

@ -7,6 +7,7 @@ description = "需要导出的依赖项(为保持内核依赖版本与调试
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
ringbuffer = "0.15.0"
memoffset = "0.9.0"
crc = { path = "../crc" }

View File

@ -6,4 +6,6 @@ pub extern crate thingbuf;
pub extern crate memoffset;
pub extern crate ringbuffer;
pub extern crate crc;

View File

@ -491,7 +491,7 @@ impl SignalArch for X86_64SignalArch {
"Error occurred when handling signal: {}, pid={:?}, errcode={:?}",
sig_number as i32,
ProcessManager::current_pcb().pid(),
res.unwrap_err()
res.as_ref().unwrap_err()
);
}
}

View File

@ -8,6 +8,7 @@ use x86::time::rdtsc;
use x86_64::registers::model_specific::EferFlags;
use crate::driver::tty::serial::serial8250::send_to_default_serial8250_port;
use crate::filesystem::procfs::kmsg::kmsg_init;
use crate::include::bindings::bindings::{
multiboot2_get_load_base, multiboot2_get_memory, multiboot2_iter, multiboot_mmap_entry_t,
multiboot_tag_load_base_addr_t,
@ -419,6 +420,8 @@ pub fn mm_init() {
unsafe { allocator_init() };
// enable mmio
mmio_init();
// enable KMSG
kmsg_init();
}
unsafe fn allocator_init() {

View File

@ -594,8 +594,12 @@ pub extern "C" fn rs_e1000e_init() {
pub fn e1000e_init() -> () {
match e1000e_probe() {
Ok(_code) => kinfo!("Successfully init e1000e device!"),
Err(_error) => kinfo!("Error occurred!"),
Ok(_code) => {
kinfo!("Successfully init e1000e device!");
}
Err(_error) => {
kinfo!("Error occurred!");
}
}
}

View File

@ -1196,7 +1196,7 @@ impl Drop for FATFileSystem {
if r.is_err() {
kerror!(
"Umount FAT filesystem failed: errno={:?}, FS detail:{self:?}",
r.unwrap_err()
r.as_ref().unwrap_err()
);
}
}

View File

@ -0,0 +1,151 @@
use super::log::{LogLevel, LogMessage};
use crate::libs::spinlock::SpinLock;
use alloc::{borrow::ToOwned, string::ToString, vec::Vec};
use kdepends::ringbuffer::{AllocRingBuffer, RingBuffer};
use system_error::SystemError;
/// 缓冲区容量
const KMSG_BUFFER_CAPACITY: usize = 1024;
/// 全局环形缓冲区
pub static mut KMSG: Option<SpinLock<Kmsg>> = None;
/// 初始化KMSG
pub fn kmsg_init() {
let kmsg = SpinLock::new(Kmsg::new());
unsafe { KMSG = Some(kmsg) };
}
/// 日志
pub struct Kmsg {
/// 环形缓冲区
buffer: AllocRingBuffer<LogMessage>,
/// 缓冲区字节数组
data: Vec<u8>,
/// 能够输出到控制台的日志级别当console_loglevel为DEFAULT时表示可以打印所有级别的日志消息到控制台
console_loglevel: LogLevel,
/// 判断buffer在上一次转成字节数组之后是否发生变动
is_changed: bool,
}
impl Kmsg {
pub fn new() -> Self {
Kmsg {
buffer: AllocRingBuffer::new(KMSG_BUFFER_CAPACITY),
data: Vec::new(),
console_loglevel: LogLevel::DEFAULT,
is_changed: false,
}
}
/// 添加日志消息
pub fn push(&mut self, msg: LogMessage) {
self.buffer.push(msg);
self.is_changed = true;
}
/// 读取缓冲区
pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, SystemError> {
self.tobytes();
match self.console_loglevel {
LogLevel::DEFAULT => self.read_all(buf),
_ => self.read_level(buf),
}
}
/// 读取缓冲区所有日志消息
fn read_all(&mut self, buf: &mut [u8]) -> Result<usize, SystemError> {
let len = self.data.len().min(buf.len());
// 拷贝数据
let src = &self.data[0..len];
buf[0..src.len()].copy_from_slice(src);
return Ok(src.len());
}
/// 读取缓冲区特定level的日志消息
fn read_level(&mut self, buf: &mut [u8]) -> Result<usize, SystemError> {
let mut data_level: Vec<u8> = Vec::new();
for msg in self.buffer.iter() {
if msg.level() == self.console_loglevel {
data_level.append(&mut msg.to_string().as_bytes().to_owned());
}
}
let len = data_level.len().min(buf.len());
// 拷贝数据
let src = &data_level[0..len];
buf[0..src.len()].copy_from_slice(src);
// 将控制台输出日志level改回默认否则之后都是打印特定level的日志消息
self.console_loglevel = LogLevel::DEFAULT;
return Ok(data_level.len());
}
/// 读取并清空缓冲区
pub fn read_clear(&mut self, buf: &mut [u8]) -> Result<usize, SystemError> {
let r = self.read_all(buf);
self.clear()?;
return r;
}
/// 清空缓冲区
pub fn clear(&mut self) -> Result<usize, SystemError> {
self.buffer.clear();
self.data.clear();
return Ok(0);
}
/// 设置输出到控制台的日志级别
pub fn set_level(&mut self, log_level: usize) -> Result<usize, SystemError> {
let log_level = log_level - 1;
self.console_loglevel = match log_level {
0 => LogLevel::EMERG,
1 => LogLevel::ALERT,
2 => LogLevel::CRIT,
3 => LogLevel::ERR,
4 => LogLevel::WARN,
5 => LogLevel::NOTICE,
6 => LogLevel::INFO,
7 => LogLevel::DEBUG,
8 => LogLevel::DEFAULT,
_ => return Err(SystemError::EINVAL),
};
return Ok(0);
}
/// 将环形缓冲区的日志消息转成字节数组以拷入用户buf
fn tobytes(&mut self) -> usize {
if self.is_changed {
self.data.clear();
if self.console_loglevel == LogLevel::DEFAULT {
for msg in self.buffer.iter() {
self.data.append(&mut msg.to_string().as_bytes().to_owned());
}
}
self.is_changed = false;
}
return self.data.len();
}
// 返回内核缓冲区所占字节数
pub fn data_size(&mut self) -> Result<usize, SystemError> {
return Ok(self.tobytes());
}
}

View File

@ -0,0 +1,107 @@
use core::fmt::{Display, Formatter, Result};
use alloc::string::String;
use crate::time::TimeSpec;
// /// 日志类型
// #[derive(Default, Clone, Debug)]
// pub enum LogType {
// /// 启动信息
// Startup,
// /// 驱动信息
// Driver,
// /// 系统信息
// System,
// /// 硬件信息
// Hardware,
// /// 内核模块信息
// KernelModule,
// /// 内核调试信息
// KernelDebug,
// #[default]
// Default,
// }
/// 日志级别
#[derive(Default, Clone, PartialEq, Debug)]
pub enum LogLevel {
EMERG = 0,
ALERT = 1,
CRIT = 2,
ERR = 3,
WARN = 4,
NOTICE = 5,
INFO = 6,
DEBUG = 7,
#[default]
DEFAULT = 8,
}
impl From<usize> for LogLevel {
fn from(value: usize) -> Self {
match value {
0 => LogLevel::EMERG,
1 => LogLevel::ALERT,
2 => LogLevel::CRIT,
3 => LogLevel::ERR,
4 => LogLevel::WARN,
5 => LogLevel::NOTICE,
6 => LogLevel::INFO,
7 => LogLevel::DEBUG,
_ => LogLevel::DEFAULT,
}
}
}
/// 日志消息
#[derive(Default, Clone, Debug)]
pub struct LogMessage {
/// 时间戳
timestamp: TimeSpec,
/// 日志级别
level: LogLevel,
// /// 日志类型
// log_type: LogType,
/// 日志消息
message: String,
}
impl LogMessage {
pub fn new(timestamp: TimeSpec, level: LogLevel, message: String) -> Self {
LogMessage {
timestamp,
level,
message,
}
}
pub fn level(&self) -> LogLevel {
self.level.clone()
}
}
impl Display for LogMessage {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let timestamp = &self.timestamp;
let level = match self.level {
LogLevel::EMERG => "EMERG",
LogLevel::ALERT => "ALERT",
LogLevel::CRIT => "CRIT",
LogLevel::ERR => "ERR",
LogLevel::WARN => "WARNING",
LogLevel::NOTICE => "NOTICE",
LogLevel::INFO => "INFO",
LogLevel::DEBUG => "DEBUG",
LogLevel::DEFAULT => "Default",
};
let message = &self.message;
let res = format!(
"<{}>[{}.{}] : {}\n",
level, timestamp.tv_sec, timestamp.tv_nsec, message
);
return write!(f, "{}", res);
}
}

View File

@ -33,6 +33,10 @@ use super::vfs::{
FileSystem, FsInfo, IndexNode, InodeId, Metadata,
};
pub mod kmsg;
pub mod log;
mod syscall;
/// @brief 进程文件类型
/// @usage 用于定义进程文件夹下的各类文件类型
#[derive(Debug)]
@ -42,6 +46,8 @@ pub enum ProcFileType {
ProcStatus = 0,
/// meminfo
ProcMeminfo = 1,
/// kmsg
ProcKmsg = 2,
//todo: 其他文件类型
///默认文件类型
Default,
@ -52,6 +58,7 @@ impl From<u8> for ProcFileType {
match value {
0 => ProcFileType::ProcStatus,
1 => ProcFileType::ProcMeminfo,
2 => ProcFileType::ProcKmsg,
_ => ProcFileType::Default,
}
}
@ -336,6 +343,19 @@ impl ProcFS {
panic!("create meminfo error");
}
// 创建kmsg文件
let binding = inode.create("kmsg", FileType::File, ModeType::from_bits_truncate(0o444));
if let Ok(kmsg) = binding {
let kmsg_file = kmsg
.as_any_ref()
.downcast_ref::<LockedProcFSInode>()
.unwrap();
kmsg_file.0.lock().fdata.pid = Pid::new(1);
kmsg_file.0.lock().fdata.ftype = ProcFileType::ProcKmsg;
} else {
panic!("create ksmg error");
}
return result;
}
@ -456,6 +476,7 @@ impl IndexNode for LockedProcFSInode {
match inode.fdata.ftype {
ProcFileType::ProcStatus => return inode.proc_read(offset, len, buf, private_data),
ProcFileType::ProcMeminfo => return inode.proc_read(offset, len, buf, private_data),
ProcFileType::ProcKmsg => (),
ProcFileType::Default => (),
};

View File

@ -0,0 +1,77 @@
use core::usize;
use system_error::SystemError;
use crate::syscall::Syscall;
use super::kmsg::KMSG;
/// 操作内核环形缓冲区
enum SyslogAction {
/// Close the log. Currently a NOP.
SyslogActionClose = 0,
/// Open the log. Currently a NOP.
SyslogActionOpen = 1,
/// Read from the log.
SyslogActionRead = 2,
/// Read and clear all messages remaining in the ring buffer.
SyslogActionReadClear = 4,
/// Clear ring buffer.
SyslogActionClear = 5,
/// Set level of messages printed to console.
SyslogActionConsoleLevel = 8,
/// Return size of the log buffer.
SyslogActionSizeBuffer = 10,
/// Invalid SyslogAction
SyslogActionInval,
}
impl From<usize> for SyslogAction {
fn from(value: usize) -> Self {
match value {
0 => SyslogAction::SyslogActionClose,
1 => SyslogAction::SyslogActionOpen,
2 => SyslogAction::SyslogActionRead,
4 => SyslogAction::SyslogActionReadClear,
5 => SyslogAction::SyslogActionClear,
8 => SyslogAction::SyslogActionConsoleLevel,
10 => SyslogAction::SyslogActionSizeBuffer,
_ => SyslogAction::SyslogActionInval,
}
}
}
impl Syscall {
/// # 操作内核环形缓冲区
///
/// ## 参数
/// - syslog_action_type: 操作码
/// - buf用户缓冲区
/// - len: 需要从内核环形缓冲区读取的字节数。如果操作码为8即SyslogActionConsoleLevel则len为待设置的日志级别
///
/// ## 返回值
/// - 成功Ok(usize)
/// - 失败Err(SystemError) 操作失败返回posix错误码
///
pub fn do_syslog(
syslog_action_type: usize,
buf: &mut [u8],
len: usize,
) -> Result<usize, SystemError> {
let syslog_action = SyslogAction::from(syslog_action_type);
let mut kmsg_guard = unsafe { KMSG.as_ref().unwrap().lock_irqsave() };
match syslog_action {
SyslogAction::SyslogActionClose => Ok(0),
SyslogAction::SyslogActionOpen => Ok(0),
SyslogAction::SyslogActionRead => kmsg_guard.read(buf),
SyslogAction::SyslogActionReadClear => kmsg_guard.read_clear(buf),
SyslogAction::SyslogActionClear => kmsg_guard.clear(),
SyslogAction::SyslogActionSizeBuffer => kmsg_guard.data_size(),
SyslogAction::SyslogActionConsoleLevel => kmsg_guard.set_level(len),
SyslogAction::SyslogActionInval => return Err(SystemError::EINVAL),
}
}
}

View File

@ -462,7 +462,7 @@ impl Drop for File {
"pid: {:?} failed to close file: {:?}, errno={:?}",
ProcessManager::current_pcb().pid(),
self,
r.unwrap_err()
r.as_ref().unwrap_err()
);
}
}

View File

@ -1,7 +1,17 @@
use core::fmt::{self, Write};
use alloc::string::ToString;
use super::lib_ui::textui::{textui_putstr, FontColor};
use crate::{
filesystem::procfs::{
kmsg::KMSG,
log::{LogLevel, LogMessage},
},
time::TimeSpec,
};
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ($crate::libs::printk::__printk(format_args!($($arg)*)));
@ -30,14 +40,15 @@ macro_rules! printk_color {
#[macro_export]
macro_rules! kdebug {
($($arg:tt)*) => {
$crate::libs::printk::Logger.log(7,format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*)));
$crate::libs::printk::PrintkWriter.__write_fmt(format_args!("[ DEBUG ] ({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*)))
}
}
#[macro_export]
macro_rules! kinfo {
($($arg:tt)*) => {
$crate::libs::printk::Logger.log(6,format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*)));
$crate::libs::printk::PrintkWriter.__write_fmt(format_args!("[ INFO ] ({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*)))
}
}
@ -45,6 +56,7 @@ macro_rules! kinfo {
#[macro_export]
macro_rules! kwarn {
($($arg:tt)*) => {
$crate::libs::printk::Logger.log(4,format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*)));
$crate::libs::printk::PrintkWriter.__write_string_color($crate::libs::lib_ui::textui::FontColor::YELLOW, $crate::libs::lib_ui::textui::FontColor::BLACK, "[ WARN ] ");
$crate::libs::printk::PrintkWriter.__write_fmt(format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*)));
}
@ -53,6 +65,7 @@ macro_rules! kwarn {
#[macro_export]
macro_rules! kerror {
($($arg:tt)*) => {
$crate::libs::printk::Logger.log(3,format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*)));
$crate::libs::printk::PrintkWriter.__write_string_color($crate::libs::lib_ui::textui::FontColor::RED, $crate::libs::lib_ui::textui::FontColor::BLACK, "[ ERROR ] ");
$crate::libs::printk::PrintkWriter.__write_fmt(format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*)));
}
@ -61,6 +74,7 @@ macro_rules! kerror {
#[macro_export]
macro_rules! kBUG {
($($arg:tt)*) => {
$crate::libs::printk::Logger.log(1,format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*)));
$crate::libs::printk::PrintkWriter.__write_string_color($crate::libs::lib_ui::textui::FontColor::RED, $crate::libs::lib_ui::textui::FontColor::BLACK, "[ BUG ] ");
$crate::libs::printk::PrintkWriter.__write_fmt(format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*)));
}
@ -97,3 +111,17 @@ impl fmt::Write for PrintkWriter {
pub fn __printk(args: fmt::Arguments) {
PrintkWriter.write_fmt(args).unwrap();
}
pub struct Logger;
impl Logger {
pub fn log(&self, log_level: usize, message: fmt::Arguments) {
if unsafe { !KMSG.is_none() } {
let timestamp: TimeSpec = TimeSpec::now();
let log_level = LogLevel::from(log_level.clone());
let log_message = LogMessage::new(timestamp, log_level, message.to_string());
unsafe { KMSG.as_ref().unwrap().lock_irqsave().push(log_message) };
}
}
}

View File

@ -853,10 +853,20 @@ impl Syscall {
}
SYS_GETTID => Self::gettid().map(|tid| tid.into()),
SYS_GETUID => Self::getuid().map(|uid| uid.into()),
SYS_SYSLOG => {
kwarn!("SYS_SYSLOG has not yet been implemented");
Ok(0)
let syslog_action_type = args[0] as usize;
let buf_vaddr = args[1];
let len = args[2];
let from_user = frame.from_user();
let mut user_buffer_writer =
UserBufferWriter::new(buf_vaddr as *mut u8, len, from_user)?;
let user_buf = user_buffer_writer.buffer(0)?;
let res = Self::do_syslog(syslog_action_type, user_buf, len);
res
}
SYS_GETGID => Self::getgid().map(|gid| gid.into()),
SYS_SETUID => {
kwarn!("SYS_SETUID has not yet been implemented");

View File

@ -1,8 +1,11 @@
use core::{
fmt,
intrinsics::unlikely,
ops::{self, Sub},
};
use crate::arch::CurrentTimeArch;
use self::timekeep::ktime_get_real_ns;
pub mod clocksource;
@ -55,6 +58,27 @@ impl TimeSpec {
tv_nsec: nsec,
};
}
/// 获取当前时间
pub fn now() -> Self {
#[cfg(target_arch = "x86_64")]
{
use crate::arch::driver::tsc::TSCManager;
let khz = TSCManager::cpu_khz();
if unlikely(khz == 0) {
return TimeSpec::default();
} else {
return Self::from(Duration::from_millis(
CurrentTimeArch::get_cycles() as u64 / khz,
));
}
}
#[cfg(target_arch = "riscv64")]
{
unimplemented!("TimeSpec::now()")
}
}
}
impl Sub for TimeSpec {

View File

@ -129,7 +129,7 @@ impl Timer {
if unlikely(r.is_err()) {
kerror!(
"Failed to run timer function: {self:?} {:?}",
r.err().unwrap()
r.as_ref().err().unwrap()
);
}
}