forked from M-Labs/artiq
firmware: runtime changes for exception
Ported from: M-Labs/artiq-zynq#162 This includes new API for exception handling, some refactoring to avoid code duplication for exception structures, and modified protocols to send nested exceptions and avoid string allocation.
This commit is contained in:
parent
536b3e0c26
commit
4132c450a5
|
@ -253,6 +253,7 @@ dependencies = [
|
|||
"byteorder",
|
||||
"cslice",
|
||||
"dyld",
|
||||
"eh",
|
||||
"failure",
|
||||
"failure_derive",
|
||||
"io",
|
||||
|
|
|
@ -117,7 +117,8 @@ static mut API: &'static [(&'static str, *const ())] = &[
|
|||
api!(_Unwind_Resume = ::unwind::_Unwind_Resume),
|
||||
api!(__artiq_personality = ::eh_artiq::personality),
|
||||
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),
|
||||
|
||||
/* proxified syscalls */
|
||||
api!(core_log),
|
||||
|
|
|
@ -10,12 +10,15 @@
|
|||
// except according to those terms.
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
use core::{ptr, mem};
|
||||
use cslice::CSlice;
|
||||
use core::mem;
|
||||
use cslice::AsCSlice;
|
||||
use unwind as uw;
|
||||
use libc::{c_int, c_void};
|
||||
|
||||
use eh::dwarf::{self, EHAction, EHContext};
|
||||
use eh::{self, dwarf::{self, EHAction, EHContext}};
|
||||
|
||||
pub type Exception<'a> = eh::eh_artiq::Exception<'a>;
|
||||
pub type StackPointerBacktrace = eh::eh_artiq::StackPointerBacktrace;
|
||||
|
||||
type _Unwind_Stop_Fn = extern "C" fn(version: c_int,
|
||||
actions: uw::_Unwind_Action,
|
||||
|
@ -30,40 +33,74 @@ extern {
|
|||
stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Exception<'a> {
|
||||
pub name: CSlice<'a, u8>,
|
||||
pub file: CSlice<'a, u8>,
|
||||
pub line: u32,
|
||||
pub column: u32,
|
||||
pub function: CSlice<'a, u8>,
|
||||
pub message: CSlice<'a, u8>,
|
||||
pub param: [i64; 3]
|
||||
}
|
||||
pub static mut PAYLOAD_ADDRESS: usize = 0;
|
||||
|
||||
const EXCEPTION_CLASS: uw::_Unwind_Exception_Class = 0x4d_4c_42_53_41_52_54_51; /* 'MLBSARTQ' */
|
||||
|
||||
const MAX_INFLIGHT_EXCEPTIONS: usize = 10;
|
||||
const MAX_BACKTRACE_SIZE: usize = 128;
|
||||
|
||||
#[repr(C)]
|
||||
struct ExceptionInfo {
|
||||
uw_exception: uw::_Unwind_Exception,
|
||||
exception: Option<Exception<'static>>,
|
||||
handled: bool,
|
||||
backtrace: [usize; MAX_BACKTRACE_SIZE],
|
||||
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(payload_addr: usize) {
|
||||
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;
|
||||
PAYLOAD_ADDRESS = payload_addr;
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
// actually this is not the SP, but frame pointer
|
||||
// but it serves its purpose, and getting SP will somehow cause segfault...
|
||||
const UNW_FP_REG: c_int = 12;
|
||||
|
||||
#[cfg(any(target_arch = "riscv32"))]
|
||||
const UNWIND_DATA_REG: (i32, i32) = (10, 11); // X10, X11
|
||||
#[cfg(any(target_arch = "riscv32"))]
|
||||
const UNW_FP_REG: c_int = 2;
|
||||
|
||||
#[export_name="__artiq_personality"]
|
||||
pub extern fn personality(version: c_int,
|
||||
actions: uw::_Unwind_Action,
|
||||
_actions: uw::_Unwind_Action,
|
||||
uw_exception_class: uw::_Unwind_Exception_Class,
|
||||
uw_exception: *mut uw::_Unwind_Exception,
|
||||
context: *mut uw::_Unwind_Context)
|
||||
|
@ -85,32 +122,20 @@ pub extern fn personality(version: c_int,
|
|||
get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
|
||||
};
|
||||
|
||||
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
|
||||
let exception = &exception_info.exception.unwrap();
|
||||
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 id = exception.id;
|
||||
|
||||
let name_ptr = exception.name.as_ptr();
|
||||
let len = exception.name.len();
|
||||
let eh_action = match dwarf::find_eh_action(lsda, &eh_context, name_ptr, len) {
|
||||
let eh_action = match dwarf::find_eh_action(lsda, &eh_context, id) {
|
||||
Ok(action) => action,
|
||||
Err(_) => return uw::_URC_FATAL_PHASE1_ERROR,
|
||||
};
|
||||
|
||||
if actions as u32 & uw::_UA_SEARCH_PHASE as u32 != 0 {
|
||||
match eh_action {
|
||||
EHAction::None |
|
||||
EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND,
|
||||
EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND,
|
||||
EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR,
|
||||
}
|
||||
} else {
|
||||
match eh_action {
|
||||
EHAction::None => return uw::_URC_CONTINUE_UNWIND,
|
||||
EHAction::Cleanup(lpad) |
|
||||
EHAction::Catch(lpad) => {
|
||||
if actions as u32 & uw::_UA_HANDLER_FRAME as u32 != 0 {
|
||||
exception_info.handled = true
|
||||
}
|
||||
|
||||
// Pass a pair of the unwinder exception and ARTIQ exception
|
||||
// (which immediately follows).
|
||||
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
|
||||
|
@ -124,94 +149,209 @@ pub extern fn personality(version: c_int,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[export_name="__artiq_raise"]
|
||||
#[unwind(allowed)]
|
||||
pub unsafe extern fn raise(exception: *const Exception) -> ! {
|
||||
let count = EXCEPTION_BUFFER.exception_count;
|
||||
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);
|
||||
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 {
|
||||
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 {
|
||||
// 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!();
|
||||
}
|
||||
|
||||
|
||||
#[export_name="__artiq_resume"]
|
||||
#[unwind(allowed)]
|
||||
pub unsafe extern fn 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!()
|
||||
}
|
||||
|
||||
#[export_name="__artiq_end_catch"]
|
||||
#[unwind(allowed)]
|
||||
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) {
|
||||
_uw_exception: *mut uw::_Unwind_Exception) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn uncaught_exception() -> ! {
|
||||
unsafe {
|
||||
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
|
||||
|
||||
exception_info.exception = None;
|
||||
// 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 {
|
||||
::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())
|
||||
}
|
||||
}
|
||||
|
||||
extern fn uncaught_exception(_version: c_int,
|
||||
|
||||
// stop function which would be executed when we unwind each frame
|
||||
extern fn stop_fn(_version: c_int,
|
||||
actions: uw::_Unwind_Action,
|
||||
_uw_exception_class: uw::_Unwind_Exception_Class,
|
||||
uw_exception: *mut uw::_Unwind_Exception,
|
||||
_uw_exception: *mut uw::_Unwind_Exception,
|
||||
context: *mut uw::_Unwind_Context,
|
||||
_stop_parameter: *mut c_void)
|
||||
-> uw::_Unwind_Reason_Code {
|
||||
_stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code {
|
||||
unsafe {
|
||||
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
|
||||
|
||||
if exception_info.backtrace_size < exception_info.backtrace.len() {
|
||||
let backtrace_size = EXCEPTION_BUFFER.backtrace_size;
|
||||
if backtrace_size < MAX_BACKTRACE_SIZE {
|
||||
let ip = uw::_Unwind_GetIP(context);
|
||||
exception_info.backtrace[exception_info.backtrace_size] = ip;
|
||||
exception_info.backtrace_size += 1;
|
||||
let fp = uw::_Unwind_GetGR(context, UNW_FP_REG);
|
||||
if PAYLOAD_ADDRESS == 0 || ip > PAYLOAD_ADDRESS {
|
||||
let ip = ip - PAYLOAD_ADDRESS;
|
||||
EXCEPTION_BUFFER.backtrace[backtrace_size] = (ip, fp);
|
||||
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 = fp;
|
||||
sp_info.current_backtrace_size = backtrace_size + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if actions as u32 & uw::_UA_END_OF_STACK as u32 != 0 {
|
||||
::terminate(&exception_info.exception.unwrap(),
|
||||
exception_info.backtrace[..exception_info.backtrace_size].as_mut())
|
||||
uncaught_exception()
|
||||
} else {
|
||||
uw::_URC_NO_REASON
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We can unfortunately not use mem::zeroed in a static, so Option<> is used as a workaround.
|
||||
// See https://github.com/rust-lang/rust/issues/39498.
|
||||
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,
|
||||
handled: true,
|
||||
backtrace: [0; MAX_BACKTRACE_SIZE],
|
||||
backtrace_size: 0
|
||||
};
|
||||
static EXCEPTION_ID_LOOKUP: [(&str, u32); 10] = [
|
||||
("RuntimeError", 0),
|
||||
("RTIOUnderflow", 1),
|
||||
("RTIOOverflow", 2),
|
||||
("RTIODestinationUnreachable", 3),
|
||||
("DMAError", 4),
|
||||
("I2CError", 5),
|
||||
("CacheError", 6),
|
||||
("SPIError", 7),
|
||||
("ZeroDivisionError", 8),
|
||||
("IndexError", 9)
|
||||
];
|
||||
|
||||
#[export_name="__artiq_raise"]
|
||||
#[unwind(allowed)]
|
||||
pub unsafe extern fn raise(exception: *const Exception) -> ! {
|
||||
// Zing! The Exception<'a> to Exception<'static> transmute is not really sound in case
|
||||
// the exception is ever captured. Fortunately, they currently aren't, and we save
|
||||
// on the hassle of having to allocate exceptions somewhere except on stack.
|
||||
INFLIGHT.exception = Some(mem::transmute::<Exception, Exception<'static>>(*exception));
|
||||
INFLIGHT.handled = false;
|
||||
|
||||
let result = uw::_Unwind_RaiseException(&mut INFLIGHT.uw_exception);
|
||||
assert!(result == uw::_URC_END_OF_STACK);
|
||||
|
||||
INFLIGHT.backtrace_size = 0;
|
||||
let _result = _Unwind_ForcedUnwind(&mut INFLIGHT.uw_exception,
|
||||
uncaught_exception, ptr::null_mut());
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
#[export_name="__artiq_reraise"]
|
||||
#[unwind(allowed)]
|
||||
pub unsafe extern fn reraise() -> ! {
|
||||
use cslice::AsCSlice;
|
||||
|
||||
if INFLIGHT.handled {
|
||||
match INFLIGHT.exception {
|
||||
Some(ref exception) => raise(exception),
|
||||
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]
|
||||
})
|
||||
}
|
||||
} else {
|
||||
uw::_Unwind_Resume(&mut INFLIGHT.uw_exception)
|
||||
pub fn get_exception_id(name: &str) -> u32 {
|
||||
for (n, id) in EXCEPTION_ID_LOOKUP.iter() {
|
||||
if *n == name {
|
||||
return *id
|
||||
}
|
||||
}
|
||||
unimplemented!("unallocated internal exception id")
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![feature(lang_items, llvm_asm, panic_unwind, libc, unwind_attributes,
|
||||
panic_info_message, nll)]
|
||||
panic_info_message, nll, const_in_array_repeat_expressions)]
|
||||
#![no_std]
|
||||
|
||||
extern crate libc;
|
||||
|
@ -80,8 +80,9 @@ macro_rules! println {
|
|||
macro_rules! raise {
|
||||
($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({
|
||||
use cslice::AsCSlice;
|
||||
let name_id = $crate::eh_artiq::get_exception_id($name);
|
||||
let exn = $crate::eh_artiq::Exception {
|
||||
name: concat!("0:artiq.coredevice.exceptions.", $name).as_c_slice(),
|
||||
id: name_id,
|
||||
file: file!().as_c_slice(),
|
||||
line: line!(),
|
||||
column: column!(),
|
||||
|
@ -164,12 +165,12 @@ extern fn rpc_recv(slot: *mut ()) -> usize {
|
|||
&Err(ref exception) =>
|
||||
unsafe {
|
||||
eh_artiq::raise(&eh_artiq::Exception {
|
||||
name: exception.name.as_bytes().as_c_slice(),
|
||||
file: exception.file.as_bytes().as_c_slice(),
|
||||
id: exception.id,
|
||||
file: exception.file,
|
||||
line: exception.line,
|
||||
column: exception.column,
|
||||
function: exception.function.as_bytes().as_c_slice(),
|
||||
message: exception.message.as_bytes().as_c_slice(),
|
||||
function: exception.function,
|
||||
message: exception.message,
|
||||
param: exception.param
|
||||
})
|
||||
}
|
||||
|
@ -177,27 +178,13 @@ extern fn rpc_recv(slot: *mut ()) -> usize {
|
|||
})
|
||||
}
|
||||
|
||||
fn terminate(exception: &eh_artiq::Exception, backtrace: &mut [usize]) -> ! {
|
||||
let mut cursor = 0;
|
||||
for index in 0..backtrace.len() {
|
||||
if backtrace[index] > kernel_proto::KERNELCPU_PAYLOAD_ADDRESS {
|
||||
backtrace[cursor] = backtrace[index] - kernel_proto::KERNELCPU_PAYLOAD_ADDRESS;
|
||||
cursor += 1;
|
||||
}
|
||||
}
|
||||
let backtrace = &mut backtrace.as_mut()[0..cursor];
|
||||
|
||||
fn terminate(exceptions: &'static [Option<eh_artiq::Exception<'static>>],
|
||||
stack_pointers: &'static [eh_artiq::StackPointerBacktrace],
|
||||
backtrace: &mut [(usize, usize)]) -> ! {
|
||||
send(&RunException {
|
||||
exception: kernel_proto::Exception {
|
||||
name: str::from_utf8(exception.name.as_ref()).unwrap(),
|
||||
file: str::from_utf8(exception.file.as_ref()).unwrap(),
|
||||
line: exception.line,
|
||||
column: exception.column,
|
||||
function: str::from_utf8(exception.function.as_ref()).unwrap(),
|
||||
message: str::from_utf8(exception.message.as_ref()).unwrap(),
|
||||
param: exception.param,
|
||||
},
|
||||
backtrace: backtrace
|
||||
exceptions,
|
||||
stack_pointers,
|
||||
backtrace
|
||||
});
|
||||
loop {}
|
||||
}
|
||||
|
@ -472,6 +459,7 @@ unsafe fn attribute_writeback(typeinfo: *const ()) {
|
|||
|
||||
#[no_mangle]
|
||||
pub unsafe fn main() {
|
||||
eh_artiq::reset_exception_buffer(KERNELCPU_PAYLOAD_ADDRESS);
|
||||
let image = slice::from_raw_parts_mut(kernel_proto::KERNELCPU_PAYLOAD_ADDRESS as *mut u8,
|
||||
kernel_proto::KERNELCPU_LAST_ADDRESS -
|
||||
kernel_proto::KERNELCPU_PAYLOAD_ADDRESS);
|
||||
|
|
|
@ -202,8 +202,7 @@ unsafe fn get_ttype_entry(
|
|||
pub unsafe fn find_eh_action(
|
||||
lsda: *const u8,
|
||||
context: &EHContext<'_>,
|
||||
name: *const u8,
|
||||
len: usize,
|
||||
id: u32,
|
||||
) -> Result<EHAction, ()> {
|
||||
if lsda.is_null() {
|
||||
return Ok(EHAction::None);
|
||||
|
@ -275,20 +274,10 @@ pub unsafe fn find_eh_action(
|
|||
return Ok(EHAction::Catch(lpad));
|
||||
}
|
||||
// this seems to be target dependent
|
||||
let clause_ptr = *(catch_type as *const CSlice<u8>);
|
||||
let clause_name_ptr = (clause_ptr).as_ptr();
|
||||
let clause_name_len = (clause_ptr).len();
|
||||
if clause_name_len == 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))
|
||||
{
|
||||
let clause_id = *(catch_type as *const u32);
|
||||
if clause_id == id {
|
||||
return Ok(EHAction::Catch(lpad));
|
||||
}
|
||||
}
|
||||
} else if ar_filter < 0 {
|
||||
// FIXME: how to handle this?
|
||||
break;
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
// ARTIQ Exception struct declaration
|
||||
use cslice::CSlice;
|
||||
|
||||
// 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)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Exception<'a> {
|
||||
pub id: u32,
|
||||
pub file: CSlice<'a, u8>,
|
||||
pub line: u32,
|
||||
pub column: u32,
|
||||
pub function: CSlice<'a, u8>,
|
||||
pub message: CSlice<'a, u8>,
|
||||
pub param: [i64; 3]
|
||||
}
|
||||
|
||||
fn str_err(_: core::str::Utf8Error) -> 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> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "Exception {} from {} in {}:{}:{}, message: {}",
|
||||
self.id,
|
||||
exception_str(&self.function).map_err(str_err)?,
|
||||
exception_str(&self.file).map_err(str_err)?,
|
||||
self.line, self.column,
|
||||
exception_str(&self.message).map_err(str_err)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct StackPointerBacktrace {
|
||||
pub stack_pointer: usize,
|
||||
pub initial_backtrace_size: usize,
|
||||
pub current_backtrace_size: usize,
|
||||
}
|
||||
|
|
@ -87,5 +87,5 @@ unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> Result<EHAction,
|
|||
get_text_start: &|| uw::_Unwind_GetTextRelBase(context),
|
||||
get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
|
||||
};
|
||||
dwarf::find_eh_action(lsda, &eh_context, core::ptr::null(), 0)
|
||||
dwarf::find_eh_action(lsda, &eh_context, 0)
|
||||
}
|
||||
|
|
|
@ -7,3 +7,4 @@ extern crate libc;
|
|||
|
||||
pub mod dwarf;
|
||||
pub mod eh_rust;
|
||||
pub mod eh_artiq;
|
||||
|
|
|
@ -15,6 +15,7 @@ cslice = { version = "0.3" }
|
|||
log = { version = "0.4", default-features = false, optional = true }
|
||||
io = { path = "../libio", features = ["byteorder"] }
|
||||
dyld = { path = "../libdyld" }
|
||||
eh = { path = "../libeh" }
|
||||
|
||||
[features]
|
||||
alloc = ["io/alloc"]
|
||||
|
|
|
@ -6,17 +6,6 @@ pub const KERNELCPU_PAYLOAD_ADDRESS: usize = 0x45060000;
|
|||
pub const KERNELCPU_LAST_ADDRESS: usize = 0x4fffffff;
|
||||
pub const KSUPPORT_HEADER_SIZE: usize = 0x80;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Exception<'a> {
|
||||
pub name: &'a str,
|
||||
pub file: &'a str,
|
||||
pub line: u32,
|
||||
pub column: u32,
|
||||
pub function: &'a str,
|
||||
pub message: &'a str,
|
||||
pub param: [i64; 3]
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Message<'a> {
|
||||
LoadRequest(&'a [u8]),
|
||||
|
@ -47,8 +36,9 @@ pub enum Message<'a> {
|
|||
|
||||
RunFinished,
|
||||
RunException {
|
||||
exception: Exception<'a>,
|
||||
backtrace: &'a [usize]
|
||||
exceptions: &'a [Option<eh::eh_artiq::Exception<'a>>],
|
||||
stack_pointers: &'a [eh::eh_artiq::StackPointerBacktrace],
|
||||
backtrace: &'a [(usize, usize)]
|
||||
},
|
||||
RunAborted,
|
||||
|
||||
|
@ -59,7 +49,7 @@ pub enum Message<'a> {
|
|||
data: *const *const ()
|
||||
},
|
||||
RpcRecvRequest(*mut ()),
|
||||
RpcRecvReply(Result<usize, Exception<'a>>),
|
||||
RpcRecvReply(Result<usize, eh::eh_artiq::Exception<'a>>),
|
||||
RpcFlush,
|
||||
|
||||
CacheGetRequest { key: &'a str },
|
||||
|
|
|
@ -13,6 +13,7 @@ extern crate log;
|
|||
extern crate byteorder;
|
||||
extern crate io;
|
||||
extern crate dyld;
|
||||
extern crate eh;
|
||||
|
||||
// Internal protocols.
|
||||
pub mod kernel_proto;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use core::str::Utf8Error;
|
||||
use alloc::{vec::Vec, string::String};
|
||||
use alloc::vec::Vec;
|
||||
use eh::eh_artiq::{Exception, StackPointerBacktrace};
|
||||
use cslice::CSlice;
|
||||
|
||||
use io::{Read, ProtoRead, Write, ProtoWrite, Error as IoError, ReadStringError};
|
||||
|
||||
|
@ -70,13 +72,13 @@ pub enum Request {
|
|||
|
||||
RpcReply { tag: Vec<u8> },
|
||||
RpcException {
|
||||
name: String,
|
||||
message: String,
|
||||
id: u32,
|
||||
message: u32,
|
||||
param: [i64; 3],
|
||||
file: String,
|
||||
file: u32,
|
||||
line: u32,
|
||||
column: u32,
|
||||
function: String,
|
||||
function: u32,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -95,14 +97,9 @@ pub enum Reply<'a> {
|
|||
},
|
||||
KernelStartupFailed,
|
||||
KernelException {
|
||||
name: &'a str,
|
||||
message: &'a str,
|
||||
param: [i64; 3],
|
||||
file: &'a str,
|
||||
line: u32,
|
||||
column: u32,
|
||||
function: &'a str,
|
||||
backtrace: &'a [usize],
|
||||
exceptions: &'a [Option<Exception<'a>>],
|
||||
stack_pointers: &'a [StackPointerBacktrace],
|
||||
backtrace: &'a [(usize, usize)],
|
||||
async_errors: u8
|
||||
},
|
||||
|
||||
|
@ -126,15 +123,15 @@ impl Request {
|
|||
tag: reader.read_bytes()?
|
||||
},
|
||||
8 => Request::RpcException {
|
||||
name: reader.read_string()?,
|
||||
message: reader.read_string()?,
|
||||
id: reader.read_u32()?,
|
||||
message: reader.read_u32()?,
|
||||
param: [reader.read_u64()? as i64,
|
||||
reader.read_u64()? as i64,
|
||||
reader.read_u64()? as i64],
|
||||
file: reader.read_string()?,
|
||||
file: reader.read_u32()?,
|
||||
line: reader.read_u32()?,
|
||||
column: reader.read_u32()?,
|
||||
function: reader.read_string()?
|
||||
function: reader.read_u32()?
|
||||
},
|
||||
|
||||
ty => return Err(Error::UnknownPacket(ty))
|
||||
|
@ -142,6 +139,18 @@ impl Request {
|
|||
}
|
||||
}
|
||||
|
||||
fn write_exception_string<'a, W>(writer: &mut W, s: &CSlice<'a, u8>) -> Result<(), IoError<W::WriteError>>
|
||||
where W: Write + ?Sized
|
||||
{
|
||||
if s.len() == usize::MAX {
|
||||
writer.write_u32(u32::MAX)?;
|
||||
writer.write_u32(s.as_ptr() as u32)?;
|
||||
} else {
|
||||
writer.write_string(core::str::from_utf8(s.as_ref()).unwrap())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<'a> Reply<'a> {
|
||||
pub fn write_to<W>(&self, writer: &mut W) -> Result<(), IoError<W::WriteError>>
|
||||
where W: Write + ?Sized
|
||||
|
@ -171,22 +180,36 @@ impl<'a> Reply<'a> {
|
|||
writer.write_u8(8)?;
|
||||
},
|
||||
Reply::KernelException {
|
||||
name, message, param, file, line, column, function, backtrace,
|
||||
exceptions,
|
||||
stack_pointers,
|
||||
backtrace,
|
||||
async_errors
|
||||
} => {
|
||||
writer.write_u8(9)?;
|
||||
writer.write_string(name)?;
|
||||
writer.write_string(message)?;
|
||||
writer.write_u64(param[0] as u64)?;
|
||||
writer.write_u64(param[1] as u64)?;
|
||||
writer.write_u64(param[2] as u64)?;
|
||||
writer.write_string(file)?;
|
||||
writer.write_u32(line)?;
|
||||
writer.write_u32(column)?;
|
||||
writer.write_string(function)?;
|
||||
writer.write_u32(exceptions.len() as u32)?;
|
||||
for exception in exceptions.iter() {
|
||||
let exception = exception.as_ref().unwrap();
|
||||
writer.write_u32(exception.id as u32)?;
|
||||
write_exception_string(writer, &exception.message)?;
|
||||
writer.write_u64(exception.param[0] as u64)?;
|
||||
writer.write_u64(exception.param[1] as u64)?;
|
||||
writer.write_u64(exception.param[2] as u64)?;
|
||||
write_exception_string(writer, &exception.file)?;
|
||||
writer.write_u32(exception.line)?;
|
||||
writer.write_u32(exception.column)?;
|
||||
write_exception_string(writer, &exception.function)?;
|
||||
}
|
||||
|
||||
for sp in stack_pointers.iter() {
|
||||
writer.write_u32(sp.stack_pointer as u32)?;
|
||||
writer.write_u32(sp.initial_backtrace_size as u32)?;
|
||||
writer.write_u32(sp.current_backtrace_size as u32)?;
|
||||
}
|
||||
|
||||
writer.write_u32(backtrace.len() as u32)?;
|
||||
for &addr in backtrace {
|
||||
writer.write_u32(addr as u32)?
|
||||
for &(addr, sp) in backtrace {
|
||||
writer.write_u32(addr as u32)?;
|
||||
writer.write_u32(sp as u32)?;
|
||||
}
|
||||
writer.write_u8(async_errors)?;
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use core::{mem, str, cell::{Cell, RefCell}, fmt::Write as FmtWrite};
|
||||
use alloc::{vec::Vec, string::String};
|
||||
use byteorder::{ByteOrder, NativeEndian};
|
||||
use cslice::CSlice;
|
||||
|
||||
use io::{Read, Write, Error as IoError};
|
||||
use board_misoc::{ident, cache, config};
|
||||
|
@ -291,7 +292,7 @@ fn process_host_message(io: &Io,
|
|||
}
|
||||
|
||||
host::Request::RpcException {
|
||||
name, message, param, file, line, column, function
|
||||
id, message, param, file, line, column, function
|
||||
} => {
|
||||
if session.kernel_state != KernelState::RpcWait {
|
||||
unexpected!("unsolicited RPC reply")
|
||||
|
@ -305,16 +306,18 @@ fn process_host_message(io: &Io,
|
|||
}
|
||||
})?;
|
||||
|
||||
let exn = kern::Exception {
|
||||
name: name.as_ref(),
|
||||
message: message.as_ref(),
|
||||
unsafe {
|
||||
let exn = eh::eh_artiq::Exception {
|
||||
id: id,
|
||||
message: CSlice::new(message as *const u8, usize::MAX),
|
||||
param: param,
|
||||
file: file.as_ref(),
|
||||
file: CSlice::new(file as *const u8, usize::MAX),
|
||||
line: line,
|
||||
column: column,
|
||||
function: function.as_ref()
|
||||
function: CSlice::new(function as *const u8, usize::MAX),
|
||||
};
|
||||
kern_send(io, &kern::RpcRecvReply(Err(exn)))?;
|
||||
}
|
||||
|
||||
session.kernel_state = KernelState::Running
|
||||
}
|
||||
|
@ -438,7 +441,8 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
|
|||
}
|
||||
}
|
||||
&kern::RunException {
|
||||
exception: kern::Exception { name, message, param, file, line, column, function },
|
||||
exceptions,
|
||||
stack_pointers,
|
||||
backtrace
|
||||
} => {
|
||||
unsafe { kernel::stop() }
|
||||
|
@ -448,19 +452,15 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
|
|||
match stream {
|
||||
None => {
|
||||
error!("exception in flash kernel");
|
||||
error!("{}: {} {:?}", name, message, param);
|
||||
error!("at {}:{}:{} in {}", file, line, column, function);
|
||||
for exception in exceptions {
|
||||
error!("{:?}", exception.unwrap());
|
||||
}
|
||||
return Ok(true)
|
||||
},
|
||||
Some(ref mut stream) => {
|
||||
host_write(stream, host::Reply::KernelException {
|
||||
name: name,
|
||||
message: message,
|
||||
param: param,
|
||||
file: file,
|
||||
line: line,
|
||||
column: column,
|
||||
function: function,
|
||||
exceptions: exceptions,
|
||||
stack_pointers: stack_pointers,
|
||||
backtrace: backtrace,
|
||||
async_errors: unsafe { get_async_errors() }
|
||||
}).map_err(|e| e.into())
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#![feature(libc, panic_unwind, unwind_attributes, rustc_private, int_bits_const)]
|
||||
#![feature(libc, panic_unwind, unwind_attributes, rustc_private, int_bits_const, const_in_array_repeat_expressions)]
|
||||
#![crate_name = "artiq_support"]
|
||||
#![crate_type = "cdylib"]
|
||||
|
||||
|
@ -58,15 +58,22 @@ mod cslice {
|
|||
pub mod eh {
|
||||
#[path = "../../firmware/libeh/dwarf.rs"]
|
||||
pub mod dwarf;
|
||||
#[path = "../../firmware/libeh/eh_artiq.rs"]
|
||||
pub mod eh_artiq;
|
||||
}
|
||||
#[path = "../../firmware/ksupport/eh_artiq.rs"]
|
||||
pub mod eh_artiq;
|
||||
|
||||
use std::{str, process};
|
||||
|
||||
fn terminate(exception: &eh_artiq::Exception, mut _backtrace: &mut [usize]) -> ! {
|
||||
fn terminate(exceptions: &'static [Option<eh_artiq::Exception<'static>>],
|
||||
_stack_pointers: &'static [eh_artiq::StackPointerBacktrace],
|
||||
_backtrace: &'static mut [(usize, usize)]) -> ! {
|
||||
println!("{}", exceptions.len());
|
||||
for exception in exceptions.iter() {
|
||||
let exception = exception.as_ref().unwrap();
|
||||
println!("Uncaught {}: {} ({}, {}, {})",
|
||||
str::from_utf8(exception.name.as_ref()).unwrap(),
|
||||
exception.id,
|
||||
str::from_utf8(exception.message.as_ref()).unwrap(),
|
||||
exception.param[0],
|
||||
exception.param[1],
|
||||
|
@ -75,6 +82,7 @@ fn terminate(exception: &eh_artiq::Exception, mut _backtrace: &mut [usize]) -> !
|
|||
str::from_utf8(exception.file.as_ref()).unwrap(),
|
||||
exception.line,
|
||||
exception.column);
|
||||
}
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
|
|
|
@ -257,7 +257,7 @@
|
|||
cargoDeps = rustPlatform.fetchCargoTarball {
|
||||
name = "artiq-firmware-cargo-deps";
|
||||
src = "${self}/artiq/firmware";
|
||||
sha256 = "sha256-Lf6M4M/jdRiO5MsWSoqtOSNfRIhbze+qvg4kaiiBWW4=";
|
||||
sha256 = "sha256-YyycMsDzR+JRcMZJd6A/CRi2J9nKmaWY/KXUnAQaZ00=";
|
||||
};
|
||||
nativeBuildInputs = [
|
||||
(pkgs.python3.withPackages(ps: [ migen misoc artiq ]))
|
||||
|
|
Loading…
Reference in New Issue