-
Notifications
You must be signed in to change notification settings - Fork 144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Runtime error handling #135
Changes from 29 commits
f456afb
2d0ec74
25a3a68
a2d1765
74dfdf6
1daa9af
5908401
3df2847
9581fae
6e4716a
e22def6
bd25a80
7edeb1f
23fb8d7
f6686b4
86867c2
859a195
f7007f8
0163202
bf934ef
0eef67e
f08a35c
ceddf2a
f798b0a
080118a
e0477fb
b011e4f
ad47ff7
eb61ba6
60ef89b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -228,7 +228,22 @@ def _run(self, run_condition): | |
for recv_port in self.service_to_runtime_ack: | ||
data = recv_port.recv() | ||
if not enum_equal(data, MGMT_RESPONSE.DONE): | ||
raise RuntimeError(f"Runtime Received {data}") | ||
if enum_equal(data, MGMT_RESPONSE.ERROR): | ||
# Receive all errors from the ProcessModels | ||
error_cnt = 0 | ||
for actors in \ | ||
self._messaging_infrastructure.actors: | ||
actors.join() | ||
if actors.exception: | ||
_, traceback = actors.exception | ||
print(traceback) | ||
error_cnt += 1 | ||
|
||
raise RuntimeError( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could define own Execptions. RuntimeError for actual errors in the Runtime and ProcessModelError for ... you know what. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This exception is a RuntimeError, due to other Exceptions happened in the ProcessModel. The details have been printed already, this is just to stop the Runtime and tell the user that there have been other Exceptions (+ the number of them). For all the details the user only has to scroll upwards in the console. |
||
f"{error_cnt} Exception(s) occurred. See " | ||
f"output above for details.") | ||
else: | ||
raise RuntimeError(f"Runtime Received {data}") | ||
if run_condition.blocking: | ||
self.current_ts += self.num_steps | ||
self._is_running = False | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
# Copyright (C) 2021 Intel Corporation | ||
# SPDX-License-Identifier: BSD-3-Clause | ||
# See: https://spdx.org/licenses/ | ||
|
||
import unittest | ||
|
||
from lava.magma.core.decorator import implements, requires, tag | ||
from lava.magma.core.model.py.model import PyLoihiProcessModel | ||
from lava.magma.core.model.py.ports import PyOutPort, PyInPort | ||
from lava.magma.core.model.py.type import LavaPyType | ||
from lava.magma.core.process.ports.ports import OutPort, InPort | ||
from lava.magma.core.process.process import AbstractProcess | ||
from lava.magma.core.resources import CPU | ||
from lava.magma.core.run_configs import Loihi1SimCfg | ||
from lava.magma.core.sync.protocols.loihi_protocol import LoihiProtocol | ||
from lava.magma.core.run_conditions import RunSteps | ||
|
||
|
||
# A minimal process with an OutPort | ||
class P1(AbstractProcess): | ||
def __init__(self, **kwargs): | ||
super().__init__(**kwargs) | ||
self.out = OutPort(shape=(2,)) | ||
|
||
|
||
# A minimal process with an InPort | ||
class P2(AbstractProcess): | ||
def __init__(self, **kwargs): | ||
super().__init__(**kwargs) | ||
self.inp = InPort(shape=(2,)) | ||
|
||
|
||
# A minimal process with an InPort | ||
class P3(AbstractProcess): | ||
def __init__(self, **kwargs): | ||
super().__init__(**kwargs) | ||
self.inp = InPort(shape=(2,)) | ||
|
||
|
||
# A minimal PyProcModel implementing P1 | ||
@implements(proc=P1, protocol=LoihiProtocol) | ||
@requires(CPU) | ||
@tag('floating_pt') | ||
class PyProcModel1(PyLoihiProcessModel): | ||
out: PyOutPort = LavaPyType(PyOutPort.VEC_DENSE, int) | ||
|
||
def run_spk(self): | ||
if self.current_ts > 1: | ||
# Raise exception | ||
raise AssertionError("All the error info") | ||
|
||
|
||
# A minimal PyProcModel implementing P2 | ||
@implements(proc=P2, protocol=LoihiProtocol) | ||
@requires(CPU) | ||
@tag('floating_pt') | ||
class PyProcModel2(PyLoihiProcessModel): | ||
inp: PyInPort = LavaPyType(PyInPort.VEC_DENSE, int) | ||
|
||
def run_spk(self): | ||
if self.current_ts > 1: | ||
# Raise exception | ||
raise TypeError("All the error info") | ||
|
||
|
||
# A minimal PyProcModel implementing P3 | ||
@implements(proc=P3, protocol=LoihiProtocol) | ||
@requires(CPU) | ||
@tag('floating_pt') | ||
class PyProcModel3(PyLoihiProcessModel): | ||
inp: PyInPort = LavaPyType(PyInPort.VEC_DENSE, int) | ||
|
||
def run_spk(self): | ||
... | ||
|
||
|
||
class TestExceptionHandling(unittest.TestCase): | ||
def test_one_pm(self): | ||
"""Checks the forwarding of exceptions within a ProcessModel to the | ||
runtime.""" | ||
|
||
# Create an instance of P1 | ||
proc = P1() | ||
|
||
# Run the network for 1 time step -> no exception | ||
proc.run(condition=RunSteps(num_steps=1), run_cfg=Loihi1SimCfg()) | ||
|
||
# Run the network for another time step -> expect exception | ||
with self.assertRaises(RuntimeError) as context: | ||
proc.run(condition=RunSteps(num_steps=1), run_cfg=Loihi1SimCfg()) | ||
|
||
exception = context.exception | ||
self.assertEqual(RuntimeError, type(exception)) | ||
# 1 exception in the ProcessModel expected | ||
self.assertTrue('1 Exception(s) occurred' in str(exception)) | ||
|
||
def test_two_pm(self): | ||
"""Checks the forwarding of exceptions within two ProcessModel to the | ||
runtime.""" | ||
|
||
# Create a sender instance of P1 and a receiver instance of P2 | ||
sender = P1() | ||
recv = P2() | ||
|
||
# Connect sender with receiver | ||
sender.out.connect(recv.inp) | ||
|
||
# Run the network for 1 time step -> no exception | ||
sender.run(condition=RunSteps(num_steps=1), run_cfg=Loihi1SimCfg()) | ||
|
||
# Run the network for another time step -> expect exception | ||
with self.assertRaises(RuntimeError) as context: | ||
sender.run(condition=RunSteps(num_steps=1), run_cfg=Loihi1SimCfg()) | ||
|
||
exception = context.exception | ||
self.assertEqual(RuntimeError, type(exception)) | ||
# 2 Exceptions in the ProcessModels expected | ||
self.assertTrue('2 Exception(s) occurred' in str(exception)) | ||
|
||
def test_three_pm(self): | ||
"""Checks the forwarding of exceptions within three ProcessModel to the | ||
runtime.""" | ||
|
||
# Create a sender instance of P1 and receiver instances of P2 and P3 | ||
sender = P1() | ||
recv1 = P2() | ||
recv2 = P3() | ||
|
||
# Connect sender with receiver | ||
sender.out.connect([recv1.inp, recv2.inp]) | ||
|
||
# Run the network for 1 time step -> no exception | ||
sender.run(condition=RunSteps(num_steps=1), run_cfg=Loihi1SimCfg()) | ||
|
||
# Run the network for another time step -> expect exception | ||
with self.assertRaises(RuntimeError) as context: | ||
sender.run(condition=RunSteps(num_steps=1), run_cfg=Loihi1SimCfg()) | ||
|
||
exception = context.exception | ||
self.assertEqual(RuntimeError, type(exception)) | ||
# 2 Exceptions in the ProcessModels expected | ||
self.assertTrue('2 Exception(s) occurred' in str(exception)) | ||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main(buffer=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this actually identify the problem from which the error came? Perhaps attach this to the Exception object.
You should print the class name, Process name and id.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be clear. Both the processes should identify themselves but also the RuntimeServices in case the error happened in the RuntimeService.
Perhaps you even want to distinguish them clearly in the command line prints.
Perhaps you even want to first print a summary of everything that has thrown any errors at the top of the command line to orient the user because otherwise there could be quite a messy stack trace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The printed traceback leads you to the line of code the exception occurred, e.g., "line 50, in run_spk
raise AssertionError("All the error info")" -> line 50 is within PyProcModel1. So you know exactly which exception happened and on which code line.
These tracebacks are printed for every exception occurred in all ProcessModels.