Refactor panic stack walking

This commit is contained in:
Andrey Turkin
2024-07-16 06:47:12 +03:00
parent 755694494b
commit 14eb140f7a
11 changed files with 159 additions and 147 deletions
-1
View File
@@ -3,7 +3,6 @@ use core::arch::asm;
use fdt::{node::NodeProperty, Fdt};
use super::gic::GicDistIf;
use log::info;
use syscall::{
error::{Error, EINVAL},
Result,
+1 -1
View File
@@ -2,8 +2,8 @@ use rmm::VirtualAddress;
use crate::{
exception_stack,
interrupt::stack_trace,
memory::{ArchIntCtx, GenericPfFlags},
panic::stack_trace,
syscall,
syscall::flag::*,
};
+1 -1
View File
@@ -12,7 +12,7 @@ pub mod trace;
use crate::cpu_set::LogicalCpuId;
pub use self::{handler::InterruptStack, trace::stack_trace};
pub use self::handler::InterruptStack;
/// Clear interrupts
#[inline(always)]
+22 -32
View File
@@ -1,38 +1,28 @@
use core::{arch::asm, mem};
use crate::{memory::KernelMapper, paging::VirtualAddress};
pub struct StackTrace {
pub fp: usize,
pub pc_ptr: *const usize,
}
/// Get a stack trace
//TODO: Check for stack being mapped before dereferencing
#[inline(never)]
pub unsafe fn stack_trace() {
let mut fp: usize;
asm!("mov {}, fp", out(reg) fp);
impl StackTrace {
#[inline(always)]
pub unsafe fn start() -> Option<Self> {
let fp: usize;
asm!("mov {}, fp", out(reg) fp);
let pc_ptr = fp.checked_add(mem::size_of::<usize>())?;
Some(StackTrace {
fp,
pc_ptr: pc_ptr as *const usize,
})
}
println!("TRACE: {:>016x}", fp);
let mapper = KernelMapper::lock();
//Maximum 64 frames
for _frame in 0..64 {
if let Some(pc_fp) = fp.checked_add(mem::size_of::<usize>()) {
if mapper.translate(VirtualAddress::new(fp)).is_some()
&& mapper.translate(VirtualAddress::new(pc_fp)).is_some()
{
let pc = *(pc_fp as *const usize);
if pc == 0 {
println!(" {:>016x}: EMPTY RETURN", fp);
break;
}
println!(" FP {:>016x}: PC {:>016x}", fp, pc);
fp = *(fp as *const usize);
//TODO symbol_trace(pc);
} else {
println!(" {:>016x}: GUARD PAGE", fp);
break;
}
} else {
println!(" {:>016x}: fp OVERFLOW", fp);
}
pub unsafe fn next(self) -> Option<Self> {
let fp = *(self.fp as *const usize);
let pc_ptr = fp.checked_add(mem::size_of::<usize>())?;
Some(StackTrace {
fp: fp,
pc_ptr: pc_ptr as *const usize,
})
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ use rmm::{
VirtualAddress, KILOBYTE, MEGABYTE,
};
use crate::{cpu_set::LogicalCpuId, init::device_tree::MEMORY_MAP, paging::entry::EntryFlags};
use crate::{init::device_tree::MEMORY_MAP, paging::entry::EntryFlags};
use super::CurrentRmmArch as RmmA;
+2 -2
View File
@@ -1,8 +1,8 @@
use x86::irq::PageFaultError;
use crate::{
interrupt::stack_trace, interrupt_error, interrupt_stack, ksignal, memory::GenericPfFlags,
paging::VirtualAddress, ptrace, syscall::flag::*,
interrupt_error, interrupt_stack, ksignal, memory::GenericPfFlags, paging::VirtualAddress,
panic::stack_trace, ptrace, syscall::flag::*,
};
interrupt_stack!(divide_by_zero, |stack| {
+2 -2
View File
@@ -1,8 +1,8 @@
use x86::irq::PageFaultError;
use crate::{
interrupt::stack_trace, interrupt_error, interrupt_stack, ksignal, memory::GenericPfFlags,
paging::VirtualAddress, ptrace, syscall::flag::*,
interrupt_error, interrupt_stack, ksignal, memory::GenericPfFlags, paging::VirtualAddress,
panic::stack_trace, ptrace, syscall::flag::*,
};
interrupt_stack!(divide_by_zero, |stack| {
-2
View File
@@ -3,8 +3,6 @@
pub mod ipi;
pub mod trace;
pub use self::trace::stack_trace;
pub use super::{
device::local_apic::bsp_apic_id,
idt::{available_irqs_iter, is_reserved, set_reserved},
+25 -101
View File
@@ -1,107 +1,31 @@
use core::{mem, str};
use core::mem;
use goblin::elf::sym;
use rustc_demangle::demangle;
use crate::{memory::KernelMapper, paging::VirtualAddress, USER_END_OFFSET};
/// Get a stack trace
//TODO: Check for stack being mapped before dereferencing
#[inline(never)]
pub unsafe fn stack_trace() {
let mut sp: usize;
#[cfg(target_arch = "x86")]
core::arch::asm!("mov {}, ebp", out(reg) sp);
#[cfg(target_arch = "x86_64")]
core::arch::asm!("mov {}, rbp", out(reg) sp);
println!("TRACE: {:>016X}", sp);
//Maximum 64 frames
let mapper = KernelMapper::lock();
for _frame in 0..64 {
if let Some(ip_bp) = sp.checked_add(mem::size_of::<usize>()) {
let bp_virt = VirtualAddress::new(sp);
let ip_bp_virt = VirtualAddress::new(ip_bp);
if bp_virt.data() >= USER_END_OFFSET
&& ip_bp_virt.data() >= USER_END_OFFSET
&& mapper.translate(bp_virt).is_some()
&& mapper.translate(ip_bp_virt).is_some()
{
let ip = (ip_bp as *const usize).read();
if ip == 0 {
println!(" {:>016X}: EMPTY RETURN", sp);
break;
}
println!(" {:>016X}: {:>016X}", sp, ip);
sp = (sp as *const usize).read();
symbol_trace(ip);
} else {
println!(" {:>016X}: GUARD PAGE", sp);
break;
}
} else {
println!(" {:>016X}: RBP OVERFLOW", sp);
break;
}
}
pub struct StackTrace {
pub fp: usize,
pub pc_ptr: *const usize,
}
/// Get a symbol
//TODO: Do not create Elf object for every symbol lookup
#[inline(never)]
pub unsafe fn symbol_trace(addr: usize) {
use core::{slice, sync::atomic::Ordering};
impl StackTrace {
#[inline(always)]
pub unsafe fn start() -> Option<Self> {
let mut fp: usize;
#[cfg(target_arch = "x86")]
core::arch::asm!("mov {}, ebp", out(reg) fp);
#[cfg(target_arch = "x86_64")]
core::arch::asm!("mov {}, rbp", out(reg) fp);
let pc_ptr = fp.checked_add(mem::size_of::<usize>())?;
Some(Self {
fp,
pc_ptr: pc_ptr as *const usize,
})
}
use crate::{
elf::Elf,
start::{KERNEL_BASE, KERNEL_SIZE},
};
let kernel_ptr = (KERNEL_BASE.load(Ordering::SeqCst) + crate::PHYS_OFFSET) as *const u8;
let kernel_slice = slice::from_raw_parts(kernel_ptr, KERNEL_SIZE.load(Ordering::SeqCst));
if let Ok(elf) = Elf::from(kernel_slice) {
let mut strtab_opt = None;
for section in elf.sections() {
if section.sh_type == ::goblin::elf::section_header::SHT_STRTAB {
strtab_opt = Some(section);
break;
}
}
if let Some(symbols) = elf.symbols() {
for sym in symbols {
if sym::st_type(sym.st_info) == sym::STT_FUNC
&& addr >= sym.st_value as usize
&& addr < (sym.st_value + sym.st_size) as usize
{
println!(
" {:>016X}+{:>04X}",
sym.st_value,
addr - sym.st_value as usize
);
if let Some(strtab) = strtab_opt {
let start = strtab.sh_offset as usize + sym.st_name as usize;
let mut end = start;
while end < elf.data.len() {
let b = elf.data[end];
end += 1;
if b == 0 {
break;
}
}
if end > start {
let sym_slice = &elf.data[start..end - 1];
if let Ok(sym_name) = str::from_utf8(sym_slice) {
println!(" {:#}", demangle(sym_name));
}
}
}
}
}
}
pub unsafe fn next(self) -> Option<Self> {
let fp = *(self.fp as *const usize);
let pc_ptr = fp.checked_add(mem::size_of::<usize>())?;
Some(Self {
fp: fp,
pc_ptr: pc_ptr as *const usize,
})
}
}
-1
View File
@@ -124,7 +124,6 @@ use spinning_top::RwSpinlock;
mod memory;
/// Panic
#[cfg(not(test))]
mod panic;
mod percpu;
+105 -3
View File
@@ -1,16 +1,28 @@
//! Intrinsics for panic handling
use core::panic::PanicInfo;
use core::{panic::PanicInfo, slice, str, sync::atomic::Ordering};
use goblin::elf::sym;
use rmm::VirtualAddress;
use rustc_demangle::demangle;
use crate::{context, cpu_id, interrupt, syscall};
use crate::{
arch::{consts::USER_END_OFFSET, interrupt::trace::StackTrace},
context, cpu_id,
elf::Elf,
interrupt,
memory::KernelMapper,
start::KERNEL_SIZE,
syscall,
};
/// Required to handle panics
#[cfg(not(test))]
#[panic_handler]
fn rust_begin_unwind(info: &PanicInfo) -> ! {
println!("KERNEL PANIC: {}", info);
unsafe {
interrupt::stack_trace();
stack_trace();
}
let context_lock = context::current();
@@ -33,3 +45,93 @@ fn rust_begin_unwind(info: &PanicInfo) -> ! {
}
}
}
/// Get a stack trace
#[inline(never)]
pub unsafe fn stack_trace() {
let mapper = KernelMapper::lock();
let mut frame = StackTrace::start();
//Maximum 64 frames
for _ in 0..64 {
if let Some(frame_) = frame {
let fp_virt = VirtualAddress::new(frame_.fp);
let pc_virt = VirtualAddress::new(frame_.pc_ptr as usize);
if fp_virt.data() >= USER_END_OFFSET
&& pc_virt.data() >= USER_END_OFFSET
&& (fp_virt.data() as *const usize).is_aligned()
&& (pc_virt.data() as *const usize).is_aligned()
&& mapper.translate(fp_virt).is_some()
&& mapper.translate(pc_virt).is_some()
{
let pc = *frame_.pc_ptr;
if pc == 0 {
println!(" {:>016x}: EMPTY RETURN", frame_.fp);
break;
} else {
println!(" FP {:>016x}: PC {:>016x}", frame_.fp, pc);
symbol_trace(pc);
frame = frame_.next();
}
} else {
println!(" {:>016x}: GUARD PAGE", frame_.fp);
break;
}
} else {
break;
}
}
}
///
/// Get a symbol
//TODO: Do not create Elf object for every symbol lookup
#[inline(never)]
pub unsafe fn symbol_trace(addr: usize) {
let kernel_ptr = crate::KERNEL_OFFSET as *const u8;
let kernel_slice = slice::from_raw_parts(kernel_ptr, KERNEL_SIZE.load(Ordering::SeqCst));
if let Ok(elf) = Elf::from(kernel_slice) {
let mut strtab_opt = None;
for section in elf.sections() {
if section.sh_type == ::goblin::elf::section_header::SHT_STRTAB {
strtab_opt = Some(section);
break;
}
}
if let Some(symbols) = elf.symbols() {
for sym in symbols {
if sym::st_type(sym.st_info) == sym::STT_FUNC
&& addr >= sym.st_value as usize
&& addr < (sym.st_value + sym.st_size) as usize
{
println!(
" {:>016X}+{:>04X}",
sym.st_value,
addr - sym.st_value as usize
);
if let Some(strtab) = strtab_opt {
let start = strtab.sh_offset as usize + sym.st_name as usize;
let mut end = start;
while end < elf.data.len() {
let b = elf.data[end];
end += 1;
if b == 0 {
break;
}
}
if end > start {
let sym_slice = &elf.data[start..end - 1];
if let Ok(sym_name) = str::from_utf8(sym_slice) {
println!(" {:#}", demangle(sym_name));
}
}
}
}
}
}
}
}