Add zero-cost exception support to runtime and host.

This commit is contained in:
whitequark 2015-08-08 16:01:08 +03:00
parent 1d61e446cb
commit 27d2390fed
11 changed files with 238 additions and 67 deletions

View File

@ -86,6 +86,7 @@ class TException(types.TMono):
("__file__", TStr()), ("__file__", TStr()),
("__line__", TInt(types.TValue(32))), ("__line__", TInt(types.TValue(32))),
("__col__", TInt(types.TValue(32))), ("__col__", TInt(types.TValue(32))),
("__func__", TStr()),
("__message__", TStr()), ("__message__", TStr()),
("__param0__", TInt(types.TValue(64))), ("__param0__", TInt(types.TValue(64))),
("__param1__", TInt(types.TValue(64))), ("__param1__", TInt(types.TValue(64))),

View File

@ -157,7 +157,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
def visit_function(self, node, is_lambda, is_internal): def visit_function(self, node, is_lambda, is_internal):
if is_lambda: 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() typ = node.type.find()
else: else:
name = node.name name = node.name
@ -471,9 +471,11 @@ class ARTIQIRGenerator(algorithm.Visitor):
loc_file = ir.Constant(self.current_loc.source_buffer.name, builtins.TStr()) 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_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_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, "__file__", loc_file))
self.append(ir.SetAttr(exn, "__line__", loc_line)) self.append(ir.SetAttr(exn, "__line__", loc_line))
self.append(ir.SetAttr(exn, "__col__", loc_column)) self.append(ir.SetAttr(exn, "__col__", loc_column))
self.append(ir.SetAttr(exn, "__func__", loc_function))
if self.unwind_target is not None: if self.unwind_target is not None:
self.append(ir.Raise(exn, self.unwind_target)) self.append(ir.Raise(exn, self.unwind_target))
@ -1237,6 +1239,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
ir.Constant("<not thrown>", builtins.TStr()), # file ir.Constant("<not thrown>", builtins.TStr()), # file
ir.Constant(0, builtins.TInt(types.TValue(32))), # line ir.Constant(0, builtins.TInt(types.TValue(32))), # line
ir.Constant(0, builtins.TInt(types.TValue(32))), # column ir.Constant(0, builtins.TInt(types.TValue(32))), # column
ir.Constant("<not thrown>", builtins.TStr()), # function
] ]
if message is None: if message is None:

View File

@ -1,5 +1,6 @@
import struct import struct
import logging import logging
import traceback
from enum import Enum from enum import Enum
from fractions import Fraction from fractions import Fraction
@ -18,11 +19,12 @@ class _H2DMsgType(Enum):
RUN_KERNEL = 5 RUN_KERNEL = 5
RPC_REPLY = 6 RPC_REPLY = 6
RPC_EXCEPTION = 7
FLASH_READ_REQUEST = 7 FLASH_READ_REQUEST = 8
FLASH_WRITE_REQUEST = 8 FLASH_WRITE_REQUEST = 9
FLASH_ERASE_REQUEST = 9 FLASH_ERASE_REQUEST = 10
FLASH_REMOVE_REQUEST = 10 FLASH_REMOVE_REQUEST = 11
class _D2HMsgType(Enum): class _D2HMsgType(Enum):
@ -223,16 +225,12 @@ class CommGeneric:
self._read_empty(_D2HMsgType.CLOCK_SWITCH_COMPLETED) self._read_empty(_D2HMsgType.CLOCK_SWITCH_COMPLETED)
def load(self, kernel_library): def get_log(self):
self._write_header(_H2DMsgType.LOAD_LIBRARY) self._write_empty(_H2DMsgType.LOG_REQUEST)
self._write_chunk(kernel_library)
self._write_flush()
self._read_empty(_D2HMsgType.LOAD_COMPLETED) self._read_header()
self._read_expect(_D2HMsgType.LOG_REPLY)
def run(self): return self._read_chunk(self._read_length).decode('utf-8')
self._write_empty(_H2DMsgType.RUN_KERNEL)
logger.debug("running kernel")
def flash_storage_read(self, key): def flash_storage_read(self, key):
self._write_header(_H2DMsgType.FLASH_READ_REQUEST) self._write_header(_H2DMsgType.FLASH_READ_REQUEST)
@ -267,7 +265,18 @@ class CommGeneric:
self._read_empty(_D2HMsgType.FLASH_OK_REPLY) 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": if tag == "n":
return None return None
elif tag == "b": elif tag == "b":
@ -286,37 +295,83 @@ class CommGeneric:
elt_tag = chr(self._read_int8()) elt_tag = chr(self._read_int8())
length = self._read_int32() length = self._read_int32()
return [self._receive_rpc_value(elt_tag) for _ in range(length)] return [self._receive_rpc_value(elt_tag) for _ in range(length)]
elif tag == "o":
return rpc_map[self._read_int32()]
else: else:
raise IOError("Unknown RPC value tag: {}", tag) raise IOError("Unknown RPC value tag: {}", tag)
def _receive_rpc_values(self): def _receive_rpc_values(self, rpc_map):
result = [] result = []
while True: while True:
tag = chr(self._read_int8()) tag = chr(self._read_int8())
if tag == "\x00": if tag == "\x00":
return result return result
else: else:
result.append(self._receive_rpc_value(tag)) result.append(self._receive_rpc_value(tag, rpc_map))
def _serve_rpc(self, rpc_map): def _serve_rpc(self, rpc_map):
service = self._read_int32() service = self._read_int32()
args = self._receive_rpc_values() args = self._receive_rpc_values(rpc_map)
logger.debug("rpc service: %d %r", service, args) logger.debug("rpc service: %d %r", service, args)
eid, result = rpc_wrapper.run_rpc(rpc_map[rpc_num], args) try:
logger.debug("rpc service: %d %r == %r (eid %d)", service, args, result = rpc_map[rpc_num](args)
result, eid) 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_header(_H2DMsgType.RPC_EXCEPTION)
self._write_int32(eid) self._write_string(exn.name)
self._write_int32(result) self._write_string(exn.message)
self._write_flush() 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): def _serve_exception(self):
eid = self._read_int32() name = self._read_string()
params = [self._read_int64() for _ in range(3)] message = self._read_string()
rpc_wrapper.filter_rpc_exception(eid) params = [self._read_int64() for _ in range(3)]
raise exception(self.core, *params)
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): def serve(self, rpc_map):
while True: while True:
@ -328,10 +383,3 @@ class CommGeneric:
else: else:
self._read_expect(_D2HMsgType.KERNEL_FINISHED) self._read_expect(_D2HMsgType.KERNEL_FINISHED)
return 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')

View File

@ -1,5 +1,13 @@
from artiq.language.core import ARTIQException 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): class InternalError(ARTIQException):
"""Raised when the runtime encounters an internal error condition.""" """Raised when the runtime encounters an internal error condition."""

View File

@ -2,6 +2,7 @@
Core ARTIQ extensions to the Python language. Core ARTIQ extensions to the Python language.
""" """
import linecache
from collections import namedtuple from collections import namedtuple
from functools import wraps from functools import wraps
@ -278,7 +279,8 @@ class ARTIQException(Exception):
"""Base class for exceptions raised or passed through the core device.""" """Base class for exceptions raised or passed through the core device."""
# Try and create an instance of the specific class, if one exists. # 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): def find_subclass(cls):
if cls.__name__ == name: if cls.__name__ == name:
return cls return cls
@ -293,15 +295,30 @@ class ARTIQException(Exception):
more_specific_cls = cls more_specific_cls = cls
exn = Exception.__new__(more_specific_cls) exn = Exception.__new__(more_specific_cls)
exn.__init__(name, message, params) exn.__init__(name, message, params,
filename, line, column, function)
return exn return exn
def __init__(self, name, message, params): def __init__(self, name, message, params,
filename, line, column, function):
Exception.__init__(self, name, message, *params) Exception.__init__(self, name, message, *params)
self.name, self.message, self.params = 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): def __str__(self):
lines = []
if type(self).__name__ == self.name: if type(self).__name__ == self.name:
return self.message.format(*self.params) lines.append(self.message.format(*self.params))
else: 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 "<unknown>"))
return "\n".join(lines)

View File

@ -12,6 +12,7 @@ struct artiq_exception {
const char *file; const char *file;
int32_t line; int32_t line;
int32_t column; int32_t column;
const char *function;
const char *message; const char *message;
int64_t param[3]; int64_t param[3];
}; };

View File

@ -30,7 +30,7 @@ void kloader_start_bridge()
start_kernel_cpu(NULL); 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; static struct dyld_info library_info;
struct msg_load_request request = { struct msg_load_request request = {
@ -56,7 +56,7 @@ static int load_or_start_kernel(void *library, int run_kernel)
return 1; return 1;
} }
int kloader_load_library(void *library) int kloader_load_library(const void *library)
{ {
if(!kernel_cpu_reset_read()) { if(!kernel_cpu_reset_read()) {
log("BUG: attempted to load kernel library while kernel CPU is running"); 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); 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() void kloader_start_kernel()
{ {
load_or_start_kernel(NULL, 1); load_or_start_kernel(NULL, 1);

View File

@ -1,6 +1,8 @@
#ifndef __KLOADER_H #ifndef __KLOADER_H
#define __KLOADER_H #define __KLOADER_H
#include "artiq_personality.h"
#define KERNELCPU_EXEC_ADDRESS 0x40400000 #define KERNELCPU_EXEC_ADDRESS 0x40400000
#define KERNELCPU_PAYLOAD_ADDRESS 0x40420000 #define KERNELCPU_PAYLOAD_ADDRESS 0x40420000
#define KERNELCPU_LAST_ADDRESS (0x4fffffff - 1024*1024) #define KERNELCPU_LAST_ADDRESS (0x4fffffff - 1024*1024)
@ -8,7 +10,9 @@
extern long long int now; 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); void kloader_start_bridge(void);
int kloader_start_idle_kernel(void); int kloader_start_idle_kernel(void);

View File

@ -246,7 +246,8 @@ long long int now_init(void)
reply = mailbox_wait_and_receive(); reply = mailbox_wait_and_receive();
if(reply->type != MESSAGE_TYPE_NOW_INIT_REPLY) { 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); while(1);
} }
now = reply->now; now = reply->now;
@ -281,7 +282,8 @@ int watchdog_set(int ms)
reply = mailbox_wait_and_receive(); reply = mailbox_wait_and_receive();
if(reply->type != MESSAGE_TYPE_WATCHDOG_SET_REPLY) { 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); while(1);
} }
id = reply->id; id = reply->id;
@ -302,7 +304,7 @@ void watchdog_clear(int id)
int rpc(int rpc_num, ...) int rpc(int rpc_num, ...)
{ {
struct msg_rpc_request request; struct msg_rpc_request request;
struct msg_rpc_reply *reply; struct msg_base *reply;
request.type = MESSAGE_TYPE_RPC_REQUEST; request.type = MESSAGE_TYPE_RPC_REQUEST;
request.rpc_num = rpc_num; request.rpc_num = rpc_num;
@ -311,20 +313,20 @@ int rpc(int rpc_num, ...)
va_end(request.args); va_end(request.args);
reply = mailbox_wait_and_receive(); reply = mailbox_wait_and_receive();
if(reply->type != MESSAGE_TYPE_RPC_REPLY) { if(reply->type == MESSAGE_TYPE_RPC_REPLY) {
log("Malformed MESSAGE_TYPE_RPC_REPLY reply type"); int result = ((struct msg_rpc_reply *)reply)->result;
while(1); mailbox_acknowledge();
} return result;
} else if(reply->type == MESSAGE_TYPE_RPC_EXCEPTION) {
if(reply->exception != NULL) {
struct artiq_exception 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(); mailbox_acknowledge();
__artiq_raise(&exception); __artiq_raise(&exception);
} else { } else {
int retval = reply->retval; log("Malformed MESSAGE_TYPE_RPC_REQUEST reply type %d",
mailbox_acknowledge(); reply->type);
return retval; while(1);
} }
} }

View File

@ -16,6 +16,7 @@ enum {
MESSAGE_TYPE_WATCHDOG_CLEAR, MESSAGE_TYPE_WATCHDOG_CLEAR,
MESSAGE_TYPE_RPC_REQUEST, MESSAGE_TYPE_RPC_REQUEST,
MESSAGE_TYPE_RPC_REPLY, MESSAGE_TYPE_RPC_REPLY,
MESSAGE_TYPE_RPC_EXCEPTION,
MESSAGE_TYPE_LOG, MESSAGE_TYPE_LOG,
MESSAGE_TYPE_BRG_READY, MESSAGE_TYPE_BRG_READY,
@ -37,7 +38,7 @@ struct msg_base {
/* kernel messages */ /* kernel messages */
struct msg_load_request { struct msg_load_request {
void *library; const void *library;
struct dyld_info *library_info; struct dyld_info *library_info;
int run_kernel; int run_kernel;
}; };
@ -86,9 +87,13 @@ struct msg_rpc_request {
}; };
struct msg_rpc_reply { struct msg_rpc_reply {
int type;
int result;
};
struct msg_rpc_exception {
int type; int type;
struct artiq_exception *exception; struct artiq_exception *exception;
int retval;
}; };
struct msg_log { struct msg_log {

View File

@ -128,6 +128,13 @@ static int32_t in_packet_int32()
return result; 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) static const void *in_packet_bytes(int *length)
{ {
*length = in_packet_int32(); *length = in_packet_int32();
@ -310,6 +317,7 @@ enum {
REMOTEMSG_TYPE_RUN_KERNEL, REMOTEMSG_TYPE_RUN_KERNEL,
REMOTEMSG_TYPE_RPC_REPLY, REMOTEMSG_TYPE_RPC_REPLY,
REMOTEMSG_TYPE_RPC_EXCEPTION,
REMOTEMSG_TYPE_FLASH_READ_REQUEST, REMOTEMSG_TYPE_FLASH_READ_REQUEST,
REMOTEMSG_TYPE_FLASH_WRITE_REQUEST, REMOTEMSG_TYPE_FLASH_WRITE_REQUEST,
@ -452,15 +460,44 @@ static int process_input(void)
case REMOTEMSG_TYPE_RPC_REPLY: { case REMOTEMSG_TYPE_RPC_REPLY: {
struct msg_rpc_reply reply; struct msg_rpc_reply reply;
int result = in_packet_int32();
if(user_kernel_state != USER_KERNEL_WAIT_RPC) { if(user_kernel_state != USER_KERNEL_WAIT_RPC) {
log("Unsolicited RPC reply"); log("Unsolicited RPC reply");
return 0; // restart session return 0; // restart session
} }
reply.type = MESSAGE_TYPE_RPC_REPLY; reply.type = MESSAGE_TYPE_RPC_REPLY;
// FIXME memcpy(&reply.eid, &buffer_in[9], 4); reply.result = result;
// memcpy(&reply.retval, &buffer_in[13], 4);
mailbox_send_and_wait(&reply); 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; user_kernel_state = USER_KERNEL_RUNNING;
break; break;
} }
@ -509,8 +546,6 @@ static int send_rpc_value(const char **tag, void *value)
break; break;
case 'l': { // list(elt='a) case 'l': { // list(elt='a)
size = sizeof(void*);
struct { uint32_t length; void *elements; } *list = value; struct { uint32_t length; void *elements; } *list = value;
void *element = list->elements; 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); element = (void*)((intptr_t)element + element_size);
} }
*tag = tag_copy; *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; break;
} }
@ -575,10 +622,29 @@ static int process_kmsg(struct msg_base *umsg)
case MESSAGE_TYPE_EXCEPTION: { case MESSAGE_TYPE_EXCEPTION: {
struct msg_exception *msg = (struct msg_exception *)umsg; struct msg_exception *msg = (struct msg_exception *)umsg;
out_packet_empty(REMOTEMSG_TYPE_KERNEL_EXCEPTION); out_packet_start(REMOTEMSG_TYPE_KERNEL_EXCEPTION);
// memcpy(&buffer_out[9], &msg->eid, 4);
// memcpy(&buffer_out[13], msg->eparams, 3*8); out_packet_string(msg->exception->name);
// submit_output(9+4+3*8); 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(); kloader_stop();
user_kernel_state = USER_KERNEL_LOADED; user_kernel_state = USER_KERNEL_LOADED;