From 7b32f5080f42bcbf7d2421013f3ea53c776a063c Mon Sep 17 00:00:00 2001 From: LoGin Date: Tue, 7 Nov 2023 21:39:27 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=86=85=E5=AD=98=E5=88=86?= =?UTF-8?q?=E9=85=8D=E6=97=A5=E5=BF=97=E7=9B=91=E8=A7=86=E5=99=A8=20(#424)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 完成内存日志监视,并输出日志到文件 * 修复进程退出后,procfs查看进程status文件会崩溃的问题 * 修复signal唤醒进程的判断条件问题 --- .gitignore | 1 + .vscode/settings.json | 3 +- Makefile | 6 +- kernel/Cargo.toml | 12 +- kernel/build.rs | 1 - kernel/crates/crc/src/lib.rs | 19 +- kernel/crates/kdepends/Cargo.toml | 18 ++ kernel/crates/kdepends/src/lib.rs | 9 + kernel/crates/klog_types/Cargo.toml | 9 + kernel/crates/klog_types/src/lib.rs | 241 +++++++++++++++ kernel/src/arch/x86_64/process/mod.rs | 2 +- kernel/src/arch/x86_64/smp/mod.rs | 2 +- kernel/src/debug/klog/mm.rs | 78 +++++ kernel/src/debug/klog/mod.rs | 1 + kernel/src/debug/mod.rs | 1 + .../driver/base/platform/platform_device.rs | 2 +- kernel/src/driver/tty/mod.rs | 2 +- kernel/src/filesystem/procfs/mod.rs | 33 +- kernel/src/ipc/signal.rs | 34 ++- kernel/src/lib.rs | 5 +- kernel/src/libs/ida/src/lib.rs | 4 +- kernel/src/mm/allocator/kernel_allocator.rs | 38 ++- kernel/src/process/mod.rs | 10 + tools/Cargo.toml | 6 + tools/Makefile | 9 + tools/debugging/logmonitor/.gitignore | 1 + tools/debugging/logmonitor/Cargo.toml | 17 ++ tools/debugging/logmonitor/README.md | 10 + tools/debugging/logmonitor/src/app.rs | 212 +++++++++++++ .../debugging/logmonitor/src/backend/error.rs | 38 +++ .../debugging/logmonitor/src/backend/event.rs | 12 + .../logmonitor/src/backend/loader.rs | 122 ++++++++ tools/debugging/logmonitor/src/backend/mod.rs | 127 ++++++++ .../logmonitor/src/backend/monitor/logset.rs | 113 +++++++ .../logmonitor/src/backend/monitor/mm.rs | 286 ++++++++++++++++++ .../logmonitor/src/backend/monitor/mod.rs | 45 +++ tools/debugging/logmonitor/src/command.rs | 32 ++ .../debugging/logmonitor/src/constant/mod.rs | 6 + tools/debugging/logmonitor/src/event.rs | 83 +++++ tools/debugging/logmonitor/src/handler.rs | 35 +++ tools/debugging/logmonitor/src/lib.rs | 25 ++ tools/debugging/logmonitor/src/logging.rs | 51 ++++ tools/debugging/logmonitor/src/main.rs | 98 ++++++ tools/debugging/logmonitor/src/tui.rs | 77 +++++ tools/debugging/logmonitor/src/ui.rs | 134 ++++++++ tools/run-qemu.sh | 22 +- 46 files changed, 2033 insertions(+), 59 deletions(-) create mode 100644 kernel/crates/kdepends/Cargo.toml create mode 100644 kernel/crates/kdepends/src/lib.rs create mode 100644 kernel/crates/klog_types/Cargo.toml create mode 100644 kernel/crates/klog_types/src/lib.rs create mode 100644 kernel/src/debug/klog/mm.rs create mode 100644 kernel/src/debug/klog/mod.rs create mode 100644 kernel/src/debug/mod.rs create mode 100644 tools/Cargo.toml create mode 100644 tools/Makefile create mode 100644 tools/debugging/logmonitor/.gitignore create mode 100644 tools/debugging/logmonitor/Cargo.toml create mode 100644 tools/debugging/logmonitor/README.md create mode 100644 tools/debugging/logmonitor/src/app.rs create mode 100644 tools/debugging/logmonitor/src/backend/error.rs create mode 100644 tools/debugging/logmonitor/src/backend/event.rs create mode 100644 tools/debugging/logmonitor/src/backend/loader.rs create mode 100644 tools/debugging/logmonitor/src/backend/mod.rs create mode 100644 tools/debugging/logmonitor/src/backend/monitor/logset.rs create mode 100644 tools/debugging/logmonitor/src/backend/monitor/mm.rs create mode 100644 tools/debugging/logmonitor/src/backend/monitor/mod.rs create mode 100644 tools/debugging/logmonitor/src/command.rs create mode 100644 tools/debugging/logmonitor/src/constant/mod.rs create mode 100644 tools/debugging/logmonitor/src/event.rs create mode 100644 tools/debugging/logmonitor/src/handler.rs create mode 100644 tools/debugging/logmonitor/src/lib.rs create mode 100644 tools/debugging/logmonitor/src/logging.rs create mode 100644 tools/debugging/logmonitor/src/main.rs create mode 100644 tools/debugging/logmonitor/src/tui.rs create mode 100644 tools/debugging/logmonitor/src/ui.rs diff --git a/.gitignore b/.gitignore index b2aeedff..f3428642 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ cppcheck.xml Cargo.lock .cache compile_commands.json +/logs/ diff --git a/.vscode/settings.json b/.vscode/settings.json index 27244317..9c0c36db 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -177,11 +177,10 @@ }, "C_Cpp.errorSquiggles": "enabled", "esbonio.sphinx.confDir": "", - "rust-analyzer.cargo.target": "x86_64-unknown-none", "rust-analyzer.checkOnSave.allTargets": false, "rust-analyzer.linkedProjects": [ "./kernel/Cargo.toml", - "./kernel/src/libs/ida/Cargo.toml" + "./tools/Cargo.toml", ], "rust-analyzer.check.overrideCommand": [ "make", diff --git a/Makefile b/Makefile index 39316c44..f709ce38 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -SUBDIRS = kernel user +SUBDIRS = kernel user tools # ifndef $(EMULATOR) ifeq ($(EMULATOR), ) @@ -156,6 +156,10 @@ fmt: @echo "格式化代码" FMT_CHECK=$(FMT_CHECK) $(MAKE) fmt -C kernel FMT_CHECK=$(FMT_CHECK) $(MAKE) fmt -C user + +log-monitor: + @echo "启动日志监控" + @sh -c "cd tools/debugging/logmonitor && cargo run --release -- --log-dir $(ROOT_PATH)/logs/ --kernel $(ROOT_PATH)/bin/kernel/kernel.elf" help: @echo "编译:" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index faf47f4c..69f24627 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -10,13 +10,17 @@ edition = "2021" crate-type = ["staticlib"] [workspace] -members = [ "src/libs/intertrait" ] +members = [ + "crates/*", + "src/libs/intertrait" +] [features] default = ["backtrace"] # 内核栈回溯 backtrace = [] + # 运行时依赖项 [dependencies] x86 = "0.52.0" @@ -25,8 +29,6 @@ bit_field = "0.10" bitflags = "1.3.2" bitfield-struct = "0.5.3" virtio-drivers = { git = "https://git.mirrors.dragonos.org/DragonOS-Community/virtio-drivers.git", rev = "f1d1cbb" } -# 一个无锁MPSC队列 -thingbuf = { version = "0.1.3", default-features = false, features = ["alloc"] } smoltcp = { git = "https://git.mirrors.dragonos.org/DragonOS-Community/smoltcp.git", rev = "9027825", default-features = false, features = ["log", "alloc", "socket-raw", "socket-udp", "socket-tcp", "socket-icmp", "socket-dhcpv4", "socket-dns", "proto-ipv4", "proto-ipv6"]} # num-traits 0.2.15 num-traits = { git = "https://git.mirrors.dragonos.org/DragonOS-Community/num-traits.git", rev="1597c1c", default-features = false } @@ -35,7 +37,6 @@ num-derive = "0.3" # 一个no_std的hashmap、hashset hashbrown = "0.13.2" elf = { version = "0.7.2", default-features = false } -memoffset = "0.9.0" atomic_enum = "0.2.0" raw-cpuid = "11.0.1" acpi = { git = "https://git.mirrors.dragonos.org/DragonOS-Community/acpi-rs.git", rev = "fb69243dcf" } @@ -43,6 +44,9 @@ intertrait = { path = "src/libs/intertrait" } linkme = "0.2" ida = { path = "src/libs/ida" } mini-backtrace = { git = "https://git.mirrors.dragonos.org/DragonOS-Community/mini-backtrace.git", rev = "ba98506685" } +klog_types = { path = "crates/klog_types" } +kdepends = { path = "crates/kdepends" } + # 构建时依赖项 [build-dependencies] diff --git a/kernel/build.rs b/kernel/build.rs index da452751..d1e58aa5 100644 --- a/kernel/build.rs +++ b/kernel/build.rs @@ -90,7 +90,6 @@ impl CFilesBuilder { #[cfg(target_arch = "x86_64")] c.define("__x86_64__", None); } - } fn setup_global_include_dir(c: &mut Build) { diff --git a/kernel/crates/crc/src/lib.rs b/kernel/crates/crc/src/lib.rs index cf4ba68a..1ce97b1c 100644 --- a/kernel/crates/crc/src/lib.rs +++ b/kernel/crates/crc/src/lib.rs @@ -1,22 +1,9 @@ #![cfg_attr(not(test), no_std)] - +#![feature(const_for)] +#![feature(const_mut_refs)] +#![feature(const_trait_impl)] #[cfg(test)] extern crate std; pub mod crc64; pub mod tables; - -pub fn add(left: usize, right: usize) -> usize { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} diff --git a/kernel/crates/kdepends/Cargo.toml b/kernel/crates/kdepends/Cargo.toml new file mode 100644 index 00000000..25062aad --- /dev/null +++ b/kernel/crates/kdepends/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "kdepends" +version = "0.1.0" +edition = "2021" +description = "需要导出的依赖项(为保持内核依赖版本与调试器依赖项版本相同,因此把公共依赖项写在这里)" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +memoffset = "0.9.0" +crc = { path = "../crc" } + +# 一个无锁MPSC队列 +[dependencies.thingbuf] +git = "https://git.mirrors.dragonos.org/DragonOS-Community/thingbuf.git" +rev = "2dded730c3" +default-features = false +features = ["alloc", "static"] diff --git a/kernel/crates/kdepends/src/lib.rs b/kernel/crates/kdepends/src/lib.rs new file mode 100644 index 00000000..6129ec46 --- /dev/null +++ b/kernel/crates/kdepends/src/lib.rs @@ -0,0 +1,9 @@ +#![no_std] + +#[allow(unused)] +#[macro_use] +pub extern crate thingbuf; + +pub extern crate memoffset; + +pub extern crate crc; diff --git a/kernel/crates/klog_types/Cargo.toml b/kernel/crates/klog_types/Cargo.toml new file mode 100644 index 00000000..5081ea7a --- /dev/null +++ b/kernel/crates/klog_types/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "klog_types" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +kdepends = { path = "../kdepends" } diff --git a/kernel/crates/klog_types/src/lib.rs b/kernel/crates/klog_types/src/lib.rs new file mode 100644 index 00000000..877d3386 --- /dev/null +++ b/kernel/crates/klog_types/src/lib.rs @@ -0,0 +1,241 @@ +#![no_std] +#![feature(const_refs_to_cell)] +#![feature(const_size_of_val)] + +extern crate alloc; +use core::{fmt::Debug, mem::size_of_val}; + +use alloc::format; +use kdepends::{memoffset::offset_of, thingbuf::StaticThingBuf}; + +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct AllocatorLog { + /// 日志的id + pub id: u64, + /// 日志类型 + pub type_: AllocatorLogType, + /// 日志的时间 + pub time: u64, + + /// 日志的来源 + pub source: LogSource, + + /// 日志的来源pid + pub pid: Option, + + pub checksum: u64, +} + +impl AllocatorLog { + /// 创建一个日志 + /// + /// ## 参数 + /// + /// - `id`:日志的id + /// - `type_`:日志类型 + /// - `source`:日志来源 + /// - `pid`:日志来源的pid + /// - `time`:日志的时间 + pub fn new( + id: u64, + type_: AllocatorLogType, + source: LogSource, + pid: Option, + time: u64, + ) -> Self { + let mut x = Self { + id, + type_, + time, + source, + pid, + checksum: 0, + }; + let checksum = Self::calculate_checksum(&x); + x.checksum = checksum; + return x; + } + + pub const fn zeroed() -> Self { + return Self { + id: 0, + type_: AllocatorLogType::Undefined, + time: 0, + source: LogSource::Undefined, + pid: None, + checksum: 0, + }; + } + + /// 计算日志的校验和 + pub fn calculate_checksum(value: &Self) -> u64 { + let buf = unsafe { + core::slice::from_raw_parts( + value as *const _ as *const u8, + core::mem::size_of::() - core::mem::size_of::(), + ) + }; + let checksum = kdepends::crc::crc64::crc64_be(0, buf); + return checksum; + } + + /// 验证日志的校验和 + pub fn validate_checksum(&self) -> bool { + let checksum = Self::calculate_checksum(self); + return checksum == self.checksum; + } + + /// 当前日志是否有效 + pub fn is_valid(&self) -> bool { + if self.validate_checksum() == false { + return false; + } + + if self.id == 0 { + return false; + } + + return true; + } +} + +impl PartialOrd for AllocatorLog { + fn partial_cmp(&self, other: &Self) -> Option { + return self.id.partial_cmp(&other.id); + } +} + +impl Ord for AllocatorLog { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + return self.id.cmp(&other.id); + } +} + +/// 内存分配器日志类型 +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum AllocatorLogType { + Undefined, + Alloc(AllocLogItem), + AllocZeroed(AllocLogItem), + Free(AllocLogItem), +} + +#[repr(C)] +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct AllocLogItem { + pub layout: core::alloc::Layout, + pub vaddr: Option, + pub paddr: Option, +} + +impl AllocLogItem { + pub fn new(layout: core::alloc::Layout, vaddr: Option, paddr: Option) -> Self { + return Self { + layout, + vaddr, + paddr, + }; + } +} + +impl Debug for AllocLogItem { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("AllocLogItem") + .field("layout", &self.layout) + .field( + "vaddr", + &format_args!("{:#x}", *self.vaddr.as_ref().unwrap_or(&0)), + ) + .field( + "paddr", + &format_args!("{:#x}", self.paddr.as_ref().unwrap_or(&0)), + ) + .finish() + } +} + +#[repr(u8)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum LogSource { + Undefined = 0, + Bump = 1, + Buddy = 2, + Slab = 3, +} + +pub struct MMLogCycle; + +impl MMLogCycle { + pub const fn new() -> Self { + Self {} + } +} + +impl kdepends::thingbuf::Recycle for MMLogCycle { + fn new_element(&self) -> AllocatorLog { + AllocatorLog::zeroed() + } + + fn recycle(&self, element: &mut AllocatorLog) { + *element = AllocatorLog::zeroed(); + } +} + +/// 内存分配器日志通道 +#[repr(C)] +pub struct MMLogChannel { + pub magic: u32, + /// 日志元素的大小 + pub element_size: u32, + /// 日志通道每个槽的大小(字节) + pub slot_size: u32, + pub capacity: u64, + pub slots_offset: u64, + pub buf: StaticThingBuf, +} + +impl Debug for MMLogChannel { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("MMLogChannel") + .field("magic", &format!("{:#x}", self.magic)) + .field("element_size", &self.element_size) + .field("capacity", &self.capacity) + .field("slots_offset", &self.slots_offset) + .field( + "buf", + &format!( + "StaticThingBuf", + self.capacity + ), + ) + .finish() + } +} + +impl MMLogChannel { + /// 日志通道的魔数 + pub const MM_LOG_CHANNEL_MAGIC: u32 = 0x4d4c4348; + + /// 创建一个大小为`capacity`日志通道 + pub const fn new(capacity: usize) -> Self { + let buffer = StaticThingBuf::with_recycle(MMLogCycle::new()); + assert!(buffer.offset_of_slots() != 0); + let slot_total_size = size_of_val(&buffer) - buffer.offset_of_slots(); + let slot_size = slot_total_size / capacity; + assert!(slot_size != 0); + assert!(slot_size > size_of_val(&AllocatorLog::zeroed())); + + let r = Self { + magic: Self::MM_LOG_CHANNEL_MAGIC, + element_size: core::mem::size_of::() as u32, + capacity: capacity as u64, + slot_size: slot_size as u32, + slots_offset: (offset_of!(MMLogChannel, buf) + buffer.offset_of_slots()) as u64, + buf: buffer, + }; + + return r; + } +} diff --git a/kernel/src/arch/x86_64/process/mod.rs b/kernel/src/arch/x86_64/process/mod.rs index 23c2a955..358d8be2 100644 --- a/kernel/src/arch/x86_64/process/mod.rs +++ b/kernel/src/arch/x86_64/process/mod.rs @@ -11,7 +11,7 @@ use alloc::{ vec::Vec, }; -use memoffset::offset_of; +use kdepends::memoffset::offset_of; use x86::{controlregs::Cr4, segmentation::SegmentSelector}; use crate::{ diff --git a/kernel/src/arch/x86_64/smp/mod.rs b/kernel/src/arch/x86_64/smp/mod.rs index 3e04c63b..8e1553c3 100644 --- a/kernel/src/arch/x86_64/smp/mod.rs +++ b/kernel/src/arch/x86_64/smp/mod.rs @@ -4,7 +4,7 @@ use core::{ sync::atomic::{compiler_fence, AtomicBool, Ordering}, }; -use memoffset::offset_of; +use kdepends::memoffset::offset_of; use crate::{ arch::process::table::TSSManager, exception::InterruptArch, diff --git a/kernel/src/debug/klog/mm.rs b/kernel/src/debug/klog/mm.rs new file mode 100644 index 00000000..f970e8d9 --- /dev/null +++ b/kernel/src/debug/klog/mm.rs @@ -0,0 +1,78 @@ +extern crate klog_types; + +use core::intrinsics::unlikely; + +use klog_types::{AllocatorLog, AllocatorLogType, LogSource, MMLogChannel}; + +use crate::{ + arch::CurrentTimeArch, + process::{Pid, ProcessManager}, + time::TimeArch, +}; + +/// 全局的内存分配器日志通道 +/// +/// 标记为`no_mangle`是为了让调试器能够找到这个变量 +#[no_mangle] +static __MM_ALLOCATOR_LOG_CHANNEL: MMLogChannel<{ MMDebugLogManager::MAX_ALLOC_LOG_NUM }> = + MMLogChannel::new(MMDebugLogManager::MAX_ALLOC_LOG_NUM); + +/// 全局的内存分配器日志id分配器 +/// +/// id从1开始, 因为0是无效的id +static __MM_DEBUG_LOG_IDA: ida::IdAllocator = ida::IdAllocator::new(1, usize::MAX); + +/// 记录内存分配器的日志 +/// +/// ## 参数 +/// +/// - `log_type`:日志类型 +/// - `source`:日志来源 +pub fn mm_debug_log(log_type: AllocatorLogType, source: LogSource) { + let pid = if unlikely(!ProcessManager::initialized()) { + Some(Pid::new(0)) + } else { + Some(ProcessManager::current_pcb().pid()) + }; + MMDebugLogManager::log(log_type, source, pid); +} + +#[derive(Debug)] +pub(super) struct MMDebugLogManager; + +impl MMDebugLogManager { + /// 最大的内存分配器日志数量 + pub const MAX_ALLOC_LOG_NUM: usize = 100000; + + /// 记录内存分配器的日志 + /// + /// ## 参数 + /// + /// - `log_type`:日志类型 + /// - `source`:日志来源 + /// - `pid`:日志来源的pid + pub fn log(log_type: AllocatorLogType, source: LogSource, pid: Option) { + let id = __MM_DEBUG_LOG_IDA.alloc().unwrap(); + let log = AllocatorLog::new( + id as u64, + log_type, + source, + pid.map(|p| p.data()), + CurrentTimeArch::get_cycles() as u64, + ); + + let mut log = log; + loop { + let r = __MM_ALLOCATOR_LOG_CHANNEL.buf.push(log); + if let Err(r) = r { + // 如果日志通道满了,就把最早的日志丢弃 + if __MM_ALLOCATOR_LOG_CHANNEL.buf.remaining() == 0 { + __MM_ALLOCATOR_LOG_CHANNEL.buf.pop(); + } + log = r.into_inner(); + } else { + break; + } + } + } +} diff --git a/kernel/src/debug/klog/mod.rs b/kernel/src/debug/klog/mod.rs new file mode 100644 index 00000000..6d106477 --- /dev/null +++ b/kernel/src/debug/klog/mod.rs @@ -0,0 +1 @@ +pub mod mm; diff --git a/kernel/src/debug/mod.rs b/kernel/src/debug/mod.rs new file mode 100644 index 00000000..5516f607 --- /dev/null +++ b/kernel/src/debug/mod.rs @@ -0,0 +1 @@ +pub mod klog; diff --git a/kernel/src/driver/base/platform/platform_device.rs b/kernel/src/driver/base/platform/platform_device.rs index 3fa1893c..4b4bb28f 100644 --- a/kernel/src/driver/base/platform/platform_device.rs +++ b/kernel/src/driver/base/platform/platform_device.rs @@ -26,7 +26,7 @@ use crate::{ use super::{super::device::DeviceState, platform_bus, platform_bus_device, CompatibleTable}; /// 平台设备id分配器 -static PLATFORM_DEVID_IDA: IdAllocator = IdAllocator::new(i32::MAX as usize); +static PLATFORM_DEVID_IDA: IdAllocator = IdAllocator::new(0, i32::MAX as usize); #[inline(always)] pub fn platform_device_manager() -> &'static PlatformDeviceManager { diff --git a/kernel/src/driver/tty/mod.rs b/kernel/src/driver/tty/mod.rs index 65a68d6e..c32bd6b4 100644 --- a/kernel/src/driver/tty/mod.rs +++ b/kernel/src/driver/tty/mod.rs @@ -2,7 +2,7 @@ use core::intrinsics::unlikely; use alloc::string::String; -use thingbuf::mpsc::{ +use kdepends::thingbuf::mpsc::{ self, errors::{TryRecvError, TrySendError}, }; diff --git a/kernel/src/filesystem/procfs/mod.rs b/kernel/src/filesystem/procfs/mod.rs index 59736267..1e5e74c3 100644 --- a/kernel/src/filesystem/procfs/mod.rs +++ b/kernel/src/filesystem/procfs/mod.rs @@ -179,23 +179,24 @@ impl ProcFSInode { ); pdata.append(&mut format!("\nvrtime:\t{}", vrtime).as_bytes().to_owned()); - let binding = pcb.basic().user_vm().unwrap(); - let address_space_guard = binding.read(); - // todo: 当前进程运行过程中占用内存的峰值 - let hiwater_vm: u64 = 0; - // 进程代码段的大小 - let text = (address_space_guard.end_code - address_space_guard.start_code) / 1024; - // 进程数据段的大小 - let data = (address_space_guard.end_data - address_space_guard.start_data) / 1024; - drop(address_space_guard); + if let Some(user_vm) = pcb.basic().user_vm() { + let address_space_guard = user_vm.read(); + // todo: 当前进程运行过程中占用内存的峰值 + let hiwater_vm: u64 = 0; + // 进程代码段的大小 + let text = (address_space_guard.end_code - address_space_guard.start_code) / 1024; + // 进程数据段的大小 + let data = (address_space_guard.end_data - address_space_guard.start_data) / 1024; + drop(address_space_guard); + pdata.append( + &mut format!("\nVmPeak:\t{} kB", hiwater_vm) + .as_bytes() + .to_owned(), + ); + pdata.append(&mut format!("\nVmData:\t{} kB", data).as_bytes().to_owned()); + pdata.append(&mut format!("\nVmExe:\t{} kB", text).as_bytes().to_owned()); + } - pdata.append( - &mut format!("\nVmPeak:\t{} kB", hiwater_vm) - .as_bytes() - .to_owned(), - ); - pdata.append(&mut format!("\nVmData:\t{} kB", data).as_bytes().to_owned()); - pdata.append(&mut format!("\nVmExe:\t{} kB", text).as_bytes().to_owned()); pdata.append( &mut format!("\nflags: {:?}\n", pcb.flags().clone()) .as_bytes() diff --git a/kernel/src/ipc/signal.rs b/kernel/src/ipc/signal.rs index a3261cd6..b47dd001 100644 --- a/kernel/src/ipc/signal.rs +++ b/kernel/src/ipc/signal.rs @@ -50,10 +50,13 @@ impl Signal { kwarn!("No such process."); return retval; } + + let pcb = pcb.unwrap(); // println!("Target pcb = {:?}", pcb.as_ref().unwrap()); compiler_fence(core::sync::atomic::Ordering::SeqCst); // 发送信号 - retval = self.send_signal(info, pcb.unwrap(), PidType::PID); + retval = self.send_signal(info, pcb.clone(), PidType::PID); + compiler_fence(core::sync::atomic::Ordering::SeqCst); return retval; } @@ -282,8 +285,33 @@ fn signal_wake_up(pcb: Arc, _guard: SpinLockGuard Self { + pub const fn new(initial_id: usize, max_id: usize) -> Self { Self { - current_id: AtomicUsize::new(0), + current_id: AtomicUsize::new(initial_id), max_id, dead: AtomicBool::new(false), } diff --git a/kernel/src/mm/allocator/kernel_allocator.rs b/kernel/src/mm/allocator/kernel_allocator.rs index 8069c047..7fbe547d 100644 --- a/kernel/src/mm/allocator/kernel_allocator.rs +++ b/kernel/src/mm/allocator/kernel_allocator.rs @@ -1,5 +1,8 @@ +use klog_types::AllocLogItem; + use crate::{ arch::mm::LockedFrameAllocator, + debug::klog::mm::mm_debug_log, libs::align::page_align_up, mm::{MMArch, MemoryManagementArch, VirtAddr}, }; @@ -81,15 +84,46 @@ impl LocalAlloc for KernelAllocator { /// 为内核slab分配器实现GlobalAlloc特性 unsafe impl GlobalAlloc for KernelAllocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - return self.local_alloc(layout); + let r = self.local_alloc(layout); + mm_debug_log( + klog_types::AllocatorLogType::Alloc(AllocLogItem::new( + layout.clone(), + Some(r as usize), + None, + )), + klog_types::LogSource::Buddy, + ); + + return r; + // self.local_alloc_zeroed(layout, 0) } unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - self.local_alloc_zeroed(layout) + let r = self.local_alloc_zeroed(layout); + + mm_debug_log( + klog_types::AllocatorLogType::AllocZeroed(AllocLogItem::new( + layout.clone(), + Some(r as usize), + None, + )), + klog_types::LogSource::Buddy, + ); + + return r; } unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + mm_debug_log( + klog_types::AllocatorLogType::Free(AllocLogItem::new( + layout.clone(), + Some(ptr as usize), + None, + )), + klog_types::LogSource::Buddy, + ); + self.local_dealloc(ptr, layout); } } diff --git a/kernel/src/process/mod.rs b/kernel/src/process/mod.rs index ac9a3813..028b94d4 100644 --- a/kernel/src/process/mod.rs +++ b/kernel/src/process/mod.rs @@ -121,6 +121,11 @@ impl ProcessManager { kinfo!("Process Manager initialized."); } + /// 判断进程管理器是否已经初始化完成 + pub fn initialized() -> bool { + unsafe { __PROCESS_MANAGEMENT_INIT_DONE } + } + /// 获取当前进程的pcb pub fn current_pcb() -> Arc { if unlikely(unsafe { !__PROCESS_MANAGEMENT_INIT_DONE }) { @@ -461,6 +466,11 @@ impl ProcessState { return matches!(self, ProcessState::Blocked(_)); } + #[inline(always)] + pub fn is_blocked_interruptable(&self) -> bool { + return matches!(self, ProcessState::Blocked(true)); + } + #[inline(always)] pub fn is_exited(&self) -> bool { return matches!(self, ProcessState::Exited(_)); diff --git a/tools/Cargo.toml b/tools/Cargo.toml new file mode 100644 index 00000000..7a532e97 --- /dev/null +++ b/tools/Cargo.toml @@ -0,0 +1,6 @@ +[workspace] +members = [ + "debugging/logmonitor", +] + +resolver = "2" \ No newline at end of file diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 00000000..93bb1ab2 --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,9 @@ +.PHONY: fmt +fmt: + @cargo fmt --all $(FMT_CHECK) + +clean: + @cargo clean + +check: + @cargo check --all \ No newline at end of file diff --git a/tools/debugging/logmonitor/.gitignore b/tools/debugging/logmonitor/.gitignore new file mode 100644 index 00000000..bf119211 --- /dev/null +++ b/tools/debugging/logmonitor/.gitignore @@ -0,0 +1 @@ +/logs/ \ No newline at end of file diff --git a/tools/debugging/logmonitor/Cargo.toml b/tools/debugging/logmonitor/Cargo.toml new file mode 100644 index 00000000..d2b6d585 --- /dev/null +++ b/tools/debugging/logmonitor/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "logmonitor" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +klog_types = { path = "../../../kernel/crates/klog_types" } +crossterm = "0.26.1" +ratatui = "0.24.0" +clap = { version = "4.4.7", features = ["color", "error-context", "help", "std", "suggestions", "usage", "derive"] } +rand = "0.8.5" +goblin = "0.7.1" +simple_logger = { git = "https://git.mirrors.dragonos.org/DragonOS-Community/rust-simple_logger.git", "rev" = "36ab404868" } +log = "0.4.20" +lazy_static = "1.4.0" diff --git a/tools/debugging/logmonitor/README.md b/tools/debugging/logmonitor/README.md new file mode 100644 index 00000000..4820babc --- /dev/null +++ b/tools/debugging/logmonitor/README.md @@ -0,0 +1,10 @@ +# 日志监视程序 + +本程序监视DragonOS内核的环形缓冲区日志,并将其显示在屏幕上。 + + +## 使用方法 + +1. 默认情况下,DragonOS内核已启用内存分配器的日志记录。 +2. 当qemu启动后,在DragonOS项目的根目录中,运行`make log-monitor`。 +3. 在`logs`目录查看日志文件。 diff --git a/tools/debugging/logmonitor/src/app.rs b/tools/debugging/logmonitor/src/app.rs new file mode 100644 index 00000000..ab652a0b --- /dev/null +++ b/tools/debugging/logmonitor/src/app.rs @@ -0,0 +1,212 @@ +use std::error; + +use rand::{distributions::Uniform, prelude::Distribution, rngs::ThreadRng}; +use ratatui::widgets::ListState; + +/// Application result type. +pub type AppResult = std::result::Result>; + +/// Application. +#[derive(Debug)] +pub struct App<'a> { + /// APP的标题 + pub title: &'a str, + /// Is the application running? + pub running: bool, + + pub enhanced_graphics: bool, + + /// counter + pub counter: u8, + + pub tabs: TabsState<'a>, + + pub memory_log_sparkline: Signal, + + logs: Vec, + pub stateful_logs: StatefulList<(&'a str, &'a str)>, + + backend_log_receiver: Option>, +} + +impl<'a> App<'a> { + /// Constructs a new instance of [`App`]. + pub fn new(title: &'a str) -> Self { + let mut rand_signal = RandomSignal::new(0, 100); + let sparkline_points = rand_signal.by_ref().take(300).collect(); + let sparkline = Signal { + source: rand_signal, + points: sparkline_points, + tick_rate: 1, + }; + + Self { + title, + running: true, + enhanced_graphics: true, + counter: 0, + tabs: TabsState::new(vec!["Tab0", "Tab1", "Tab2"]), + memory_log_sparkline: sparkline, + logs: Vec::new(), + stateful_logs: StatefulList::with_items(vec![]), + backend_log_receiver: None, + } + } + + pub fn set_backend_log_receiver(&mut self, receiver: std::sync::mpsc::Receiver) { + self.backend_log_receiver = Some(receiver); + } + + /// Handles the tick event of the terminal. + pub fn tick(&mut self) { + self.memory_log_sparkline.on_tick(); + self.handle_logs_on_tick(); + } + + /// 当到达tick时,处理日志 + fn handle_logs_on_tick(&mut self) { + let logs_to_push = self + .backend_log_receiver + .as_ref() + .map(|rv| rv.try_iter().collect::>()); + + if let Some(logs) = logs_to_push { + for log in logs { + self.push_log(log); + } + } + } + + /// Set running to false to quit the application. + pub fn quit(&mut self) { + self.running = false; + } + + pub fn increment_counter(&mut self) { + if let Some(res) = self.counter.checked_add(1) { + self.counter = res; + } + } + + pub fn decrement_counter(&mut self) { + if let Some(res) = self.counter.checked_sub(1) { + self.counter = res; + } + } + + pub fn push_log(&mut self, log: String) { + self.logs.push(log); + } + + pub fn logs(&self) -> &Vec { + &self.logs + } +} + +#[derive(Debug)] +pub struct TabsState<'a> { + pub titles: Vec<&'a str>, + pub index: usize, +} + +impl<'a> TabsState<'a> { + pub fn new(titles: Vec<&'a str>) -> TabsState { + TabsState { titles, index: 0 } + } + pub fn next(&mut self) { + self.index = (self.index + 1) % self.titles.len(); + } + + pub fn previous(&mut self) { + if self.index > 0 { + self.index -= 1; + } else { + self.index = self.titles.len() - 1; + } + } +} + +#[derive(Clone, Debug)] +pub struct Signal { + source: S, + pub points: Vec, + tick_rate: usize, +} + +impl Signal +where + S: Iterator, +{ + fn on_tick(&mut self) { + for _ in 0..self.tick_rate { + self.points.remove(0); + } + self.points + .extend(self.source.by_ref().take(self.tick_rate)); + } +} + +#[derive(Clone, Debug)] +pub struct RandomSignal { + distribution: Uniform, + rng: ThreadRng, +} + +impl RandomSignal { + pub fn new(lower: u64, upper: u64) -> RandomSignal { + RandomSignal { + distribution: Uniform::new(lower, upper), + rng: rand::thread_rng(), + } + } +} + +impl Iterator for RandomSignal { + type Item = u64; + fn next(&mut self) -> Option { + Some(self.distribution.sample(&mut self.rng)) + } +} + +#[derive(Debug)] +pub struct StatefulList { + pub state: ListState, + pub items: Vec, +} + +impl StatefulList { + pub fn with_items(items: Vec) -> StatefulList { + StatefulList { + state: ListState::default(), + items, + } + } + + pub fn next(&mut self) { + let i = match self.state.selected() { + Some(i) => { + if i >= self.items.len() - 1 { + 0 + } else { + i + 1 + } + } + None => 0, + }; + self.state.select(Some(i)); + } + + pub fn previous(&mut self) { + let i = match self.state.selected() { + Some(i) => { + if i == 0 { + self.items.len() - 1 + } else { + i - 1 + } + } + None => 0, + }; + self.state.select(Some(i)); + } +} diff --git a/tools/debugging/logmonitor/src/backend/error.rs b/tools/debugging/logmonitor/src/backend/error.rs new file mode 100644 index 00000000..e80a54a6 --- /dev/null +++ b/tools/debugging/logmonitor/src/backend/error.rs @@ -0,0 +1,38 @@ +use std::{error::Error, fmt::Display}; + +#[derive(Debug)] +pub enum BackendErrorKind { + FileNotFound, + KernelLoadError, +} + +#[derive(Debug)] +pub struct BackendError { + kind: BackendErrorKind, + message: Option, +} + +impl BackendError { + pub fn new(kind: BackendErrorKind, message: Option) -> Self { + Self { kind, message } + } +} + +impl Error for BackendError {} + +impl Display for BackendError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + BackendErrorKind::FileNotFound => { + write!(f, "File not found: {:?}", self.message.as_ref().unwrap()) + } + BackendErrorKind::KernelLoadError => { + write!( + f, + "Failed to load kernel: {:?}", + self.message.as_ref().unwrap() + ) + } + } + } +} diff --git a/tools/debugging/logmonitor/src/backend/event.rs b/tools/debugging/logmonitor/src/backend/event.rs new file mode 100644 index 00000000..3c02ac9b --- /dev/null +++ b/tools/debugging/logmonitor/src/backend/event.rs @@ -0,0 +1,12 @@ +#[derive(Debug, Clone)] +pub enum BackendEvent { + StartUp(StartUpEvent), +} + +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub struct StartUpEvent { + started: bool, + message: String, + timestamp: std::time::Instant, +} diff --git a/tools/debugging/logmonitor/src/backend/loader.rs b/tools/debugging/logmonitor/src/backend/loader.rs new file mode 100644 index 00000000..ed57e527 --- /dev/null +++ b/tools/debugging/logmonitor/src/backend/loader.rs @@ -0,0 +1,122 @@ +use std::{ops::Deref, path::PathBuf}; + +use goblin::elf::Sym; +use log::info; + +use crate::app::AppResult; + +use super::error::{BackendError, BackendErrorKind}; + +#[derive(Debug)] +pub struct KernelLoader; + +impl KernelLoader { + pub fn load(kernel: &PathBuf) -> AppResult { + info!("Loading kernel: {:?}", kernel); + let kernel_bytes = std::fs::read(kernel)?; + let elf = goblin::elf::Elf::parse(&kernel_bytes).map_err(|e| { + BackendError::new( + BackendErrorKind::KernelLoadError, + Some(format!("Failed to load kernel: {:?}", e)), + ) + })?; + let mut result = KernelMetadata::new(kernel.clone()); + + info!("Parsing symbols..."); + for sym in elf.syms.iter() { + let name = elf.strtab.get_at(sym.st_name).unwrap_or(""); + result.add_symbol(sym.clone(), name.to_string()); + } + info!("Parsed {} symbols", result.symbols().len()); + info!("Loaded kernel: {:?}", kernel); + return Ok(result); + } +} + +#[derive(Debug)] +pub struct KernelMetadata { + pub kernel: PathBuf, + sym_collection: SymbolCollection, +} + +impl KernelMetadata { + pub fn new(kernel: PathBuf) -> Self { + Self { + kernel, + sym_collection: SymbolCollection::new(), + } + } + + pub fn symbols(&self) -> &[Symbol] { + &self.sym_collection.symbols + } + + pub fn sym_collection(&self) -> &SymbolCollection { + &self.sym_collection + } + + pub fn add_symbol(&mut self, sym: Sym, name: String) { + self.sym_collection.add_symbol(sym, name); + } +} + +#[derive(Debug)] +pub struct SymbolCollection { + symbols: Vec, +} + +impl SymbolCollection { + pub fn new() -> Self { + Self { + symbols: Vec::new(), + } + } + + pub fn add_symbol(&mut self, sym: Sym, name: String) { + self.symbols.push(Symbol::new(sym, name)); + } + + #[allow(dead_code)] + pub fn len(&self) -> usize { + self.symbols.len() + } + + pub fn find_by_name(&self, name: &str) -> Option<&Symbol> { + self.symbols.iter().find(|sym| sym.name() == name) + } +} + +#[derive(Debug, Clone)] +pub struct Symbol { + sym: Sym, + name: String, +} + +impl Symbol { + pub fn new(sym: Sym, name: String) -> Self { + Self { sym, name } + } + + pub fn name(&self) -> &str { + &self.name + } + + /// Returns the virtual address of the symbol. + #[allow(dead_code)] + pub fn vaddr(&self) -> usize { + self.sym.st_value as usize + } + + /// Returns the offset of the symbol in the kernel memory. + pub fn memory_offset(&self) -> u64 { + self.sym.st_value & (!0xffff_8000_0000_0000) + } +} + +impl Deref for Symbol { + type Target = Sym; + + fn deref(&self) -> &Self::Target { + &self.sym + } +} diff --git a/tools/debugging/logmonitor/src/backend/mod.rs b/tools/debugging/logmonitor/src/backend/mod.rs new file mode 100644 index 00000000..fb4609b5 --- /dev/null +++ b/tools/debugging/logmonitor/src/backend/mod.rs @@ -0,0 +1,127 @@ +use std::{ + path::PathBuf, + sync::{mpsc, Arc, Mutex, RwLock, Weak}, + thread::JoinHandle, +}; + +use log::info; + +use crate::{command::CommandLineArgs, event::Event}; + +pub mod error; +pub mod event; +mod loader; +mod monitor; + +#[derive(Debug)] +pub struct AppBackend { + _command_line_args: CommandLineArgs, + _sender_to_frontend: mpsc::Sender, + data: Arc>, + main_thread: RwLock>>, + /// All threads spawned by the backend.(Except the main thread) + threads: Mutex>>, +} + +impl AppBackend { + pub fn new(command_line_args: CommandLineArgs, sender: mpsc::Sender) -> Arc { + let r = Arc::new(Self { + _command_line_args: command_line_args.clone(), + _sender_to_frontend: sender.clone(), + data: Arc::new(Mutex::new(BackendData::new())), + main_thread: RwLock::new(None), + threads: Mutex::new(Vec::new()), + }); + + r.data.lock().unwrap().kmem_path = Some(PathBuf::from(&command_line_args.kmem)); + + let main_thread = { + let cmdargs = command_line_args.clone(); + let instance = r.clone(); + let sd = sender.clone(); + let dt = r.data.clone(); + std::thread::spawn(move || { + let mut backend = BackendThread::new(cmdargs, sd, Arc::downgrade(&instance), dt); + backend.run_main(); + }) + }; + + *r.main_thread.write().unwrap() = Some(main_thread); + + return r; + } +} + +#[derive(Debug)] +struct BackendData { + kernel_metadata: Option, + /// Path to the QEMU shm which contains the kernel memory. + kmem_path: Option, +} + +impl BackendData { + pub fn new() -> Self { + Self { + kernel_metadata: None, + kmem_path: None, + } + } +} + +#[derive(Debug)] +pub struct BackendThread { + _sender_to_frontend: mpsc::Sender, + command_line_args: CommandLineArgs, + shared_data: Arc>, + backend_instance: Weak, +} + +impl BackendThread { + fn new( + command_line_args: CommandLineArgs, + sender: mpsc::Sender, + backend_instance: Weak, + backend_data: Arc>, + ) -> Self { + Self { + command_line_args, + _sender_to_frontend: sender, + backend_instance, + shared_data: backend_data, + } + } + + pub fn run_main(&mut self) { + info!("DragonOS Log Monitor started."); + self.load_kernel(); + self.run_mm_monitor(); + loop { + // info!("BackendThread::run()"); + std::thread::sleep(std::time::Duration::from_secs(1)); + } + } + + /// 启动内存管理监视器 + fn run_mm_monitor(&mut self) { + info!("run_mm_monitor()"); + let mm_monitor = monitor::mm::MMLogMonitor::new(self.shared_data.clone()); + let handle = std::thread::spawn(move || { + mm_monitor.run(); + }); + + self.backend_instance + .upgrade() + .unwrap() + .threads + .lock() + .unwrap() + .push(handle); + } + + /// 加载DragonOS内核并初始化 + fn load_kernel(&self) { + let res = loader::KernelLoader::load(&self.command_line_args.kernel) + .expect("Failed to load kernel"); + self.shared_data.lock().unwrap().kernel_metadata = Some(res); + } +} diff --git a/tools/debugging/logmonitor/src/backend/monitor/logset.rs b/tools/debugging/logmonitor/src/backend/monitor/logset.rs new file mode 100644 index 00000000..26478252 --- /dev/null +++ b/tools/debugging/logmonitor/src/backend/monitor/logset.rs @@ -0,0 +1,113 @@ +use std::{collections::BTreeMap, fmt::Debug, fs::File, io::Write, path::PathBuf}; + +use log::error; + +use crate::constant::CMD_ARGS; + +/// 日志集合 +/// +/// 所有的日志都会被存到这个集合中, 以便于进行各种操作 +/// +/// 日志集合的后端可以在日志插入前后做一些操作(需要实现[`LogSetBackend`]) +#[derive(Debug)] +#[allow(dead_code)] +pub struct LogSet { + inner: BTreeMap, + backend: Box>, + name: String, + file_path: PathBuf, + log_file: Option, +} + +#[allow(dead_code)] +impl LogSet { + pub fn new(name: String, backend: Option>>) -> Self { + let mut file_path = CMD_ARGS.read().unwrap().as_ref().unwrap().log_dir.clone(); + file_path.push(format!("{}-{}.log", name, std::process::id())); + + let log_file = File::create(&file_path).expect("Failed to create log file."); + + Self { + inner: BTreeMap::new(), + backend: backend.unwrap_or_else(|| Box::new(DefaultBackend::new())), + name, + file_path, + log_file: Some(log_file), + } + } + + pub fn insert(&mut self, key: K, value: V) { + let cloned_value = value.clone(); + self.backend.before_insert(&self.name, &value); + + let prev = self.inner.insert(key, value); + if let Some(prev) = prev { + if prev.ne(&cloned_value) { + error!( + "LogSet::insert(): prev != cloned_value: prev: {:?}, cloned_value: {:?}", + prev, cloned_value + ); + } + } else { + self.log_file + .as_mut() + .map(|f| writeln!(f, "{:?}", cloned_value).ok()); + } + + self.backend.after_insert(&self.name, &cloned_value); + } + + pub fn file_path(&self) -> &PathBuf { + &self.file_path + } + + pub fn len(&self) -> usize { + self.inner.len() + } + + pub fn get(&self, key: &K) -> Option<&V> { + self.inner.get(key) + } + + pub fn get_mut(&mut self, key: &K) -> Option<&mut V> { + self.inner.get_mut(key) + } + + pub fn remove(&mut self, key: &K) -> Option { + self.inner.remove(key) + } + + pub fn clear(&mut self) { + self.inner.clear(); + } + + pub fn iter(&self) -> impl Iterator { + self.inner.iter() + } + + pub fn contains_key(&self, key: &K) -> bool { + self.inner.contains_key(key) + } +} + +/// 日志集合的后端, 用于在日志插入前后做一些操作 +pub trait LogSetBackend: Debug { + fn before_insert(&mut self, _log_set_name: &str, _log: &V) {} + + fn after_insert(&mut self, _log_set_name: &str, _log: &V) {} +} + +#[derive(Debug)] +struct DefaultBackend(()); + +impl DefaultBackend { + pub const fn new() -> Self { + Self(()) + } +} + +impl LogSetBackend for DefaultBackend { + fn before_insert(&mut self, _log_set_name: &str, _log: &V) {} + + fn after_insert(&mut self, _log_set_name: &str, _log: &V) {} +} diff --git a/tools/debugging/logmonitor/src/backend/monitor/mm.rs b/tools/debugging/logmonitor/src/backend/monitor/mm.rs new file mode 100644 index 00000000..48e7cad8 --- /dev/null +++ b/tools/debugging/logmonitor/src/backend/monitor/mm.rs @@ -0,0 +1,286 @@ +use std::{ + fs::File, + mem::size_of, + os::unix::prelude::FileExt, + path::PathBuf, + sync::{atomic::AtomicBool, mpsc, Arc, Mutex, Weak}, + thread::JoinHandle, +}; + +use klog_types::{AllocatorLog, MMLogChannel}; +use log::info; + +use crate::backend::{ + loader::Symbol, + monitor::{logset::LogSet, ObjectWrapper}, + BackendData, +}; + +#[derive(Debug)] +pub struct MMLogMonitor { + channel_symbol: Option, + shared_data: Arc>, + /// All threads spawned by the mm log monitor. + threads: Mutex>>, + stop_child_threads: AtomicBool, + self_ref: Weak, + + mm_log_receiver: Mutex>, + mm_log_sender: mpsc::Sender, +} + +impl MMLogMonitor { + pub fn new(shared_data: Arc>) -> Arc { + let guard = shared_data.lock().unwrap(); + let mm_log_buffer_symbol: Option = guard + .kernel_metadata + .as_ref() + .map(|km| { + km.sym_collection() + .find_by_name("__MM_ALLOCATOR_LOG_CHANNEL") + .map(|s| s.clone()) + }) + .flatten(); + drop(guard); + + info!("mm_log_buffer_symbol: {:?}", mm_log_buffer_symbol); + + let mm_log_worker_mpsc: ( + mpsc::Sender, + mpsc::Receiver, + ) = mpsc::channel::(); + + let r = Self { + channel_symbol: mm_log_buffer_symbol, + shared_data, + threads: Mutex::new(Vec::new()), + stop_child_threads: AtomicBool::new(false), + self_ref: Weak::new(), + mm_log_receiver: Mutex::new(mm_log_worker_mpsc.1), + mm_log_sender: mm_log_worker_mpsc.0, + }; + + let r = Arc::new(r); + unsafe { + let self_ref = Arc::downgrade(&r); + let r_ptr = r.as_ref() as *const Self as *mut Self; + (*r_ptr).self_ref = self_ref; + } + + return r; + } + + pub fn run(&self) { + info!("MMLogMonitor::run()"); + + self.create_threads(); + + let mut logs_set = + LogSet::>::new("mm_allocator_log".to_string(), None); + + self.handle_logs(&mut logs_set); + // std::thread::sleep(std::time::Duration::from_micros(50)); + } + + fn handle_logs(&self, logs_set: &mut LogSet>) { + let mut last_cnt = 0; + let mut last_time = std::time::Instant::now(); + let mm_log_receiver = self.mm_log_receiver.lock().unwrap(); + loop { + let logs = mm_log_receiver.recv(); + if logs.is_err() { + return; + } + + let logs = logs.unwrap(); + + for log in logs.logs { + logs_set.insert(log.id as usize, log); + } + + let x = logs_set.len(); + // info!("logs_set.len(): {}", x); + let current_time = std::time::Instant::now(); + if current_time.duration_since(last_time).as_secs() >= 1 { + info!("memory log rate: {} logs/s", x - last_cnt); + last_cnt = x; + last_time = current_time; + } + } + } + + // fn show_speed(&self, ) + + fn create_threads(&self) { + let km = self + .shared_data + .lock() + .unwrap() + .kmem_path + .clone() + .expect("DragonOS memory map file not specified."); + let monitor_weak = self.self_ref.clone(); + + let handle = std::thread::spawn(move || { + let mut monitor_thread = MMMonitorThread::new(monitor_weak, PathBuf::from(km)); + monitor_thread.run(); + }); + + self.threads.lock().unwrap().push(handle); + } +} + +#[derive(Debug)] +struct MMMonitorThread { + mm_log_monitor: Weak, + kmem_path: PathBuf, +} + +impl MMMonitorThread { + /// Constructs a new instance of [`MMMonitorThread`]. + /// + /// ## Parameters + /// + /// - `mm_log_monitor`: The [`MMLogMonitor`] instance. + /// - `kmem_path`: The path to the kernel memory file. + pub fn new(mm_log_monitor: Weak, kmem_path: PathBuf) -> Self { + Self { + mm_log_monitor, + kmem_path, + } + } + + pub fn run(&mut self) { + info!("MMMonitorThread::run(): kmem_path: {:?}", self.kmem_path); + + let mut kmem_file = self.open_kmem_file().expect("Failed to open kmem file."); + + info!("Channel header loaded!"); + + let channel_header: ObjectWrapper> = self.load_header(&mut kmem_file); + + // 循环读取 + + self.process_logs(&mut kmem_file, &channel_header); + } + + /// 处理内核内存分配日志 + fn process_logs(&self, kmem_file: &mut File, channel_header: &ObjectWrapper>) { + let cap = channel_header.capacity; + let mut buf = vec![0u8; (cap * channel_header.slot_size as u64) as usize]; + let symbol = self + .mm_log_channel_symbol() + .expect("Failed to get memory log channel symbol."); + + let sym_offset = symbol.memory_offset(); + + let slots_offset = channel_header.slots_offset + sym_offset; + let sender = self.mm_log_monitor.upgrade().unwrap().mm_log_sender.clone(); + loop { + if self.should_stop() { + break; + } + + let r = kmem_file + .read_at(&mut buf, slots_offset) + .expect("Failed to read kmem file."); + assert!(r == buf.len()); + + let mut logs = Vec::new(); + + for chunck in buf.chunks(channel_header.slot_size as usize) { + let log_item = { + let log: Option> = + ObjectWrapper::new(&chunck[0..channel_header.element_size as usize]); + let log: ObjectWrapper = log.unwrap(); + + if log.is_valid() { + Some(log) + } else { + None + } + }; + if let Some(log_item) = log_item { + logs.push(log_item); + } + } + // 收集所有校验和正确的日志 + // info!("valid_cnt: {}, invalid_cnt: {}", valid_cnt, invalid_cnt); + // info!("to send {} logs", logs.len()); + if !logs.is_empty() { + sender.send(MMLogWorkerResult::new(logs)).unwrap(); + } + } + } + + fn open_kmem_file(&self) -> std::io::Result { + std::fs::OpenOptions::new().read(true).open(&self.kmem_path) + } + + fn load_header(&self, kmem_file: &mut File) -> ObjectWrapper> { + let mut buf = [0u8; size_of::>()]; + let symbol = self + .mm_log_channel_symbol() + .expect("Failed to get memory log channel symbol."); + + let sym_offset = symbol.memory_offset(); + + let channel_header: Option>>; + + loop { + let _r = kmem_file.read_at(&mut buf, sym_offset); + + let header: ObjectWrapper> = + ObjectWrapper::new(&buf).expect("Failed to parse MMLogChannel header."); + if header.magic == MMLogChannel::<1>::MM_LOG_CHANNEL_MAGIC { + info!("channel_header: {:?}", header); + channel_header = Some(header); + break; + } else { + info!("MM Log Channel not found... Maybe the kernel not started? Or the kernel version is not supported?"); + } + + std::thread::sleep(std::time::Duration::from_secs(1)); + } + + return channel_header.unwrap(); + } + + /// Get the symbol of the memory log channel. + fn mm_log_channel_symbol(&self) -> Option { + self.mm_log_monitor + .upgrade() + .unwrap() + .channel_symbol + .clone() + } + + /// Check if the monitor worker thread should stop. + fn should_stop(&self) -> bool { + self.mm_log_monitor + .upgrade() + .map(|mm_log_monitor| { + mm_log_monitor + .stop_child_threads + .load(std::sync::atomic::Ordering::Relaxed) + }) + .unwrap_or(true) + } +} + +/// 内存日志监视器工作线程处理的结果 +#[derive(Debug)] +struct MMLogWorkerResult { + logs: Vec>, +} + +impl MMLogWorkerResult { + /// 创建一个新的内存日志监视器工作线程处理的结果 + /// + /// ## 参数 + /// + /// - `logs`:处理的日志 + pub fn new(logs: Vec>) -> Self { + Self { logs } + } +} diff --git a/tools/debugging/logmonitor/src/backend/monitor/mod.rs b/tools/debugging/logmonitor/src/backend/monitor/mod.rs new file mode 100644 index 00000000..32de8f18 --- /dev/null +++ b/tools/debugging/logmonitor/src/backend/monitor/mod.rs @@ -0,0 +1,45 @@ +use std::{ + fmt::Debug, + ops::{Deref, DerefMut}, +}; + +pub mod logset; +pub mod mm; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct ObjectWrapper { + object: Box, +} + +impl ObjectWrapper { + pub fn new(buf: &[u8]) -> Option { + if buf.len() != std::mem::size_of::() { + println!( + "ObjectWrapper::new(): buf.len() '{}' != std::mem::size_of::(): '{}'", + buf.len(), + std::mem::size_of::() + ); + return None; + } + let x = unsafe { std::ptr::read(buf.as_ptr() as *const T) }; + + let object = Box::new(x); + + // let object = ManuallyDrop::new(x); + Some(Self { object }) + } +} + +impl DerefMut for ObjectWrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.object + } +} + +impl Deref for ObjectWrapper { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.object + } +} diff --git a/tools/debugging/logmonitor/src/command.rs b/tools/debugging/logmonitor/src/command.rs new file mode 100644 index 00000000..0eeeac1d --- /dev/null +++ b/tools/debugging/logmonitor/src/command.rs @@ -0,0 +1,32 @@ +use std::path::PathBuf; + +use clap::Parser; + +#[derive(Debug, Parser, Clone)] +pub struct CommandLineArgs { + #[arg(short, long)] + /// The kernel ELF file to load. + pub kernel: PathBuf, + + /// The kernel memory file to load. + #[arg(long, value_parser=kmem_file_parser, default_value = "/dev/shm/dragonos-qemu-shm.ram")] + pub kmem: String, + + /// If set, the monitor will start the TUI. + #[arg(long, default_value = "false")] + pub tui: bool, + + /// The directory to store the log files. + #[arg(long, default_value = "logs")] + pub log_dir: PathBuf, +} + +/// 用于解析kmem参数的函数 +fn kmem_file_parser(s: &str) -> Result { + log::warn!("kmem_file_parser: {}", s); + if s.len() == 0 { + return Ok("/dev/shm/dragonos-qemu-shm.ram".to_string()); + } else { + return Ok(s.to_string()); + } +} diff --git a/tools/debugging/logmonitor/src/constant/mod.rs b/tools/debugging/logmonitor/src/constant/mod.rs new file mode 100644 index 00000000..78b24ccc --- /dev/null +++ b/tools/debugging/logmonitor/src/constant/mod.rs @@ -0,0 +1,6 @@ +use std::sync::RwLock; + +use crate::command::CommandLineArgs; + +/// 启动时的命令行参数 +pub static CMD_ARGS: RwLock> = RwLock::new(None); diff --git a/tools/debugging/logmonitor/src/event.rs b/tools/debugging/logmonitor/src/event.rs new file mode 100644 index 00000000..b13d3786 --- /dev/null +++ b/tools/debugging/logmonitor/src/event.rs @@ -0,0 +1,83 @@ +use crate::app::AppResult; +use crate::backend::event::BackendEvent; +use crossterm::event::{self, Event as CrosstermEvent, KeyEvent, MouseEvent}; +use std::sync::mpsc; +use std::thread; +use std::time::{Duration, Instant}; + +/// Terminal events. +#[derive(Clone, Debug)] +pub enum Event { + /// Terminal tick. + Tick, + /// Key press. + Key(KeyEvent), + /// Mouse click/scroll. + Mouse(MouseEvent), + /// Terminal resize. + Resize(u16, u16), + Backend(BackendEvent), +} + +/// Terminal event handler. +#[allow(dead_code)] +#[derive(Debug)] +pub struct EventHandler { + /// Event sender channel. + sender: mpsc::Sender, + /// Event receiver channel. + receiver: mpsc::Receiver, + /// Event handler thread. + handler: thread::JoinHandle<()>, +} + +impl EventHandler { + /// Constructs a new instance of [`EventHandler`]. + pub fn new(tick_rate: u64) -> Self { + let tick_rate = Duration::from_millis(tick_rate); + let (sender, receiver) = mpsc::channel(); + let handler = { + let sender = sender.clone(); + thread::spawn(move || { + let mut last_tick = Instant::now(); + loop { + let timeout = tick_rate + .checked_sub(last_tick.elapsed()) + .unwrap_or(tick_rate); + + if event::poll(timeout).expect("no events available") { + match event::read().expect("unable to read event") { + CrosstermEvent::Key(e) => sender.send(Event::Key(e)), + CrosstermEvent::Mouse(e) => sender.send(Event::Mouse(e)), + CrosstermEvent::Resize(w, h) => sender.send(Event::Resize(w, h)), + _ => unimplemented!(), + } + .expect("failed to send terminal event") + } + + if last_tick.elapsed() >= tick_rate { + sender.send(Event::Tick).expect("failed to send tick event"); + last_tick = Instant::now(); + } + } + }) + }; + Self { + sender, + receiver, + handler, + } + } + + /// Receive the next event from the handler thread. + /// + /// This function will always block the current thread if + /// there is no data available and it's possible for more data to be sent. + pub fn next(&self) -> AppResult { + Ok(self.receiver.recv()?) + } + + pub fn sender(&self) -> mpsc::Sender { + self.sender.clone() + } +} diff --git a/tools/debugging/logmonitor/src/handler.rs b/tools/debugging/logmonitor/src/handler.rs new file mode 100644 index 00000000..1fba099b --- /dev/null +++ b/tools/debugging/logmonitor/src/handler.rs @@ -0,0 +1,35 @@ +use crate::{ + app::{App, AppResult}, + backend::event::BackendEvent, +}; +use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; + +/// Handles the key events and updates the state of [`App`]. +pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { + match key_event.code { + // Exit application on `ESC` or `q` + KeyCode::Esc | KeyCode::Char('q') => { + app.quit(); + } + // Exit application on `Ctrl-C` + KeyCode::Char('c') | KeyCode::Char('C') => { + if key_event.modifiers == KeyModifiers::CONTROL { + app.quit(); + } + } + // Counter handlers + KeyCode::Right => { + app.increment_counter(); + } + KeyCode::Left => { + app.decrement_counter(); + } + // Other handlers you could add here. + _ => {} + } + Ok(()) +} + +pub fn handle_backend_events(_backend_event: BackendEvent, _app: &mut App) -> AppResult<()> { + return Ok(()); +} diff --git a/tools/debugging/logmonitor/src/lib.rs b/tools/debugging/logmonitor/src/lib.rs new file mode 100644 index 00000000..b06c324f --- /dev/null +++ b/tools/debugging/logmonitor/src/lib.rs @@ -0,0 +1,25 @@ +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] + +extern crate clap; + +extern crate lazy_static; + +/// Application. +pub mod app; + +/// Terminal events handler. +pub mod event; + +/// Widget renderer. +pub mod ui; + +/// Terminal user interface. +pub mod tui; + +pub mod backend; +pub mod command; +pub mod constant; +/// Event handler. +pub mod handler; +pub mod logging; diff --git a/tools/debugging/logmonitor/src/logging.rs b/tools/debugging/logmonitor/src/logging.rs new file mode 100644 index 00000000..48d7f13b --- /dev/null +++ b/tools/debugging/logmonitor/src/logging.rs @@ -0,0 +1,51 @@ +use std::sync::mpsc; + +use log::LevelFilter; +use simple_logger::LogBackend; + +use crate::command::CommandLineArgs; + +/// Initialize the logging system. +pub fn init(cmd_args: &CommandLineArgs) -> LoggingInitResult { + let mut builder = simple_logger::SimpleLogger::new().with_level(LevelFilter::Info); + + let mut result = LoggingInitResult::new(None); + + if cmd_args.tui { + let channel: (mpsc::Sender, mpsc::Receiver) = mpsc::channel::(); + builder = builder.with_backend(Box::new(TUILoggingBackend::new(channel.0))); + result.tui_receiver = Some(channel.1); + } + + builder.init().expect("failed to initialize logging"); + + return result; +} + +#[derive(Debug)] +pub struct LoggingInitResult { + /// Logging backend receiver. + pub tui_receiver: Option>, +} + +impl LoggingInitResult { + pub fn new(tui_receiver: Option>) -> Self { + Self { tui_receiver } + } +} + +pub struct TUILoggingBackend { + sender: mpsc::Sender, +} + +impl TUILoggingBackend { + pub fn new(sender: mpsc::Sender) -> Self { + Self { sender } + } +} + +impl LogBackend for TUILoggingBackend { + fn log(&self, message: String) { + self.sender.send(message).ok(); + } +} diff --git a/tools/debugging/logmonitor/src/main.rs b/tools/debugging/logmonitor/src/main.rs new file mode 100644 index 00000000..f1d901bd --- /dev/null +++ b/tools/debugging/logmonitor/src/main.rs @@ -0,0 +1,98 @@ +use clap::Parser; +use logmonitor::app::{App, AppResult}; +use logmonitor::command::{self, CommandLineArgs}; +use logmonitor::constant::CMD_ARGS; +use logmonitor::event::{Event, EventHandler}; +use logmonitor::handler::{handle_backend_events, handle_key_events}; +use logmonitor::logging::LoggingInitResult; +use logmonitor::tui::Tui; +use ratatui::backend::CrosstermBackend; +use ratatui::Terminal; +use std::io; + +extern crate log; + +fn main() -> AppResult<()> { + let command_line_args = command::CommandLineArgs::parse(); + *CMD_ARGS.write().unwrap() = Some(command_line_args.clone()); + println!("{:?}", command_line_args); + prepare_env(); + + let logging_init_result = logmonitor::logging::init(&command_line_args); + if !command_line_args.tui { + return start_headless_app(command_line_args, logging_init_result); + } else { + return start_tui_app(command_line_args, logging_init_result); + } +} + +fn prepare_env() { + // 创建日志文件夹 + let p = CMD_ARGS.read().unwrap().clone(); + let log_dir = p.unwrap().log_dir; + std::fs::create_dir_all(log_dir).expect("Failed to create log directory."); +} + +/// 启动无界面应用 +fn start_headless_app( + cmdargs: CommandLineArgs, + _logging_init_result: LoggingInitResult, +) -> AppResult<()> { + let mut app = App::new("DragonOS Log Monitor"); + let events = EventHandler::new(250); + let _app_backend = logmonitor::backend::AppBackend::new(cmdargs.clone(), events.sender()); + + while app.running { + match events.next()? { + Event::Tick => app.tick(), + Event::Key(key_event) => handle_key_events(key_event, &mut app)?, + Event::Mouse(_) => {} + Event::Resize(_, _) => {} + Event::Backend(e) => { + handle_backend_events(e, &mut app)?; + } + } + } + println!("Headless mode not implemented yet."); + Ok(()) +} + +/// 启动TUI应用 +fn start_tui_app( + cmdargs: CommandLineArgs, + logging_init_result: LoggingInitResult, +) -> AppResult<()> { + // Create an application. + let mut app = App::new("DragonOS Log Monitor"); + if let Some(receiver) = logging_init_result.tui_receiver { + app.set_backend_log_receiver(receiver); + } + + // Initialize the terminal user interface. + let backend = CrosstermBackend::new(io::stderr()); + let terminal = Terminal::new(backend)?; + let events = EventHandler::new(250); + let mut tui = Tui::new(terminal, events); + tui.init()?; + let _app_backend = logmonitor::backend::AppBackend::new(cmdargs.clone(), tui.events.sender()); + + // Start the main loop. + while app.running { + // Render the user interface. + tui.draw(&mut app)?; + // Handle events. + match tui.events.next()? { + Event::Tick => app.tick(), + Event::Key(key_event) => handle_key_events(key_event, &mut app)?, + Event::Mouse(_) => {} + Event::Resize(_, _) => {} + Event::Backend(e) => { + handle_backend_events(e, &mut app)?; + } + } + } + + // Exit the user interface. + tui.exit()?; + Ok(()) +} diff --git a/tools/debugging/logmonitor/src/tui.rs b/tools/debugging/logmonitor/src/tui.rs new file mode 100644 index 00000000..e962785f --- /dev/null +++ b/tools/debugging/logmonitor/src/tui.rs @@ -0,0 +1,77 @@ +use crate::app::{App, AppResult}; +use crate::event::EventHandler; +use crate::ui; +use crossterm::event::DisableMouseCapture; +use crossterm::terminal::{self, EnterAlternateScreen, LeaveAlternateScreen}; +use std::io; +use std::panic; + +use ratatui::backend::Backend; +use ratatui::Terminal; + +/// Representation of a terminal user interface. +/// +/// It is responsible for setting up the terminal, +/// initializing the interface and handling the draw events. +#[derive(Debug)] +pub struct Tui { + /// Interface to the Terminal. + terminal: Terminal, + /// Terminal event handler. + pub events: EventHandler, +} + +impl Tui { + /// Constructs a new instance of [`Tui`]. + pub fn new(terminal: Terminal, events: EventHandler) -> Self { + Self { terminal, events } + } + + /// Initializes the terminal interface. + /// + /// It enables the raw mode and sets terminal properties. + pub fn init(&mut self) -> AppResult<()> { + terminal::enable_raw_mode()?; + crossterm::execute!(io::stderr(), EnterAlternateScreen, DisableMouseCapture)?; + + // Define a custom panic hook to reset the terminal properties. + // This way, you won't have your terminal messed up if an unexpected error happens. + let panic_hook = panic::take_hook(); + panic::set_hook(Box::new(move |panic| { + Self::reset().expect("failed to reset the terminal"); + panic_hook(panic); + })); + + self.terminal.hide_cursor()?; + self.terminal.clear()?; + Ok(()) + } + + /// [`Draw`] the terminal interface by [`rendering`] the widgets. + /// + /// [`Draw`]: ratatui::Terminal::draw + /// [`rendering`]: crate::ui:render + pub fn draw(&mut self, app: &mut App) -> AppResult<()> { + self.terminal.draw(|frame| ui::render(app, frame))?; + Ok(()) + } + + /// Resets the terminal interface. + /// + /// This function is also used for the panic hook to revert + /// the terminal properties if unexpected errors occur. + fn reset() -> AppResult<()> { + terminal::disable_raw_mode()?; + crossterm::execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture)?; + Ok(()) + } + + /// Exits the terminal interface. + /// + /// It disables the raw mode and reverts back the terminal properties. + pub fn exit(&mut self) -> AppResult<()> { + Self::reset()?; + self.terminal.show_cursor()?; + Ok(()) + } +} diff --git a/tools/debugging/logmonitor/src/ui.rs b/tools/debugging/logmonitor/src/ui.rs new file mode 100644 index 00000000..8ea854aa --- /dev/null +++ b/tools/debugging/logmonitor/src/ui.rs @@ -0,0 +1,134 @@ +use ratatui::{ + prelude::{Constraint, Layout, Rect}, + style::{Color, Modifier, Style}, + symbols, + text::{self, Span, Text}, + widgets::{Block, Borders, List, ListItem, Sparkline, Tabs}, + Frame, +}; + +use crate::app::App; + +/// Renders the user interface widgets. +pub fn render(app: &mut App, frame: &mut Frame) { + // This is where you add new widgets. + // See the following resources: + // - https://docs.rs/ratatui/latest/ratatui/widgets/index.html + // - https://github.com/ratatui-org/ratatui/tree/master/examples + // frame.render_widget( + // Paragraph::new(format!( + // "This is a tui template.\n\ + // Press `Esc`, `Ctrl-C` or `q` to stop running.\n\ + // Press left and right to increment and decrement the counter respectively.\n\ + // Counter: {}", + // app.counter + // )) + // .block( + // Block::default() + // .title("Template") + // .title_alignment(Alignment::Center) + // .borders(Borders::ALL) + // .border_type(BorderType::Rounded), + // ) + // .style(Style::default().fg(Color::Cyan).bg(Color::Black)) + // .alignment(Alignment::Center), + // frame.size(), + // ) + + let chunks = Layout::default() + .constraints([Constraint::Length(3), Constraint::Min(0)]) + .split(frame.size()); + let titles = app + .tabs + .titles + .iter() + .map(|t| text::Line::from(Span::styled(*t, Style::default().fg(Color::Green)))) + .collect(); + let tabs = Tabs::new(titles) + .block(Block::default().borders(Borders::ALL).title(app.title)) + .highlight_style(Style::default().fg(Color::Yellow)) + .select(app.tabs.index); + frame.render_widget(tabs, chunks[0]); + + match app.tabs.index { + 0 => draw_first_tab(frame, app, chunks[1]), + _ => {} + } +} + +fn draw_first_tab(f: &mut Frame, app: &mut App, area: Rect) { + let chunks = Layout::default() + .constraints([ + Constraint::Min(1), + Constraint::Min(3), + Constraint::Length(7), + ]) + .split(area); + draw_memory_logging_speed_gauges(f, app, chunks[0]); + // draw_charts(f, app, chunks[1]); + draw_footer(f, app, chunks[2]); +} + +/// 绘制内存日志产生数量的图表 +fn draw_memory_logging_speed_gauges(f: &mut Frame, app: &mut App, area: Rect) { + let chunks = Layout::default() + .constraints([Constraint::Length(3)]) + .margin(1) + .split(area); + let block = Block::default().borders(Borders::ALL).title("Speed:"); + f.render_widget(block, area); + + let sparkline = Sparkline::default() + .block(Block::default().title("Memory Log Speed:")) + .style(Style::default().fg(Color::Green)) + .data(&app.memory_log_sparkline.points) + .bar_set(if app.enhanced_graphics { + symbols::bar::NINE_LEVELS + } else { + symbols::bar::THREE_LEVELS + }); + f.render_widget(sparkline, chunks[0]); +} + +fn draw_footer(f: &mut Frame, app: &mut App, area: Rect) { + let _block = Block::default().borders(Borders::ALL).title(Span::styled( + "Logs", + Style::default() + .fg(Color::Magenta) + .add_modifier(Modifier::BOLD), + )); + + let info_style = Style::default().fg(Color::Blue); + let warning_style = Style::default().fg(Color::Yellow); + let error_style = Style::default().fg(Color::Magenta); + let critical_style = Style::default().fg(Color::Red); + + let binding = app.logs().clone(); + let log_list = binding + .iter() + .map(|log_str| { + let _style = match log_str { + log if log.contains("INFO") => info_style, + log if log.contains("WARNING") => warning_style, + log if log.contains("ERROR") => error_style, + log if log.contains("CRITICAL") => critical_style, + _ => Style::default().fg(Color::White), + }; + + // println!("log_str: {}", log_str); + + ListItem::new(Text::from(log_str.clone())) + }) + .collect::>(); + + let items_num = 5; + let list_to_show = log_list.split_at(if log_list.len() > items_num { + log_list.len() - items_num + } else { + 0 + }); + + let logs = + List::new(list_to_show.1).block(Block::default().borders(Borders::ALL).title("List")); + f.render_stateful_widget(logs, area, &mut app.stateful_logs.state); +} diff --git a/tools/run-qemu.sh b/tools/run-qemu.sh index 69b46b7d..d3b4f8b6 100644 --- a/tools/run-qemu.sh +++ b/tools/run-qemu.sh @@ -51,6 +51,9 @@ fi QEMU=qemu-system-x86_64 QEMU_DISK_IMAGE="../bin/disk.img" QEMU_MEMORY="512M" +QEMU_MEMORY_BACKEND="dragonos-qemu-shm.ram" +QEMU_MEMORY_BACKEND_PATH_PREFIX="/dev/shm" +QEMU_SHM_OBJECT="-object memory-backend-file,size=${QEMU_MEMORY},id=${QEMU_MEMORY_BACKEND},mem-path=${QEMU_MEMORY_BACKEND_PATH_PREFIX}/${QEMU_MEMORY_BACKEND},share=on " QEMU_SMP="2,cores=2,threads=1,sockets=1" QEMU_MONITOR="stdio" QEMU_TRACE="${qemu_trace_std}" @@ -65,17 +68,20 @@ if [ -n "${qemu_accel}" ]; then QEMU_ACCELARATE="-machine accel=${qemu_accel} -enable-kvm " fi +QEMU_MACHINE=" -machine q35,memory-backend=${QEMU_MEMORY_BACKEND} " + # ps: 下面这条使用tap的方式,无法dhcp获取到ip,暂时不知道为什么 -# QEMU_DEVICES="-device ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0 -net nic,netdev=nic0 -netdev tap,id=nic0,model=virtio-net-pci,script=qemu/ifup-nat,downscript=qemu/ifdown-nat -usb -device qemu-xhci,id=xhci,p2=8,p3=4 -machine q35 " -QEMU_DEVICES="-device ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0 -netdev user,id=hostnet0,hostfwd=tcp::12580-:12580 -device virtio-net-pci,vectors=5,netdev=hostnet0,id=net0 -usb -device qemu-xhci,id=xhci,p2=8,p3=4 -machine q35 " +# QEMU_DEVICES="-device ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0 -net nic,netdev=nic0 -netdev tap,id=nic0,model=virtio-net-pci,script=qemu/ifup-nat,downscript=qemu/ifdown-nat -usb -device qemu-xhci,id=xhci,p2=8,p3=4 " +QEMU_DEVICES="-device ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0 -netdev user,id=hostnet0,hostfwd=tcp::12580-:12580 -device virtio-net-pci,vectors=5,netdev=hostnet0,id=net0 -usb -device qemu-xhci,id=xhci,p2=8,p3=4 " # E1000E -# QEMU_DEVICES="-device ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0 -netdev user,id=hostnet0,hostfwd=tcp::12580-:12580 -net nic,model=e1000e,netdev=hostnet0,id=net0 -netdev user,id=hostnet1,hostfwd=tcp::12581-:12581 -device virtio-net-pci,vectors=5,netdev=hostnet1,id=net1 -usb -device qemu-xhci,id=xhci,p2=8,p3=4 -machine q35 " +# QEMU_DEVICES="-device ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0 -netdev user,id=hostnet0,hostfwd=tcp::12580-:12580 -net nic,model=e1000e,netdev=hostnet0,id=net0 -netdev user,id=hostnet1,hostfwd=tcp::12581-:12581 -device virtio-net-pci,vectors=5,netdev=hostnet1,id=net1 -usb -device qemu-xhci,id=xhci,p2=8,p3=4 " QEMU_ARGUMENT="-d ${QEMU_DISK_IMAGE} -m ${QEMU_MEMORY} -smp ${QEMU_SMP} -boot order=d -monitor ${QEMU_MONITOR} -d ${qemu_trace_std} " -QEMU_ARGUMENT+="-s -S -cpu ${QEMU_CPU_FEATURES} -rtc ${QEMU_RTC_CLOCK} -serial ${QEMU_SERIAL} -drive ${QEMU_DRIVE} ${QEMU_DEVICES}" - +QEMU_ARGUMENT+="-s -S ${QEMU_MACHINE} -cpu ${QEMU_CPU_FEATURES} -rtc ${QEMU_RTC_CLOCK} -serial ${QEMU_SERIAL} -drive ${QEMU_DRIVE} ${QEMU_DEVICES}" +QEMU_ARGUMENT+=" ${QEMU_SHM_OBJECT} " QEMU_ARGUMENT+=" ${QEMU_ACCELARATE} " + if [ $flag_can_run -eq 1 ]; then while true;do case "$1" in @@ -100,6 +106,9 @@ if [ $flag_can_run -eq 1 ]; then esac done +# 删除共享内存 +sudo rm -rf ${QEMU_MEMORY_BACKEND_PATH_PREFIX}/${QEMU_MEMORY_BACKEND} + if [ ${BIOS_TYPE} == uefi ] ;then if [ ${ARCH} == x86_64 ] ;then sudo ${QEMU} -bios arch/x86_64/efi/OVMF-pure-efi.fd ${QEMU_ARGUMENT} @@ -109,7 +118,8 @@ if [ ${BIOS_TYPE} == uefi ] ;then else sudo ${QEMU} ${QEMU_ARGUMENT} fi - +# 删除共享内存 +sudo rm -rf ${QEMU_MEMORY_BACKEND_PATH_PREFIX}/${QEMU_MEMORY_BACKEND} else echo "不满足运行条件" fi