Add zero-cost exception support to runtime and host.

pull/235/head
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()),
("__line__", TInt(types.TValue(32))),
("__col__", TInt(types.TValue(32))),
("__func__", TStr()),
("__message__", TStr()),
("__param0__", 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):
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("<not thrown>", builtins.TStr()), # file
ir.Constant(0, builtins.TInt(types.TValue(32))), # line
ir.Constant(0, builtins.TInt(types.TValue(32))), # column
ir.Constant("<not thrown>", builtins.TStr()), # function
]
if message is None:

View File

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

View File

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

View File

@ -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 "<unknown>"))
return "\n".join(lines)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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