mirror of https://github.com/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.pull/1840/head
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,133 +122,236 @@ 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,
|
||||
uw_exception as uw::_Unwind_Word);
|
||||
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1,
|
||||
exception as *const _ as uw::_Unwind_Word);
|
||||
uw::_Unwind_SetIP(context, lpad);
|
||||
return uw::_URC_INSTALL_CONTEXT;
|
||||
}
|
||||
EHAction::Terminate => return uw::_URC_FATAL_PHASE2_ERROR,
|
||||
match eh_action {
|
||||
EHAction::None => return uw::_URC_CONTINUE_UNWIND,
|
||||
EHAction::Cleanup(lpad) |
|
||||
EHAction::Catch(lpad) => {
|
||||
// Pass a pair of the unwinder exception and ARTIQ exception
|
||||
// (which immediately follows).
|
||||
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
|
||||
uw_exception as uw::_Unwind_Word);
|
||||
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1,
|
||||
exception as *const _ as uw::_Unwind_Word);
|
||||
uw::_Unwind_SetIP(context, lpad);
|
||||
return uw::_URC_INSTALL_CONTEXT;
|
||||
}
|
||||
EHAction::Terminate => return uw::_URC_FATAL_PHASE2_ERROR,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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) {
|
||||
unsafe {
|
||||
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
|
||||
_uw_exception: *mut uw::_Unwind_Exception) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
exception_info.exception = None;
|
||||
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 {
|
||||
::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,
|
||||
actions: uw::_Unwind_Action,
|
||||
_uw_exception_class: uw::_Unwind_Exception_Class,
|
||||
uw_exception: *mut uw::_Unwind_Exception,
|
||||
context: *mut uw::_Unwind_Context,
|
||||
_stop_parameter: *mut c_void)
|
||||
-> uw::_Unwind_Reason_Code {
|
||||
|
||||
// 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,
|
||||
context: *mut uw::_Unwind_Context,
|
||||
_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]
|
||||
})
|
||||
pub fn get_exception_id(name: &str) -> u32 {
|
||||
for (n, id) in EXCEPTION_ID_LOOKUP.iter() {
|
||||
if *n == name {
|
||||
return *id
|
||||
}
|
||||
} else {
|
||||
uw::_Unwind_Resume(&mut INFLIGHT.uw_exception)
|
||||
}
|
||||
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,19 +274,9 @@ 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))
|
||||
{
|
||||
return Ok(EHAction::Catch(lpad));
|
||||
}
|
||||
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?
|
||||
|
|
|
@ -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(),
|
||||
param: param,
|
||||
file: file.as_ref(),
|
||||
line: line,
|
||||
column: column,
|
||||
function: function.as_ref()
|
||||
};
|
||||
kern_send(io, &kern::RpcRecvReply(Err(exn)))?;
|
||||
unsafe {
|
||||
let exn = eh::eh_artiq::Exception {
|
||||
id: id,
|
||||
message: CSlice::new(message as *const u8, usize::MAX),
|
||||
param: param,
|
||||
file: CSlice::new(file as *const u8, usize::MAX),
|
||||
line: line,
|
||||
column: column,
|
||||
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,23 +58,31 @@ 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]) -> ! {
|
||||
println!("Uncaught {}: {} ({}, {}, {})",
|
||||
str::from_utf8(exception.name.as_ref()).unwrap(),
|
||||
str::from_utf8(exception.message.as_ref()).unwrap(),
|
||||
exception.param[0],
|
||||
exception.param[1],
|
||||
exception.param[2]);
|
||||
println!("at {}:{}:{}",
|
||||
str::from_utf8(exception.file.as_ref()).unwrap(),
|
||||
exception.line,
|
||||
exception.column);
|
||||
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 {}: {} ({}, {}, {})",
|
||||
exception.id,
|
||||
str::from_utf8(exception.message.as_ref()).unwrap(),
|
||||
exception.param[0],
|
||||
exception.param[1],
|
||||
exception.param[2]);
|
||||
println!("at {}:{}:{}",
|
||||
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