From 27d2390fedaea863cd201f1221bef18e800e7158 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 8 Aug 2015 16:01:08 +0300 Subject: [PATCH] Add zero-cost exception support to runtime and host. --- artiq/compiler/builtins.py | 1 + .../compiler/transforms/artiq_ir_generator.py | 5 +- artiq/coredevice/comm_generic.py | 118 ++++++++++++------ artiq/coredevice/exceptions.py | 8 ++ artiq/language/core.py | 27 +++- soc/runtime/artiq_personality.h | 1 + soc/runtime/kloader.c | 20 ++- soc/runtime/kloader.h | 6 +- soc/runtime/ksupport.c | 28 +++-- soc/runtime/messages.h | 9 +- soc/runtime/session.c | 82 ++++++++++-- 11 files changed, 238 insertions(+), 67 deletions(-) diff --git a/artiq/compiler/builtins.py b/artiq/compiler/builtins.py index 6b9feb9e2..86a356ef3 100644 --- a/artiq/compiler/builtins.py +++ b/artiq/compiler/builtins.py @@ -86,6 +86,7 @@ class TException(types.TMono): ("__file__", TStr()), ("__line__", TInt(types.TValue(32))), ("__col__", TInt(types.TValue(32))), + ("__func__", TStr()), ("__message__", TStr()), ("__param0__", TInt(types.TValue(64))), ("__param1__", TInt(types.TValue(64))), diff --git a/artiq/compiler/transforms/artiq_ir_generator.py b/artiq/compiler/transforms/artiq_ir_generator.py index 84bcbc6c5..8e51b8a3e 100644 --- a/artiq/compiler/transforms/artiq_ir_generator.py +++ b/artiq/compiler/transforms/artiq_ir_generator.py @@ -157,7 +157,7 @@ class ARTIQIRGenerator(algorithm.Visitor): def visit_function(self, node, is_lambda, is_internal): if is_lambda: - name = "lambda.{}.{}".format(node.loc.line(), node.loc.column()) + name = "lambda@{}:{}".format(node.loc.line(), node.loc.column()) typ = node.type.find() else: name = node.name @@ -471,9 +471,11 @@ class ARTIQIRGenerator(algorithm.Visitor): loc_file = ir.Constant(self.current_loc.source_buffer.name, builtins.TStr()) loc_line = ir.Constant(self.current_loc.line(), builtins.TInt(types.TValue(32))) loc_column = ir.Constant(self.current_loc.column(), builtins.TInt(types.TValue(32))) + loc_function = ir.Constant(".".join(self.name), builtins.TStr()) self.append(ir.SetAttr(exn, "__file__", loc_file)) self.append(ir.SetAttr(exn, "__line__", loc_line)) self.append(ir.SetAttr(exn, "__col__", loc_column)) + self.append(ir.SetAttr(exn, "__func__", loc_function)) if self.unwind_target is not None: self.append(ir.Raise(exn, self.unwind_target)) @@ -1237,6 +1239,7 @@ class ARTIQIRGenerator(algorithm.Visitor): ir.Constant("", builtins.TStr()), # file ir.Constant(0, builtins.TInt(types.TValue(32))), # line ir.Constant(0, builtins.TInt(types.TValue(32))), # column + ir.Constant("", builtins.TStr()), # function ] if message is None: diff --git a/artiq/coredevice/comm_generic.py b/artiq/coredevice/comm_generic.py index b386a7430..df0cbb4cd 100644 --- a/artiq/coredevice/comm_generic.py +++ b/artiq/coredevice/comm_generic.py @@ -1,5 +1,6 @@ import struct import logging +import traceback from enum import Enum from fractions import Fraction @@ -18,11 +19,12 @@ class _H2DMsgType(Enum): RUN_KERNEL = 5 RPC_REPLY = 6 + RPC_EXCEPTION = 7 - FLASH_READ_REQUEST = 7 - FLASH_WRITE_REQUEST = 8 - FLASH_ERASE_REQUEST = 9 - FLASH_REMOVE_REQUEST = 10 + FLASH_READ_REQUEST = 8 + FLASH_WRITE_REQUEST = 9 + FLASH_ERASE_REQUEST = 10 + FLASH_REMOVE_REQUEST = 11 class _D2HMsgType(Enum): @@ -223,16 +225,12 @@ class CommGeneric: self._read_empty(_D2HMsgType.CLOCK_SWITCH_COMPLETED) - def load(self, kernel_library): - self._write_header(_H2DMsgType.LOAD_LIBRARY) - self._write_chunk(kernel_library) - self._write_flush() + def get_log(self): + self._write_empty(_H2DMsgType.LOG_REQUEST) - self._read_empty(_D2HMsgType.LOAD_COMPLETED) - - def run(self): - self._write_empty(_H2DMsgType.RUN_KERNEL) - logger.debug("running kernel") + self._read_header() + self._read_expect(_D2HMsgType.LOG_REPLY) + return self._read_chunk(self._read_length).decode('utf-8') def flash_storage_read(self, key): self._write_header(_H2DMsgType.FLASH_READ_REQUEST) @@ -267,7 +265,18 @@ class CommGeneric: self._read_empty(_D2HMsgType.FLASH_OK_REPLY) - def _receive_rpc_value(self, tag): + def load(self, kernel_library): + self._write_header(_H2DMsgType.LOAD_LIBRARY) + self._write_chunk(kernel_library) + self._write_flush() + + self._read_empty(_D2HMsgType.LOAD_COMPLETED) + + def run(self): + self._write_empty(_H2DMsgType.RUN_KERNEL) + logger.debug("running kernel") + + def _receive_rpc_value(self, tag, rpc_map): if tag == "n": return None elif tag == "b": @@ -286,37 +295,83 @@ class CommGeneric: elt_tag = chr(self._read_int8()) length = self._read_int32() return [self._receive_rpc_value(elt_tag) for _ in range(length)] + elif tag == "o": + return rpc_map[self._read_int32()] else: raise IOError("Unknown RPC value tag: {}", tag) - def _receive_rpc_values(self): + def _receive_rpc_values(self, rpc_map): result = [] while True: tag = chr(self._read_int8()) if tag == "\x00": return result else: - result.append(self._receive_rpc_value(tag)) + result.append(self._receive_rpc_value(tag, rpc_map)) def _serve_rpc(self, rpc_map): service = self._read_int32() - args = self._receive_rpc_values() + args = self._receive_rpc_values(rpc_map) logger.debug("rpc service: %d %r", service, args) - eid, result = rpc_wrapper.run_rpc(rpc_map[rpc_num], args) - logger.debug("rpc service: %d %r == %r (eid %d)", service, args, - result, eid) + try: + result = rpc_map[rpc_num](args) + if not isinstance(result, int) or not (-2**31 < result < 2**31-1): + raise ValueError("An RPC must return an int(width=32)") + except ARTIQException as exn: + logger.debug("rpc service: %d %r ! %r", service, args, exn) - self._write_header(_H2DMsgType.RPC_REPLY) - self._write_int32(eid) - self._write_int32(result) - self._write_flush() + self._write_header(_H2DMsgType.RPC_EXCEPTION) + self._write_string(exn.name) + self._write_string(exn.message) + for index in range(3): + self._write_int64(exn.param[index]) + + self._write_string(exn.filename) + self._write_int32(exn.line) + self._write_int32(exn.column) + self._write_string(exn.function) + + self._write_flush() + except Exception as exn: + logger.debug("rpc service: %d %r ! %r", service, args, exn) + + self._write_header(_H2DMsgType.RPC_EXCEPTION) + self._write_string(type(exn).__name__) + self._write_string(str(exn)) + for index in range(3): + self._write_int64(0) + + ((filename, line, function, _), ) = traceback.extract_tb(exn.__traceback__) + self._write_string(filename) + self._write_int32(line) + self._write_int32(-1) # column not known + self._write_string(function) + + self._write_flush() + else: + logger.debug("rpc service: %d %r == %r", service, args, result) + + self._write_header(_H2DMsgType.RPC_REPLY) + self._write_int32(result) + self._write_flush() def _serve_exception(self): - eid = self._read_int32() - params = [self._read_int64() for _ in range(3)] - rpc_wrapper.filter_rpc_exception(eid) - raise exception(self.core, *params) + name = self._read_string() + message = self._read_string() + params = [self._read_int64() for _ in range(3)] + + filename = self._read_string() + line = self._read_int32() + column = self._read_int32() + function = self._read_string() + + backtrace = [self._read_int32() for _ in range(self._read_int32())] + # we don't have debug information yet. + # print("exception backtrace:", [hex(x) for x in backtrace]) + + raise core_language.ARTIQException(name, message, params, + filename, line, column, function) def serve(self, rpc_map): while True: @@ -328,10 +383,3 @@ class CommGeneric: else: self._read_expect(_D2HMsgType.KERNEL_FINISHED) return - - def get_log(self): - self._write_empty(_H2DMsgType.LOG_REQUEST) - - self._read_header() - self._read_expect(_D2HMsgType.LOG_REPLY) - return self._read_chunk(self._read_length).decode('utf-8') diff --git a/artiq/coredevice/exceptions.py b/artiq/coredevice/exceptions.py index 05d475908..d0f54f2fa 100644 --- a/artiq/coredevice/exceptions.py +++ b/artiq/coredevice/exceptions.py @@ -1,5 +1,13 @@ from artiq.language.core import ARTIQException +class ZeroDivisionError(ARTIQException): + """Python's :class:`ZeroDivisionError`, mirrored in ARTIQ.""" + +class ValueError(ARTIQException): + """Python's :class:`ValueError`, mirrored in ARTIQ.""" + +class IndexError(ARTIQException): + """Python's :class:`IndexError`, mirrored in ARTIQ.""" class InternalError(ARTIQException): """Raised when the runtime encounters an internal error condition.""" diff --git a/artiq/language/core.py b/artiq/language/core.py index d8e24a552..a7dfecc5d 100644 --- a/artiq/language/core.py +++ b/artiq/language/core.py @@ -2,6 +2,7 @@ Core ARTIQ extensions to the Python language. """ +import linecache from collections import namedtuple from functools import wraps @@ -278,7 +279,8 @@ class ARTIQException(Exception): """Base class for exceptions raised or passed through the core device.""" # Try and create an instance of the specific class, if one exists. - def __new__(cls, name, message, params): + def __new__(cls, name, message, + params, filename, line, column, function): def find_subclass(cls): if cls.__name__ == name: return cls @@ -293,15 +295,30 @@ class ARTIQException(Exception): more_specific_cls = cls exn = Exception.__new__(more_specific_cls) - exn.__init__(name, message, params) + exn.__init__(name, message, params, + filename, line, column, function) return exn - def __init__(self, name, message, params): + def __init__(self, name, message, params, + filename, line, column, function): Exception.__init__(self, name, message, *params) self.name, self.message, self.params = name, message, params + self.filename, self.line, self.column = filename, line, column + self.function = function def __str__(self): + lines = [] + if type(self).__name__ == self.name: - return self.message.format(*self.params) + lines.append(self.message.format(*self.params)) else: - return "({}) {}".format(self.name, self.message.format(*self.params)) + lines.append("({}) {}".format(self.name, self.message.format(*self.params))) + + lines.append("Core Device Traceback (most recent call last):") + lines.append(" File \"{file}\", line {line}, column {column}, in {function}". + format(file=self.filename, line=self.line, column=self.column + 1, + function=self.function)) + line = linecache.getline(self.filename, self.line) + lines.append(" {}".format(line.strip() if line else "")) + + return "\n".join(lines) diff --git a/soc/runtime/artiq_personality.h b/soc/runtime/artiq_personality.h index 1952e7269..11a10b2b9 100644 --- a/soc/runtime/artiq_personality.h +++ b/soc/runtime/artiq_personality.h @@ -12,6 +12,7 @@ struct artiq_exception { const char *file; int32_t line; int32_t column; + const char *function; const char *message; int64_t param[3]; }; diff --git a/soc/runtime/kloader.c b/soc/runtime/kloader.c index 5d1cb0a32..14b4abb7b 100644 --- a/soc/runtime/kloader.c +++ b/soc/runtime/kloader.c @@ -30,7 +30,7 @@ void kloader_start_bridge() start_kernel_cpu(NULL); } -static int load_or_start_kernel(void *library, int run_kernel) +static int load_or_start_kernel(const void *library, int run_kernel) { static struct dyld_info library_info; struct msg_load_request request = { @@ -56,7 +56,7 @@ static int load_or_start_kernel(void *library, int run_kernel) return 1; } -int kloader_load_library(void *library) +int kloader_load_library(const void *library) { if(!kernel_cpu_reset_read()) { log("BUG: attempted to load kernel library while kernel CPU is running"); @@ -66,6 +66,22 @@ int kloader_load_library(void *library) return load_or_start_kernel(library, 0); } +void kloader_filter_backtrace(struct artiq_backtrace_item *backtrace, + size_t *backtrace_size) { + struct artiq_backtrace_item *cursor = backtrace; + + // Remove all backtrace items belonging to ksupport and subtract + // shared object base from the addresses. + for(int i = 0; i < *backtrace_size; i++) { + if(backtrace[i].function > KERNELCPU_PAYLOAD_ADDRESS) { + backtrace[i].function -= KERNELCPU_PAYLOAD_ADDRESS; + *cursor++ = backtrace[i]; + } + } + + *backtrace_size = cursor - backtrace; +} + void kloader_start_kernel() { load_or_start_kernel(NULL, 1); diff --git a/soc/runtime/kloader.h b/soc/runtime/kloader.h index 834fd8d3d..807617e25 100644 --- a/soc/runtime/kloader.h +++ b/soc/runtime/kloader.h @@ -1,6 +1,8 @@ #ifndef __KLOADER_H #define __KLOADER_H +#include "artiq_personality.h" + #define KERNELCPU_EXEC_ADDRESS 0x40400000 #define KERNELCPU_PAYLOAD_ADDRESS 0x40420000 #define KERNELCPU_LAST_ADDRESS (0x4fffffff - 1024*1024) @@ -8,7 +10,9 @@ extern long long int now; -int kloader_load_library(void *code); +int kloader_load_library(const void *code); +void kloader_filter_backtrace(struct artiq_backtrace_item *backtrace, + size_t *backtrace_size); void kloader_start_bridge(void); int kloader_start_idle_kernel(void); diff --git a/soc/runtime/ksupport.c b/soc/runtime/ksupport.c index da02bc4a0..b2082beef 100644 --- a/soc/runtime/ksupport.c +++ b/soc/runtime/ksupport.c @@ -246,7 +246,8 @@ long long int now_init(void) reply = mailbox_wait_and_receive(); if(reply->type != MESSAGE_TYPE_NOW_INIT_REPLY) { - log("Malformed MESSAGE_TYPE_NOW_INIT_REQUEST reply type"); + log("Malformed MESSAGE_TYPE_NOW_INIT_REQUEST reply type %d", + reply->type); while(1); } now = reply->now; @@ -281,7 +282,8 @@ int watchdog_set(int ms) reply = mailbox_wait_and_receive(); if(reply->type != MESSAGE_TYPE_WATCHDOG_SET_REPLY) { - log("Malformed MESSAGE_TYPE_WATCHDOG_SET_REQUEST reply type"); + log("Malformed MESSAGE_TYPE_WATCHDOG_SET_REQUEST reply type %d", + reply->type); while(1); } id = reply->id; @@ -302,7 +304,7 @@ void watchdog_clear(int id) int rpc(int rpc_num, ...) { struct msg_rpc_request request; - struct msg_rpc_reply *reply; + struct msg_base *reply; request.type = MESSAGE_TYPE_RPC_REQUEST; request.rpc_num = rpc_num; @@ -311,20 +313,20 @@ int rpc(int rpc_num, ...) va_end(request.args); reply = mailbox_wait_and_receive(); - if(reply->type != MESSAGE_TYPE_RPC_REPLY) { - log("Malformed MESSAGE_TYPE_RPC_REPLY reply type"); - while(1); - } - - if(reply->exception != NULL) { + if(reply->type == MESSAGE_TYPE_RPC_REPLY) { + int result = ((struct msg_rpc_reply *)reply)->result; + mailbox_acknowledge(); + return result; + } else if(reply->type == MESSAGE_TYPE_RPC_EXCEPTION) { struct artiq_exception exception; - memcpy(&exception, reply->exception, sizeof(exception)); + memcpy(&exception, ((struct msg_rpc_exception *)reply)->exception, + sizeof(struct artiq_exception)); mailbox_acknowledge(); __artiq_raise(&exception); } else { - int retval = reply->retval; - mailbox_acknowledge(); - return retval; + log("Malformed MESSAGE_TYPE_RPC_REQUEST reply type %d", + reply->type); + while(1); } } diff --git a/soc/runtime/messages.h b/soc/runtime/messages.h index cb0c9c20b..651c7a5ef 100644 --- a/soc/runtime/messages.h +++ b/soc/runtime/messages.h @@ -16,6 +16,7 @@ enum { MESSAGE_TYPE_WATCHDOG_CLEAR, MESSAGE_TYPE_RPC_REQUEST, MESSAGE_TYPE_RPC_REPLY, + MESSAGE_TYPE_RPC_EXCEPTION, MESSAGE_TYPE_LOG, MESSAGE_TYPE_BRG_READY, @@ -37,7 +38,7 @@ struct msg_base { /* kernel messages */ struct msg_load_request { - void *library; + const void *library; struct dyld_info *library_info; int run_kernel; }; @@ -86,9 +87,13 @@ struct msg_rpc_request { }; struct msg_rpc_reply { + int type; + int result; +}; + +struct msg_rpc_exception { int type; struct artiq_exception *exception; - int retval; }; struct msg_log { diff --git a/soc/runtime/session.c b/soc/runtime/session.c index 1ad70c272..b571c5873 100644 --- a/soc/runtime/session.c +++ b/soc/runtime/session.c @@ -128,6 +128,13 @@ static int32_t in_packet_int32() return result; } +static int64_t in_packet_int64() +{ + int64_t result; + in_packet_chunk(&result, sizeof(result)); + return result; +} + static const void *in_packet_bytes(int *length) { *length = in_packet_int32(); @@ -310,6 +317,7 @@ enum { REMOTEMSG_TYPE_RUN_KERNEL, REMOTEMSG_TYPE_RPC_REPLY, + REMOTEMSG_TYPE_RPC_EXCEPTION, REMOTEMSG_TYPE_FLASH_READ_REQUEST, REMOTEMSG_TYPE_FLASH_WRITE_REQUEST, @@ -452,15 +460,44 @@ static int process_input(void) case REMOTEMSG_TYPE_RPC_REPLY: { struct msg_rpc_reply reply; + int result = in_packet_int32(); + if(user_kernel_state != USER_KERNEL_WAIT_RPC) { log("Unsolicited RPC reply"); return 0; // restart session } reply.type = MESSAGE_TYPE_RPC_REPLY; - // FIXME memcpy(&reply.eid, &buffer_in[9], 4); - // memcpy(&reply.retval, &buffer_in[13], 4); + reply.result = result; mailbox_send_and_wait(&reply); + + user_kernel_state = USER_KERNEL_RUNNING; + break; + } + + case REMOTEMSG_TYPE_RPC_EXCEPTION: { + struct msg_rpc_exception reply; + + struct artiq_exception exception; + exception.name = in_packet_string(); + exception.message = in_packet_string(); + exception.param[0] = in_packet_int64(); + exception.param[1] = in_packet_int64(); + exception.param[2] = in_packet_int64(); + exception.file = in_packet_string(); + exception.line = in_packet_int32(); + exception.column = in_packet_int32(); + exception.function = in_packet_string(); + + if(user_kernel_state != USER_KERNEL_WAIT_RPC) { + log("Unsolicited RPC exception reply"); + return 0; // restart session + } + + reply.type = MESSAGE_TYPE_RPC_EXCEPTION; + reply.exception = &exception; + mailbox_send_and_wait(&reply); + user_kernel_state = USER_KERNEL_RUNNING; break; } @@ -509,8 +546,6 @@ static int send_rpc_value(const char **tag, void *value) break; case 'l': { // list(elt='a) - size = sizeof(void*); - struct { uint32_t length; void *elements; } *list = value; void *element = list->elements; @@ -522,6 +557,18 @@ static int send_rpc_value(const char **tag, void *value) element = (void*)((intptr_t)element + element_size); } *tag = tag_copy; + + size = sizeof(list); + break; + } + + case 'o': { // host object + struct { uint32_t id; } *object = value; + + if(!out_packet_int32(object->id)) + return -1; + + size = sizeof(object); break; } @@ -575,10 +622,29 @@ static int process_kmsg(struct msg_base *umsg) case MESSAGE_TYPE_EXCEPTION: { struct msg_exception *msg = (struct msg_exception *)umsg; - out_packet_empty(REMOTEMSG_TYPE_KERNEL_EXCEPTION); - // memcpy(&buffer_out[9], &msg->eid, 4); - // memcpy(&buffer_out[13], msg->eparams, 3*8); - // submit_output(9+4+3*8); + out_packet_start(REMOTEMSG_TYPE_KERNEL_EXCEPTION); + + out_packet_string(msg->exception->name); + out_packet_string(msg->exception->message); + out_packet_int64(msg->exception->param[0]); + out_packet_int64(msg->exception->param[1]); + out_packet_int64(msg->exception->param[2]); + + out_packet_string(msg->exception->file); + out_packet_int32(msg->exception->line); + out_packet_int32(msg->exception->column); + out_packet_string(msg->exception->function); + + kloader_filter_backtrace(msg->backtrace, + &msg->backtrace_size); + + out_packet_int32(msg->backtrace_size); + for(int i = 0; i < msg->backtrace_size; i++) { + struct artiq_backtrace_item *item = &msg->backtrace[i]; + out_packet_int32(item->function + item->offset); + } + + out_packet_finish(); kloader_stop(); user_kernel_state = USER_KERNEL_LOADED;