#![no_std]
#![feature(extract_if)]
extern crate alloc;
use alloc::{
string::{String,ToString},
vec::Vec,
};
use log::debug;
use cpu::CpuId;
use memory::VirtualAddress;
use sync_irq::IrqSafeMutex;
use core::panic::PanicInfo;
#[derive(Debug, Clone)]
pub enum FaultType {
PageFault,
GeneralProtectionFault,
SegmentNotPresent,
InvalidTSS,
DoubleFault,
DeviceNotAvailable,
InvalidOpCode,
BoundRangeExceeded,
Overflow,
NMI,
DivideByZero,
Panic,
UnknownException(u8)
}
pub fn from_exception_number(num: u8) -> FaultType {
match num {
0x0 => FaultType::DivideByZero,
0x2 => FaultType::NMI,
0x4 => FaultType::Overflow,
0x5 => FaultType::BoundRangeExceeded,
0x6 => FaultType::InvalidOpCode,
0x7 => FaultType::DeviceNotAvailable,
0x8 => FaultType::DoubleFault,
0xA => FaultType::InvalidTSS,
0xB => FaultType::SegmentNotPresent,
0xD => FaultType::GeneralProtectionFault,
0xE => FaultType::PageFault,
num => FaultType::UnknownException(num),
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum RecoveryAction {
None,
TaskRestarted,
FaultCrateReplaced,
IterativelyCrateReplaced,
MultipleFaultRecovery
}
#[derive(Debug, Clone)]
pub struct FaultEntry {
pub fault_type: FaultType,
pub error_code: Option<u64>,
pub cpu: Option<CpuId>,
pub running_task: Option<String>,
pub running_app_crate: Option<String>,
pub address_accessed: Option<VirtualAddress>,
pub instruction_pointer: Option<VirtualAddress>,
pub crate_error_occured: Option<String>,
pub replaced_crates: Vec<String>,
pub action_taken: RecoveryAction,
}
impl FaultEntry {
pub fn new (
fault_type: FaultType
) -> FaultEntry {
FaultEntry {
fault_type,
error_code: None,
cpu: None,
running_task: None,
running_app_crate: None,
address_accessed: None,
instruction_pointer: None,
crate_error_occured: None,
replaced_crates: Vec::<String>::new(),
action_taken: RecoveryAction::None,
}
}
}
static FAULT_LIST: IrqSafeMutex<Vec<FaultEntry>> = IrqSafeMutex::new(Vec::new());
pub fn clear_fault_log() {
FAULT_LIST.lock().clear();
}
fn update_and_insert_fault_entry_internal(
mut fe: FaultEntry,
instruction_pointer: Option<usize>,
) {
fe.cpu = Some(cpu::current_cpu());
let curr_task = match task::get_my_current_task() {
Some(x) => x,
_ => {
FAULT_LIST.lock().push(fe);
return
},
};
let namespace = curr_task.get_namespace();
fe.running_task = {
Some(curr_task.name.clone())
};
fe.running_app_crate = {
curr_task.app_crate.as_ref().map(|x| x.lock_as_ref().crate_name.to_string())
};
if let Some(instruction_pointer) = instruction_pointer {
let instruction_pointer = VirtualAddress::new_canonical(instruction_pointer);
fe.instruction_pointer = Some(instruction_pointer);
fe.crate_error_occured = namespace.get_crate_containing_address(instruction_pointer, false)
.map(|x| x.lock_as_ref().crate_name.to_string());
};
FAULT_LIST.lock().push(fe);
}
pub fn log_exception (
fault_type: u8,
instruction_pointer: usize,
error_code: Option<u64>,
address_accessed: Option<usize>
) {
let mut fe = FaultEntry::new(from_exception_number(fault_type));
fe.error_code = error_code;
fe.address_accessed = address_accessed.map(VirtualAddress::new_canonical);
update_and_insert_fault_entry_internal(fe, Some(instruction_pointer));
}
pub fn log_panic_entry(panic_info: &PanicInfo) {
let mut fe = FaultEntry::new(FaultType::Panic);
if let Some(location) = panic_info.location() {
let panic_file = location.file();
let mut error_crate_iter = panic_file.split('/');
error_crate_iter.next();
let error_crate_name_simple = error_crate_iter.next().unwrap_or(panic_file);
debug!("panic file {}",error_crate_name_simple);
fe.crate_error_occured = Some(error_crate_name_simple.to_string());
} else {
debug!("panic occurred but can't get location information...");
}
update_and_insert_fault_entry_internal(fe, None);
}
pub fn remove_unhandled_exceptions() -> Vec<FaultEntry> {
FAULT_LIST.lock().extract_if(|fe| fe.action_taken == RecoveryAction::None).collect::<Vec<_>>()
}
macro_rules! println_both {
($fmt:expr) => {
early_printer::println!($fmt);
app_io::println!($fmt);
};
($fmt:expr, $($arg:tt)*) => {
early_printer::println!($fmt, $($arg)*);
app_io::println!($fmt, $($arg)*);
};
}
pub fn print_fault_log() {
println_both!("------------------ FAULT LOG ---------------------------");
let list = FAULT_LIST.lock();
for x in list.iter() {
println_both!("{:?}", x);
}
println_both!("------------------ END OF LOG --------------------------");
}
pub fn log_handled_fault(fe: FaultEntry){
FAULT_LIST.lock().push(fe);
}
pub fn get_the_most_recent_match(error_crate: &str) -> Option<FaultEntry> {
debug!("getting the most recent match");
let mut fe :Option<FaultEntry> = None;
for fault_entry in FAULT_LIST.lock().iter() {
if let Some(crate_error_occured) = &fault_entry.crate_error_occured {
let error_crate_name = crate_error_occured.clone();
let error_crate_name_simple = error_crate_name.split('-').next().unwrap_or(&error_crate_name);
if error_crate_name_simple == error_crate {
let item = fault_entry.clone();
fe = Some(item);
}
}
}
if fe.is_none(){
debug!("No recent entries for the given crate {}", error_crate);
}
fe
}