From 8923feceac22c7b3293b8d3c71c34be97804e448 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Fri, 14 Jan 2022 13:35:24 +0800 Subject: [PATCH 1/2] runtime/eh_artiq: use forced unwind This patches ports the LLVM libunwind newly added forced unwinding function. This enables us to run forced unwinding to obtain correct backtrace when uncaught exceptions occur. This patch also changes the exception handling scheme from the standard two-phase unwinding to single phase using forced unwinding. This brings some performance improvement and prepared for later nested exception support. For nested exceptions, we will have to record the backtrace regardless if the exception is an uncaught exception, as there can be another exception being thrown while executing the finally block for caught exceptions, and we will lose the backtrace if we don't store it earlier before running the cleanup pads. --- src/llvm_libunwind/include/unwind.h | 9 +- src/llvm_libunwind/src/Unwind-EHABI.cpp | 170 ++++++++++++++++++++++- src/runtime/src/eh_artiq.rs | 175 ++++++++++++------------ 3 files changed, 258 insertions(+), 96 deletions(-) diff --git a/src/llvm_libunwind/include/unwind.h b/src/llvm_libunwind/include/unwind.h index b6cc704..a6f5a8a 100644 --- a/src/llvm_libunwind/include/unwind.h +++ b/src/llvm_libunwind/include/unwind.h @@ -107,9 +107,12 @@ struct _Unwind_Control_Block { } __attribute__((__aligned__(8))); typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) - (_Unwind_State state, - _Unwind_Exception* exceptionObject, - struct _Unwind_Context* context); + (int version, + _Unwind_Action actions, + uint64_t exceptionClass, + _Unwind_Exception* exceptionObject, + struct _Unwind_Context* context, + void* stop_parameter); typedef _Unwind_Reason_Code (*__personality_routine) (_Unwind_State state, diff --git a/src/llvm_libunwind/src/Unwind-EHABI.cpp b/src/llvm_libunwind/src/Unwind-EHABI.cpp index 14bc15f..93a550f 100644 --- a/src/llvm_libunwind/src/Unwind-EHABI.cpp +++ b/src/llvm_libunwind/src/Unwind-EHABI.cpp @@ -95,9 +95,11 @@ _Unwind_Reason_Code ProcessDescriptors( case Descriptor::LU32: descriptor = getNextWord(descriptor, &length); descriptor = getNextWord(descriptor, &offset); + break; case Descriptor::LU16: descriptor = getNextNibble(descriptor, &length); descriptor = getNextNibble(descriptor, &offset); + break; default: assert(false); return _URC_FAILURE; @@ -183,8 +185,14 @@ static _Unwind_Reason_Code unwindOneFrame(_Unwind_State state, if (result != _URC_CONTINUE_UNWIND) return result; - if (__unw_step(reinterpret_cast(context)) != UNW_STEP_SUCCESS) + switch (__unw_step(reinterpret_cast(context))) { + case UNW_STEP_SUCCESS: + return _URC_CONTINUE_UNWIND; + case UNW_STEP_END: + return _URC_END_OF_STACK; + default: return _URC_FAILURE; + } return _URC_CONTINUE_UNWIND; } @@ -677,6 +685,128 @@ static _Unwind_Reason_Code unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor return _URC_FATAL_PHASE2_ERROR; } +static _Unwind_Reason_Code +unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor, + _Unwind_Exception *exception_object, _Unwind_Stop_Fn stop, + void *stop_parameter) { + // See comment at the start of unwind_phase1 regarding VRS integrity. + __unw_init_local(cursor, uc); + + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p)", + static_cast(exception_object)); + + // Walk each frame until we reach where search phase said to stop. + bool end_of_stack = false; + // TODO: why can't libunwind handle end of stack properly? + // We should fix this kind of hack. + unw_word_t forced_phase2_prev_sp = 0x0; + while (!end_of_stack) { + // Get info about this frame. + unw_word_t sp; + unw_proc_info_t frameInfo; + __unw_get_reg(cursor, UNW_REG_SP, &sp); + if (sp == forced_phase2_prev_sp) { + break; + } + forced_phase2_prev_sp = sp; + if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): __unw_get_proc_info " + "failed => _URC_FATAL_PHASE2_ERROR", + static_cast(exception_object)); + return _URC_FATAL_PHASE2_ERROR; + } + + // When tracing, print state information. + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionBuf[512]; + const char *functionName = functionBuf; + unw_word_t offset; + if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf), + &offset) != UNW_ESUCCESS) || + (frameInfo.start_ip + offset > frameInfo.end_ip)) + functionName = ".anonymous."; + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): start_ip=0x%" PRIxPTR ", func=%s, sp=0x%" PRIxPTR ", " + "lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR "", + static_cast(exception_object), frameInfo.start_ip, + functionName, sp, frameInfo.lsda, + frameInfo.handler); + } + + _Unwind_Action action = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE); + _Unwind_Reason_Code stopResult = + (*stop)(1, action, exception_object->exception_class, exception_object, + (_Unwind_Context *)(cursor), stop_parameter); + + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): stop function returned %d", + (void *)exception_object, stopResult); + if (stopResult != _URC_NO_REASON) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): stopped by stop function", + (void *)exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // If there is a personality routine, tell it we are unwinding. + if (frameInfo.handler != 0) { + __personality_routine p = + (__personality_routine)(long)(frameInfo.handler); + struct _Unwind_Context *context = (struct _Unwind_Context *)(cursor); + // EHABI #7.2 + exception_object->pr_cache.fnstart = frameInfo.start_ip; + exception_object->pr_cache.ehtp = + (_Unwind_EHT_Header *)frameInfo.unwind_info; + exception_object->pr_cache.additional = frameInfo.flags; + _Unwind_Reason_Code personalityResult = + (*p)(_US_FORCE_UNWIND | _US_UNWIND_FRAME_STARTING, exception_object, + context); + switch (personalityResult) { + case _URC_CONTINUE_UNWIND: + // Continue unwinding + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): _URC_CONTINUE_UNWIND", + static_cast(exception_object)); + break; + case _URC_INSTALL_CONTEXT: + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): _URC_INSTALL_CONTEXT", + static_cast(exception_object)); + { + // EHABI #7.4.1 says we need to preserve pc for when _Unwind_Resume + // is called back, to find this same frame. + unw_word_t pc; + __unw_get_reg(cursor, UNW_REG_IP, &pc); + exception_object->unwinder_cache.reserved2 = (uint32_t)pc; + } + // We may get control back if landing pad calls _Unwind_Resume(). + __unw_resume(cursor); + break; + case _URC_END_OF_STACK: + end_of_stack = true; + break; + default: + // Personality routine returned an unknown result code. + _LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d", + personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + } + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop " + "function with _UA_END_OF_STACK", + (void *)exception_object); + _Unwind_Action lastAction = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK); + (*stop)(1, lastAction, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(cursor), stop_parameter); + return _URC_FATAL_PHASE2_ERROR; +} + + + /// Called by __cxa_throw. Only returns if there is a fatal error. _LIBUNWIND_EXPORT _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception *exception_object) { @@ -724,15 +854,36 @@ _Unwind_Resume(_Unwind_Exception *exception_object) { unw_cursor_t cursor; __unw_getcontext(&uc); - // _Unwind_RaiseException on EHABI will always set the reserved1 field to 0, - // which is in the same position as private_1 below. - // TODO(ajwong): Who wronte the above? Why is it true? - unwind_phase2(&uc, &cursor, exception_object, true); + if (exception_object->unwinder_cache.reserved1) + unwind_phase2_forced( + &uc, &cursor, exception_object, + (_Unwind_Stop_Fn)exception_object->unwinder_cache.reserved1, + (void *)exception_object->unwinder_cache.reserved3); + else + unwind_phase2(&uc, &cursor, exception_object, true); // Clients assume _Unwind_Resume() does not return, so all we can do is abort. _LIBUNWIND_ABORT("_Unwind_Resume() can't return"); } +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_ForcedUnwind(_Unwind_Exception *exception_object, _Unwind_Stop_Fn stop, + void *stop_parameter) { + _LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)", + (void *)exception_object, (void *)(uintptr_t)stop); + unw_context_t uc; + unw_cursor_t cursor; + __unw_getcontext(&uc); + + // Mark that this is a forced unwind, so _Unwind_Resume() can do + // the right thing. + exception_object->unwinder_cache.reserved1 = (uintptr_t)stop; + exception_object->unwinder_cache.reserved3 = (uintptr_t)stop_parameter; + + return unwind_phase2_forced(&uc, &cursor, exception_object, stop, + stop_parameter); +} + /// Called by personality handler during phase 2 to get LSDA for current frame. _LIBUNWIND_EXPORT uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { @@ -1002,9 +1153,14 @@ extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code __gnu_unwind_frame(_Unwind_Exception *exception_object, struct _Unwind_Context *context) { unw_cursor_t *cursor = (unw_cursor_t *)context; - if (__unw_step(cursor) != UNW_STEP_SUCCESS) + switch (__unw_step(cursor)) { + case UNW_STEP_SUCCESS: + return _URC_OK; + case UNW_STEP_END: + return _URC_END_OF_STACK; + default: return _URC_FAILURE; - return _URC_OK; + } } #endif // defined(_LIBUNWIND_ARM_EHABI) diff --git a/src/runtime/src/eh_artiq.rs b/src/runtime/src/eh_artiq.rs index b6acd18..8aa1a6a 100644 --- a/src/runtime/src/eh_artiq.rs +++ b/src/runtime/src/eh_artiq.rs @@ -15,7 +15,7 @@ use core::mem; use cslice::CSlice; use unwind as uw; -use libc::{c_int, uintptr_t}; +use libc::{c_int, c_void, uintptr_t}; use log::trace; use dwarf::eh::{self, EHAction, EHContext}; @@ -59,11 +59,25 @@ const MAX_BACKTRACE_SIZE: usize = 128; struct ExceptionInfo { uw_exception: uw::_Unwind_Exception, exception: Option>, - handled: bool, backtrace: [usize; MAX_BACKTRACE_SIZE], backtrace_size: usize } +type _Unwind_Stop_Fn = extern "C" fn(version: c_int, + actions: i32, + exception_class: uw::_Unwind_Exception_Class, + exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context, + stop_parameter: *mut c_void) + -> uw::_Unwind_Reason_Code; + +extern { + // not defined in EHABI, but LLVM added it and is useful to us + fn _Unwind_ForcedUnwind(exception: *mut uw::_Unwind_Exception, + stop_fn: _Unwind_Stop_Fn, + stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code; +} + unsafe fn find_eh_action( context: *mut uw::_Unwind_Context, foreign_exception: bool, @@ -84,29 +98,11 @@ unsafe fn find_eh_action( eh::find_eh_action(lsda, &eh_context, foreign_exception, name, len) } -pub unsafe fn artiq_personality(state: uw::_Unwind_State, - exception_object: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context) - -> uw::_Unwind_Reason_Code { - let state = state as c_int; - let action = state & uw::_US_ACTION_MASK as c_int; - let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { - // Backtraces on ARM will call the personality routine with - // state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases - // we want to continue unwinding the stack, otherwise all our backtraces - // would end at __rust_try - if state & uw::_US_FORCE_UNWIND as c_int != 0 { - return continue_unwind(exception_object, context); - } - true - } else if action == uw::_US_UNWIND_FRAME_STARTING as c_int { - false - } else if action == uw::_US_UNWIND_FRAME_RESUME as c_int { - return continue_unwind(exception_object, context); - } else { - return uw::_URC_FAILURE; - }; - +pub unsafe fn artiq_personality(_state: uw::_Unwind_State, + exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code { + // we will only do phase 2 forced unwinding now // The DWARF unwinder assumes that _Unwind_Context holds things like the function // and LSDA pointers, however ARM EHABI places them into the exception object. // To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which @@ -122,6 +118,7 @@ pub unsafe fn artiq_personality(state: uw::_Unwind_State, let exception_class = (*exception_object).exception_class; let foreign_exception = exception_class != EXCEPTION_CLASS; + assert!(!foreign_exception, "we do not expect foreign exceptions"); let exception_info = &mut *(exception_object as *mut ExceptionInfo); let (name_ptr, len) = if foreign_exception || exception_info.exception.is_none() { @@ -135,39 +132,17 @@ pub unsafe fn artiq_personality(state: uw::_Unwind_State, Err(_) => return uw::_URC_FAILURE, }; let exception = &exception_info.exception.unwrap(); - if search_phase { - match eh_action { - EHAction::None => return continue_unwind(exception_object, context), - // Actually, cleanup should not return handler found, this is to workaround - // the issue of terminating directly when no catch cause is found while - // having some cleanup routines defined by finally. - // The best way to handle this is to force unwind the stack in the raise - // function when end of stack is reached, and call terminate at the end of - // the unwind. Unfortunately, there is no forced unwind function defined - // for EHABI, and I have no idea how to implement that, so this is a hack. - EHAction::Cleanup(_) => return uw::_URC_HANDLER_FOUND, - EHAction::Catch(_) => { - // EHABI requires the personality routine to update the - // SP value in the barrier cache of the exception object. - (*exception_object).private[5] = - uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG); - return uw::_URC_HANDLER_FOUND; - } - EHAction::Terminate => return uw::_URC_FAILURE, - } - } else { - match eh_action { - EHAction::None => return continue_unwind(exception_object, context), - EHAction::Cleanup(lpad) | - EHAction::Catch(lpad) => { - uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, - exception_object as uintptr_t); - 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_FAILURE, + match eh_action { + EHAction::None => return continue_unwind(exception_object, context), + EHAction::Cleanup(lpad) | + EHAction::Catch(lpad) => { + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, + exception_object as uintptr_t); + 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_FAILURE, } // On ARM EHABI the personality routine is responsible for actually @@ -175,10 +150,11 @@ pub unsafe fn artiq_personality(state: uw::_Unwind_State, unsafe fn continue_unwind(exception_object: *mut uw::_Unwind_Exception, context: *mut uw::_Unwind_Context) -> uw::_Unwind_Reason_Code { - if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON { + let reason = __gnu_unwind_frame(exception_object, context); + if reason == uw::_URC_NO_REASON { uw::_URC_CONTINUE_UNWIND } else { - uw::_URC_FAILURE + reason } } // defined in libgcc @@ -205,30 +181,47 @@ static mut INFLIGHT: ExceptionInfo = ExceptionInfo { private: [0; uw::unwinder_private_data_size], }, exception: None, - handled: true, backtrace: [0; MAX_BACKTRACE_SIZE], backtrace_size: 0 }; pub unsafe extern fn raise(exception: *const Exception) -> ! { - trace!("Trying to raise exception"); // FIXME: unsound transmute // This would cause stack memory corruption. - INFLIGHT.exception = Some(mem::transmute::>(*exception)); - INFLIGHT.handled = false; - - let result = uw::_Unwind_RaiseException(&mut INFLIGHT.uw_exception); - assert!(result == uw::_URC_FAILURE || result == uw::_URC_END_OF_STACK); - + trace!("raising exception"); INFLIGHT.backtrace_size = 0; - // read backtrace - let _ = uw::backtrace(|ip| { - if INFLIGHT.backtrace_size < MAX_BACKTRACE_SIZE { - INFLIGHT.backtrace[INFLIGHT.backtrace_size] = ip; - INFLIGHT.backtrace_size += 1; + INFLIGHT.exception = Some(mem::transmute::>(*exception)); + + let _result = _Unwind_ForcedUnwind(&mut INFLIGHT.uw_exception, + uncaught_exception, core::ptr::null_mut()); + unreachable!() +} + +extern fn uncaught_exception(_version: c_int, + actions: i32, + _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 { + trace!("uncaught exception"); + let exception_info = &mut *(uw_exception as *mut ExceptionInfo); + + if exception_info.backtrace_size < exception_info.backtrace.len() { + let ip = uw::_Unwind_GetIP(context); + trace!("SP: {:X}, backtrace_size: {}", uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG), exception_info.backtrace_size); + exception_info.backtrace[exception_info.backtrace_size] = ip; + exception_info.backtrace_size += 1; } - }); - crate::kernel::core1::terminate(INFLIGHT.exception.as_ref().unwrap(), INFLIGHT.backtrace[..INFLIGHT.backtrace_size].as_mut()); + + if actions as u32 & uw::_US_END_OF_STACK as u32 != 0 { + crate::kernel::core1::terminate(exception_info.exception.as_ref().unwrap(), + exception_info.backtrace[..exception_info.backtrace_size].as_mut()) + } else { + uw::_URC_NO_REASON + } + } } pub unsafe extern fn reraise() -> ! { @@ -237,17 +230,25 @@ pub unsafe extern fn reraise() -> ! { // Reraise is basically cxa_rethrow, which calls _Unwind_Resume_or_Rethrow, // which for EHABI would always call _Unwind_RaiseException. 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] - }) + Some(ex) => { + // we cannot call raise directly as that would corrupt the backtrace + INFLIGHT.exception = Some(mem::transmute::>(ex)); + let _result = _Unwind_ForcedUnwind(&mut INFLIGHT.uw_exception, + uncaught_exception, core::ptr::null_mut()); + unreachable!() + }, + None => { + raise(&Exception { + name: "0:artiq.coredevice.exceptions.RuntimeError".as_c_slice(), + file: file!().as_c_slice(), + line: line!(), + column: column!(), + // https://github.com/rust-lang/rfcs/pull/1719 + function: "__artiq_reraise".as_c_slice(), + message: "No active exception to reraise".as_c_slice(), + param: [0, 0, 0] + }) + } } } @@ -266,7 +267,9 @@ macro_rules! artiq_raise { param: [$param0, $param1, $param2] }; #[allow(unused_unsafe)] - unsafe { $crate::eh_artiq::raise(&exn) } + unsafe { + $crate::eh_artiq::raise(&exn) + } }); ($name:expr, $message:expr) => ({ artiq_raise!($name, $message, 0, 0, 0) -- 2.42.0 From 6f5ba46e891bf2df2ec929393a07c1bc9c1e5d6b Mon Sep 17 00:00:00 2001 From: pca006132 Date: Fri, 14 Jan 2022 19:24:20 +0800 Subject: [PATCH 2/2] runtime/eh_artiq: support exception allocation The backtrace is now nested, and should be used together with the stack pointer array to construct the full backtrace for each exception. We now allocate exception objects in a stack, but their names are still not allocated. This is fine for exceptions raised in the driver or artiq code, but we will have to implement allocation for names of exceptions raised in RPC calls. The compiler should also emit code to store the exception names once they catch it, to prepare for later reraising. --- src/libdwarf/src/eh.rs | 15 +- src/runtime/src/comms.rs | 63 +++--- src/runtime/src/eh_artiq.rs | 358 +++++++++++++++++++++++--------- src/runtime/src/kernel/api.rs | 3 +- src/runtime/src/kernel/core1.rs | 20 +- src/runtime/src/kernel/mod.rs | 15 +- src/runtime/src/kernel/rpc.rs | 10 +- 7 files changed, 327 insertions(+), 157 deletions(-) diff --git a/src/libdwarf/src/eh.rs b/src/libdwarf/src/eh.rs index d5ebd02..634a88f 100644 --- a/src/libdwarf/src/eh.rs +++ b/src/libdwarf/src/eh.rs @@ -90,8 +90,7 @@ pub unsafe fn find_eh_action( lsda: *const u8, context: &EHContext<'_>, foreign_exception: bool, - name: *const u8, - len: usize, + id: u32, ) -> Result { if lsda.is_null() { return Ok(EHAction::None); @@ -164,19 +163,11 @@ pub unsafe fn find_eh_action( ttype_base, ttype_table, )?; - let clause_ptr = *(catch_type as *const *const CSlice); + let clause_ptr = *(catch_type as *const *const u32); if clause_ptr.is_null() { return Ok(EHAction::Catch(lpad)); } - let clause_name_ptr = (*clause_ptr).as_ptr(); - let clause_name_len = (*clause_ptr).len(); - if (clause_name_ptr == core::ptr::null() || - clause_name_ptr == name || - // somehow their name pointers might differ, but the content is the - // same - core::slice::from_raw_parts(clause_name_ptr, clause_name_len) == - core::slice::from_raw_parts(name, len)) - { + if *clause_ptr == id { return Ok(EHAction::Catch(lpad)); } } else if ar_filter < 0 { diff --git a/src/runtime/src/comms.rs b/src/runtime/src/comms.rs index 55cfc18..7c003cf 100644 --- a/src/runtime/src/comms.rs +++ b/src/runtime/src/comms.rs @@ -1,8 +1,8 @@ use core::fmt; use core::cell::RefCell; -use core::str::Utf8Error; use alloc::{vec, vec::Vec, string::String, collections::BTreeMap, rc::Rc}; use log::{info, warn, error}; +use cslice::CSlice; use num_derive::{FromPrimitive, ToPrimitive}; use num_traits::{FromPrimitive, ToPrimitive}; @@ -39,7 +39,6 @@ pub enum Error { UnexpectedPattern, UnrecognizedPacket, BufferExhausted, - Utf8Error(Utf8Error), } pub type Result = core::result::Result; @@ -51,7 +50,6 @@ impl fmt::Display for Error { Error::UnexpectedPattern => write!(f, "unexpected pattern"), Error::UnrecognizedPacket => write!(f, "unrecognized packet"), Error::BufferExhausted => write!(f, "buffer exhausted"), - Error::Utf8Error(error) => write!(f, "UTF-8 error: {}", error), } } } @@ -122,11 +120,6 @@ async fn read_bytes(stream: &TcpStream, max_length: usize) -> Result> { Ok(buffer) } -async fn read_string(stream: &TcpStream, max_length: usize) -> Result { - let bytes = read_bytes(stream, max_length).await?; - Ok(String::from_utf8(bytes).map_err(|err| Error::Utf8Error(err.utf8_error()))?) -} - const RETRY_LIMIT: usize = 100; async fn fast_send(sender: &mut Sender<'_, kernel::Message>, content: kernel::Message) { @@ -156,6 +149,16 @@ async fn fast_recv(receiver: &mut Receiver<'_, kernel::Message>) -> kernel::Mess receiver.async_recv().await } +async fn write_exception_string(stream: &TcpStream, s: CSlice<'static, u8>) -> Result<()> { + if s.len() == usize::MAX { + write_i32(stream, -1).await?; + write_i32(stream, s.as_ptr() as i32).await? + } else { + write_chunk(stream, s.as_ref()).await?; + }; + Ok(()) +} + async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc>) -> Result<()> { control.borrow_mut().tx.async_send(kernel::Message::StartRequest).await; loop { @@ -204,17 +207,17 @@ async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc (), other => panic!("expected (ignored) root value slot from kernel CPU, not {:?}", other), } - let name = read_string(stream, 16384).await?; - let message = read_string(stream, 16384).await?; + let id = read_i32(stream).await? as u32; + let message = read_i32(stream).await? as u32; let param = [read_i64(stream).await?, read_i64(stream).await?, read_i64(stream).await?]; - let file = read_string(stream, 16384).await?; + let file = read_i32(stream).await? as u32; let line = read_i32(stream).await?; let column = read_i32(stream).await?; - let function = read_string(stream, 16384).await?; + let function = read_i32(stream).await? as u32; control.tx.async_send(kernel::Message::RpcRecvReply(Err(kernel::RPCException { - name, message, param, file, line, column, function + id, message, param, file, line, column, function }))).await; }, _ => { @@ -231,29 +234,39 @@ async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc { + kernel::Message::KernelException(exceptions, stack_pointers, backtrace, async_errors) => { match stream { Some(stream) => { // only send the exception data to host if there is host, // i.e. not idle/startup kernel. write_header(stream, Reply::KernelException).await?; - write_chunk(stream, exception.name.as_ref()).await?; - write_chunk(stream, exception.message.as_ref()).await?; - write_i64(stream, exception.param[0] as i64).await?; - write_i64(stream, exception.param[1] as i64).await?; - write_i64(stream, exception.param[2] as i64).await?; - write_chunk(stream, exception.file.as_ref()).await?; - write_i32(stream, exception.line as i32).await?; - write_i32(stream, exception.column as i32).await?; - write_chunk(stream, exception.function.as_ref()).await?; + write_i32(stream, exceptions.len() as i32).await?; + for exception in exceptions.iter() { + let exception = exception.as_ref().unwrap(); + write_i32(stream, exception.id as i32).await?; + write_exception_string(stream, exception.message).await?; + write_i64(stream, exception.param[0] as i64).await?; + write_i64(stream, exception.param[1] as i64).await?; + write_i64(stream, exception.param[2] as i64).await?; + write_exception_string(stream, exception.file).await?; + write_i32(stream, exception.line as i32).await?; + write_i32(stream, exception.column as i32).await?; + write_exception_string(stream, exception.function).await?; + } + for sp in stack_pointers.iter() { + write_i32(stream, sp.stack_pointer as i32).await?; + write_i32(stream, sp.initial_backtrace_size as i32).await?; + write_i32(stream, sp.current_backtrace_size as i32).await?; + } write_i32(stream, backtrace.len() as i32).await?; - for &addr in backtrace { + for &(addr, sp) in backtrace { write_i32(stream, addr as i32).await?; + write_i32(stream, sp as i32).await?; } write_i8(stream, async_errors as i8).await?; }, None => { - error!("Uncaught kernel exception: {:?}", exception); + error!("Uncaught kernel exceptions: {:?}", exceptions); } } break; diff --git a/src/runtime/src/eh_artiq.rs b/src/runtime/src/eh_artiq.rs index 8aa1a6a..281a60c 100644 --- a/src/runtime/src/eh_artiq.rs +++ b/src/runtime/src/eh_artiq.rs @@ -16,7 +16,8 @@ use core::mem; use cslice::CSlice; use unwind as uw; use libc::{c_int, c_void, uintptr_t}; -use log::trace; +use log::{trace, error}; +use crate::kernel::KERNEL_IMAGE; use dwarf::eh::{self, EHAction, EHContext}; @@ -26,10 +27,12 @@ const EXCEPTION_CLASS: uw::_Unwind_Exception_Class = 0x4d_4c_42_53_41_52_54_51; #[cfg(target_arch = "arm")] const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 +// Note: CSlice within an exception may not be actual cslice, they may be strings that exist only +// in the host. If the length == usize:MAX, the pointer is actually a string key in the host. #[repr(C)] #[derive(Clone, Copy)] pub struct Exception<'a> { - pub name: CSlice<'a, u8>, + pub id: u32, pub file: CSlice<'a, u8>, pub line: u32, pub column: u32, @@ -42,25 +45,79 @@ fn str_err(_: core::str::Utf8Error) -> core::fmt::Error { core::fmt::Error } -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: {}", - core::str::from_utf8(self.name.as_ref()).map_err(str_err)?, - core::str::from_utf8(self.function.as_ref()).map_err(str_err)?, - core::str::from_utf8(self.file.as_ref()).map_err(str_err)?, - self.line, self.column, - core::str::from_utf8(self.message.as_ref()).map_err(str_err)?) +fn exception_str<'a>(s: &'a CSlice<'a, u8>) -> Result<&'a str, core::str::Utf8Error> { + if s.len() == usize::MAX { + Ok("") + } 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)?) + } +} + +const MAX_INFLIGHT_EXCEPTIONS: usize = 10; const MAX_BACKTRACE_SIZE: usize = 128; -#[repr(C)] -struct ExceptionInfo { - uw_exception: uw::_Unwind_Exception, - exception: Option>, - backtrace: [usize; MAX_BACKTRACE_SIZE], - backtrace_size: usize +#[derive(Debug, Default)] +pub struct StackPointerBacktrace { + pub stack_pointer: usize, + pub initial_backtrace_size: usize, + pub current_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>; MAX_INFLIGHT_EXCEPTIONS + 1], + exception_stack: [isize; MAX_INFLIGHT_EXCEPTIONS + 1], + // nested exceptions will share the backtrace buffer, treated as a tree + // backtrace contains a tuple of IP and SP + backtrace: [(usize, usize); MAX_BACKTRACE_SIZE], + backtrace_size: usize, + // stack pointers are stored to reconstruct backtrace for each exception + stack_pointers: [StackPointerBacktrace; MAX_INFLIGHT_EXCEPTIONS + 1], + // current allocated nested exceptions + exception_count: usize, +} + +static mut EXCEPTION_BUFFER: ExceptionBuffer = ExceptionBuffer { + uw_exceptions: [uw::_Unwind_Exception { + exception_class: EXCEPTION_CLASS, + exception_cleanup: cleanup, + private: [0; uw::unwinder_private_data_size], + }; MAX_INFLIGHT_EXCEPTIONS], + exceptions: [None; MAX_INFLIGHT_EXCEPTIONS + 1], + exception_stack: [-1; MAX_INFLIGHT_EXCEPTIONS + 1], + backtrace: [(0, 0); MAX_BACKTRACE_SIZE], + backtrace_size: 0, + stack_pointers: [StackPointerBacktrace { + stack_pointer: 0, + initial_backtrace_size: 0, + current_backtrace_size: 0 + }; MAX_INFLIGHT_EXCEPTIONS + 1], + exception_count: 0 +}; + +pub unsafe extern fn reset_exception_buffer() { + trace!("reset exception buffer"); + EXCEPTION_BUFFER.uw_exceptions = [uw::_Unwind_Exception { + exception_class: EXCEPTION_CLASS, + exception_cleanup: cleanup, + private: [0; uw::unwinder_private_data_size], + }; MAX_INFLIGHT_EXCEPTIONS]; + EXCEPTION_BUFFER.exceptions = [None; MAX_INFLIGHT_EXCEPTIONS + 1]; + EXCEPTION_BUFFER.exception_stack = [-1; MAX_INFLIGHT_EXCEPTIONS + 1]; + EXCEPTION_BUFFER.backtrace_size = 0; + EXCEPTION_BUFFER.exception_count = 0; } type _Unwind_Stop_Fn = extern "C" fn(version: c_int, @@ -81,8 +138,7 @@ extern { unsafe fn find_eh_action( context: *mut uw::_Unwind_Context, foreign_exception: bool, - name: *const u8, - len: usize, + id: u32, ) -> Result { let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8; let mut ip_before_instr: c_int = 0; @@ -95,7 +151,7 @@ unsafe fn find_eh_action( get_text_start: &|| uw::_Unwind_GetTextRelBase(context), get_data_start: &|| uw::_Unwind_GetDataRelBase(context), }; - eh::find_eh_action(lsda, &eh_context, foreign_exception, name, len) + eh::find_eh_action(lsda, &eh_context, foreign_exception, id) } pub unsafe fn artiq_personality(_state: uw::_Unwind_State, @@ -119,19 +175,15 @@ pub unsafe fn artiq_personality(_state: uw::_Unwind_State, let exception_class = (*exception_object).exception_class; let foreign_exception = exception_class != EXCEPTION_CLASS; assert!(!foreign_exception, "we do not expect foreign exceptions"); - let exception_info = &mut *(exception_object as *mut ExceptionInfo); + let index = EXCEPTION_BUFFER.exception_stack[EXCEPTION_BUFFER.exception_count - 1]; + assert!(index != -1); + let exception = EXCEPTION_BUFFER.exceptions[index as usize].as_ref().unwrap(); - let (name_ptr, len) = if foreign_exception || exception_info.exception.is_none() { - (core::ptr::null(), 0) - } else { - let name = (exception_info.exception.unwrap()).name; - (name.as_ptr(), name.len()) - }; - let eh_action = match find_eh_action(context, foreign_exception, name_ptr, len) { + let id = exception.id; + let eh_action = match find_eh_action(context, foreign_exception, id) { Ok(action) => action, Err(_) => return uw::_URC_FAILURE, }; - let exception = &exception_info.exception.unwrap(); match eh_action { EHAction::None => return continue_unwind(exception_object, context), EHAction::Cleanup(lpad) | @@ -165,99 +217,219 @@ pub unsafe fn artiq_personality(_state: uw::_Unwind_State, } } -extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code, - uw_exception: *mut uw::_Unwind_Exception) { - unsafe { - let exception_info = &mut *(uw_exception as *mut ExceptionInfo); +pub unsafe extern fn raise(exception: *const Exception) -> ! { + use cslice::AsCSlice; - exception_info.exception = None; + 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::>() * MAX_INFLIGHT_EXCEPTIONS) as isize { + let index = diff / (mem::size_of::>() as isize); + trace!("reraise at {}", index); + + let mut found = false; + for i in 0..=MAX_INFLIGHT_EXCEPTIONS + 1 { + if found { + if stack[i] == -1 { + stack[i - 1] = index; + assert!(i == count); + break; + } else { + stack[i - 1] = stack[i]; + } + } else { + if stack[i] == index { + found = true; + } + } + } + assert!(found); + let _result = _Unwind_ForcedUnwind(&mut EXCEPTION_BUFFER.uw_exceptions[stack[count - 1] as usize], + stop_fn, core::ptr::null_mut()); + } else { + if count < MAX_INFLIGHT_EXCEPTIONS { + trace!("raising exception at level {}", count); + let exception = &*exception; + for (i, slot) in EXCEPTION_BUFFER.exceptions.iter_mut().enumerate() { + // we should always be able to find a slot + if slot.is_none() { + *slot = Some( + *mem::transmute::<*const Exception, *const Exception<'static>> + (exception)); + EXCEPTION_BUFFER.exception_stack[count] = i as isize; + EXCEPTION_BUFFER.uw_exceptions[i].private = + [0; uw::unwinder_private_data_size]; + EXCEPTION_BUFFER.stack_pointers[i] = StackPointerBacktrace { + stack_pointer: 0, + initial_backtrace_size: EXCEPTION_BUFFER.backtrace_size, + current_backtrace_size: 0, + }; + EXCEPTION_BUFFER.exception_count += 1; + let _result = _Unwind_ForcedUnwind(&mut EXCEPTION_BUFFER.uw_exceptions[i], + stop_fn, core::ptr::null_mut()); + } + } + } else { + error!("too many nested exceptions"); + // TODO: better reporting? + let exception = Exception { + id: get_exception_id("runtimeerror"), + file: file!().as_c_slice(), + line: line!(), + column: column!(), + // https://github.com/rust-lang/rfcs/pull/1719 + function: "__artiq_raise".as_c_slice(), + message: "too many nested exceptions".as_c_slice(), + param: [0, 0, 0] + }; + EXCEPTION_BUFFER.exceptions[MAX_INFLIGHT_EXCEPTIONS] = Some(mem::transmute(exception)); + EXCEPTION_BUFFER.stack_pointers[MAX_INFLIGHT_EXCEPTIONS] = Default::default(); + EXCEPTION_BUFFER.exception_count += 1; + uncaught_exception() + } } + unreachable!(); } -static mut INFLIGHT: ExceptionInfo = ExceptionInfo { - uw_exception: uw::_Unwind_Exception { - exception_class: EXCEPTION_CLASS, - exception_cleanup: cleanup, - private: [0; uw::unwinder_private_data_size], - }, - exception: None, - backtrace: [0; MAX_BACKTRACE_SIZE], - backtrace_size: 0 -}; - -pub unsafe extern fn raise(exception: *const Exception) -> ! { - // FIXME: unsound transmute - // This would cause stack memory corruption. - trace!("raising exception"); - INFLIGHT.backtrace_size = 0; - INFLIGHT.exception = Some(mem::transmute::>(*exception)); - - let _result = _Unwind_ForcedUnwind(&mut INFLIGHT.uw_exception, - uncaught_exception, core::ptr::null_mut()); +pub unsafe extern fn resume() -> ! { + trace!("resume"); + assert!(EXCEPTION_BUFFER.exception_count != 0); + let i = EXCEPTION_BUFFER.exception_stack[EXCEPTION_BUFFER.exception_count - 1]; + assert!(i != -1); + let _result = _Unwind_ForcedUnwind(&mut EXCEPTION_BUFFER.uw_exceptions[i as usize], + stop_fn, core::ptr::null_mut()); unreachable!() } -extern fn uncaught_exception(_version: c_int, - actions: i32, - _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 { - trace!("uncaught exception"); - let exception_info = &mut *(uw_exception as *mut ExceptionInfo); +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 + }; +} - if exception_info.backtrace_size < exception_info.backtrace.len() { +extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code, + _uw_exception: *mut uw::_Unwind_Exception) { + unimplemented!() +} + +fn uncaught_exception() -> ! { + unsafe { + // dump way to reorder the stack + for i in 0..EXCEPTION_BUFFER.exception_count { + if EXCEPTION_BUFFER.exception_stack[i] != i as isize { + // find the correct index + let index = EXCEPTION_BUFFER.exception_stack + .iter() + .position(|v| *v == i as isize).unwrap(); + let a = EXCEPTION_BUFFER.exception_stack[index]; + let b = EXCEPTION_BUFFER.exception_stack[i]; + assert!(a != -1 && b != -1); + core::mem::swap(&mut EXCEPTION_BUFFER.exception_stack[index], + &mut EXCEPTION_BUFFER.exception_stack[i]); + core::mem::swap(&mut EXCEPTION_BUFFER.exceptions[a as usize], + &mut EXCEPTION_BUFFER.exceptions[b as usize]); + core::mem::swap(&mut EXCEPTION_BUFFER.stack_pointers[a as usize], + &mut EXCEPTION_BUFFER.stack_pointers[b as usize]); + } + } + } + unsafe { + crate::kernel::core1::terminate( + EXCEPTION_BUFFER.exceptions[..EXCEPTION_BUFFER.exception_count].as_ref(), + EXCEPTION_BUFFER.stack_pointers[..EXCEPTION_BUFFER.exception_count].as_ref(), + EXCEPTION_BUFFER.backtrace[..EXCEPTION_BUFFER.backtrace_size].as_mut()) + } +} + +// stop function which would be executed when we unwind each frame +extern fn stop_fn(_version: c_int, + actions: i32, + _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 load_addr = KERNEL_IMAGE.as_ref().unwrap().get_load_addr(); + let backtrace_size = EXCEPTION_BUFFER.backtrace_size; + // we try to remove unrelated backtrace here to save some buffer size + if backtrace_size < MAX_BACKTRACE_SIZE { let ip = uw::_Unwind_GetIP(context); - trace!("SP: {:X}, backtrace_size: {}", uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG), exception_info.backtrace_size); - exception_info.backtrace[exception_info.backtrace_size] = ip; - exception_info.backtrace_size += 1; + if ip >= load_addr { + let ip = ip - load_addr; + let sp = uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG); + trace!("SP: {:X}, backtrace_size: {}", sp, backtrace_size); + EXCEPTION_BUFFER.backtrace[backtrace_size] = (ip, sp); + EXCEPTION_BUFFER.backtrace_size += 1; + let last_index = EXCEPTION_BUFFER.exception_stack[EXCEPTION_BUFFER.exception_count - 1]; + assert!(last_index != -1); + let sp_info = &mut EXCEPTION_BUFFER.stack_pointers[last_index as usize]; + sp_info.stack_pointer = sp; + sp_info.current_backtrace_size = backtrace_size + 1; + } + } else { + trace!("backtrace size exceeded"); } if actions as u32 & uw::_US_END_OF_STACK as u32 != 0 { - crate::kernel::core1::terminate(exception_info.exception.as_ref().unwrap(), - exception_info.backtrace[..exception_info.backtrace_size].as_mut()) + uncaught_exception() } else { uw::_URC_NO_REASON } } } -pub unsafe extern fn reraise() -> ! { - use cslice::AsCSlice; +static EXCEPTION_ID_LOOKUP: [(&str, u32); 6] = [ + ("runtimeerror", 0), + ("RTIOUnderflow", 1), + ("RTIOOverflow", 2), + ("RTIODestinationUnreachable", 3), + ("DMAError", 4), + ("I2CError", 5), +]; - // Reraise is basically cxa_rethrow, which calls _Unwind_Resume_or_Rethrow, - // which for EHABI would always call _Unwind_RaiseException. - match INFLIGHT.exception { - Some(ex) => { - // we cannot call raise directly as that would corrupt the backtrace - INFLIGHT.exception = Some(mem::transmute::>(ex)); - let _result = _Unwind_ForcedUnwind(&mut INFLIGHT.uw_exception, - uncaught_exception, core::ptr::null_mut()); - unreachable!() - }, - None => { - raise(&Exception { - name: "0:artiq.coredevice.exceptions.RuntimeError".as_c_slice(), - file: file!().as_c_slice(), - line: line!(), - column: column!(), - // https://github.com/rust-lang/rfcs/pull/1719 - function: "__artiq_reraise".as_c_slice(), - message: "No active exception to reraise".as_c_slice(), - param: [0, 0, 0] - }) +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") } #[macro_export] macro_rules! artiq_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!(), diff --git a/src/runtime/src/kernel/api.rs b/src/runtime/src/kernel/api.rs index 4d1868e..a234c8b 100644 --- a/src/runtime/src/kernel/api.rs +++ b/src/runtime/src/kernel/api.rs @@ -204,7 +204,8 @@ pub fn resolve(required: &[u8]) -> Option { api!(_Unwind_Resume = unwind::_Unwind_Resume), api!(__artiq_personality = eh_artiq::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), // Implementations for LLVM math intrinsics api!(__powidf2), diff --git a/src/runtime/src/kernel/core1.rs b/src/runtime/src/kernel/core1.rs index 9084260..72725d1 100644 --- a/src/runtime/src/kernel/core1.rs +++ b/src/runtime/src/kernel/core1.rs @@ -182,6 +182,7 @@ pub extern "C" fn main_core1() { info!("kernel starting"); if let Some(kernel) = loaded_kernel.take() { unsafe { + eh_artiq::reset_exception_buffer(); KERNEL_CHANNEL_0TO1 = Some(core1_rx); KERNEL_CHANNEL_1TO0 = Some(core1_tx); KERNEL_IMAGE = &kernel as *const KernelImage; @@ -201,24 +202,13 @@ pub extern "C" fn main_core1() { } /// Called by eh_artiq -pub fn terminate(exception: &'static eh_artiq::Exception<'static>, backtrace: &'static mut [usize]) -> ! { - let load_addr = unsafe { - KERNEL_IMAGE.as_ref().unwrap().get_load_addr() - }; - let mut cursor = 0; - // The address in the backtrace is relocated, so we have to convert it back to the address in - // the original python script, and remove those Rust function backtrace. - for i in 0..backtrace.len() { - if backtrace[i] >= load_addr { - backtrace[cursor] = backtrace[i] - load_addr; - cursor += 1; - } - } - +pub fn terminate(exceptions: &'static [Option>], + stack_pointers: &'static [eh_artiq::StackPointerBacktrace], + backtrace: &'static mut [(usize, usize)]) -> ! { { let core1_tx = unsafe { KERNEL_CHANNEL_1TO0.as_mut().unwrap() }; let errors = unsafe { get_async_errors() }; - core1_tx.send(Message::KernelException(exception, &backtrace[..cursor], errors)); + core1_tx.send(Message::KernelException(exceptions, stack_pointers, backtrace, errors)); } loop {} } diff --git a/src/runtime/src/kernel/mod.rs b/src/runtime/src/kernel/mod.rs index 58bebf2..300acc5 100644 --- a/src/runtime/src/kernel/mod.rs +++ b/src/runtime/src/kernel/mod.rs @@ -15,13 +15,13 @@ mod cache; #[derive(Debug, Clone)] pub struct RPCException { - pub name: String, - pub message: String, + pub id: u32, + pub message: u32, pub param: [i64; 3], - pub file: String, + pub file: u32, pub line: i32, pub column: i32, - pub function: String + pub function: u32 } #[derive(Debug, Clone)] @@ -31,7 +31,10 @@ pub enum Message { LoadFailed, StartRequest, KernelFinished(u8), - KernelException(&'static eh_artiq::Exception<'static>, &'static [usize], u8), + KernelException(&'static [Option>], + &'static [eh_artiq::StackPointerBacktrace], + &'static [(usize, usize)], + u8), RpcSend { is_async: bool, data: Vec }, RpcRecvRequest(*mut ()), RpcRecvReply(Result), @@ -53,7 +56,7 @@ static CHANNEL_SEM: Semaphore = Semaphore::new(0, 1); static mut KERNEL_CHANNEL_0TO1: Option> = None; static mut KERNEL_CHANNEL_1TO0: Option> = None; -static mut KERNEL_IMAGE: *const core1::KernelImage = ptr::null(); +pub static mut KERNEL_IMAGE: *const core1::KernelImage = ptr::null(); static INIT_LOCK: Mutex<()> = Mutex::new(()); diff --git a/src/runtime/src/kernel/rpc.rs b/src/runtime/src/kernel/rpc.rs index de0dff8..496105a 100644 --- a/src/runtime/src/kernel/rpc.rs +++ b/src/runtime/src/kernel/rpc.rs @@ -1,7 +1,7 @@ //! Kernel-side RPC API use alloc::vec::Vec; -use cslice::{CSlice, AsCSlice}; +use cslice::CSlice; use crate::eh_artiq; use crate::rpc::send_args; @@ -36,12 +36,12 @@ pub extern fn rpc_recv(slot: *mut ()) -> usize { Message::RpcRecvReply(Ok(alloc_size)) => alloc_size, Message::RpcRecvReply(Err(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: CSlice::new(exception.file as *const u8, usize::MAX), line: exception.line as u32, column: exception.column as u32, - function: exception.function.as_bytes().as_c_slice(), - message: exception.message.as_bytes().as_c_slice(), + function: CSlice::new(exception.function as *const u8, usize::MAX), + message: CSlice::new(exception.message as *const u8, usize::MAX), param: exception.param }) }, -- 2.42.0