summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/generate-dfa-benchmark.py8
-rwxr-xr-xlib/automata.py11
-rw-r--r--lib/harness.py33
3 files changed, 42 insertions, 10 deletions
diff --git a/bin/generate-dfa-benchmark.py b/bin/generate-dfa-benchmark.py
index ac1bdd6..22b2b14 100755
--- a/bin/generate-dfa-benchmark.py
+++ b/bin/generate-dfa-benchmark.py
@@ -39,7 +39,7 @@ from harness import OnboardTimerHarness
opt = dict()
-def benchmark_from_runs(pta: PTA, runs: list, harness: object, benchmark_id: int = 0) -> io.StringIO:
+def benchmark_from_runs(pta: PTA, runs: list, harness: OnboardTimerHarness, benchmark_id: int = 0) -> io.StringIO:
outbuf = io.StringIO()
outbuf.write('#include "arch.h"\n')
@@ -234,7 +234,11 @@ if __name__ == '__main__':
print('DFS returned no traces -- perhaps your trace-filter is too restrictive?', file=sys.stderr)
sys.exit(1)
- harness = OnboardTimerHarness(gpio_pin = timer_pin, pta = pta, counter_limits = runner.get_counter_limits_us(opt['arch']))
+ need_return_values = False
+ if next(filter(lambda x: len(x.return_value_handlers), pta.transitions), None):
+ need_return_values = True
+
+ harness = OnboardTimerHarness(gpio_pin = timer_pin, pta = pta, counter_limits = runner.get_counter_limits_us(opt['arch']), log_return_values = need_return_values)
if len(args) > 1:
results = run_benchmark(args[1], pta, runs, opt['arch'], opt['app'], opt['run'].split(), harness, opt['sleep'], opt['repeat'], runs_total = len(runs))
diff --git a/lib/automata.py b/lib/automata.py
index 71dcacc..de40eb4 100755
--- a/lib/automata.py
+++ b/lib/automata.py
@@ -83,11 +83,14 @@ class State:
depth -- search depth
with_arguments -- perform dfs with function+argument transitions instead of just function transitions.
trace_filter -- list of lists. Each sub-list is a trace. Only traces matching one of the provided sub-lists are returned.
- E.g. trace_filter = [['init', 'foo'], ['init', 'bar']] will only return traces with init as first and foo or bar as second element.
+ E.g. trace_filter = [['init', 'foo'], ['init', 'bar']] will only return traces with init as first and foo or bar as second element.
+ trace_filter = [['init', 'foo', '$'], ['init', 'bar'], '$'] will only return the traces ['init', 'foo'] and ['init', 'bar'].
"""
+ # A '$' entry in trace_filter indicates that the trace should (successfully) terminate here regardless of `depth`.
if trace_filter is not None and next(filter(lambda x: x == '$', map(lambda x: x[0], trace_filter)), None) is not None:
yield []
+ # there may be other entries in trace_filter that still yield results.
if depth == 0:
for trans in self.outgoing_transitions.values():
if trace_filter is not None and len(list(filter(lambda x: x == trans.name, map(lambda x: x[0], trace_filter)))) == 0:
@@ -154,7 +157,8 @@ class Transition:
argument_combination: str = 'cartesian', # or 'zip'
param_update_function = None,
arg_to_param_map: dict = None,
- set_param = None):
+ set_param = None,
+ return_value_handlers: list = []):
"""
Create a new transition between two PTA states.
@@ -179,6 +183,7 @@ class Transition:
self.param_update_function = param_update_function
self.arg_to_param_map = arg_to_param_map
self.set_param = set_param
+ self.return_value_handlers = return_value_handlers
def get_duration(self, param_dict: dict = {}, args: list = []) -> float:
u"""
@@ -444,6 +449,8 @@ class PTA:
kwargs['set_param'] = transition['set_param']
if 'is_interrupt' in transition:
kwargs['is_interrupt'] = transition['is_interrupt']
+ if 'return_value' in transition:
+ kwargs['return_value_handlers'] = transition['return_value']
if not 'src' in transition:
transition['src'] = ['UNINITIALIZED']
if not 'dst' in transition:
diff --git a/lib/harness.py b/lib/harness.py
index 2002f8d..8f48d00 100644
--- a/lib/harness.py
+++ b/lib/harness.py
@@ -11,9 +11,10 @@ import re
# generated otherwise and it should also work with AnalyticModel (which does
# not have states)
class TransitionHarness:
- def __init__(self, gpio_pin = None, pta = None):
+ def __init__(self, gpio_pin = None, pta = None, log_return_values = False):
self.gpio_pin = gpio_pin
self.pta = pta
+ self.log_return_values = log_return_values
self.reset()
def reset(self):
@@ -25,6 +26,9 @@ class TransitionHarness:
ret = ''
if self.gpio_pin != None:
ret += '#define PTALOG_GPIO {}\n'.format(self.gpio_pin)
+ if self.log_return_values:
+ ret += '#define PTALOG_WITH_RETURNVALUES\n'
+ ret += 'uint16_t transition_return_value;\n'
ret += '#include "object/ptalog.h"\n'
if self.gpio_pin != None:
ret += 'PTALog ptalog({});\n'.format(self.gpio_pin)
@@ -63,7 +67,11 @@ class TransitionHarness:
def pass_transition(self, transition_id, transition_code, transition: object = None):
ret = 'ptalog.passTransition({:d});\n'.format(transition_id)
ret += 'ptalog.startTransition();\n'
- ret += '{}\n'.format(transition_code)
+ if self.log_return_values and transition and len(transition.return_value_handlers):
+ ret += 'transition_return_value = {}\n'.format(transition_code)
+ ret += 'ptalog.logReturn(transition_return_value);\n'
+ else:
+ ret += '{}\n'.format(transition_code)
ret += 'ptalog.stopTransition();\n'
return ret
@@ -117,24 +125,32 @@ class OnboardTimerHarness(TransitionHarness):
ret = 'ptalog.passTransition({:d});\n'.format(transition_id)
ret += 'ptalog.startTransition();\n'
ret += 'counter.start();\n'
- ret += '{}\n'.format(transition_code)
+ if self.log_return_values and transition and len(transition.return_value_handlers):
+ ret += 'transition_return_value = {}\n'.format(transition_code)
+ else:
+ ret += '{}\n'.format(transition_code)
ret += 'counter.stop();\n'
+ if self.log_return_values and transition and len(transition.return_value_handlers):
+ ret += 'ptalog.logReturn(transition_return_value);\n'
ret += 'ptalog.stopTransition(counter);\n'
return ret
def parser_cb(self, line):
#print('[HARNESS] got line {}'.format(line))
- if re.match(r'\[PTA\] benchmark start, id=(.*)', line):
+ if re.match(r'\[PTA\] benchmark start, id=(\S+)', line):
self.synced = True
print('[HARNESS] synced')
if self.synced:
- res = re.match(r'\[PTA\] trace=(.*) count=(.*)', line)
+ res = re.match(r'\[PTA\] trace=(\S+) count=(\S+)', line)
if res:
self.trace_id = int(res.group(1))
self.trace_length = int(res.group(2))
self.current_transition_in_trace = 0
#print('[HARNESS] trace {:d} contains {:d} transitions. Expecting {:d} transitions.'.format(self.trace_id, self.trace_length, len(self.traces[self.trace_id]['trace']) // 2))
- res = re.match(r'\[PTA\] transition=(.*) cycles=(.*)/(.*)', line)
+ if self.log_return_values:
+ res = re.match(r'\[PTA\] transition=(\S+) cycles=(\S+)/(\S+) return=(\S+)', line)
+ else:
+ res = re.match(r'\[PTA\] transition=(\S+) cycles=(\S+)/(\S+)', line)
if res:
transition_id = int(res.group(1))
# TODO Handle Overflows (requires knowledge of arch-specific max cycle value)
@@ -159,6 +175,11 @@ class OnboardTimerHarness(TransitionHarness):
transition = self.pta.transitions[transition_id]
if transition.name != log_data_target['name']:
raise RuntimeError('Log mismatch: Expected transition {:s}, got transition {:s}'.format(log_data_target['name'], transition.name))
+ if self.log_return_values and len(transition.return_value_handlers):
+ for handler in transition.return_value_handlers:
+ if 'parameter' in handler:
+ print('got return value {:x} for transition {}, which has a handler. whoop whoop.'.format(int(res.group(4)), transition.name))
+ # TODO handle value.
#print('[HARNESS] Logging data for transition {}'.format(log_data_target['name']))
if 'offline_aggregates' not in log_data_target:
log_data_target['offline_aggregates'] = {