Refactor profiling code.
This commit is contained in:
+1
-12
@@ -5,7 +5,7 @@ use core::mem;
|
||||
|
||||
use crate::LogicalCpuId;
|
||||
use crate::paging::{RmmA, RmmArch};
|
||||
use crate::percpu::{PercpuBlock, RingBuffer};
|
||||
use crate::percpu::PercpuBlock;
|
||||
|
||||
use x86::bits64::task::TaskStateSegment;
|
||||
use x86::Ring;
|
||||
@@ -204,17 +204,6 @@ pub unsafe fn init_paging(stack_offset: usize, cpu_id: LogicalCpuId) {
|
||||
profiling: None,
|
||||
};
|
||||
}
|
||||
pub unsafe fn init_allocator() {
|
||||
let percpu = PercpuBlock::current();
|
||||
|
||||
if percpu.cpu_id.get() == 4 { return }
|
||||
|
||||
let profiling = RingBuffer::create();
|
||||
|
||||
crate::scheme::debug::BUFS[percpu.cpu_id.get() as usize].store(profiling as *const _ as *mut _, core::sync::atomic::Ordering::SeqCst);
|
||||
(core::ptr::addr_of!(percpu.profiling) as *mut Option<&'static RingBuffer>).write(Some(profiling));
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(packed)]
|
||||
pub struct GdtEntry {
|
||||
|
||||
+10
-9
@@ -8,6 +8,7 @@ use hashbrown::HashMap;
|
||||
use x86::segmentation::Descriptor as X86IdtEntry;
|
||||
use x86::dtables::{self, DescriptorTablePointer};
|
||||
|
||||
use crate::profiling::maybe_setup_timer;
|
||||
use crate::{interrupt::*, LogicalCpuId};
|
||||
use crate::interrupt::irq::{__generic_interrupts_end, __generic_interrupts_start};
|
||||
use crate::ipi::IpiKind;
|
||||
@@ -111,26 +112,26 @@ const fn new_idt_reservations() -> [AtomicU64; 4] {
|
||||
}
|
||||
|
||||
/// Initialize the IDT for a
|
||||
pub unsafe fn init_paging_post_heap(is_bsp: bool, cpu_id: LogicalCpuId) {
|
||||
pub unsafe fn init_paging_post_heap(cpu_id: LogicalCpuId) {
|
||||
let mut idts_guard = IDTS.write();
|
||||
let idts_btree = idts_guard.get_or_insert_with(HashMap::new);
|
||||
|
||||
if is_bsp {
|
||||
if cpu_id == LogicalCpuId::BSP {
|
||||
idts_btree.insert(cpu_id, &mut INIT_BSP_IDT);
|
||||
} else {
|
||||
let idt = idts_btree.entry(cpu_id).or_insert_with(|| Box::leak(Box::new(Idt::new())));
|
||||
init_generic(is_bsp, idt);
|
||||
init_generic(cpu_id, idt);
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes a fully functional IDT for use before it be moved into the map. This is ONLY called
|
||||
/// on the BSP, since the kernel heap is ready for the APs.
|
||||
pub unsafe fn init_paging_bsp() {
|
||||
init_generic(true, &mut INIT_BSP_IDT);
|
||||
init_generic(LogicalCpuId::BSP, &mut INIT_BSP_IDT);
|
||||
}
|
||||
|
||||
/// Initializes an IDT for any type of processor.
|
||||
pub unsafe fn init_generic(is_bsp: bool, idt: &mut Idt) {
|
||||
pub unsafe fn init_generic(cpu_id: LogicalCpuId, idt: &mut Idt) {
|
||||
let (current_idt, current_reservations) = (&mut idt.entries, &mut idt.reservations);
|
||||
|
||||
let idtr: DescriptorTablePointer<X86IdtEntry> = DescriptorTablePointer {
|
||||
@@ -208,7 +209,7 @@ pub unsafe fn init_generic(is_bsp: bool, idt: &mut Idt) {
|
||||
// reserve bits 31:0, i.e. the first 32 interrupts, which are reserved for exceptions
|
||||
*current_reservations[0].get_mut() |= 0x0000_0000_FFFF_FFFF;
|
||||
|
||||
if is_bsp {
|
||||
if cpu_id == LogicalCpuId::BSP {
|
||||
// Set up IRQs
|
||||
current_idt[32].set_func(irq::pit_stack);
|
||||
current_idt[33].set_func(irq::keyboard);
|
||||
@@ -232,14 +233,12 @@ pub unsafe fn init_generic(is_bsp: bool, idt: &mut Idt) {
|
||||
// reserve bits 49:32, which are for the standard IRQs, and for the local apic timer and error.
|
||||
*current_reservations[0].get_mut() |= 0x0003_FFFF_0000_0000;
|
||||
} else {
|
||||
current_idt[32].set_func(irq::aux_timer);
|
||||
// TODO: use_default_irqs! but also the legacy IRQs that are only needed on one CPU
|
||||
current_idt[49].set_func(irq::lapic_error);
|
||||
|
||||
// reserve bit 49
|
||||
*current_reservations[0].get_mut() |= 1 << 32 | 1 << 49;
|
||||
*current_reservations[0].get_mut() |= 1 << 49;
|
||||
}
|
||||
|
||||
// Set IPI handlers
|
||||
current_idt[IpiKind::Wakeup as usize].set_func(ipi::wakeup);
|
||||
current_idt[IpiKind::Switch as usize].set_func(ipi::switch);
|
||||
@@ -256,6 +255,8 @@ pub unsafe fn init_generic(is_bsp: bool, idt: &mut Idt) {
|
||||
current_idt[0x80].set_flags(IdtFlags::PRESENT | IdtFlags::RING_3 | IdtFlags::INTERRUPT);
|
||||
idt.set_reserved_mut(0x80, true);
|
||||
|
||||
maybe_setup_timer(idt, cpu_id);
|
||||
|
||||
dtables::lidt(&idtr);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use core::sync::atomic::Ordering;
|
||||
|
||||
use x86::irq::PageFaultError;
|
||||
|
||||
use crate::memory::GenericPfFlags;
|
||||
use crate::scheme::serio::IS_PROFILING;
|
||||
use crate::ptrace;
|
||||
use crate::{
|
||||
interrupt::stack_trace,
|
||||
paging::VirtualAddress,
|
||||
@@ -48,46 +46,7 @@ interrupt_stack!(debug, @paranoid, |stack| {
|
||||
});
|
||||
|
||||
interrupt_stack!(non_maskable, @paranoid, |stack| {
|
||||
let Some(profiling) = crate::percpu::PercpuBlock::current().profiling else {
|
||||
return;
|
||||
};
|
||||
if !IS_PROFILING.load(Ordering::Relaxed) {
|
||||
return;
|
||||
}
|
||||
if stack.iret.cs & 0b00 == 0b11 {
|
||||
profiling.nmi_ucount.store(profiling.nmi_ucount.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
|
||||
return;
|
||||
} else if stack.iret.rflags & (1 << 9) != 0 {
|
||||
// Interrupts were enabled, i.e. we were in kmain, so ignore.
|
||||
return;
|
||||
} else {
|
||||
profiling.nmi_kcount.store(profiling.nmi_kcount.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
|
||||
};
|
||||
|
||||
let mut buf = [0_usize; 32];
|
||||
buf[0] = stack.iret.rip & !(1<<63);
|
||||
buf[1] = x86::time::rdtsc() as usize;
|
||||
|
||||
let mut bp = stack.preserved.rbp;
|
||||
|
||||
let mut len = 2;
|
||||
|
||||
for i in 2..32 {
|
||||
if bp.saturating_add(16) < crate::KERNEL_HEAP_OFFSET || bp >= crate::KERNEL_HEAP_OFFSET + crate::PML4_SIZE {
|
||||
break;
|
||||
}
|
||||
let ip = ((bp + 8) as *const usize).read();
|
||||
bp = (bp as *const usize).read();
|
||||
|
||||
if ip < crate::kernel_executable_offsets::__text_start() || ip >= crate::kernel_executable_offsets::__text_end() {
|
||||
break;
|
||||
}
|
||||
buf[i] = ip;
|
||||
|
||||
len = i + 1;
|
||||
}
|
||||
|
||||
let _ = profiling.extend(&buf[..len]);
|
||||
crate::profiling::nmi_handler(stack);
|
||||
});
|
||||
|
||||
interrupt_stack!(breakpoint, |stack| {
|
||||
|
||||
@@ -28,8 +28,9 @@ pub fn ipi(kind: IpiKind, target: IpiTarget) {
|
||||
if matches!(kind, IpiKind::Profile) {
|
||||
let icr = (target as u64) << 18 | 1 << 14 | 0b100 << 8;
|
||||
unsafe { LOCAL_APIC.set_icr(icr) };
|
||||
} else {
|
||||
let icr = (target as u64) << 18 | 1 << 14 | (kind as u64);
|
||||
unsafe { LOCAL_APIC.set_icr(icr) };
|
||||
return;
|
||||
}
|
||||
|
||||
let icr = (target as u64) << 18 | 1 << 14 | (kind as u64);
|
||||
unsafe { LOCAL_APIC.set_icr(icr) };
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
use core::slice;
|
||||
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering, AtomicU32};
|
||||
|
||||
use crate::{allocator, memory, LogicalCpuId};
|
||||
use crate::{allocator, memory, LogicalCpuId, profiling};
|
||||
#[cfg(feature = "acpi")]
|
||||
use crate::acpi;
|
||||
use crate::arch::pti;
|
||||
@@ -150,13 +150,13 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! {
|
||||
// Setup kernel heap
|
||||
allocator::init();
|
||||
|
||||
gdt::init_allocator();
|
||||
profiling::init();
|
||||
|
||||
// Set up double buffer for grpahical debug now that heap is available
|
||||
#[cfg(feature = "graphical_debug")]
|
||||
graphical_debug::init_heap();
|
||||
|
||||
idt::init_paging_post_heap(true, LogicalCpuId::BSP);
|
||||
idt::init_paging_post_heap(LogicalCpuId::BSP);
|
||||
|
||||
// Activate memory logging
|
||||
log::init();
|
||||
@@ -236,10 +236,10 @@ pub unsafe extern fn kstart_ap(args_ptr: *const KernelArgsAp) -> ! {
|
||||
// Set up GDT with TLS
|
||||
gdt::init_paging(stack_end, cpu_id);
|
||||
|
||||
gdt::init_allocator();
|
||||
profiling::init();
|
||||
|
||||
// Set up IDT for AP
|
||||
idt::init_paging_post_heap(false, cpu_id);
|
||||
idt::init_paging_post_heap(cpu_id);
|
||||
|
||||
crate::alternative::early_init(false);
|
||||
|
||||
|
||||
+6
-26
@@ -60,7 +60,6 @@ extern crate alloc;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
|
||||
use core::sync::atomic::AtomicUsize;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
use crate::scheme::SchemeNamespace;
|
||||
@@ -122,6 +121,9 @@ mod percpu;
|
||||
/// Process tracing
|
||||
mod ptrace;
|
||||
|
||||
/// Performance profiling of the kernel
|
||||
pub mod profiling;
|
||||
|
||||
/// Schemes, filesystem handlers
|
||||
mod scheme;
|
||||
|
||||
@@ -188,7 +190,7 @@ fn kmain(cpu_count: u32, bootstrap: Bootstrap) -> ! {
|
||||
info!("Env: {:?}", ::core::str::from_utf8(bootstrap.env));
|
||||
|
||||
BOOTSTRAP.call_once(|| bootstrap);
|
||||
crate::ACK.fetch_add(1, Ordering::SeqCst);
|
||||
profiling::ready_for_profiling();
|
||||
|
||||
match context::contexts_mut().spawn(userspace_init) {
|
||||
Ok(context_lock) => {
|
||||
@@ -216,32 +218,10 @@ fn kmain(cpu_count: u32, bootstrap: Bootstrap) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
pub static ACK: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
/// This is the main kernel entry point for secondary CPUs
|
||||
#[allow(unreachable_code, unused_variables)]
|
||||
fn kmain_ap(cpu_id: LogicalCpuId) -> ! {
|
||||
if cpu_id.get() == 4 {
|
||||
unsafe {
|
||||
for i in 33..255 {
|
||||
crate::idt::IDTS.write().as_mut().unwrap().get_mut(&cpu_id).unwrap().entries[i].set_func(crate::interrupt::ipi::wakeup);
|
||||
}
|
||||
|
||||
let apic = &mut crate::device::local_apic::LOCAL_APIC;
|
||||
apic.set_lvt_timer((0b01 << 17) | 32);
|
||||
apic.set_div_conf(0b1011);
|
||||
apic.set_init_count(0xffff_f);
|
||||
|
||||
while ACK.load(Ordering::Relaxed) < 4 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
interrupt::enable_and_nop();
|
||||
loop {
|
||||
interrupt::halt();
|
||||
}
|
||||
}
|
||||
}
|
||||
profiling::maybe_run_profiling_helper_forever(cpu_id);
|
||||
|
||||
if cfg!(feature = "multi_core") {
|
||||
context::init();
|
||||
@@ -249,7 +229,7 @@ fn kmain_ap(cpu_id: LogicalCpuId) -> ! {
|
||||
let pid = syscall::getpid();
|
||||
info!("AP {}: {:?}", cpu_id, pid);
|
||||
|
||||
crate::ACK.fetch_add(1, Ordering::SeqCst);
|
||||
profiling::ready_for_profiling();
|
||||
|
||||
loop {
|
||||
unsafe {
|
||||
|
||||
+1
-73
@@ -1,10 +1,6 @@
|
||||
use core::cell::UnsafeCell;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
|
||||
use crate::LogicalCpuId;
|
||||
use crate::context::switch::ContextSwitchPercpu;
|
||||
use crate::profiling::RingBuffer;
|
||||
|
||||
/// The percpu block, that stored all percpu variables.
|
||||
pub struct PercpuBlock {
|
||||
@@ -20,72 +16,4 @@ pub struct PercpuBlock {
|
||||
pub profiling: Option<&'static RingBuffer>,
|
||||
}
|
||||
|
||||
const N: usize = 64 * 1024 * 1024;
|
||||
|
||||
pub struct RingBuffer {
|
||||
head: AtomicUsize,
|
||||
tail: AtomicUsize,
|
||||
buf: &'static [UnsafeCell<usize>; N],
|
||||
pub(crate) nmi_kcount: AtomicUsize,
|
||||
pub(crate) nmi_ucount: AtomicUsize,
|
||||
}
|
||||
|
||||
impl RingBuffer {
|
||||
unsafe fn advance_head(&self, n: usize) {
|
||||
self.head.store(self.head.load(Ordering::Acquire).wrapping_add(n), Ordering::Release);
|
||||
}
|
||||
unsafe fn advance_tail(&self, n: usize) {
|
||||
self.tail.store(self.tail.load(Ordering::Acquire).wrapping_add(n), Ordering::Release);
|
||||
}
|
||||
unsafe fn sender_owned(&self) -> [&[UnsafeCell<usize>]; 2] {
|
||||
let head = self.head.load(Ordering::Acquire) % N;
|
||||
let tail = self.tail.load(Ordering::Acquire) % N;
|
||||
|
||||
if head <= tail {
|
||||
[&self.buf[tail..], &self.buf[..head]]
|
||||
} else {
|
||||
[&self.buf[tail..head], &[]]
|
||||
}
|
||||
}
|
||||
unsafe fn receiver_owned(&self) -> [&[UnsafeCell<usize>]; 2] {
|
||||
let head = self.head.load(Ordering::Acquire) % N;
|
||||
let tail = self.tail.load(Ordering::Acquire) % N;
|
||||
|
||||
if head > tail {
|
||||
[&self.buf[head..], &self.buf[..tail]]
|
||||
} else {
|
||||
[&self.buf[head..tail], &[]]
|
||||
}
|
||||
}
|
||||
pub unsafe fn extend(&self, mut slice: &[usize]) -> usize {
|
||||
let mut n = 0;
|
||||
for mut sender_slice in self.sender_owned() {
|
||||
while !slice.is_empty() && !sender_slice.is_empty() {
|
||||
sender_slice[0].get().write(slice[0]);
|
||||
slice = &slice[1..];
|
||||
sender_slice = &sender_slice[1..];
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
self.advance_tail(n);
|
||||
n
|
||||
}
|
||||
pub unsafe fn peek(&self) -> [&[usize]; 2] {
|
||||
self.receiver_owned().map(|slice| core::slice::from_raw_parts(slice.as_ptr().cast(), slice.len()))
|
||||
}
|
||||
pub unsafe fn advance(&self, n: usize) {
|
||||
self.advance_head(n)
|
||||
}
|
||||
pub fn create() -> &'static Self {
|
||||
|
||||
Box::leak(Box::new(Self {
|
||||
head: AtomicUsize::new(0),
|
||||
tail: AtomicUsize::new(0),
|
||||
buf: Box::leak(unsafe { Box::new_zeroed().assume_init() }),
|
||||
nmi_kcount: AtomicUsize::new(0),
|
||||
nmi_ucount: AtomicUsize::new(0),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// PercpuBlock::current() is implemented somewhere in the arch-specific modules
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
use core::cell::UnsafeCell;
|
||||
use core::mem::size_of;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering, AtomicPtr, AtomicBool, AtomicU32};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
|
||||
use crate::idt::Idt;
|
||||
use crate::interrupt::irq::aux_timer;
|
||||
use crate::{LogicalCpuId, interrupt};
|
||||
use crate::interrupt::InterruptStack;
|
||||
use crate::percpu::PercpuBlock;
|
||||
use crate::syscall::error::*;
|
||||
use crate::syscall::usercopy::UserSliceWo;
|
||||
|
||||
const N: usize = 64 * 1024 * 1024;
|
||||
|
||||
pub const HARDCODED_CPU_COUNT: u32 = 4;
|
||||
|
||||
pub const PROFILER_CPU: LogicalCpuId = LogicalCpuId::new(HARDCODED_CPU_COUNT);
|
||||
|
||||
pub struct RingBuffer {
|
||||
head: AtomicUsize,
|
||||
tail: AtomicUsize,
|
||||
buf: &'static [UnsafeCell<usize>; N],
|
||||
pub(crate) nmi_kcount: AtomicUsize,
|
||||
pub(crate) nmi_ucount: AtomicUsize,
|
||||
}
|
||||
|
||||
impl RingBuffer {
|
||||
unsafe fn advance_head(&self, n: usize) {
|
||||
self.head.store(self.head.load(Ordering::Acquire).wrapping_add(n), Ordering::Release);
|
||||
}
|
||||
unsafe fn advance_tail(&self, n: usize) {
|
||||
self.tail.store(self.tail.load(Ordering::Acquire).wrapping_add(n), Ordering::Release);
|
||||
}
|
||||
unsafe fn sender_owned(&self) -> [&[UnsafeCell<usize>]; 2] {
|
||||
let head = self.head.load(Ordering::Acquire) % N;
|
||||
let tail = self.tail.load(Ordering::Acquire) % N;
|
||||
|
||||
if head <= tail {
|
||||
[&self.buf[tail..], &self.buf[..head]]
|
||||
} else {
|
||||
[&self.buf[tail..head], &[]]
|
||||
}
|
||||
}
|
||||
unsafe fn receiver_owned(&self) -> [&[UnsafeCell<usize>]; 2] {
|
||||
let head = self.head.load(Ordering::Acquire) % N;
|
||||
let tail = self.tail.load(Ordering::Acquire) % N;
|
||||
|
||||
if head > tail {
|
||||
[&self.buf[head..], &self.buf[..tail]]
|
||||
} else {
|
||||
[&self.buf[head..tail], &[]]
|
||||
}
|
||||
}
|
||||
pub unsafe fn extend(&self, mut slice: &[usize]) -> usize {
|
||||
let mut n = 0;
|
||||
for mut sender_slice in self.sender_owned() {
|
||||
while !slice.is_empty() && !sender_slice.is_empty() {
|
||||
sender_slice[0].get().write(slice[0]);
|
||||
slice = &slice[1..];
|
||||
sender_slice = &sender_slice[1..];
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
self.advance_tail(n);
|
||||
n
|
||||
}
|
||||
pub unsafe fn peek(&self) -> [&[usize]; 2] {
|
||||
self.receiver_owned().map(|slice| core::slice::from_raw_parts(slice.as_ptr().cast(), slice.len()))
|
||||
}
|
||||
pub unsafe fn advance(&self, n: usize) {
|
||||
self.advance_head(n)
|
||||
}
|
||||
pub fn create() -> &'static Self {
|
||||
|
||||
Box::leak(Box::new(Self {
|
||||
head: AtomicUsize::new(0),
|
||||
tail: AtomicUsize::new(0),
|
||||
buf: Box::leak(unsafe { Box::new_zeroed().assume_init() }),
|
||||
nmi_kcount: AtomicUsize::new(0),
|
||||
nmi_ucount: AtomicUsize::new(0),
|
||||
}))
|
||||
}
|
||||
}
|
||||
const NULL: AtomicPtr<RingBuffer> = AtomicPtr::new(core::ptr::null_mut());
|
||||
pub static BUFS: [AtomicPtr<RingBuffer>; 4] = [NULL; 4];
|
||||
|
||||
pub const PROFILE_TOGGLEABLE: bool = true;
|
||||
pub static IS_PROFILING: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
pub fn serio_command(index: usize, data: u8) {
|
||||
if PROFILE_TOGGLEABLE {
|
||||
if index == 0 && data == 30 {
|
||||
log::info!("Enabling profiling");
|
||||
IS_PROFILING.store(true, Ordering::SeqCst);
|
||||
} else if index == 0 && data == 48 {
|
||||
log::info!("Disabling profiling");
|
||||
IS_PROFILING.store(false, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drain_buffer(cpu_num: LogicalCpuId, buf: UserSliceWo) -> Result<usize> {
|
||||
unsafe {
|
||||
let Some(src) = BUFS.get(cpu_num.get() as usize).ok_or(Error::new(EBADFD))?.load(Ordering::Relaxed).as_ref() else {
|
||||
return Ok(0);
|
||||
};
|
||||
let byte_slices = src.peek().map(|words| core::slice::from_raw_parts(words.as_ptr().cast::<u8>(), words.len() * size_of::<usize>()));
|
||||
|
||||
let copied_1 = buf.copy_common_bytes_from_slice(byte_slices[0])?;
|
||||
src.advance(copied_1 / size_of::<usize>());
|
||||
|
||||
let copied_2 = if let Some(remaining) = buf.advance(copied_1) {
|
||||
remaining.copy_common_bytes_from_slice(byte_slices[1])?
|
||||
} else {
|
||||
0
|
||||
};
|
||||
src.advance(copied_2 / size_of::<usize>());
|
||||
|
||||
Ok(copied_1 + copied_2)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn nmi_handler(stack: &InterruptStack) {
|
||||
let Some(profiling) = crate::percpu::PercpuBlock::current().profiling else {
|
||||
return;
|
||||
};
|
||||
if !IS_PROFILING.load(Ordering::Relaxed) {
|
||||
return;
|
||||
}
|
||||
if stack.iret.cs & 0b00 == 0b11 {
|
||||
profiling.nmi_ucount.store(profiling.nmi_ucount.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
|
||||
return;
|
||||
} else if stack.iret.rflags & (1 << 9) != 0 {
|
||||
// Interrupts were enabled, i.e. we were in kmain, so ignore.
|
||||
return;
|
||||
} else {
|
||||
profiling.nmi_kcount.store(profiling.nmi_kcount.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
|
||||
};
|
||||
|
||||
let mut buf = [0_usize; 32];
|
||||
buf[0] = stack.iret.rip & !(1<<63);
|
||||
buf[1] = x86::time::rdtsc() as usize;
|
||||
|
||||
let mut bp = stack.preserved.rbp;
|
||||
|
||||
let mut len = 2;
|
||||
|
||||
for i in 2..32 {
|
||||
if bp.saturating_add(16) < crate::KERNEL_HEAP_OFFSET || bp >= crate::KERNEL_HEAP_OFFSET + crate::PML4_SIZE {
|
||||
break;
|
||||
}
|
||||
let ip = ((bp + 8) as *const usize).read();
|
||||
bp = (bp as *const usize).read();
|
||||
|
||||
if ip < crate::kernel_executable_offsets::__text_start() || ip >= crate::kernel_executable_offsets::__text_end() {
|
||||
break;
|
||||
}
|
||||
buf[i] = ip;
|
||||
|
||||
len = i + 1;
|
||||
}
|
||||
|
||||
let _ = profiling.extend(&buf[..len]);
|
||||
}
|
||||
pub unsafe fn init() {
|
||||
let percpu = PercpuBlock::current();
|
||||
|
||||
if percpu.cpu_id == PROFILER_CPU { return }
|
||||
|
||||
let profiling = RingBuffer::create();
|
||||
|
||||
BUFS[percpu.cpu_id.get() as usize].store(profiling as *const _ as *mut _, core::sync::atomic::Ordering::SeqCst);
|
||||
(core::ptr::addr_of!(percpu.profiling) as *mut Option<&'static RingBuffer>).write(Some(profiling));
|
||||
}
|
||||
|
||||
static ACK: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
pub fn ready_for_profiling() {
|
||||
ACK.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn maybe_run_profiling_helper_forever(cpu_id: LogicalCpuId) {
|
||||
if cpu_id != PROFILER_CPU {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
for i in 33..255 {
|
||||
crate::idt::IDTS.write().as_mut().unwrap().get_mut(&cpu_id).unwrap().entries[i].set_func(crate::interrupt::ipi::wakeup);
|
||||
}
|
||||
|
||||
let apic = &mut crate::device::local_apic::LOCAL_APIC;
|
||||
apic.set_lvt_timer((0b01 << 17) | 32);
|
||||
apic.set_div_conf(0b1011);
|
||||
apic.set_init_count(0xffff_f);
|
||||
|
||||
while ACK.load(Ordering::Relaxed) < HARDCODED_CPU_COUNT {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
assert_eq!(crate::cpu_count(), HARDCODED_CPU_COUNT + 1);
|
||||
|
||||
interrupt::enable_and_nop();
|
||||
loop {
|
||||
interrupt::halt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maybe_setup_timer(idt: &mut Idt, cpu_id: LogicalCpuId) {
|
||||
if cpu_id != PROFILER_CPU {
|
||||
return;
|
||||
}
|
||||
idt.entries[32].set_func(aux_timer);
|
||||
idt.set_reserved(32, true);
|
||||
}
|
||||
+14
-26
@@ -1,9 +1,9 @@
|
||||
use core::sync::atomic::{AtomicUsize, Ordering, AtomicPtr};
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use spin::RwLock;
|
||||
|
||||
use crate::LogicalCpuId;
|
||||
use crate::arch::debug::Writer;
|
||||
use crate::event;
|
||||
use crate::percpu::RingBuffer;
|
||||
use crate::scheme::*;
|
||||
use crate::sync::WaitQueue;
|
||||
use crate::syscall::flag::{EventFlags, EVENT_READ, F_GETFL, F_SETFL, O_ACCMODE, O_NONBLOCK};
|
||||
@@ -45,7 +45,9 @@ impl KernelScheme for DebugScheme {
|
||||
|
||||
let num = match path {
|
||||
"" => !0,
|
||||
"profiling" => flags & 0xffff,
|
||||
p if p.starts_with("profiling-") => {
|
||||
path[10..].parse().map_err(|_| Error::new(ENOENT))?
|
||||
}
|
||||
|
||||
_ => return Err(Error::new(ENOENT)),
|
||||
};
|
||||
@@ -112,32 +114,18 @@ impl KernelScheme for DebugScheme {
|
||||
INPUT
|
||||
.receive_into_user(buf, handle.flags & O_NONBLOCK != O_NONBLOCK, "DebugScheme::read")
|
||||
} else {
|
||||
unsafe {
|
||||
let Some(src) = BUFS.get(handle.num).ok_or(Error::new(EBADFD))?.load(Ordering::Relaxed).as_ref() else {
|
||||
return Ok(0);
|
||||
};
|
||||
let byte_slices = src.peek().map(|words| core::slice::from_raw_parts(words.as_ptr().cast::<u8>(), words.len() * 8));
|
||||
|
||||
let copied_1 = buf.copy_common_bytes_from_slice(byte_slices[0])?;
|
||||
src.advance(copied_1 / 8);
|
||||
|
||||
let copied_2 = if let Some(remaining) = buf.advance(copied_1) {
|
||||
remaining.copy_common_bytes_from_slice(byte_slices[1])?
|
||||
} else {
|
||||
0
|
||||
};
|
||||
src.advance(copied_2 / 8);
|
||||
|
||||
Ok(copied_1 + copied_2)
|
||||
}
|
||||
crate::profiling::drain_buffer(LogicalCpuId::new(handle.num as u32), buf)
|
||||
}
|
||||
}
|
||||
|
||||
fn kwrite(&self, id: usize, buf: UserSliceRo) -> Result<usize> {
|
||||
let _handle = {
|
||||
let handle = {
|
||||
let handles = HANDLES.read();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
if handle.num != !0 {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
let mut tmp = [0_u8; 512];
|
||||
|
||||
@@ -154,10 +142,13 @@ impl KernelScheme for DebugScheme {
|
||||
Ok(buf.len())
|
||||
}
|
||||
fn kfpath(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
let _handle = {
|
||||
let handle = {
|
||||
let handles = HANDLES.read();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
if handle.num != !0 {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
// TODO: Copy elsewhere in the kernel?
|
||||
const SRC: &[u8] = b"debug:";
|
||||
@@ -167,6 +158,3 @@ impl KernelScheme for DebugScheme {
|
||||
Ok(byte_count)
|
||||
}
|
||||
}
|
||||
|
||||
const NULL: AtomicPtr<RingBuffer> = AtomicPtr::new(core::ptr::null_mut());
|
||||
pub static BUFS: [AtomicPtr<RingBuffer>; 4] = [NULL; 4];
|
||||
|
||||
+3
-13
@@ -1,7 +1,7 @@
|
||||
//! PS/2 unfortunately requires a kernel driver to prevent race conditions due
|
||||
//! to how status is utilized
|
||||
use core::str;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering, AtomicBool};
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use spin::RwLock;
|
||||
|
||||
@@ -25,20 +25,10 @@ struct Handle {
|
||||
// Using BTreeMap as hashbrown doesn't have a const constructor.
|
||||
static HANDLES: RwLock<BTreeMap<usize, Handle>> = RwLock::new(BTreeMap::new());
|
||||
|
||||
pub const PROFILE_TOGGLEABLE: bool = true;
|
||||
pub static IS_PROFILING: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// Add to the input queue
|
||||
pub fn serio_input(index: usize, data: u8) {
|
||||
if PROFILE_TOGGLEABLE {
|
||||
if index == 0 && data == 30 {
|
||||
log::info!("Enabling profiling");
|
||||
IS_PROFILING.store(true, Ordering::SeqCst);
|
||||
} else if index == 0 && data == 48 {
|
||||
log::info!("Disabling profiling");
|
||||
IS_PROFILING.store(false, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
crate::profiling::serio_command(index, data);
|
||||
|
||||
INPUT[index].send(data);
|
||||
|
||||
for (id, _handle) in HANDLES.read().iter() {
|
||||
|
||||
Reference in New Issue
Block a user