diff --git a/src/libksupport/src/eh_artiq.rs b/src/libksupport/src/eh_artiq.rs index 6f159ac..b42b880 100644 --- a/src/libksupport/src/eh_artiq.rs +++ b/src/libksupport/src/eh_artiq.rs @@ -14,8 +14,10 @@ use core::mem; +use core_io::Error as ReadError; use cslice::CSlice; use dwarf::eh::{self, EHAction, EHContext}; +use io::{Cursor, ProtoRead}; use libc::{c_int, c_void, uintptr_t}; use log::{error, trace}; use unwind as uw; @@ -295,6 +297,60 @@ pub unsafe extern "C" fn raise(exception: *const Exception) -> ! { unreachable!(); } +fn read_exception_string<'a>(reader: &mut Cursor<&[u8]>) -> Result, ReadError> { + let len = reader.read_u32()? as usize; + if len == usize::MAX { + let data = reader.read_u32()?; + Ok(unsafe { CSlice::new(data as *const u8, len) }) + } else { + let pos = reader.position(); + let slice = unsafe { + let ptr = reader.get_ref().as_ptr().offset(pos as isize); + CSlice::new(ptr, len) + }; + reader.set_position(pos + len); + Ok(slice) + } +} + +fn read_exception(raw_exception: &[u8]) -> Result { + let mut reader = Cursor::new(raw_exception); + + let mut byte = reader.read_u8()?; + // to sync + while byte != 0x5a { + byte = reader.read_u8()?; + } + // skip sync bytes, 0x09 indicates exception + while byte != 0x09 { + byte = reader.read_u8()?; + } + let _len = reader.read_u32()?; + // ignore the remaining exceptions, stack traces etc. - unwinding from another device would be unwise anyway + Ok(Exception { + id: reader.read_u32()?, + message: read_exception_string(&mut reader)?, + param: [ + reader.read_u64()? as i64, + reader.read_u64()? as i64, + reader.read_u64()? as i64, + ], + file: read_exception_string(&mut reader)?, + line: reader.read_u32()?, + column: reader.read_u32()?, + function: read_exception_string(&mut reader)?, + }) +} + +pub fn raise_raw(raw_exception: &[u8]) -> ! { + use crate::artiq_raise; + if let Ok(exception) = read_exception(raw_exception) { + unsafe { raise(&exception) }; + } else { + artiq_raise!("SubkernelError", "Error passing exception"); + } +} + pub unsafe extern "C" fn resume() -> ! { trace!("resume"); assert!(EXCEPTION_BUFFER.exception_count != 0); diff --git a/src/libksupport/src/kernel/mod.rs b/src/libksupport/src/kernel/mod.rs index b235cdd..3f90e95 100644 --- a/src/libksupport/src/kernel/mod.rs +++ b/src/libksupport/src/kernel/mod.rs @@ -24,6 +24,7 @@ pub enum SubkernelStatus { IncorrectState, CommLost, OtherError, + Exception(Vec), } #[derive(Debug, Clone)] diff --git a/src/libksupport/src/kernel/subkernel.rs b/src/libksupport/src/kernel/subkernel.rs index 511e92b..8ab2166 100644 --- a/src/libksupport/src/kernel/subkernel.rs +++ b/src/libksupport/src/kernel/subkernel.rs @@ -3,7 +3,7 @@ use alloc::vec::Vec; use cslice::CSlice; use super::{Message, SubkernelStatus, KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0}; -use crate::{artiq_raise, rpc::send_args}; +use crate::{artiq_raise, eh_artiq, rpc::send_args}; pub extern "C" fn load_run(id: u32, destination: u8, run: bool) { unsafe { @@ -51,6 +51,12 @@ pub extern "C" fn await_finish(id: u32, timeout: i64) { Message::SubkernelAwaitFinishReply { status: SubkernelStatus::OtherError, } => artiq_raise!("SubkernelError", "An error occurred during subkernel operation"), + Message::SubkernelAwaitFinishReply { + status: SubkernelStatus::Exception(raw_exception), + } => { + // reconstruct the exception here and raise it + eh_artiq::raise_raw(&raw_exception) + } _ => panic!("expected SubkernelAwaitFinishReply after SubkernelAwaitFinishRequest"), } } @@ -116,6 +122,13 @@ pub extern "C" fn await_message(id: i32, timeout: i64, tags: &CSlice, min: u status: SubkernelStatus::OtherError, .. } => artiq_raise!("SubkernelError", "An error occurred during subkernel operation"), + Message::SubkernelMsgRecvReply { + status: SubkernelStatus::Exception(raw_exception), + .. + } => { + // reconstruct the raw exception here + eh_artiq::raise_raw(&raw_exception); + } _ => panic!("expected SubkernelMsgRecvReply after SubkernelMsgRecvRequest"), } // RpcRecvRequest should be called after this to receive message data diff --git a/src/runtime/src/comms.rs b/src/runtime/src/comms.rs index 3ee3201..1453e3a 100644 --- a/src/runtime/src/comms.rs +++ b/src/runtime/src/comms.rs @@ -423,19 +423,11 @@ async fn handle_run_kernel( kernel::Message::SubkernelAwaitFinishRequest { id, timeout } => { let res = subkernel::await_finish(aux_mutex, routing_table, timer, id, timeout).await; let status = match res { - Ok(ref res) => { + Ok(res) => { if res.status == subkernel::FinishStatus::CommLost { kernel::SubkernelStatus::CommLost - } else if let Some(exception) = &res.exception { - error!("Exception in subkernel"); - match stream { - None => (), - Some(stream) => { - write_chunk(stream, exception).await?; - } - } - // will not be called after exception is served - kernel::SubkernelStatus::OtherError + } else if let Some(exception) = res.exception { + kernel::SubkernelStatus::Exception(exception) } else { kernel::SubkernelStatus::NoError } @@ -475,18 +467,11 @@ async fn handle_run_kernel( Err(SubkernelError::IncorrectState) => (kernel::SubkernelStatus::IncorrectState, 0), Err(SubkernelError::CommLost) => (kernel::SubkernelStatus::CommLost, 0), Err(SubkernelError::SubkernelException) => { - error!("Exception in subkernel"); // just retrieve the exception let status = subkernel::await_finish(aux_mutex, routing_table, timer, id as u32, timeout) .await .unwrap(); - match stream { - None => (), - Some(stream) => { - write_chunk(stream, &status.exception.unwrap()).await?; - } - } - (kernel::SubkernelStatus::OtherError, 0) + (kernel::SubkernelStatus::Exception(status.exception.unwrap()), 0) } Err(_) => (kernel::SubkernelStatus::OtherError, 0), };