diff options
-rwxr-xr-x | bin/generate-dfa-benchmark.py | 8 | ||||
-rwxr-xr-x | lib/automata.py | 11 | ||||
-rw-r--r-- | lib/harness.py | 33 |
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'] = { |