mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-08 10:15:03 +00:00
增加内存分配日志监视器 (#424)
* 完成内存日志监视,并输出日志到文件 * 修复进程退出后,procfs查看进程status文件会崩溃的问题 * 修复signal唤醒进程的判断条件问题
This commit is contained in:
parent
70a4e5550a
commit
7b32f5080f
1
.gitignore
vendored
1
.gitignore
vendored
@ -19,3 +19,4 @@ cppcheck.xml
|
|||||||
Cargo.lock
|
Cargo.lock
|
||||||
.cache
|
.cache
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
|
/logs/
|
||||||
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -177,11 +177,10 @@
|
|||||||
},
|
},
|
||||||
"C_Cpp.errorSquiggles": "enabled",
|
"C_Cpp.errorSquiggles": "enabled",
|
||||||
"esbonio.sphinx.confDir": "",
|
"esbonio.sphinx.confDir": "",
|
||||||
"rust-analyzer.cargo.target": "x86_64-unknown-none",
|
|
||||||
"rust-analyzer.checkOnSave.allTargets": false,
|
"rust-analyzer.checkOnSave.allTargets": false,
|
||||||
"rust-analyzer.linkedProjects": [
|
"rust-analyzer.linkedProjects": [
|
||||||
"./kernel/Cargo.toml",
|
"./kernel/Cargo.toml",
|
||||||
"./kernel/src/libs/ida/Cargo.toml"
|
"./tools/Cargo.toml",
|
||||||
],
|
],
|
||||||
"rust-analyzer.check.overrideCommand": [
|
"rust-analyzer.check.overrideCommand": [
|
||||||
"make",
|
"make",
|
||||||
|
6
Makefile
6
Makefile
@ -1,4 +1,4 @@
|
|||||||
SUBDIRS = kernel user
|
SUBDIRS = kernel user tools
|
||||||
|
|
||||||
# ifndef $(EMULATOR)
|
# ifndef $(EMULATOR)
|
||||||
ifeq ($(EMULATOR), )
|
ifeq ($(EMULATOR), )
|
||||||
@ -156,6 +156,10 @@ fmt:
|
|||||||
@echo "格式化代码"
|
@echo "格式化代码"
|
||||||
FMT_CHECK=$(FMT_CHECK) $(MAKE) fmt -C kernel
|
FMT_CHECK=$(FMT_CHECK) $(MAKE) fmt -C kernel
|
||||||
FMT_CHECK=$(FMT_CHECK) $(MAKE) fmt -C user
|
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:
|
help:
|
||||||
@echo "编译:"
|
@echo "编译:"
|
||||||
|
@ -10,13 +10,17 @@ edition = "2021"
|
|||||||
crate-type = ["staticlib"]
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [ "src/libs/intertrait" ]
|
members = [
|
||||||
|
"crates/*",
|
||||||
|
"src/libs/intertrait"
|
||||||
|
]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["backtrace"]
|
default = ["backtrace"]
|
||||||
# 内核栈回溯
|
# 内核栈回溯
|
||||||
backtrace = []
|
backtrace = []
|
||||||
|
|
||||||
|
|
||||||
# 运行时依赖项
|
# 运行时依赖项
|
||||||
[dependencies]
|
[dependencies]
|
||||||
x86 = "0.52.0"
|
x86 = "0.52.0"
|
||||||
@ -25,8 +29,6 @@ bit_field = "0.10"
|
|||||||
bitflags = "1.3.2"
|
bitflags = "1.3.2"
|
||||||
bitfield-struct = "0.5.3"
|
bitfield-struct = "0.5.3"
|
||||||
virtio-drivers = { git = "https://git.mirrors.dragonos.org/DragonOS-Community/virtio-drivers.git", rev = "f1d1cbb" }
|
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"]}
|
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 0.2.15
|
||||||
num-traits = { git = "https://git.mirrors.dragonos.org/DragonOS-Community/num-traits.git", rev="1597c1c", default-features = false }
|
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
|
# 一个no_std的hashmap、hashset
|
||||||
hashbrown = "0.13.2"
|
hashbrown = "0.13.2"
|
||||||
elf = { version = "0.7.2", default-features = false }
|
elf = { version = "0.7.2", default-features = false }
|
||||||
memoffset = "0.9.0"
|
|
||||||
atomic_enum = "0.2.0"
|
atomic_enum = "0.2.0"
|
||||||
raw-cpuid = "11.0.1"
|
raw-cpuid = "11.0.1"
|
||||||
acpi = { git = "https://git.mirrors.dragonos.org/DragonOS-Community/acpi-rs.git", rev = "fb69243dcf" }
|
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"
|
linkme = "0.2"
|
||||||
ida = { path = "src/libs/ida" }
|
ida = { path = "src/libs/ida" }
|
||||||
mini-backtrace = { git = "https://git.mirrors.dragonos.org/DragonOS-Community/mini-backtrace.git", rev = "ba98506685" }
|
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]
|
[build-dependencies]
|
||||||
|
@ -90,7 +90,6 @@ impl CFilesBuilder {
|
|||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
c.define("__x86_64__", None);
|
c.define("__x86_64__", None);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_global_include_dir(c: &mut Build) {
|
fn setup_global_include_dir(c: &mut Build) {
|
||||||
|
@ -1,22 +1,9 @@
|
|||||||
#![cfg_attr(not(test), no_std)]
|
#![cfg_attr(not(test), no_std)]
|
||||||
|
#![feature(const_for)]
|
||||||
|
#![feature(const_mut_refs)]
|
||||||
|
#![feature(const_trait_impl)]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate std;
|
extern crate std;
|
||||||
|
|
||||||
pub mod crc64;
|
pub mod crc64;
|
||||||
pub mod tables;
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
18
kernel/crates/kdepends/Cargo.toml
Normal file
18
kernel/crates/kdepends/Cargo.toml
Normal file
@ -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"]
|
9
kernel/crates/kdepends/src/lib.rs
Normal file
9
kernel/crates/kdepends/src/lib.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
#[macro_use]
|
||||||
|
pub extern crate thingbuf;
|
||||||
|
|
||||||
|
pub extern crate memoffset;
|
||||||
|
|
||||||
|
pub extern crate crc;
|
9
kernel/crates/klog_types/Cargo.toml
Normal file
9
kernel/crates/klog_types/Cargo.toml
Normal file
@ -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" }
|
241
kernel/crates/klog_types/src/lib.rs
Normal file
241
kernel/crates/klog_types/src/lib.rs
Normal file
@ -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<usize>,
|
||||||
|
|
||||||
|
pub checksum: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AllocatorLog {
|
||||||
|
/// 创建一个日志
|
||||||
|
///
|
||||||
|
/// ## 参数
|
||||||
|
///
|
||||||
|
/// - `id`:日志的id
|
||||||
|
/// - `type_`:日志类型
|
||||||
|
/// - `source`:日志来源
|
||||||
|
/// - `pid`:日志来源的pid
|
||||||
|
/// - `time`:日志的时间
|
||||||
|
pub fn new(
|
||||||
|
id: u64,
|
||||||
|
type_: AllocatorLogType,
|
||||||
|
source: LogSource,
|
||||||
|
pid: Option<usize>,
|
||||||
|
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::<Self>() - core::mem::size_of::<u64>(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
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<core::cmp::Ordering> {
|
||||||
|
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<usize>,
|
||||||
|
pub paddr: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AllocLogItem {
|
||||||
|
pub fn new(layout: core::alloc::Layout, vaddr: Option<usize>, paddr: Option<usize>) -> 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<AllocatorLog> for MMLogCycle {
|
||||||
|
fn new_element(&self) -> AllocatorLog {
|
||||||
|
AllocatorLog::zeroed()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recycle(&self, element: &mut AllocatorLog) {
|
||||||
|
*element = AllocatorLog::zeroed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 内存分配器日志通道
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct MMLogChannel<const CAP: usize> {
|
||||||
|
pub magic: u32,
|
||||||
|
/// 日志元素的大小
|
||||||
|
pub element_size: u32,
|
||||||
|
/// 日志通道每个槽的大小(字节)
|
||||||
|
pub slot_size: u32,
|
||||||
|
pub capacity: u64,
|
||||||
|
pub slots_offset: u64,
|
||||||
|
pub buf: StaticThingBuf<AllocatorLog, CAP, MMLogCycle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const CAP: usize> Debug for MMLogChannel<CAP> {
|
||||||
|
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<AllocatorLog, {}, MMLogCycle>",
|
||||||
|
self.capacity
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const CAP: usize> MMLogChannel<CAP> {
|
||||||
|
/// 日志通道的魔数
|
||||||
|
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::<AllocatorLog>() as u32,
|
||||||
|
capacity: capacity as u64,
|
||||||
|
slot_size: slot_size as u32,
|
||||||
|
slots_offset: (offset_of!(MMLogChannel<CAP>, buf) + buffer.offset_of_slots()) as u64,
|
||||||
|
buf: buffer,
|
||||||
|
};
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@ use alloc::{
|
|||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
|
|
||||||
use memoffset::offset_of;
|
use kdepends::memoffset::offset_of;
|
||||||
use x86::{controlregs::Cr4, segmentation::SegmentSelector};
|
use x86::{controlregs::Cr4, segmentation::SegmentSelector};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -4,7 +4,7 @@ use core::{
|
|||||||
sync::atomic::{compiler_fence, AtomicBool, Ordering},
|
sync::atomic::{compiler_fence, AtomicBool, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
use memoffset::offset_of;
|
use kdepends::memoffset::offset_of;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::process::table::TSSManager, exception::InterruptArch,
|
arch::process::table::TSSManager, exception::InterruptArch,
|
||||||
|
78
kernel/src/debug/klog/mm.rs
Normal file
78
kernel/src/debug/klog/mm.rs
Normal file
@ -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<Pid>) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
kernel/src/debug/klog/mod.rs
Normal file
1
kernel/src/debug/klog/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod mm;
|
1
kernel/src/debug/mod.rs
Normal file
1
kernel/src/debug/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod klog;
|
@ -26,7 +26,7 @@ use crate::{
|
|||||||
use super::{super::device::DeviceState, platform_bus, platform_bus_device, CompatibleTable};
|
use super::{super::device::DeviceState, platform_bus, platform_bus_device, CompatibleTable};
|
||||||
|
|
||||||
/// 平台设备id分配器
|
/// 平台设备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)]
|
#[inline(always)]
|
||||||
pub fn platform_device_manager() -> &'static PlatformDeviceManager {
|
pub fn platform_device_manager() -> &'static PlatformDeviceManager {
|
||||||
|
@ -2,7 +2,7 @@ use core::intrinsics::unlikely;
|
|||||||
|
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
|
|
||||||
use thingbuf::mpsc::{
|
use kdepends::thingbuf::mpsc::{
|
||||||
self,
|
self,
|
||||||
errors::{TryRecvError, TrySendError},
|
errors::{TryRecvError, TrySendError},
|
||||||
};
|
};
|
||||||
|
@ -179,23 +179,24 @@ impl ProcFSInode {
|
|||||||
);
|
);
|
||||||
pdata.append(&mut format!("\nvrtime:\t{}", vrtime).as_bytes().to_owned());
|
pdata.append(&mut format!("\nvrtime:\t{}", vrtime).as_bytes().to_owned());
|
||||||
|
|
||||||
let binding = pcb.basic().user_vm().unwrap();
|
if let Some(user_vm) = pcb.basic().user_vm() {
|
||||||
let address_space_guard = binding.read();
|
let address_space_guard = user_vm.read();
|
||||||
// todo: 当前进程运行过程中占用内存的峰值
|
// todo: 当前进程运行过程中占用内存的峰值
|
||||||
let hiwater_vm: u64 = 0;
|
let hiwater_vm: u64 = 0;
|
||||||
// 进程代码段的大小
|
// 进程代码段的大小
|
||||||
let text = (address_space_guard.end_code - address_space_guard.start_code) / 1024;
|
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;
|
let data = (address_space_guard.end_data - address_space_guard.start_data) / 1024;
|
||||||
drop(address_space_guard);
|
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(
|
pdata.append(
|
||||||
&mut format!("\nflags: {:?}\n", pcb.flags().clone())
|
&mut format!("\nflags: {:?}\n", pcb.flags().clone())
|
||||||
.as_bytes()
|
.as_bytes()
|
||||||
|
@ -50,10 +50,13 @@ impl Signal {
|
|||||||
kwarn!("No such process.");
|
kwarn!("No such process.");
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let pcb = pcb.unwrap();
|
||||||
// println!("Target pcb = {:?}", pcb.as_ref().unwrap());
|
// println!("Target pcb = {:?}", pcb.as_ref().unwrap());
|
||||||
compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
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);
|
compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
@ -282,8 +285,33 @@ fn signal_wake_up(pcb: Arc<ProcessControlBlock>, _guard: SpinLockGuard<SignalStr
|
|||||||
// 如果不是 fatal 的就只唤醒 stop 的进程来响应
|
// 如果不是 fatal 的就只唤醒 stop 的进程来响应
|
||||||
// kdebug!("signal_wake_up");
|
// kdebug!("signal_wake_up");
|
||||||
// 如果目标进程已经在运行,则发起一个ipi,使得它陷入内核
|
// 如果目标进程已经在运行,则发起一个ipi,使得它陷入内核
|
||||||
let r = ProcessManager::wakeup_stop(&pcb);
|
let state = pcb.sched_info().state();
|
||||||
if r.is_ok() {
|
let mut wakeup_ok = true;
|
||||||
|
if state.is_blocked_interruptable() {
|
||||||
|
ProcessManager::wakeup(&pcb).unwrap_or_else(|e| {
|
||||||
|
wakeup_ok = false;
|
||||||
|
kwarn!(
|
||||||
|
"Current pid: {:?}, signal_wake_up target {:?} error: {:?}",
|
||||||
|
ProcessManager::current_pcb().pid(),
|
||||||
|
pcb.pid(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else if state.is_stopped() {
|
||||||
|
ProcessManager::wakeup_stop(&pcb).unwrap_or_else(|e| {
|
||||||
|
wakeup_ok = false;
|
||||||
|
kwarn!(
|
||||||
|
"Current pid: {:?}, signal_wake_up target {:?} error: {:?}",
|
||||||
|
ProcessManager::current_pcb().pid(),
|
||||||
|
pcb.pid(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
wakeup_ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if wakeup_ok {
|
||||||
ProcessManager::kick(&pcb);
|
ProcessManager::kick(&pcb);
|
||||||
} else {
|
} else {
|
||||||
if fatal {
|
if fatal {
|
||||||
|
@ -39,6 +39,7 @@ mod arch;
|
|||||||
mod libs;
|
mod libs;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod include;
|
mod include;
|
||||||
|
mod debug;
|
||||||
mod driver; // 如果driver依赖了libs,应该在libs后面导出
|
mod driver; // 如果driver依赖了libs,应该在libs后面导出
|
||||||
mod exception;
|
mod exception;
|
||||||
mod filesystem;
|
mod filesystem;
|
||||||
@ -60,17 +61,17 @@ extern crate bitflags;
|
|||||||
extern crate elf;
|
extern crate elf;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
extern crate memoffset;
|
|
||||||
extern crate num;
|
extern crate num;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate num_derive;
|
extern crate num_derive;
|
||||||
extern crate smoltcp;
|
extern crate smoltcp;
|
||||||
extern crate thingbuf;
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate intertrait;
|
extern crate intertrait;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
extern crate x86;
|
extern crate x86;
|
||||||
|
|
||||||
|
extern crate klog_types;
|
||||||
|
|
||||||
use crate::mm::allocator::kernel_allocator::KernelAllocator;
|
use crate::mm::allocator::kernel_allocator::KernelAllocator;
|
||||||
|
|
||||||
use crate::process::ProcessManager;
|
use crate::process::ProcessManager;
|
||||||
|
@ -16,9 +16,9 @@ pub struct IdAllocator {
|
|||||||
|
|
||||||
impl IdAllocator {
|
impl IdAllocator {
|
||||||
/// 创建一个新的id分配器
|
/// 创建一个新的id分配器
|
||||||
pub const fn new(max_id: usize) -> Self {
|
pub const fn new(initial_id: usize, max_id: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
current_id: AtomicUsize::new(0),
|
current_id: AtomicUsize::new(initial_id),
|
||||||
max_id,
|
max_id,
|
||||||
dead: AtomicBool::new(false),
|
dead: AtomicBool::new(false),
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
|
use klog_types::AllocLogItem;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::mm::LockedFrameAllocator,
|
arch::mm::LockedFrameAllocator,
|
||||||
|
debug::klog::mm::mm_debug_log,
|
||||||
libs::align::page_align_up,
|
libs::align::page_align_up,
|
||||||
mm::{MMArch, MemoryManagementArch, VirtAddr},
|
mm::{MMArch, MemoryManagementArch, VirtAddr},
|
||||||
};
|
};
|
||||||
@ -81,15 +84,46 @@ impl LocalAlloc for KernelAllocator {
|
|||||||
/// 为内核slab分配器实现GlobalAlloc特性
|
/// 为内核slab分配器实现GlobalAlloc特性
|
||||||
unsafe impl GlobalAlloc for KernelAllocator {
|
unsafe impl GlobalAlloc for KernelAllocator {
|
||||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
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)
|
// self.local_alloc_zeroed(layout, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
|
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) {
|
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);
|
self.local_dealloc(ptr, layout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,6 +121,11 @@ impl ProcessManager {
|
|||||||
kinfo!("Process Manager initialized.");
|
kinfo!("Process Manager initialized.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 判断进程管理器是否已经初始化完成
|
||||||
|
pub fn initialized() -> bool {
|
||||||
|
unsafe { __PROCESS_MANAGEMENT_INIT_DONE }
|
||||||
|
}
|
||||||
|
|
||||||
/// 获取当前进程的pcb
|
/// 获取当前进程的pcb
|
||||||
pub fn current_pcb() -> Arc<ProcessControlBlock> {
|
pub fn current_pcb() -> Arc<ProcessControlBlock> {
|
||||||
if unlikely(unsafe { !__PROCESS_MANAGEMENT_INIT_DONE }) {
|
if unlikely(unsafe { !__PROCESS_MANAGEMENT_INIT_DONE }) {
|
||||||
@ -461,6 +466,11 @@ impl ProcessState {
|
|||||||
return matches!(self, ProcessState::Blocked(_));
|
return matches!(self, ProcessState::Blocked(_));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_blocked_interruptable(&self) -> bool {
|
||||||
|
return matches!(self, ProcessState::Blocked(true));
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_exited(&self) -> bool {
|
pub fn is_exited(&self) -> bool {
|
||||||
return matches!(self, ProcessState::Exited(_));
|
return matches!(self, ProcessState::Exited(_));
|
||||||
|
6
tools/Cargo.toml
Normal file
6
tools/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"debugging/logmonitor",
|
||||||
|
]
|
||||||
|
|
||||||
|
resolver = "2"
|
9
tools/Makefile
Normal file
9
tools/Makefile
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
.PHONY: fmt
|
||||||
|
fmt:
|
||||||
|
@cargo fmt --all $(FMT_CHECK)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@cargo clean
|
||||||
|
|
||||||
|
check:
|
||||||
|
@cargo check --all
|
1
tools/debugging/logmonitor/.gitignore
vendored
Normal file
1
tools/debugging/logmonitor/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/logs/
|
17
tools/debugging/logmonitor/Cargo.toml
Normal file
17
tools/debugging/logmonitor/Cargo.toml
Normal file
@ -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"
|
10
tools/debugging/logmonitor/README.md
Normal file
10
tools/debugging/logmonitor/README.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# 日志监视程序
|
||||||
|
|
||||||
|
本程序监视DragonOS内核的环形缓冲区日志,并将其显示在屏幕上。
|
||||||
|
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
1. 默认情况下,DragonOS内核已启用内存分配器的日志记录。
|
||||||
|
2. 当qemu启动后,在DragonOS项目的根目录中,运行`make log-monitor`。
|
||||||
|
3. 在`logs`目录查看日志文件。
|
212
tools/debugging/logmonitor/src/app.rs
Normal file
212
tools/debugging/logmonitor/src/app.rs
Normal file
@ -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<T> = std::result::Result<T, Box<dyn error::Error>>;
|
||||||
|
|
||||||
|
/// 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<RandomSignal>,
|
||||||
|
|
||||||
|
logs: Vec<String>,
|
||||||
|
pub stateful_logs: StatefulList<(&'a str, &'a str)>,
|
||||||
|
|
||||||
|
backend_log_receiver: Option<std::sync::mpsc::Receiver<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<String>) {
|
||||||
|
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::<Vec<String>>());
|
||||||
|
|
||||||
|
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<String> {
|
||||||
|
&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<S: Iterator> {
|
||||||
|
source: S,
|
||||||
|
pub points: Vec<S::Item>,
|
||||||
|
tick_rate: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Signal<S>
|
||||||
|
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<u64>,
|
||||||
|
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<u64> {
|
||||||
|
Some(self.distribution.sample(&mut self.rng))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct StatefulList<T> {
|
||||||
|
pub state: ListState,
|
||||||
|
pub items: Vec<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> StatefulList<T> {
|
||||||
|
pub fn with_items(items: Vec<T>) -> StatefulList<T> {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
38
tools/debugging/logmonitor/src/backend/error.rs
Normal file
38
tools/debugging/logmonitor/src/backend/error.rs
Normal file
@ -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<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BackendError {
|
||||||
|
pub fn new(kind: BackendErrorKind, message: Option<String>) -> 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()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
tools/debugging/logmonitor/src/backend/event.rs
Normal file
12
tools/debugging/logmonitor/src/backend/event.rs
Normal file
@ -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,
|
||||||
|
}
|
122
tools/debugging/logmonitor/src/backend/loader.rs
Normal file
122
tools/debugging/logmonitor/src/backend/loader.rs
Normal file
@ -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<KernelMetadata> {
|
||||||
|
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<Symbol>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
127
tools/debugging/logmonitor/src/backend/mod.rs
Normal file
127
tools/debugging/logmonitor/src/backend/mod.rs
Normal file
@ -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<Event>,
|
||||||
|
data: Arc<Mutex<BackendData>>,
|
||||||
|
main_thread: RwLock<Option<std::thread::JoinHandle<()>>>,
|
||||||
|
/// All threads spawned by the backend.(Except the main thread)
|
||||||
|
threads: Mutex<Vec<JoinHandle<()>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppBackend {
|
||||||
|
pub fn new(command_line_args: CommandLineArgs, sender: mpsc::Sender<Event>) -> Arc<Self> {
|
||||||
|
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<loader::KernelMetadata>,
|
||||||
|
/// Path to the QEMU shm which contains the kernel memory.
|
||||||
|
kmem_path: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BackendData {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
kernel_metadata: None,
|
||||||
|
kmem_path: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BackendThread {
|
||||||
|
_sender_to_frontend: mpsc::Sender<Event>,
|
||||||
|
command_line_args: CommandLineArgs,
|
||||||
|
shared_data: Arc<Mutex<BackendData>>,
|
||||||
|
backend_instance: Weak<AppBackend>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BackendThread {
|
||||||
|
fn new(
|
||||||
|
command_line_args: CommandLineArgs,
|
||||||
|
sender: mpsc::Sender<Event>,
|
||||||
|
backend_instance: Weak<AppBackend>,
|
||||||
|
backend_data: Arc<Mutex<BackendData>>,
|
||||||
|
) -> 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);
|
||||||
|
}
|
||||||
|
}
|
113
tools/debugging/logmonitor/src/backend/monitor/logset.rs
Normal file
113
tools/debugging/logmonitor/src/backend/monitor/logset.rs
Normal file
@ -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<K, V> {
|
||||||
|
inner: BTreeMap<K, V>,
|
||||||
|
backend: Box<dyn LogSetBackend<K, V>>,
|
||||||
|
name: String,
|
||||||
|
file_path: PathBuf,
|
||||||
|
log_file: Option<File>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl<K: Ord, V: Clone + PartialEq + Debug> LogSet<K, V> {
|
||||||
|
pub fn new(name: String, backend: Option<Box<dyn LogSetBackend<K, V>>>) -> 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<V> {
|
||||||
|
self.inner.remove(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.inner.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
|
||||||
|
self.inner.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains_key(&self, key: &K) -> bool {
|
||||||
|
self.inner.contains_key(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 日志集合的后端, 用于在日志插入前后做一些操作
|
||||||
|
pub trait LogSetBackend<K, V>: 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<K, V> LogSetBackend<K, V> for DefaultBackend {
|
||||||
|
fn before_insert(&mut self, _log_set_name: &str, _log: &V) {}
|
||||||
|
|
||||||
|
fn after_insert(&mut self, _log_set_name: &str, _log: &V) {}
|
||||||
|
}
|
286
tools/debugging/logmonitor/src/backend/monitor/mm.rs
Normal file
286
tools/debugging/logmonitor/src/backend/monitor/mm.rs
Normal file
@ -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<Symbol>,
|
||||||
|
shared_data: Arc<Mutex<BackendData>>,
|
||||||
|
/// All threads spawned by the mm log monitor.
|
||||||
|
threads: Mutex<Vec<JoinHandle<()>>>,
|
||||||
|
stop_child_threads: AtomicBool,
|
||||||
|
self_ref: Weak<Self>,
|
||||||
|
|
||||||
|
mm_log_receiver: Mutex<mpsc::Receiver<MMLogWorkerResult>>,
|
||||||
|
mm_log_sender: mpsc::Sender<MMLogWorkerResult>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MMLogMonitor {
|
||||||
|
pub fn new(shared_data: Arc<Mutex<BackendData>>) -> Arc<Self> {
|
||||||
|
let guard = shared_data.lock().unwrap();
|
||||||
|
let mm_log_buffer_symbol: Option<Symbol> = 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<MMLogWorkerResult>,
|
||||||
|
mpsc::Receiver<MMLogWorkerResult>,
|
||||||
|
) = mpsc::channel::<MMLogWorkerResult>();
|
||||||
|
|
||||||
|
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::<usize, ObjectWrapper<AllocatorLog>>::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<usize, ObjectWrapper<AllocatorLog>>) {
|
||||||
|
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<MMLogMonitor>,
|
||||||
|
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<MMLogMonitor>, 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<MMLogChannel<1>> = 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<MMLogChannel<1>>) {
|
||||||
|
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<AllocatorLog>> =
|
||||||
|
ObjectWrapper::new(&chunck[0..channel_header.element_size as usize]);
|
||||||
|
let log: ObjectWrapper<AllocatorLog> = 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::File> {
|
||||||
|
std::fs::OpenOptions::new().read(true).open(&self.kmem_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_header(&self, kmem_file: &mut File) -> ObjectWrapper<MMLogChannel<1>> {
|
||||||
|
let mut buf = [0u8; size_of::<MMLogChannel<1>>()];
|
||||||
|
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<ObjectWrapper<MMLogChannel<1>>>;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let _r = kmem_file.read_at(&mut buf, sym_offset);
|
||||||
|
|
||||||
|
let header: ObjectWrapper<MMLogChannel<1>> =
|
||||||
|
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<Symbol> {
|
||||||
|
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<ObjectWrapper<AllocatorLog>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MMLogWorkerResult {
|
||||||
|
/// 创建一个新的内存日志监视器工作线程处理的结果
|
||||||
|
///
|
||||||
|
/// ## 参数
|
||||||
|
///
|
||||||
|
/// - `logs`:处理的日志
|
||||||
|
pub fn new(logs: Vec<ObjectWrapper<AllocatorLog>>) -> Self {
|
||||||
|
Self { logs }
|
||||||
|
}
|
||||||
|
}
|
45
tools/debugging/logmonitor/src/backend/monitor/mod.rs
Normal file
45
tools/debugging/logmonitor/src/backend/monitor/mod.rs
Normal file
@ -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<T> {
|
||||||
|
object: Box<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Debug + Sized> ObjectWrapper<T> {
|
||||||
|
pub fn new(buf: &[u8]) -> Option<Self> {
|
||||||
|
if buf.len() != std::mem::size_of::<T>() {
|
||||||
|
println!(
|
||||||
|
"ObjectWrapper::new(): buf.len() '{}' != std::mem::size_of::<T>(): '{}'",
|
||||||
|
buf.len(),
|
||||||
|
std::mem::size_of::<T>()
|
||||||
|
);
|
||||||
|
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<T> DerefMut for ObjectWrapper<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for ObjectWrapper<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.object
|
||||||
|
}
|
||||||
|
}
|
32
tools/debugging/logmonitor/src/command.rs
Normal file
32
tools/debugging/logmonitor/src/command.rs
Normal file
@ -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<String, String> {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
6
tools/debugging/logmonitor/src/constant/mod.rs
Normal file
6
tools/debugging/logmonitor/src/constant/mod.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
|
use crate::command::CommandLineArgs;
|
||||||
|
|
||||||
|
/// 启动时的命令行参数
|
||||||
|
pub static CMD_ARGS: RwLock<Option<CommandLineArgs>> = RwLock::new(None);
|
83
tools/debugging/logmonitor/src/event.rs
Normal file
83
tools/debugging/logmonitor/src/event.rs
Normal file
@ -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>,
|
||||||
|
/// Event receiver channel.
|
||||||
|
receiver: mpsc::Receiver<Event>,
|
||||||
|
/// 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<Event> {
|
||||||
|
Ok(self.receiver.recv()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sender(&self) -> mpsc::Sender<Event> {
|
||||||
|
self.sender.clone()
|
||||||
|
}
|
||||||
|
}
|
35
tools/debugging/logmonitor/src/handler.rs
Normal file
35
tools/debugging/logmonitor/src/handler.rs
Normal file
@ -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(());
|
||||||
|
}
|
25
tools/debugging/logmonitor/src/lib.rs
Normal file
25
tools/debugging/logmonitor/src/lib.rs
Normal file
@ -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;
|
51
tools/debugging/logmonitor/src/logging.rs
Normal file
51
tools/debugging/logmonitor/src/logging.rs
Normal file
@ -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<String>, mpsc::Receiver<String>) = mpsc::channel::<String>();
|
||||||
|
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<mpsc::Receiver<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LoggingInitResult {
|
||||||
|
pub fn new(tui_receiver: Option<mpsc::Receiver<String>>) -> Self {
|
||||||
|
Self { tui_receiver }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TUILoggingBackend {
|
||||||
|
sender: mpsc::Sender<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TUILoggingBackend {
|
||||||
|
pub fn new(sender: mpsc::Sender<String>) -> Self {
|
||||||
|
Self { sender }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LogBackend for TUILoggingBackend {
|
||||||
|
fn log(&self, message: String) {
|
||||||
|
self.sender.send(message).ok();
|
||||||
|
}
|
||||||
|
}
|
98
tools/debugging/logmonitor/src/main.rs
Normal file
98
tools/debugging/logmonitor/src/main.rs
Normal file
@ -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(())
|
||||||
|
}
|
77
tools/debugging/logmonitor/src/tui.rs
Normal file
77
tools/debugging/logmonitor/src/tui.rs
Normal file
@ -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<B: Backend> {
|
||||||
|
/// Interface to the Terminal.
|
||||||
|
terminal: Terminal<B>,
|
||||||
|
/// Terminal event handler.
|
||||||
|
pub events: EventHandler,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: Backend> Tui<B> {
|
||||||
|
/// Constructs a new instance of [`Tui`].
|
||||||
|
pub fn new(terminal: Terminal<B>, 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(())
|
||||||
|
}
|
||||||
|
}
|
134
tools/debugging/logmonitor/src/ui.rs
Normal file
134
tools/debugging/logmonitor/src/ui.rs
Normal file
@ -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::<Vec<ListItem>>();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
@ -51,6 +51,9 @@ fi
|
|||||||
QEMU=qemu-system-x86_64
|
QEMU=qemu-system-x86_64
|
||||||
QEMU_DISK_IMAGE="../bin/disk.img"
|
QEMU_DISK_IMAGE="../bin/disk.img"
|
||||||
QEMU_MEMORY="512M"
|
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_SMP="2,cores=2,threads=1,sockets=1"
|
||||||
QEMU_MONITOR="stdio"
|
QEMU_MONITOR="stdio"
|
||||||
QEMU_TRACE="${qemu_trace_std}"
|
QEMU_TRACE="${qemu_trace_std}"
|
||||||
@ -65,17 +68,20 @@ if [ -n "${qemu_accel}" ]; then
|
|||||||
QEMU_ACCELARATE="-machine accel=${qemu_accel} -enable-kvm "
|
QEMU_ACCELARATE="-machine accel=${qemu_accel} -enable-kvm "
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
QEMU_MACHINE=" -machine q35,memory-backend=${QEMU_MEMORY_BACKEND} "
|
||||||
|
|
||||||
# ps: 下面这条使用tap的方式,无法dhcp获取到ip,暂时不知道为什么
|
# 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 -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 -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 "
|
||||||
# E1000E
|
# 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="-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} "
|
QEMU_ARGUMENT+=" ${QEMU_ACCELARATE} "
|
||||||
|
|
||||||
|
|
||||||
if [ $flag_can_run -eq 1 ]; then
|
if [ $flag_can_run -eq 1 ]; then
|
||||||
while true;do
|
while true;do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@ -100,6 +106,9 @@ if [ $flag_can_run -eq 1 ]; then
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# 删除共享内存
|
||||||
|
sudo rm -rf ${QEMU_MEMORY_BACKEND_PATH_PREFIX}/${QEMU_MEMORY_BACKEND}
|
||||||
|
|
||||||
if [ ${BIOS_TYPE} == uefi ] ;then
|
if [ ${BIOS_TYPE} == uefi ] ;then
|
||||||
if [ ${ARCH} == x86_64 ] ;then
|
if [ ${ARCH} == x86_64 ] ;then
|
||||||
sudo ${QEMU} -bios arch/x86_64/efi/OVMF-pure-efi.fd ${QEMU_ARGUMENT}
|
sudo ${QEMU} -bios arch/x86_64/efi/OVMF-pure-efi.fd ${QEMU_ARGUMENT}
|
||||||
@ -109,7 +118,8 @@ if [ ${BIOS_TYPE} == uefi ] ;then
|
|||||||
else
|
else
|
||||||
sudo ${QEMU} ${QEMU_ARGUMENT}
|
sudo ${QEMU} ${QEMU_ARGUMENT}
|
||||||
fi
|
fi
|
||||||
|
# 删除共享内存
|
||||||
|
sudo rm -rf ${QEMU_MEMORY_BACKEND_PATH_PREFIX}/${QEMU_MEMORY_BACKEND}
|
||||||
else
|
else
|
||||||
echo "不满足运行条件"
|
echo "不满足运行条件"
|
||||||
fi
|
fi
|
||||||
|
Loading…
x
Reference in New Issue
Block a user