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
pca006132 2022-01-25 15:10:09 +08:00 committed by Sébastien Bourdeauducq
parent 536b3e0c26
commit 4132c450a5
15 changed files with 433 additions and 243 deletions

View File

@ -253,6 +253,7 @@ dependencies = [
"byteorder",
"cslice",
"dyld",
"eh",
"failure",
"failure_derive",
"io",

View File

@ -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),

View File

@ -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")
}

View File

@ -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);

View File

@ -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?

View File

@ -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,
}

View File

@ -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)
}

View File

@ -7,3 +7,4 @@ extern crate libc;
pub mod dwarf;
pub mod eh_rust;
pub mod eh_artiq;

View File

@ -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"]

View File

@ -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 },

View File

@ -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;

View File

@ -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)?;
},

View File

@ -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())

View File

@ -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);
}

View File

@ -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 ]))