forked from M-Labs/nac3
runtime/eh_artiq: support exception allocation
The backtrace is now nested, and should be used together with the stack pointer array to construct the full backtrace for each exception. We now allocate exception objects in a stack, but their names are still not allocated. This is fine for exceptions raised in the driver or artiq code, but we will have to implement allocation for names of exceptions raised in RPC calls. The compiler should also emit code to store the exception names once they catch it, to prepare for later reraising.
This commit is contained in:
parent
8923feceac
commit
6f5ba46e89
|
@ -90,8 +90,7 @@ pub unsafe fn find_eh_action(
|
||||||
lsda: *const u8,
|
lsda: *const u8,
|
||||||
context: &EHContext<'_>,
|
context: &EHContext<'_>,
|
||||||
foreign_exception: bool,
|
foreign_exception: bool,
|
||||||
name: *const u8,
|
id: u32,
|
||||||
len: usize,
|
|
||||||
) -> Result<EHAction, ()> {
|
) -> Result<EHAction, ()> {
|
||||||
if lsda.is_null() {
|
if lsda.is_null() {
|
||||||
return Ok(EHAction::None);
|
return Ok(EHAction::None);
|
||||||
|
@ -164,19 +163,11 @@ pub unsafe fn find_eh_action(
|
||||||
ttype_base,
|
ttype_base,
|
||||||
ttype_table,
|
ttype_table,
|
||||||
)?;
|
)?;
|
||||||
let clause_ptr = *(catch_type as *const *const CSlice<u8>);
|
let clause_ptr = *(catch_type as *const *const u32);
|
||||||
if clause_ptr.is_null() {
|
if clause_ptr.is_null() {
|
||||||
return Ok(EHAction::Catch(lpad));
|
return Ok(EHAction::Catch(lpad));
|
||||||
}
|
}
|
||||||
let clause_name_ptr = (*clause_ptr).as_ptr();
|
if *clause_ptr == id {
|
||||||
let clause_name_len = (*clause_ptr).len();
|
|
||||||
if (clause_name_ptr == core::ptr::null() ||
|
|
||||||
clause_name_ptr == name ||
|
|
||||||
// somehow their name pointers might differ, but the content is the
|
|
||||||
// same
|
|
||||||
core::slice::from_raw_parts(clause_name_ptr, clause_name_len) ==
|
|
||||||
core::slice::from_raw_parts(name, len))
|
|
||||||
{
|
|
||||||
return Ok(EHAction::Catch(lpad));
|
return Ok(EHAction::Catch(lpad));
|
||||||
}
|
}
|
||||||
} else if ar_filter < 0 {
|
} else if ar_filter < 0 {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use core::str::Utf8Error;
|
|
||||||
use alloc::{vec, vec::Vec, string::String, collections::BTreeMap, rc::Rc};
|
use alloc::{vec, vec::Vec, string::String, collections::BTreeMap, rc::Rc};
|
||||||
use log::{info, warn, error};
|
use log::{info, warn, error};
|
||||||
|
use cslice::CSlice;
|
||||||
|
|
||||||
use num_derive::{FromPrimitive, ToPrimitive};
|
use num_derive::{FromPrimitive, ToPrimitive};
|
||||||
use num_traits::{FromPrimitive, ToPrimitive};
|
use num_traits::{FromPrimitive, ToPrimitive};
|
||||||
|
@ -39,7 +39,6 @@ pub enum Error {
|
||||||
UnexpectedPattern,
|
UnexpectedPattern,
|
||||||
UnrecognizedPacket,
|
UnrecognizedPacket,
|
||||||
BufferExhausted,
|
BufferExhausted,
|
||||||
Utf8Error(Utf8Error),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = core::result::Result<T, Error>;
|
pub type Result<T> = core::result::Result<T, Error>;
|
||||||
|
@ -51,7 +50,6 @@ impl fmt::Display for Error {
|
||||||
Error::UnexpectedPattern => write!(f, "unexpected pattern"),
|
Error::UnexpectedPattern => write!(f, "unexpected pattern"),
|
||||||
Error::UnrecognizedPacket => write!(f, "unrecognized packet"),
|
Error::UnrecognizedPacket => write!(f, "unrecognized packet"),
|
||||||
Error::BufferExhausted => write!(f, "buffer exhausted"),
|
Error::BufferExhausted => write!(f, "buffer exhausted"),
|
||||||
Error::Utf8Error(error) => write!(f, "UTF-8 error: {}", error),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,11 +120,6 @@ async fn read_bytes(stream: &TcpStream, max_length: usize) -> Result<Vec<u8>> {
|
||||||
Ok(buffer)
|
Ok(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read_string(stream: &TcpStream, max_length: usize) -> Result<String> {
|
|
||||||
let bytes = read_bytes(stream, max_length).await?;
|
|
||||||
Ok(String::from_utf8(bytes).map_err(|err| Error::Utf8Error(err.utf8_error()))?)
|
|
||||||
}
|
|
||||||
|
|
||||||
const RETRY_LIMIT: usize = 100;
|
const RETRY_LIMIT: usize = 100;
|
||||||
|
|
||||||
async fn fast_send(sender: &mut Sender<'_, kernel::Message>, content: kernel::Message) {
|
async fn fast_send(sender: &mut Sender<'_, kernel::Message>, content: kernel::Message) {
|
||||||
|
@ -156,6 +149,16 @@ async fn fast_recv(receiver: &mut Receiver<'_, kernel::Message>) -> kernel::Mess
|
||||||
receiver.async_recv().await
|
receiver.async_recv().await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn write_exception_string(stream: &TcpStream, s: CSlice<'static, u8>) -> Result<()> {
|
||||||
|
if s.len() == usize::MAX {
|
||||||
|
write_i32(stream, -1).await?;
|
||||||
|
write_i32(stream, s.as_ptr() as i32).await?
|
||||||
|
} else {
|
||||||
|
write_chunk(stream, s.as_ref()).await?;
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kernel::Control>>) -> Result<()> {
|
async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kernel::Control>>) -> Result<()> {
|
||||||
control.borrow_mut().tx.async_send(kernel::Message::StartRequest).await;
|
control.borrow_mut().tx.async_send(kernel::Message::StartRequest).await;
|
||||||
loop {
|
loop {
|
||||||
|
@ -204,17 +207,17 @@ async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kern
|
||||||
kernel::Message::RpcRecvRequest(_) => (),
|
kernel::Message::RpcRecvRequest(_) => (),
|
||||||
other => panic!("expected (ignored) root value slot from kernel CPU, not {:?}", other),
|
other => panic!("expected (ignored) root value slot from kernel CPU, not {:?}", other),
|
||||||
}
|
}
|
||||||
let name = read_string(stream, 16384).await?;
|
let id = read_i32(stream).await? as u32;
|
||||||
let message = read_string(stream, 16384).await?;
|
let message = read_i32(stream).await? as u32;
|
||||||
let param = [read_i64(stream).await?,
|
let param = [read_i64(stream).await?,
|
||||||
read_i64(stream).await?,
|
read_i64(stream).await?,
|
||||||
read_i64(stream).await?];
|
read_i64(stream).await?];
|
||||||
let file = read_string(stream, 16384).await?;
|
let file = read_i32(stream).await? as u32;
|
||||||
let line = read_i32(stream).await?;
|
let line = read_i32(stream).await?;
|
||||||
let column = read_i32(stream).await?;
|
let column = read_i32(stream).await?;
|
||||||
let function = read_string(stream, 16384).await?;
|
let function = read_i32(stream).await? as u32;
|
||||||
control.tx.async_send(kernel::Message::RpcRecvReply(Err(kernel::RPCException {
|
control.tx.async_send(kernel::Message::RpcRecvReply(Err(kernel::RPCException {
|
||||||
name, message, param, file, line, column, function
|
id, message, param, file, line, column, function
|
||||||
}))).await;
|
}))).await;
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -231,29 +234,39 @@ async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kern
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
},
|
},
|
||||||
kernel::Message::KernelException(exception, backtrace, async_errors) => {
|
kernel::Message::KernelException(exceptions, stack_pointers, backtrace, async_errors) => {
|
||||||
match stream {
|
match stream {
|
||||||
Some(stream) => {
|
Some(stream) => {
|
||||||
// only send the exception data to host if there is host,
|
// only send the exception data to host if there is host,
|
||||||
// i.e. not idle/startup kernel.
|
// i.e. not idle/startup kernel.
|
||||||
write_header(stream, Reply::KernelException).await?;
|
write_header(stream, Reply::KernelException).await?;
|
||||||
write_chunk(stream, exception.name.as_ref()).await?;
|
write_i32(stream, exceptions.len() as i32).await?;
|
||||||
write_chunk(stream, exception.message.as_ref()).await?;
|
for exception in exceptions.iter() {
|
||||||
|
let exception = exception.as_ref().unwrap();
|
||||||
|
write_i32(stream, exception.id as i32).await?;
|
||||||
|
write_exception_string(stream, exception.message).await?;
|
||||||
write_i64(stream, exception.param[0] as i64).await?;
|
write_i64(stream, exception.param[0] as i64).await?;
|
||||||
write_i64(stream, exception.param[1] as i64).await?;
|
write_i64(stream, exception.param[1] as i64).await?;
|
||||||
write_i64(stream, exception.param[2] as i64).await?;
|
write_i64(stream, exception.param[2] as i64).await?;
|
||||||
write_chunk(stream, exception.file.as_ref()).await?;
|
write_exception_string(stream, exception.file).await?;
|
||||||
write_i32(stream, exception.line as i32).await?;
|
write_i32(stream, exception.line as i32).await?;
|
||||||
write_i32(stream, exception.column as i32).await?;
|
write_i32(stream, exception.column as i32).await?;
|
||||||
write_chunk(stream, exception.function.as_ref()).await?;
|
write_exception_string(stream, exception.function).await?;
|
||||||
|
}
|
||||||
|
for sp in stack_pointers.iter() {
|
||||||
|
write_i32(stream, sp.stack_pointer as i32).await?;
|
||||||
|
write_i32(stream, sp.initial_backtrace_size as i32).await?;
|
||||||
|
write_i32(stream, sp.current_backtrace_size as i32).await?;
|
||||||
|
}
|
||||||
write_i32(stream, backtrace.len() as i32).await?;
|
write_i32(stream, backtrace.len() as i32).await?;
|
||||||
for &addr in backtrace {
|
for &(addr, sp) in backtrace {
|
||||||
write_i32(stream, addr as i32).await?;
|
write_i32(stream, addr as i32).await?;
|
||||||
|
write_i32(stream, sp as i32).await?;
|
||||||
}
|
}
|
||||||
write_i8(stream, async_errors as i8).await?;
|
write_i8(stream, async_errors as i8).await?;
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
error!("Uncaught kernel exception: {:?}", exception);
|
error!("Uncaught kernel exceptions: {:?}", exceptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -16,7 +16,8 @@ use core::mem;
|
||||||
use cslice::CSlice;
|
use cslice::CSlice;
|
||||||
use unwind as uw;
|
use unwind as uw;
|
||||||
use libc::{c_int, c_void, uintptr_t};
|
use libc::{c_int, c_void, uintptr_t};
|
||||||
use log::trace;
|
use log::{trace, error};
|
||||||
|
use crate::kernel::KERNEL_IMAGE;
|
||||||
|
|
||||||
use dwarf::eh::{self, EHAction, EHContext};
|
use dwarf::eh::{self, EHAction, EHContext};
|
||||||
|
|
||||||
|
@ -26,10 +27,12 @@ const EXCEPTION_CLASS: uw::_Unwind_Exception_Class = 0x4d_4c_42_53_41_52_54_51;
|
||||||
#[cfg(target_arch = "arm")]
|
#[cfg(target_arch = "arm")]
|
||||||
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1
|
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1
|
||||||
|
|
||||||
|
// Note: CSlice within an exception may not be actual cslice, they may be strings that exist only
|
||||||
|
// in the host. If the length == usize:MAX, the pointer is actually a string key in the host.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Exception<'a> {
|
pub struct Exception<'a> {
|
||||||
pub name: CSlice<'a, u8>,
|
pub id: u32,
|
||||||
pub file: CSlice<'a, u8>,
|
pub file: CSlice<'a, u8>,
|
||||||
pub line: u32,
|
pub line: u32,
|
||||||
pub column: u32,
|
pub column: u32,
|
||||||
|
@ -42,25 +45,79 @@ fn str_err(_: core::str::Utf8Error) -> core::fmt::Error {
|
||||||
core::fmt::Error
|
core::fmt::Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn exception_str<'a>(s: &'a CSlice<'a, u8>) -> Result<&'a str, core::str::Utf8Error> {
|
||||||
|
if s.len() == usize::MAX {
|
||||||
|
Ok("<host string>")
|
||||||
|
} else {
|
||||||
|
core::str::from_utf8(s.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> core::fmt::Debug for Exception<'a> {
|
impl<'a> core::fmt::Debug for Exception<'a> {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
write!(f, "Exception {} from {} in {}:{}:{}, message: {}",
|
write!(f, "Exception {} from {} in {}:{}:{}, message: {}",
|
||||||
core::str::from_utf8(self.name.as_ref()).map_err(str_err)?,
|
self.id,
|
||||||
core::str::from_utf8(self.function.as_ref()).map_err(str_err)?,
|
exception_str(&self.function).map_err(str_err)?,
|
||||||
core::str::from_utf8(self.file.as_ref()).map_err(str_err)?,
|
exception_str(&self.file).map_err(str_err)?,
|
||||||
self.line, self.column,
|
self.line, self.column,
|
||||||
core::str::from_utf8(self.message.as_ref()).map_err(str_err)?)
|
exception_str(&self.message).map_err(str_err)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MAX_INFLIGHT_EXCEPTIONS: usize = 10;
|
||||||
const MAX_BACKTRACE_SIZE: usize = 128;
|
const MAX_BACKTRACE_SIZE: usize = 128;
|
||||||
|
|
||||||
#[repr(C)]
|
#[derive(Debug, Default)]
|
||||||
struct ExceptionInfo {
|
pub struct StackPointerBacktrace {
|
||||||
uw_exception: uw::_Unwind_Exception,
|
pub stack_pointer: usize,
|
||||||
exception: Option<Exception<'static>>,
|
pub initial_backtrace_size: usize,
|
||||||
backtrace: [usize; MAX_BACKTRACE_SIZE],
|
pub current_backtrace_size: usize,
|
||||||
backtrace_size: usize
|
}
|
||||||
|
|
||||||
|
struct ExceptionBuffer {
|
||||||
|
// we need n _Unwind_Exception, because each will have their own private data
|
||||||
|
uw_exceptions: [uw::_Unwind_Exception; MAX_INFLIGHT_EXCEPTIONS],
|
||||||
|
exceptions: [Option<Exception<'static>>; MAX_INFLIGHT_EXCEPTIONS + 1],
|
||||||
|
exception_stack: [isize; MAX_INFLIGHT_EXCEPTIONS + 1],
|
||||||
|
// nested exceptions will share the backtrace buffer, treated as a tree
|
||||||
|
// backtrace contains a tuple of IP and SP
|
||||||
|
backtrace: [(usize, usize); MAX_BACKTRACE_SIZE],
|
||||||
|
backtrace_size: usize,
|
||||||
|
// stack pointers are stored to reconstruct backtrace for each exception
|
||||||
|
stack_pointers: [StackPointerBacktrace; MAX_INFLIGHT_EXCEPTIONS + 1],
|
||||||
|
// current allocated nested exceptions
|
||||||
|
exception_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut EXCEPTION_BUFFER: ExceptionBuffer = ExceptionBuffer {
|
||||||
|
uw_exceptions: [uw::_Unwind_Exception {
|
||||||
|
exception_class: EXCEPTION_CLASS,
|
||||||
|
exception_cleanup: cleanup,
|
||||||
|
private: [0; uw::unwinder_private_data_size],
|
||||||
|
}; MAX_INFLIGHT_EXCEPTIONS],
|
||||||
|
exceptions: [None; MAX_INFLIGHT_EXCEPTIONS + 1],
|
||||||
|
exception_stack: [-1; MAX_INFLIGHT_EXCEPTIONS + 1],
|
||||||
|
backtrace: [(0, 0); MAX_BACKTRACE_SIZE],
|
||||||
|
backtrace_size: 0,
|
||||||
|
stack_pointers: [StackPointerBacktrace {
|
||||||
|
stack_pointer: 0,
|
||||||
|
initial_backtrace_size: 0,
|
||||||
|
current_backtrace_size: 0
|
||||||
|
}; MAX_INFLIGHT_EXCEPTIONS + 1],
|
||||||
|
exception_count: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
pub unsafe extern fn reset_exception_buffer() {
|
||||||
|
trace!("reset exception buffer");
|
||||||
|
EXCEPTION_BUFFER.uw_exceptions = [uw::_Unwind_Exception {
|
||||||
|
exception_class: EXCEPTION_CLASS,
|
||||||
|
exception_cleanup: cleanup,
|
||||||
|
private: [0; uw::unwinder_private_data_size],
|
||||||
|
}; MAX_INFLIGHT_EXCEPTIONS];
|
||||||
|
EXCEPTION_BUFFER.exceptions = [None; MAX_INFLIGHT_EXCEPTIONS + 1];
|
||||||
|
EXCEPTION_BUFFER.exception_stack = [-1; MAX_INFLIGHT_EXCEPTIONS + 1];
|
||||||
|
EXCEPTION_BUFFER.backtrace_size = 0;
|
||||||
|
EXCEPTION_BUFFER.exception_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
type _Unwind_Stop_Fn = extern "C" fn(version: c_int,
|
type _Unwind_Stop_Fn = extern "C" fn(version: c_int,
|
||||||
|
@ -81,8 +138,7 @@ extern {
|
||||||
unsafe fn find_eh_action(
|
unsafe fn find_eh_action(
|
||||||
context: *mut uw::_Unwind_Context,
|
context: *mut uw::_Unwind_Context,
|
||||||
foreign_exception: bool,
|
foreign_exception: bool,
|
||||||
name: *const u8,
|
id: u32,
|
||||||
len: usize,
|
|
||||||
) -> Result<EHAction, ()> {
|
) -> Result<EHAction, ()> {
|
||||||
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
|
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
|
||||||
let mut ip_before_instr: c_int = 0;
|
let mut ip_before_instr: c_int = 0;
|
||||||
|
@ -95,7 +151,7 @@ unsafe fn find_eh_action(
|
||||||
get_text_start: &|| uw::_Unwind_GetTextRelBase(context),
|
get_text_start: &|| uw::_Unwind_GetTextRelBase(context),
|
||||||
get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
|
get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
|
||||||
};
|
};
|
||||||
eh::find_eh_action(lsda, &eh_context, foreign_exception, name, len)
|
eh::find_eh_action(lsda, &eh_context, foreign_exception, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn artiq_personality(_state: uw::_Unwind_State,
|
pub unsafe fn artiq_personality(_state: uw::_Unwind_State,
|
||||||
|
@ -119,19 +175,15 @@ pub unsafe fn artiq_personality(_state: uw::_Unwind_State,
|
||||||
let exception_class = (*exception_object).exception_class;
|
let exception_class = (*exception_object).exception_class;
|
||||||
let foreign_exception = exception_class != EXCEPTION_CLASS;
|
let foreign_exception = exception_class != EXCEPTION_CLASS;
|
||||||
assert!(!foreign_exception, "we do not expect foreign exceptions");
|
assert!(!foreign_exception, "we do not expect foreign exceptions");
|
||||||
let exception_info = &mut *(exception_object as *mut ExceptionInfo);
|
let index = EXCEPTION_BUFFER.exception_stack[EXCEPTION_BUFFER.exception_count - 1];
|
||||||
|
assert!(index != -1);
|
||||||
|
let exception = EXCEPTION_BUFFER.exceptions[index as usize].as_ref().unwrap();
|
||||||
|
|
||||||
let (name_ptr, len) = if foreign_exception || exception_info.exception.is_none() {
|
let id = exception.id;
|
||||||
(core::ptr::null(), 0)
|
let eh_action = match find_eh_action(context, foreign_exception, id) {
|
||||||
} else {
|
|
||||||
let name = (exception_info.exception.unwrap()).name;
|
|
||||||
(name.as_ptr(), name.len())
|
|
||||||
};
|
|
||||||
let eh_action = match find_eh_action(context, foreign_exception, name_ptr, len) {
|
|
||||||
Ok(action) => action,
|
Ok(action) => action,
|
||||||
Err(_) => return uw::_URC_FAILURE,
|
Err(_) => return uw::_URC_FAILURE,
|
||||||
};
|
};
|
||||||
let exception = &exception_info.exception.unwrap();
|
|
||||||
match eh_action {
|
match eh_action {
|
||||||
EHAction::None => return continue_unwind(exception_object, context),
|
EHAction::None => return continue_unwind(exception_object, context),
|
||||||
EHAction::Cleanup(lpad) |
|
EHAction::Cleanup(lpad) |
|
||||||
|
@ -165,99 +217,219 @@ pub unsafe fn artiq_personality(_state: uw::_Unwind_State,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code,
|
|
||||||
uw_exception: *mut uw::_Unwind_Exception) {
|
|
||||||
unsafe {
|
|
||||||
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
|
|
||||||
|
|
||||||
exception_info.exception = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static mut INFLIGHT: ExceptionInfo = ExceptionInfo {
|
|
||||||
uw_exception: uw::_Unwind_Exception {
|
|
||||||
exception_class: EXCEPTION_CLASS,
|
|
||||||
exception_cleanup: cleanup,
|
|
||||||
private: [0; uw::unwinder_private_data_size],
|
|
||||||
},
|
|
||||||
exception: None,
|
|
||||||
backtrace: [0; MAX_BACKTRACE_SIZE],
|
|
||||||
backtrace_size: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
pub unsafe extern fn raise(exception: *const Exception) -> ! {
|
pub unsafe extern fn raise(exception: *const Exception) -> ! {
|
||||||
// FIXME: unsound transmute
|
use cslice::AsCSlice;
|
||||||
// This would cause stack memory corruption.
|
|
||||||
trace!("raising exception");
|
|
||||||
INFLIGHT.backtrace_size = 0;
|
|
||||||
INFLIGHT.exception = Some(mem::transmute::<Exception, Exception<'static>>(*exception));
|
|
||||||
|
|
||||||
let _result = _Unwind_ForcedUnwind(&mut INFLIGHT.uw_exception,
|
let count = EXCEPTION_BUFFER.exception_count;
|
||||||
uncaught_exception, core::ptr::null_mut());
|
let stack = &mut EXCEPTION_BUFFER.exception_stack;
|
||||||
|
let diff = exception as isize - EXCEPTION_BUFFER.exceptions.as_ptr() as isize;
|
||||||
|
if 0 <= diff && diff <= (mem::size_of::<Option<Exception>>() * MAX_INFLIGHT_EXCEPTIONS) as isize {
|
||||||
|
let index = diff / (mem::size_of::<Option<Exception>>() as isize);
|
||||||
|
trace!("reraise at {}", index);
|
||||||
|
|
||||||
|
let mut found = false;
|
||||||
|
for i in 0..=MAX_INFLIGHT_EXCEPTIONS + 1 {
|
||||||
|
if found {
|
||||||
|
if stack[i] == -1 {
|
||||||
|
stack[i - 1] = index;
|
||||||
|
assert!(i == count);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
stack[i - 1] = stack[i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if stack[i] == index {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(found);
|
||||||
|
let _result = _Unwind_ForcedUnwind(&mut EXCEPTION_BUFFER.uw_exceptions[stack[count - 1] as usize],
|
||||||
|
stop_fn, core::ptr::null_mut());
|
||||||
|
} else {
|
||||||
|
if count < MAX_INFLIGHT_EXCEPTIONS {
|
||||||
|
trace!("raising exception at level {}", count);
|
||||||
|
let exception = &*exception;
|
||||||
|
for (i, slot) in EXCEPTION_BUFFER.exceptions.iter_mut().enumerate() {
|
||||||
|
// we should always be able to find a slot
|
||||||
|
if slot.is_none() {
|
||||||
|
*slot = Some(
|
||||||
|
*mem::transmute::<*const Exception, *const Exception<'static>>
|
||||||
|
(exception));
|
||||||
|
EXCEPTION_BUFFER.exception_stack[count] = i as isize;
|
||||||
|
EXCEPTION_BUFFER.uw_exceptions[i].private =
|
||||||
|
[0; uw::unwinder_private_data_size];
|
||||||
|
EXCEPTION_BUFFER.stack_pointers[i] = StackPointerBacktrace {
|
||||||
|
stack_pointer: 0,
|
||||||
|
initial_backtrace_size: EXCEPTION_BUFFER.backtrace_size,
|
||||||
|
current_backtrace_size: 0,
|
||||||
|
};
|
||||||
|
EXCEPTION_BUFFER.exception_count += 1;
|
||||||
|
let _result = _Unwind_ForcedUnwind(&mut EXCEPTION_BUFFER.uw_exceptions[i],
|
||||||
|
stop_fn, core::ptr::null_mut());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!("too many nested exceptions");
|
||||||
|
// TODO: better reporting?
|
||||||
|
let exception = Exception {
|
||||||
|
id: get_exception_id("runtimeerror"),
|
||||||
|
file: file!().as_c_slice(),
|
||||||
|
line: line!(),
|
||||||
|
column: column!(),
|
||||||
|
// https://github.com/rust-lang/rfcs/pull/1719
|
||||||
|
function: "__artiq_raise".as_c_slice(),
|
||||||
|
message: "too many nested exceptions".as_c_slice(),
|
||||||
|
param: [0, 0, 0]
|
||||||
|
};
|
||||||
|
EXCEPTION_BUFFER.exceptions[MAX_INFLIGHT_EXCEPTIONS] = Some(mem::transmute(exception));
|
||||||
|
EXCEPTION_BUFFER.stack_pointers[MAX_INFLIGHT_EXCEPTIONS] = Default::default();
|
||||||
|
EXCEPTION_BUFFER.exception_count += 1;
|
||||||
|
uncaught_exception()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern fn resume() -> ! {
|
||||||
|
trace!("resume");
|
||||||
|
assert!(EXCEPTION_BUFFER.exception_count != 0);
|
||||||
|
let i = EXCEPTION_BUFFER.exception_stack[EXCEPTION_BUFFER.exception_count - 1];
|
||||||
|
assert!(i != -1);
|
||||||
|
let _result = _Unwind_ForcedUnwind(&mut EXCEPTION_BUFFER.uw_exceptions[i as usize],
|
||||||
|
stop_fn, core::ptr::null_mut());
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn uncaught_exception(_version: c_int,
|
pub unsafe extern fn end_catch() {
|
||||||
|
let mut count = EXCEPTION_BUFFER.exception_count;
|
||||||
|
assert!(count != 0);
|
||||||
|
// we remove all exceptions with SP <= current exception SP
|
||||||
|
// i.e. the outer exception escapes the finally block
|
||||||
|
let index = EXCEPTION_BUFFER.exception_stack[count - 1] as usize;
|
||||||
|
EXCEPTION_BUFFER.exception_stack[count - 1] = -1;
|
||||||
|
EXCEPTION_BUFFER.exceptions[index] = None;
|
||||||
|
let outer_sp = EXCEPTION_BUFFER.stack_pointers
|
||||||
|
[index].stack_pointer;
|
||||||
|
count -= 1;
|
||||||
|
for i in (0..count).rev() {
|
||||||
|
let index = EXCEPTION_BUFFER.exception_stack[i];
|
||||||
|
assert!(index != -1);
|
||||||
|
let index = index as usize;
|
||||||
|
let sp = EXCEPTION_BUFFER.stack_pointers[index].stack_pointer;
|
||||||
|
if sp >= outer_sp {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
EXCEPTION_BUFFER.exceptions[index] = None;
|
||||||
|
EXCEPTION_BUFFER.exception_stack[i] = -1;
|
||||||
|
count -= 1;
|
||||||
|
}
|
||||||
|
EXCEPTION_BUFFER.exception_count = count;
|
||||||
|
EXCEPTION_BUFFER.backtrace_size = if count > 0 {
|
||||||
|
let index = EXCEPTION_BUFFER.exception_stack[count - 1];
|
||||||
|
assert!(index != -1);
|
||||||
|
EXCEPTION_BUFFER.stack_pointers[index as usize].current_backtrace_size
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code,
|
||||||
|
_uw_exception: *mut uw::_Unwind_Exception) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn uncaught_exception() -> ! {
|
||||||
|
unsafe {
|
||||||
|
// dump way to reorder the stack
|
||||||
|
for i in 0..EXCEPTION_BUFFER.exception_count {
|
||||||
|
if EXCEPTION_BUFFER.exception_stack[i] != i as isize {
|
||||||
|
// find the correct index
|
||||||
|
let index = EXCEPTION_BUFFER.exception_stack
|
||||||
|
.iter()
|
||||||
|
.position(|v| *v == i as isize).unwrap();
|
||||||
|
let a = EXCEPTION_BUFFER.exception_stack[index];
|
||||||
|
let b = EXCEPTION_BUFFER.exception_stack[i];
|
||||||
|
assert!(a != -1 && b != -1);
|
||||||
|
core::mem::swap(&mut EXCEPTION_BUFFER.exception_stack[index],
|
||||||
|
&mut EXCEPTION_BUFFER.exception_stack[i]);
|
||||||
|
core::mem::swap(&mut EXCEPTION_BUFFER.exceptions[a as usize],
|
||||||
|
&mut EXCEPTION_BUFFER.exceptions[b as usize]);
|
||||||
|
core::mem::swap(&mut EXCEPTION_BUFFER.stack_pointers[a as usize],
|
||||||
|
&mut EXCEPTION_BUFFER.stack_pointers[b as usize]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
crate::kernel::core1::terminate(
|
||||||
|
EXCEPTION_BUFFER.exceptions[..EXCEPTION_BUFFER.exception_count].as_ref(),
|
||||||
|
EXCEPTION_BUFFER.stack_pointers[..EXCEPTION_BUFFER.exception_count].as_ref(),
|
||||||
|
EXCEPTION_BUFFER.backtrace[..EXCEPTION_BUFFER.backtrace_size].as_mut())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop function which would be executed when we unwind each frame
|
||||||
|
extern fn stop_fn(_version: c_int,
|
||||||
actions: i32,
|
actions: i32,
|
||||||
_uw_exception_class: uw::_Unwind_Exception_Class,
|
_uw_exception_class: uw::_Unwind_Exception_Class,
|
||||||
uw_exception: *mut uw::_Unwind_Exception,
|
_uw_exception: *mut uw::_Unwind_Exception,
|
||||||
context: *mut uw::_Unwind_Context,
|
context: *mut uw::_Unwind_Context,
|
||||||
_stop_parameter: *mut c_void)
|
_stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code {
|
||||||
-> uw::_Unwind_Reason_Code {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
trace!("uncaught exception");
|
let load_addr = KERNEL_IMAGE.as_ref().unwrap().get_load_addr();
|
||||||
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
|
let backtrace_size = EXCEPTION_BUFFER.backtrace_size;
|
||||||
|
// we try to remove unrelated backtrace here to save some buffer size
|
||||||
if exception_info.backtrace_size < exception_info.backtrace.len() {
|
if backtrace_size < MAX_BACKTRACE_SIZE {
|
||||||
let ip = uw::_Unwind_GetIP(context);
|
let ip = uw::_Unwind_GetIP(context);
|
||||||
trace!("SP: {:X}, backtrace_size: {}", uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG), exception_info.backtrace_size);
|
if ip >= load_addr {
|
||||||
exception_info.backtrace[exception_info.backtrace_size] = ip;
|
let ip = ip - load_addr;
|
||||||
exception_info.backtrace_size += 1;
|
let sp = uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG);
|
||||||
|
trace!("SP: {:X}, backtrace_size: {}", sp, backtrace_size);
|
||||||
|
EXCEPTION_BUFFER.backtrace[backtrace_size] = (ip, sp);
|
||||||
|
EXCEPTION_BUFFER.backtrace_size += 1;
|
||||||
|
let last_index = EXCEPTION_BUFFER.exception_stack[EXCEPTION_BUFFER.exception_count - 1];
|
||||||
|
assert!(last_index != -1);
|
||||||
|
let sp_info = &mut EXCEPTION_BUFFER.stack_pointers[last_index as usize];
|
||||||
|
sp_info.stack_pointer = sp;
|
||||||
|
sp_info.current_backtrace_size = backtrace_size + 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
trace!("backtrace size exceeded");
|
||||||
}
|
}
|
||||||
|
|
||||||
if actions as u32 & uw::_US_END_OF_STACK as u32 != 0 {
|
if actions as u32 & uw::_US_END_OF_STACK as u32 != 0 {
|
||||||
crate::kernel::core1::terminate(exception_info.exception.as_ref().unwrap(),
|
uncaught_exception()
|
||||||
exception_info.backtrace[..exception_info.backtrace_size].as_mut())
|
|
||||||
} else {
|
} else {
|
||||||
uw::_URC_NO_REASON
|
uw::_URC_NO_REASON
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe extern fn reraise() -> ! {
|
static EXCEPTION_ID_LOOKUP: [(&str, u32); 6] = [
|
||||||
use cslice::AsCSlice;
|
("runtimeerror", 0),
|
||||||
|
("RTIOUnderflow", 1),
|
||||||
|
("RTIOOverflow", 2),
|
||||||
|
("RTIODestinationUnreachable", 3),
|
||||||
|
("DMAError", 4),
|
||||||
|
("I2CError", 5),
|
||||||
|
];
|
||||||
|
|
||||||
// Reraise is basically cxa_rethrow, which calls _Unwind_Resume_or_Rethrow,
|
pub fn get_exception_id(name: &str) -> u32 {
|
||||||
// which for EHABI would always call _Unwind_RaiseException.
|
for (n, id) in EXCEPTION_ID_LOOKUP.iter() {
|
||||||
match INFLIGHT.exception {
|
if *n == name {
|
||||||
Some(ex) => {
|
return *id
|
||||||
// we cannot call raise directly as that would corrupt the backtrace
|
|
||||||
INFLIGHT.exception = Some(mem::transmute::<Exception, Exception<'static>>(ex));
|
|
||||||
let _result = _Unwind_ForcedUnwind(&mut INFLIGHT.uw_exception,
|
|
||||||
uncaught_exception, core::ptr::null_mut());
|
|
||||||
unreachable!()
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
raise(&Exception {
|
|
||||||
name: "0:artiq.coredevice.exceptions.RuntimeError".as_c_slice(),
|
|
||||||
file: file!().as_c_slice(),
|
|
||||||
line: line!(),
|
|
||||||
column: column!(),
|
|
||||||
// https://github.com/rust-lang/rfcs/pull/1719
|
|
||||||
function: "__artiq_reraise".as_c_slice(),
|
|
||||||
message: "No active exception to reraise".as_c_slice(),
|
|
||||||
param: [0, 0, 0]
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
unimplemented!("unallocated internal exception id")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! artiq_raise {
|
macro_rules! artiq_raise {
|
||||||
($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({
|
($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({
|
||||||
use cslice::AsCSlice;
|
use cslice::AsCSlice;
|
||||||
|
let name_id = $crate::eh_artiq::get_exception_id($name);
|
||||||
let exn = $crate::eh_artiq::Exception {
|
let exn = $crate::eh_artiq::Exception {
|
||||||
name: concat!("0:artiq.coredevice.exceptions.", $name).as_c_slice(),
|
id: name_id,
|
||||||
file: file!().as_c_slice(),
|
file: file!().as_c_slice(),
|
||||||
line: line!(),
|
line: line!(),
|
||||||
column: column!(),
|
column: column!(),
|
||||||
|
|
|
@ -204,7 +204,8 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
|
||||||
api!(_Unwind_Resume = unwind::_Unwind_Resume),
|
api!(_Unwind_Resume = unwind::_Unwind_Resume),
|
||||||
api!(__artiq_personality = eh_artiq::artiq_personality),
|
api!(__artiq_personality = eh_artiq::artiq_personality),
|
||||||
api!(__artiq_raise = eh_artiq::raise),
|
api!(__artiq_raise = eh_artiq::raise),
|
||||||
api!(__artiq_reraise = eh_artiq::reraise),
|
api!(__artiq_resume = eh_artiq::resume),
|
||||||
|
api!(__artiq_end_catch = eh_artiq::end_catch),
|
||||||
|
|
||||||
// Implementations for LLVM math intrinsics
|
// Implementations for LLVM math intrinsics
|
||||||
api!(__powidf2),
|
api!(__powidf2),
|
||||||
|
|
|
@ -182,6 +182,7 @@ pub extern "C" fn main_core1() {
|
||||||
info!("kernel starting");
|
info!("kernel starting");
|
||||||
if let Some(kernel) = loaded_kernel.take() {
|
if let Some(kernel) = loaded_kernel.take() {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
eh_artiq::reset_exception_buffer();
|
||||||
KERNEL_CHANNEL_0TO1 = Some(core1_rx);
|
KERNEL_CHANNEL_0TO1 = Some(core1_rx);
|
||||||
KERNEL_CHANNEL_1TO0 = Some(core1_tx);
|
KERNEL_CHANNEL_1TO0 = Some(core1_tx);
|
||||||
KERNEL_IMAGE = &kernel as *const KernelImage;
|
KERNEL_IMAGE = &kernel as *const KernelImage;
|
||||||
|
@ -201,24 +202,13 @@ pub extern "C" fn main_core1() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called by eh_artiq
|
/// Called by eh_artiq
|
||||||
pub fn terminate(exception: &'static eh_artiq::Exception<'static>, backtrace: &'static mut [usize]) -> ! {
|
pub fn terminate(exceptions: &'static [Option<eh_artiq::Exception<'static>>],
|
||||||
let load_addr = unsafe {
|
stack_pointers: &'static [eh_artiq::StackPointerBacktrace],
|
||||||
KERNEL_IMAGE.as_ref().unwrap().get_load_addr()
|
backtrace: &'static mut [(usize, usize)]) -> ! {
|
||||||
};
|
|
||||||
let mut cursor = 0;
|
|
||||||
// The address in the backtrace is relocated, so we have to convert it back to the address in
|
|
||||||
// the original python script, and remove those Rust function backtrace.
|
|
||||||
for i in 0..backtrace.len() {
|
|
||||||
if backtrace[i] >= load_addr {
|
|
||||||
backtrace[cursor] = backtrace[i] - load_addr;
|
|
||||||
cursor += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let core1_tx = unsafe { KERNEL_CHANNEL_1TO0.as_mut().unwrap() };
|
let core1_tx = unsafe { KERNEL_CHANNEL_1TO0.as_mut().unwrap() };
|
||||||
let errors = unsafe { get_async_errors() };
|
let errors = unsafe { get_async_errors() };
|
||||||
core1_tx.send(Message::KernelException(exception, &backtrace[..cursor], errors));
|
core1_tx.send(Message::KernelException(exceptions, stack_pointers, backtrace, errors));
|
||||||
}
|
}
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,13 +15,13 @@ mod cache;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RPCException {
|
pub struct RPCException {
|
||||||
pub name: String,
|
pub id: u32,
|
||||||
pub message: String,
|
pub message: u32,
|
||||||
pub param: [i64; 3],
|
pub param: [i64; 3],
|
||||||
pub file: String,
|
pub file: u32,
|
||||||
pub line: i32,
|
pub line: i32,
|
||||||
pub column: i32,
|
pub column: i32,
|
||||||
pub function: String
|
pub function: u32
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -31,7 +31,10 @@ pub enum Message {
|
||||||
LoadFailed,
|
LoadFailed,
|
||||||
StartRequest,
|
StartRequest,
|
||||||
KernelFinished(u8),
|
KernelFinished(u8),
|
||||||
KernelException(&'static eh_artiq::Exception<'static>, &'static [usize], u8),
|
KernelException(&'static [Option<eh_artiq::Exception<'static>>],
|
||||||
|
&'static [eh_artiq::StackPointerBacktrace],
|
||||||
|
&'static [(usize, usize)],
|
||||||
|
u8),
|
||||||
RpcSend { is_async: bool, data: Vec<u8> },
|
RpcSend { is_async: bool, data: Vec<u8> },
|
||||||
RpcRecvRequest(*mut ()),
|
RpcRecvRequest(*mut ()),
|
||||||
RpcRecvReply(Result<usize, RPCException>),
|
RpcRecvReply(Result<usize, RPCException>),
|
||||||
|
@ -53,7 +56,7 @@ static CHANNEL_SEM: Semaphore = Semaphore::new(0, 1);
|
||||||
static mut KERNEL_CHANNEL_0TO1: Option<sync_channel::Receiver<'static, Message>> = None;
|
static mut KERNEL_CHANNEL_0TO1: Option<sync_channel::Receiver<'static, Message>> = None;
|
||||||
static mut KERNEL_CHANNEL_1TO0: Option<sync_channel::Sender<'static, Message>> = None;
|
static mut KERNEL_CHANNEL_1TO0: Option<sync_channel::Sender<'static, Message>> = None;
|
||||||
|
|
||||||
static mut KERNEL_IMAGE: *const core1::KernelImage = ptr::null();
|
pub static mut KERNEL_IMAGE: *const core1::KernelImage = ptr::null();
|
||||||
|
|
||||||
static INIT_LOCK: Mutex<()> = Mutex::new(());
|
static INIT_LOCK: Mutex<()> = Mutex::new(());
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Kernel-side RPC API
|
//! Kernel-side RPC API
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use cslice::{CSlice, AsCSlice};
|
use cslice::CSlice;
|
||||||
|
|
||||||
use crate::eh_artiq;
|
use crate::eh_artiq;
|
||||||
use crate::rpc::send_args;
|
use crate::rpc::send_args;
|
||||||
|
@ -36,12 +36,12 @@ pub extern fn rpc_recv(slot: *mut ()) -> usize {
|
||||||
Message::RpcRecvReply(Ok(alloc_size)) => alloc_size,
|
Message::RpcRecvReply(Ok(alloc_size)) => alloc_size,
|
||||||
Message::RpcRecvReply(Err(exception)) => unsafe {
|
Message::RpcRecvReply(Err(exception)) => unsafe {
|
||||||
eh_artiq::raise(&eh_artiq::Exception {
|
eh_artiq::raise(&eh_artiq::Exception {
|
||||||
name: exception.name.as_bytes().as_c_slice(),
|
id: exception.id,
|
||||||
file: exception.file.as_bytes().as_c_slice(),
|
file: CSlice::new(exception.file as *const u8, usize::MAX),
|
||||||
line: exception.line as u32,
|
line: exception.line as u32,
|
||||||
column: exception.column as u32,
|
column: exception.column as u32,
|
||||||
function: exception.function.as_bytes().as_c_slice(),
|
function: CSlice::new(exception.function as *const u8, usize::MAX),
|
||||||
message: exception.message.as_bytes().as_c_slice(),
|
message: CSlice::new(exception.message as *const u8, usize::MAX),
|
||||||
param: exception.param
|
param: exception.param
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue