增加内存分配日志监视器 (#424)

* 完成内存日志监视,并输出日志到文件
* 修复进程退出后,procfs查看进程status文件会崩溃的问题
* 修复signal唤醒进程的判断条件问题
This commit is contained in:
LoGin
2023-11-07 21:39:27 +08:00
committed by GitHub
parent 70a4e5550a
commit 7b32f5080f
46 changed files with 2033 additions and 59 deletions

View File

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

View File

@ -90,7 +90,6 @@ impl CFilesBuilder {
#[cfg(target_arch = "x86_64")]
c.define("__x86_64__", None);
}
}
fn setup_global_include_dir(c: &mut Build) {

View File

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

View 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"]

View File

@ -0,0 +1,9 @@
#![no_std]
#[allow(unused)]
#[macro_use]
pub extern crate thingbuf;
pub extern crate memoffset;
pub extern crate crc;

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

View 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;
}
}

View File

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

View File

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

View 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;
}
}
}
}

View File

@ -0,0 +1 @@
pub mod mm;

1
kernel/src/debug/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod klog;

View File

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

View File

@ -2,7 +2,7 @@ use core::intrinsics::unlikely;
use alloc::string::String;
use thingbuf::mpsc::{
use kdepends::thingbuf::mpsc::{
self,
errors::{TryRecvError, TrySendError},
};

View File

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

View File

@ -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<ProcessControlBlock>, _guard: SpinLockGuard<SignalStr
// 如果不是 fatal 的就只唤醒 stop 的进程来响应
// kdebug!("signal_wake_up");
// 如果目标进程已经在运行则发起一个ipi使得它陷入内核
let r = ProcessManager::wakeup_stop(&pcb);
if r.is_ok() {
let state = pcb.sched_info().state();
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);
} else {
if fatal {

View File

@ -39,6 +39,7 @@ mod arch;
mod libs;
#[macro_use]
mod include;
mod debug;
mod driver; // 如果driver依赖了libs应该在libs后面导出
mod exception;
mod filesystem;
@ -60,17 +61,17 @@ extern crate bitflags;
extern crate elf;
#[macro_use]
extern crate lazy_static;
extern crate memoffset;
extern crate num;
#[macro_use]
extern crate num_derive;
extern crate smoltcp;
extern crate thingbuf;
#[macro_use]
extern crate intertrait;
#[cfg(target_arch = "x86_64")]
extern crate x86;
extern crate klog_types;
use crate::mm::allocator::kernel_allocator::KernelAllocator;
use crate::process::ProcessManager;

View File

@ -16,9 +16,9 @@ pub struct IdAllocator {
impl IdAllocator {
/// 创建一个新的id分配器
pub const fn new(max_id: usize) -> 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),
}

View File

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

View File

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