Skip to content

Commit

Permalink
Add zero-cost exception support to runtime and host.
Browse files Browse the repository at this point in the history
whitequark committed Aug 8, 2015
1 parent 1d61e44 commit 27d2390
Showing 11 changed files with 238 additions and 67 deletions.
1 change: 1 addition & 0 deletions artiq/compiler/builtins.py
Original file line number Diff line number Diff line change
@@ -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))),
5 changes: 4 additions & 1 deletion artiq/compiler/transforms/artiq_ir_generator.py
Original file line number Diff line number Diff line change
@@ -157,7 +157,7 @@ def visit_ModuleT(self, node):

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 @@ def raise_exn(self, exn):
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 @@ def alloc_exn(self, typ, message=None, param0=None, param1=None, param2=None):
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:
118 changes: 83 additions & 35 deletions artiq/coredevice/comm_generic.py
Original file line number Diff line number Diff line change
@@ -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 @@ def switch_clock(self, external):

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()

self._read_empty(_D2HMsgType.LOAD_COMPLETED)
def get_log(self):
self._write_empty(_H2DMsgType.LOG_REQUEST)

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 @@ def flash_storage_remove(self, key):

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 @@ def _receive_rpc_value(self, tag):
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_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(eid)
self._write_int32(result)
self._write_flush()
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 @@ def serve(self, rpc_map):
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')
8 changes: 8 additions & 0 deletions artiq/coredevice/exceptions.py
Original file line number Diff line number Diff line change
@@ -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."""
27 changes: 22 additions & 5 deletions artiq/language/core.py
Original file line number Diff line number Diff line change
@@ -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 @@ def find_subclass(cls):
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)
1 change: 1 addition & 0 deletions soc/runtime/artiq_personality.h
Original file line number Diff line number Diff line change
@@ -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];
};
20 changes: 18 additions & 2 deletions soc/runtime/kloader.c
Original file line number Diff line number Diff line change
@@ -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);
6 changes: 5 additions & 1 deletion soc/runtime/kloader.h
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
#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)
#define KSUPPORT_HEADER_SIZE 0x80

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);
28 changes: 15 additions & 13 deletions soc/runtime/ksupport.c
Original file line number Diff line number Diff line change
@@ -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);
}
}

9 changes: 7 additions & 2 deletions soc/runtime/messages.h
Original file line number Diff line number Diff line change
@@ -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 {
82 changes: 74 additions & 8 deletions soc/runtime/session.c
Original file line number Diff line number Diff line change
@@ -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;

0 comments on commit 27d2390

Please sign in to comment.