Browse Source

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

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

* 通过klogctl实现dmesg

* 改用ConstGenericRingBuffer作内核缓冲区

* 更改缓冲区容量

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

* fix warning

* 完善do_syslog注释

* 将KMSG接入kinfo、kdebug等

* fix warning

* 修复显示的秒数不正确,·以及无法通过CI的问题
Jomo 1 year ago
parent
commit
8d72b68da9

+ 4 - 2
.github/actions/import-toolchain/action.yml

@@ -15,7 +15,8 @@ runs:
         with:
           path: |
             ~/opt
-          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('tools/build_gcc_toolchain.sh') }}-${{ hashFiles('tools/install_musl_gcc.sh') }}
+            ~/.bashrc
+          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('tools/build_gcc_toolchain.sh') }}
 
       - name: Cache build tools
         id: cache-build-tools
@@ -28,7 +29,8 @@ runs:
             ~/.cargo
             ~/.rustup
             ~/.bashrc
-          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.dadk_version }}-${{ hashFiles('.github/workflows/cache-toolchain.yml') }}
+            ~/opt
+          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.dadk_version }}-${{ hashFiles('.github/workflows/cache-toolchain.yml') }}-${{ hashFiles('tools/install_musl_gcc.sh') }}
       
       - uses: ./.github/actions/install-apt-packages
       

+ 6 - 4
.github/workflows/cache-toolchain.yml

@@ -18,14 +18,14 @@ jobs:
           with:
             path: |
               ~/opt
-            key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('tools/build_gcc_toolchain.sh') }}-${{ hashFiles('tools/install_musl_gcc.sh') }}
+              ~/.bashrc
+            key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('tools/build_gcc_toolchain.sh') }}
         
         - if: ${{ steps.cache-dragonos-gcc.outputs.cache-hit != 'true' }}
           name: build dragonos-gcc
           continue-on-error: true
           run: |
               bash tools/build_gcc_toolchain.sh -f
-              bash tools/install_musl_gcc.sh
         
         - uses: ./.github/actions/install-apt-packages
 
@@ -40,13 +40,15 @@ jobs:
               ~/.cargo
               ~/.rustup
               ~/.bashrc
-            key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.dadk_version }}-${{ hashFiles('.github/workflows/cache-toolchain.yml') }}
+              ~/opt
+            key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.dadk_version }}-${{ hashFiles('.github/workflows/cache-toolchain.yml') }}-${{ hashFiles('tools/install_musl_gcc.sh') }}
         
         - if: ${{ steps.cache-build-tools.outputs.cache-hit != 'true' }}
           name: Install toolchain
           continue-on-error: false
           run:  | 
-           
+            USE_GITHUB=1 bash tools/install_musl_gcc.sh
+
             cargo install cargo-binutils
             rustup toolchain install nightly-x86_64-unknown-linux-gnu
             rustup toolchain install nightly-2023-01-21-x86_64-unknown-linux-gnu

+ 12 - 3
.github/workflows/makefile.yml

@@ -2,7 +2,7 @@ name: Build Check
 
 on:
   push:
-    branches: [ "master", "patch-add-riscv64-github-workflow" ]
+    branches: [ "master" ]
   pull_request:
     branches: [ "master" ]
 
@@ -50,6 +50,7 @@ jobs:
     - uses: ./.github/actions/import-toolchain
 
     - name: Run kernel static test
+      shell: bash -ileo pipefail {0}
       env:
           ARCH: ${{ matrix.arch }}
       run: bash -c "source ~/.cargo/env && cd kernel && make test"
@@ -68,7 +69,14 @@ jobs:
     - name: build the DragonOS
       env:
           ARCH: x86_64
-      run: bash -c "source ~/.cargo/env && export DragonOS_GCC=$HOME/opt/dragonos-gcc/gcc-x86_64-unknown-none/bin && make -j $(nproc) "
+      shell: bash -ileo pipefail {0}
+
+      run: |
+        source ~/.bashrc
+        source ~/.cargo/env
+        export DragonOS_GCC=$HOME/opt/dragonos-gcc/gcc-x86_64-unknown-none/bin
+        
+        make -j $(nproc)
 
   
   build-riscv64:
@@ -84,8 +92,9 @@ jobs:
     - uses: ./.github/actions/import-toolchain
 
     - name: build the DragonOS
+      shell: bash -ileo pipefail {0}
       env:
           ARCH: riscv64
 
-      run: bash -c "source ~/.cargo/env && make kernel -j $(nproc)"
+      run: source ~/.bashrc && source ~/.cargo/env && make kernel -j $(nproc)
 

+ 7 - 1
.vscode/settings.json

@@ -172,7 +172,13 @@
         "clocksource.h": "c",
         "ata.h": "c",
         "barrier": "c",
-        "charconv": "c"
+        "charconv": "c",
+        "printf.h": "c",
+        "klog.h": "c",
+        "sqlite3ext.h": "c",
+        "malloc.h": "c",
+        "*.o": "c",
+        "k_log.h": "c"
     },
     "C_Cpp.errorSquiggles": "enabled",
     "esbonio.sphinx.confDir": "",

+ 1 - 0
kernel/crates/kdepends/Cargo.toml

@@ -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" }
 

+ 2 - 0
kernel/crates/kdepends/src/lib.rs

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

+ 1 - 1
kernel/src/arch/x86_64/ipc/signal.rs

@@ -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()
             );
         }
     }

+ 3 - 0
kernel/src/arch/x86_64/mm/mod.rs

@@ -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() {

+ 6 - 2
kernel/src/driver/net/e1000e/e1000e.rs

@@ -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!");
+        }
     }
 }
 

+ 1 - 1
kernel/src/filesystem/fat/fs.rs

@@ -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()
             );
         }
     }

+ 151 - 0
kernel/src/filesystem/procfs/kmsg.rs

@@ -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());
+    }
+}

+ 107 - 0
kernel/src/filesystem/procfs/log.rs

@@ -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);
+    }
+}

+ 21 - 0
kernel/src/filesystem/procfs/mod.rs

@@ -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 => (),
         };
 

+ 77 - 0
kernel/src/filesystem/procfs/syscall.rs

@@ -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),
+        }
+    }
+}

+ 1 - 1
kernel/src/filesystem/vfs/file.rs

@@ -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()
             );
         }
     }

+ 29 - 1
kernel/src/libs/printk.rs

@@ -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) };
+        }
+    }
+}

+ 12 - 2
kernel/src/syscall/mod.rs

@@ -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");

+ 24 - 0
kernel/src/time/mod.rs

@@ -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 {

+ 1 - 1
kernel/src/time/timer.rs

@@ -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()
             );
         }
     }

+ 24 - 4
tools/install_musl_gcc.sh

@@ -3,12 +3,32 @@
 # 该脚本会自动下载musl交叉编译工具链,并将其添加到PATH中
 #########################################################################
 
+export USE_GITHUB=${USE_GITHUB:=0}
+
+
+
 MUSL_GCC_DATE="231114"
 MUSL_GCC_VERSION="9.4.0"
-MUSL_GCC_X86_64_TAR="x86_64-linux-musl-cross-gcc-${MUSL_GCC_VERSION}-${MUSL_GCC_DATE}.tar.xz"
-MUSL_GCC_RISCV64_TAR="riscv64-linux-musl-cross-gcc-${MUSL_GCC_VERSION}-${MUSL_GCC_DATE}.tar.xz"
-MUSL_GCC_X86_64_DOWNLOAD_URL="https://mirrors.dragonos.org.cn/pub/third_party/toolchain/gcc/${MUSL_GCC_X86_64_TAR}"
-MUSL_GCC_RISCV64_DOWNLOAD_URL="https://mirrors.dragonos.org.cn/pub/third_party/toolchain/gcc/${MUSL_GCC_RISCV64_TAR}"
+MUSL_GCC_X86_64_TAR=
+MUSL_GCC_RISCV64_TAR=
+
+MUSL_GCC_X86_64_DOWNLOAD_URL=""
+MUSL_GCC_RISCV64_DOWNLOAD_URL=""
+if [ $USE_GITHUB -eq 1 ]; then
+    echo "Download from github"
+
+    MUSL_GCC_X86_64_TAR=x86_64-linux-musl-cross-gcc-${MUSL_GCC_VERSION}.tar.xz
+    MUSL_GCC_RISCV64_TAR=riscv64-linux-musl-cross-gcc-${MUSL_GCC_VERSION}.tar.xz
+    MUSL_GCC_X86_64_DOWNLOAD_URL="https://github.com/DragonOS-Community/musl-cross-make/releases/download/${MUSL_GCC_VERSION}-${MUSL_GCC_DATE}/${MUSL_GCC_X86_64_TAR}"
+    MUSL_GCC_RISCV64_DOWNLOAD_URL="https://github.com/DragonOS-Community/musl-cross-make/releases/download/${MUSL_GCC_VERSION}-${MUSL_GCC_DATE}/${MUSL_GCC_RISCV64_TAR}"
+    https://github.com/DragonOS-Community/musl-cross-make/releases/download/9.4.0-231114/riscv64-linux-musl-cross-gcc-9.4.0.tar.xz
+else
+    echo "Download from mirrors.dragonos.org.cn"
+    MUSL_GCC_X86_64_TAR="x86_64-linux-musl-cross-gcc-${MUSL_GCC_VERSION}-${MUSL_GCC_DATE}.tar.xz"
+    MUSL_GCC_RISCV64_TAR="riscv64-linux-musl-cross-gcc-${MUSL_GCC_VERSION}-${MUSL_GCC_DATE}.tar.xz"
+    MUSL_GCC_X86_64_DOWNLOAD_URL="https://mirrors.dragonos.org.cn/pub/third_party/toolchain/gcc/${MUSL_GCC_X86_64_TAR}"
+    MUSL_GCC_RISCV64_DOWNLOAD_URL="https://mirrors.dragonos.org.cn/pub/third_party/toolchain/gcc/${MUSL_GCC_RISCV64_TAR}"
+fi
 
 
 INSTALL_POS="$HOME/opt/"

+ 2 - 3
user/Makefile

@@ -51,9 +51,8 @@ endif
 .PHONY: dadk_run
 dadk_run: install_dadk
 	mkdir -p $(DADK_CACHE_DIR)
-# 之所以在这里临时设置ARCH为空,是因为如果要设置这个环境变量,应当在DADK的配置文件中设置
-	ARCH= dadk --config-dir dadk/config --cache-dir $(DADK_CACHE_DIR) --dragonos-dir $(ROOT_PATH)/bin/sysroot build
-	ARCH= dadk --config-dir dadk/config --cache-dir $(DADK_CACHE_DIR) --dragonos-dir $(ROOT_PATH)/bin/sysroot install
+	dadk --config-dir dadk/config --cache-dir $(DADK_CACHE_DIR) --dragonos-dir $(ROOT_PATH)/bin/sysroot build
+	dadk --config-dir dadk/config --cache-dir $(DADK_CACHE_DIR) --dragonos-dir $(ROOT_PATH)/bin/sysroot install
 
 .PHONY: dadk_clean
 dadk_clean: install_dadk

+ 1 - 0
user/apps/dmesg/.gitignore

@@ -0,0 +1 @@
+dmesg

+ 17 - 0
user/apps/dmesg/Makefile

@@ -0,0 +1,17 @@
+ifeq ($(ARCH), x86_64)
+export PREFIX=x86_64-linux-musl-
+else ifeq ($(ARCH), riscv64)
+export PREFIX=riscv64-linux-musl-
+endif
+
+
+export CC=$(PREFIX)gcc
+
+all: dmesg
+	mv dmesg $(DADK_CURRENT_BUILD_DIR)
+
+dmesg: main.c
+	$(CC) -static -o dmesg main.c dmesg.c
+
+clean:
+	rm dmesg *.o

+ 86 - 0
user/apps/dmesg/dmesg.c

@@ -0,0 +1,86 @@
+#include "dmesg.h"
+
+/**
+ * @brief 识别dmesg程序的第一个选项参数
+ *
+ * @param arg dmesg命令第一个选项参数
+ * @return int 有效时返回对应选项码,无效时返回 -1
+ */
+int getopt(char *arg)
+{
+    if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
+        return 0;
+    else if (!strcmp(arg, "-c") || !strcmp(arg, "--read-clear"))
+        return 4;
+    else if (!strcmp(arg, "-C") || !strcmp(arg, "--clear"))
+        return 5;
+    else if (!strcmp(arg, "-l") || !strcmp(arg, "--level"))
+        return 8;
+
+    return -1;
+}
+
+/**
+ * @brief 识别dmesg程序的第二个选项参数
+ *
+ * @param arg dmesg命令第一个选项参数
+ * @return int 有效时返回设置的日志级别,无效时返回 -1
+ */
+int getlevel(char *arg)
+{
+    if (!strcmp(arg, "EMERG") || !strcmp(arg, "emerg"))
+        return 0;
+    else if (!strcmp(arg, "ALERT") || !strcmp(arg, "alert"))
+        return 1;
+    else if (!strcmp(arg, "CRIT") || !strcmp(arg, "crit"))
+        return 2;
+    else if (!strcmp(arg, "ERR") || !strcmp(arg, "err"))
+        return 3;
+    else if (!strcmp(arg, "WARN") || !strcmp(arg, "warn"))
+        return 4;
+    else if (!strcmp(arg, "NOTICE") || !strcmp(arg, "notice"))
+        return 5;
+    else if (!strcmp(arg, "INFO") || !strcmp(arg, "info"))
+        return 6;
+    else if (!strcmp(arg, "DEBUG") || !strcmp(arg, "debug"))
+        return 7;
+    else
+    {
+        printf("dmesg: unknown level '%s'\n", arg);
+    }
+    return -2;
+}
+
+/**
+ * @brief 打印dmesg手册
+ */
+void print_help_msg()
+{
+    const char *help_msg = "Usage:\n"
+                           " dmesg [options]\n\n"
+                           "Display or control the kernel ring buffer.\n\n"
+                           "Options:\n"
+                           " -C, --clear                 clear the kernel ring buffer\n"
+                           " -c, --read-clear            read and clear all messages\n"
+                           " -l, --level <list>          restrict output to defined levels\n"
+                           " -h, --help                  display this help\n\n"
+                           "Supported log levels (priorities):\n"
+                           "   emerg - system is unusable\n"
+                           "   alert - action must be taken immediately\n"
+                           "    crit - critical conditions\n"
+                           "     err - error conditions\n"
+                           "    warn - warning conditions\n"
+                           "  notice - normal but significant condition\n"
+                           "    info - informational\n"
+                           "   debug - debug-level messages\n";
+    printf("%s\n", help_msg);
+}
+
+/**
+ * @brief 打印dmesg错误使用的信息
+ */
+void print_bad_usage_msg()
+{
+    const char *bad_usage_msg = "dmesg: bad usage\nTry 'dmesg --help' for more information.";
+    printf("%s\n", bad_usage_msg);
+}

+ 31 - 0
user/apps/dmesg/dmesg.h

@@ -0,0 +1,31 @@
+#pragma once
+
+#include <stdio.h>
+#include <malloc.h>
+#include <string.h>
+
+/**
+ * @brief 识别dmesg程序的第一个选项参数
+ *
+ * @param arg dmesg命令第一个选项参数
+ * @return int 有效时返回对应选项码,无效时返回 -1
+ */
+int getopt(char *arg);
+
+/**
+ * @brief 识别dmesg程序的第二个选项参数
+ *
+ * @param arg dmesg命令第一个选项参数
+ * @return int 有效时返回设置的日志级别,无效时返回 -1
+ */
+int getlevel(char *arg);
+
+/**
+ * @brief 打印dmesg手册
+ */
+void print_help_msg();
+
+/**
+ * @brief 打印dmesg错误使用的信息
+ */
+void print_bad_usage_msg();

+ 113 - 0
user/apps/dmesg/main.c

@@ -0,0 +1,113 @@
+#include "dmesg.h"
+
+int main(int argc, char **argv)
+{
+    unsigned int len = 1;
+    char *buf = NULL;
+    int opt;
+    unsigned int color = 65280;
+
+    // 获取内核缓冲区大小
+    len = klogctl(10, buf, len);
+
+    if (len < 16 * 1024)
+        len = 16 * 1024;
+    if (len > 16 * 1024 * 1024)
+        len = 16 * 1024 * 1024;
+
+    buf = malloc(len);
+    if (buf == NULL)
+    {
+        perror("");
+        return -1;
+    }
+
+    if (argc == 1)
+    {
+        // 无选项参数,默认打印所有日志消息
+        len = klogctl(2, buf, len);
+    }
+    else
+    {
+        // 获取第一个选项参数
+        opt = getopt(argv[1]);
+
+        // 无效参数
+        if (opt == -1)
+        {
+            print_bad_usage_msg();
+            return -1;
+        }
+        // 打印帮助手册
+        else if (opt == 0)
+        {
+            print_help_msg();
+            return 0;
+        }
+        // 4 -> 读取内核缓冲区后,清空缓冲区
+        // 5 -> 清空内核缓冲区
+        else if (opt == 4 || opt == 5)
+        {
+            len = klogctl(opt, buf, len);
+        }
+        // 读取特定日志级别的消息
+        else if (opt == 8)
+        {
+            // 无指定日志级别参数,打印错误使用信息
+            if (argc < 3)
+            {
+                print_bad_usage_msg();
+                return -1;
+            }
+
+            int level = -1;
+
+            // 获取日志级别
+            // 这里加1的原因是:如果klogctl的第三个参数是0,不会发生系统调用
+            level = getlevel(argv[2]) + 1;
+
+            if (level == -1)
+                return -1;
+
+            klogctl(8, buf, level);
+            len = klogctl(2, buf, len);
+        }
+    }
+
+    // 当前打印内容
+    // 0: 日志级别
+    // 1: 时间戳
+    // 2: 代码行号
+    // 3: 日志消息
+    unsigned int content = 0;
+    for (int i = 0; i < len; i++)
+    {
+        char c[2];
+        c[0] = buf[i];
+        c[1] = '\0';
+        syscall(100000, &c[0], color, 0);
+        if (content == 0 && buf[i] == '>')
+        {
+            content++;
+        }
+        else if (content == 1 && buf[i] == ']')
+        {
+            color = 16744448;
+            content++;
+        }
+        else if (content == 2 && buf[i] == ')')
+        {
+            color = 16777215;
+            content++;
+        }
+        else if (content == 3 && buf[i] == '\n')
+        {
+            color = 65280;
+            content = 0;
+        }
+    }
+
+    free(buf);
+
+    return 0;
+}

+ 23 - 0
user/dadk/config/dmesg-0.1.0.dadk

@@ -0,0 +1,23 @@
+{
+  "name": "dmesg",
+  "version": "0.1.0",
+  "description": "查看日志",
+  "task_type": {
+    "BuildFromSource": {
+      "Local": {
+        "path": "apps/dmesg"
+      }
+    }
+  },
+  "depends": [ ],
+  "build": {
+    "build_command": "make"
+  },
+  "install": {
+    "in_dragonos_path": "/bin"
+  },
+  "clean": {
+    "clean_command": "make clean"
+  },
+  "envs": []
+}