summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/generate-dfa-benchmark.py32
-rw-r--r--lib/harness.py80
-rw-r--r--lib/runner.py30
3 files changed, 109 insertions, 33 deletions
diff --git a/bin/generate-dfa-benchmark.py b/bin/generate-dfa-benchmark.py
index b88cf3b..6ccb1ef 100755
--- a/bin/generate-dfa-benchmark.py
+++ b/bin/generate-dfa-benchmark.py
@@ -40,16 +40,19 @@ Options:
import getopt
import json
+import os
import re
import runner
import sys
+import tarfile
import time
import io
import yaml
from aspectc import Repo
from automata import PTA
from codegen import *
-from harness import OnboardTimerHarness
+from harness import OnboardTimerHarness, TransitionHarness
+from utils import flatten
opt = dict()
@@ -77,7 +80,7 @@ def benchmark_from_runs(pta: PTA, runs: list, harness: OnboardTimerHarness, benc
# When flashing first and then starting the log, the first log lines may be lost.
# To work around this, we flash first, then start the log, and use this delay statement to ensure that no output is lost.
# This is also useful to faciliate MIMOSA calibration after flashing.
- outbuf.write('arch.delay_ms(10000);\n')
+ outbuf.write('arch.delay_ms(12000);\n')
outbuf.write('while (1) {\n')
outbuf.write(harness.start_benchmark())
@@ -161,7 +164,7 @@ def run_benchmark(application_file: str, pta: PTA, runs: list, arch: str, app: s
return results
runner.flash(arch, app, run_args)
- monitor = runner.get_monitor(arch, callback = harness.parser_cb)
+ monitor = runner.get_monitor(arch, callback = harness.parser_cb, mimosa = {'shunt': 82})
if arch == 'posix':
print('[RUN] Will run benchmark for {:.0f} seconds'.format(run_timeout))
@@ -176,10 +179,9 @@ def run_benchmark(application_file: str, pta: PTA, runs: list, arch: str, app: s
print('[RUN] {:d}/{:d} ({:.0f}%), current benchmark at {:.0f}%'.format(run_offset, runs_total, run_offset * 100 / runs_total, slept * 100 / run_timeout))
except KeyboardInterrupt:
pass
- lines = monitor.get_lines()
monitor.close()
- return [(runs, harness, lines)]
+ return [(runs, harness, monitor)]
if __name__ == '__main__':
@@ -280,6 +282,7 @@ if __name__ == '__main__':
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)
+ harness = TransitionHarness(gpio_pin = timer_pin, pta = pta, 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), dummy = 'dummy' in opt)
@@ -287,10 +290,25 @@ if __name__ == '__main__':
'opt' : opt,
'pta' : pta.to_json(),
'traces' : list(map(lambda x: x[1].traces, results)),
- 'raw_output' : list(map(lambda x: x[2], results)),
+ 'raw_output' : list(map(lambda x: x[2].get_lines(), results)),
+ 'files' : list(map(lambda x: x[2].get_files(), results)),
+ 'configs' : list(map(lambda x: x[2].get_config(), results)),
}
- with open(time.strftime('ptalog-%Y%m%d-%H%M%S.json'), 'w') as f:
+ extra_files = flatten(json_out['files'])
+ output_prefix = time.strftime('/home/derf/var/ess/aemr/data/ptalog-%Y%m%d-%H%M%S')
+ with open('{}.json'.format(output_prefix), 'w') as f:
json.dump(json_out, f)
+ if len(extra_files):
+ with tarfile.open('{}.tar'.format(output_prefix), 'w') as tar:
+ tar.add('{}.json'.format(output_prefix))
+ for extra_file in extra_files:
+ tar.add(extra_file)
+ print(' --> {}.tar'.format(output_prefix))
+ os.remove('{}.json'.format(output_prefix))
+ for extra_file in extra_files:
+ os.remove(extra_file)
+ else:
+ print(' --> {}.json'.format(output_prefix))
else:
outbuf = benchmark_from_runs(pta, runs, harness)
print(outbuf.getvalue())
diff --git a/lib/harness.py b/lib/harness.py
index af74977..76b658c 100644
--- a/lib/harness.py
+++ b/lib/harness.py
@@ -96,25 +96,66 @@ class TransitionHarness:
def stop_benchmark(self):
return ''
+ def _append_nondeterministic_parameter_value(self, log_data_target, parameter_name, parameter_value):
+ if log_data_target['parameter'][parameter_name] is None:
+ log_data_target['parameter'][parameter_name] = list()
+ log_data_target['parameter'][parameter_name].append(parameter_value)
+
def parser_cb(self, line):
- pass
-
- def parse_log(self, lines):
- sync = False
- for line in lines:
- print(line)
- res = re.fullmatch(r'\[PTA\] (.*=.*)', line)
- if re.fullmatch(r'\[PTA\] benchmark start, id=(.*)', line):
- print('> got sync')
- sync = True
- elif not sync:
- continue
- elif re.fullmatch(r'\[PTA\] trace, count=(.*)', line):
- print('> got transition')
- pass
- elif res:
- print(dict(map(lambda x: x.split('='), res.group(1).split())))
- pass
+ #print('[HARNESS] got line {}'.format(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=(\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))
+ if self.log_return_values:
+ res = re.match(r'\[PTA\] transition=(\S+) return=(\S+)', line)
+ else:
+ res = re.match(r'\[PTA\] transition=(\S+)', line)
+ if res:
+ transition_id = int(res.group(1))
+ # self.traces contains transitions and states, UART output only contains transitions -> use index * 2
+ try:
+ log_data_target = self.traces[self.trace_id]['trace'][self.current_transition_in_trace * 2]
+ except IndexError:
+ transition_name = None
+ if self.pta:
+ transition_name = self.pta.transitions[transition_id].name
+ print('[HARNESS] benchmark id={:d} trace={:d}: transition #{:d} (ID {:d}, name {}) is out of bounds'.format(0, self.trace_id, self.current_transition_in_trace, transition_id, transition_name))
+ print(' Offending line: {}'.format(line))
+ return
+ if log_data_target['isa'] != 'transition':
+ raise RuntimeError('Log mismatch: Expected transition, got {:s}'.format(log_data_target['isa']))
+ if self.pta:
+ 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:
+ parameter_value = return_value = int(res.group(2))
+
+ if 'return_values' not in log_data_target:
+ log_data_target['return_values'] = list()
+ log_data_target['return_values'].append(return_value)
+
+ if 'formula' in handler:
+ parameter_value = handler['formula'].eval(return_value)
+
+ self._append_nondeterministic_parameter_value(log_data_target, handler['parameter'], parameter_value)
+ for following_log_data_target in self.traces[self.trace_id]['trace'][(self.current_transition_in_trace * 2 + 1) :]:
+ self._append_nondeterministic_parameter_value(following_log_data_target, handler['parameter'], parameter_value)
+ if 'apply_from' in handler and any(map(lambda x: x['name'] == handler['apply_from'], self.traces[self.trace_id]['trace'][: (self.current_transition_in_trace * 2 + 1)])):
+ for preceding_log_data_target in reversed(self.traces[self.trace_id]['trace'][: (self.current_transition_in_trace * 2)]):
+ self._append_nondeterministic_parameter_value(preceding_log_data_target, handler['parameter'], parameter_value)
+ if preceding_log_data_target['name'] == handler['apply_from']:
+ break
+ self.current_transition_in_trace += 1
class OnboardTimerHarness(TransitionHarness):
"""Bar."""
@@ -179,13 +220,12 @@ class OnboardTimerHarness(TransitionHarness):
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)
cycles = int(res.group(2))
overflow = int(res.group(3))
if overflow >= self.counter_max_overflow:
raise RuntimeError('Counter overflow ({:d}/{:d}) in benchmark id={:d} trace={:d}: transition #{:d} (ID {:d})'.format(cycles, overflow, 0, self.trace_id, self.current_transition_in_trace, transition_id))
duration_us = cycles * self.one_cycle_in_us + overflow * self.one_overflow_in_us
- # self.traces contains transitions and states, UART output only contains trnasitions -> use index * 2
+ # self.traces contains transitions and states, UART output only contains transitions -> use index * 2
try:
log_data_target = self.traces[self.trace_id]['trace'][self.current_transition_in_trace * 2]
except IndexError:
diff --git a/lib/runner.py b/lib/runner.py
index e5c75b8..588bd4d 100644
--- a/lib/runner.py
+++ b/lib/runner.py
@@ -114,9 +114,15 @@ class SerialMonitor:
time.sleep(timeout)
return self.reader.get_lines()
- def get_lines(self) ->list:
+ def get_lines(self) -> list:
return self.reader.get_lines()
+ def get_files(self) -> list:
+ return list()
+
+ def get_config(self) -> dict:
+ return dict()
+
def close(self):
"""Close serial connection."""
self.worker.stop()
@@ -147,13 +153,12 @@ class MIMOSAMonitor(SerialMonitor):
raise RuntimeError('{} returned {}'.format(' '.join(cmd), res.returncode))
def _start_mimosa(self):
+ self._mimosactl('disconnect')
self._mimosacmd(['--start'])
self._mimosacmd(['--parameter', 'offset', str(self._offset)])
self._mimosacmd(['--parameter', 'shunt', str(self._shunt)])
self._mimosacmd(['--parameter', 'voltage', str(self._voltage)])
self._mimosacmd(['--mimosa-start'])
- time.sleep(1)
- self._mimosactl('disconnect')
time.sleep(2)
self._mimosactl('1k') # 987 ohm
time.sleep(2)
@@ -166,7 +171,11 @@ class MIMOSAMonitor(SerialMonitor):
mtime_changed = True
mim_file = None
time.sleep(1)
- for filename in os.listdir():
+ # reverse sort ensures that we will get the latest file, which must
+ # belong to the current measurements. This ensures that older .mim
+ # files lying around in the directory will not confuse our
+ # heuristic.
+ for filename in sorted(os.listdir(), reverse = True):
if re.search(r'[.]mim$', filename):
mim_file = filename
break
@@ -180,8 +189,17 @@ class MIMOSAMonitor(SerialMonitor):
def close(self):
super().close()
- mim_file = self._stop_mimosa()
- os.remove(mim_file)
+ self.mim_file = self._stop_mimosa()
+
+ def get_files(self) -> list:
+ return [self.mim_file]
+
+ def get_config(self) -> dict:
+ return {
+ 'offset' : self._offset,
+ 'shunt' : self._shunt,
+ 'voltage' : self._voltage,
+ }
class ShellMonitor:
"""SerialMonitor runs a program and captures its output for a specific amount of time."""