176 lines
6.9 KiB
Python
Executable File
176 lines
6.9 KiB
Python
Executable File
#
|
|
# Copyright (c) 2009-2011, ETH Zurich.
|
|
# All rights reserved.
|
|
#
|
|
# This file is distributed under the terms in the attached LICENSE file.
|
|
# If you do not find this file, copies can be found by writing to:
|
|
# ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
|
|
#
|
|
|
|
import os
|
|
import types
|
|
import string
|
|
import datetime
|
|
import debug
|
|
import re
|
|
|
|
class Harness:
|
|
RAW_FILE_NAME = 'raw.txt'
|
|
MENU_LST_FILE_NAME = 'menu.lst'
|
|
BOOT_FILE_NAME = 'bootlog.txt'
|
|
TERM_FILTER = re.compile("\[\d\d?m")
|
|
|
|
def _clean_line(self, line):
|
|
# filter output line of control characters
|
|
filtered_out = filter(lambda c: c in string.printable, line.rstrip())
|
|
# Delete terminal color codes from output
|
|
filtered_out = self.TERM_FILTER.sub('', filtered_out)
|
|
return filtered_out
|
|
|
|
def _write_menu_lst_debug(self, test, build, machine, path):
|
|
# Ignore for tests that do not implement get_modules
|
|
if hasattr(test, "get_modules"):
|
|
menu_lst_file_name = os.path.join(path, self.MENU_LST_FILE_NAME)
|
|
debug.verbose("harness: writing menu.lst to %s" % menu_lst_file_name)
|
|
with open(menu_lst_file_name, "w") as menu:
|
|
menu.write( test.get_modules(build, machine).get_menu_data("/") )
|
|
|
|
def run_test(self, build, machine, test, path):
|
|
# Open files for raw output from the victim and log data from the test
|
|
raw_file_name = os.path.join(path, self.RAW_FILE_NAME)
|
|
debug.verbose('open %s for raw output' % raw_file_name)
|
|
raw_file = open(raw_file_name, 'w')
|
|
|
|
# run the test, dumping the output to the raw file as we go
|
|
try:
|
|
debug.verbose('harness: setup test')
|
|
test.setup(build, machine, path)
|
|
self._write_menu_lst_debug(test, build, machine, path)
|
|
debug.verbose('harness: run test')
|
|
starttime = datetime.datetime.now()
|
|
for out in test.run(build, machine, path):
|
|
# timedelta for the time this line was emitted from the start of the run
|
|
timestamp = datetime.datetime.now() - starttime
|
|
# format as string, discarding sub-second precision
|
|
timestr = str(timestamp).split('.', 1)[0]
|
|
debug.debug('[%s] %s' % (timestr, self._clean_line(out)))
|
|
# log full raw line (without timestamp) to output file
|
|
raw_file.write(out)
|
|
debug.verbose('harness: output complete')
|
|
except KeyboardInterrupt:
|
|
# let the user know that we are on our way out
|
|
debug.error('Interrupted! Performing cleanup...')
|
|
raise
|
|
finally:
|
|
raw_file.close()
|
|
debug.verbose('harness: cleanup test')
|
|
test.cleanup(machine)
|
|
|
|
def process_output(self, test, path):
|
|
"""Process raw.txt and return array of output lines that begins with grubs
|
|
output, avoids having encoding issues when generating other report files"""
|
|
|
|
raw_file_name = os.path.join(path, self.RAW_FILE_NAME)
|
|
|
|
if os.path.exists(raw_file_name):
|
|
idx = 0
|
|
with open(raw_file_name, 'r') as rf:
|
|
lines = rf.readlines()
|
|
for idx, line in enumerate(lines):
|
|
if line.strip() == "root (nd)" or \
|
|
line.strip().startswith("Kernel starting at address") or \
|
|
"Barrelfish CPU driver starting on ARMv8" in line:
|
|
break
|
|
if idx == len(lines)-1:
|
|
debug.verbose('magic string "root (nd)" or "Kernel starting at address" not found, assuming no garbage in output')
|
|
idx=0
|
|
|
|
return [ unicode(self._clean_line(l), errors='replace') for l in lines[idx:] ]
|
|
|
|
# file did not exist
|
|
return ["could not open %s to process test output" % raw_file_name]
|
|
|
|
def extract_errors(self, test, path):
|
|
raw_file_name = os.path.join(path, self.RAW_FILE_NAME)
|
|
debug.verbose('open %s for raw input' % raw_file_name)
|
|
raw_file = open(raw_file_name, 'r')
|
|
|
|
try:
|
|
results = test.process_data(path, raw_file)
|
|
finally:
|
|
raw_file.close()
|
|
|
|
errors = [results.reason()]
|
|
try:
|
|
errors += results.errors
|
|
except:
|
|
pass
|
|
|
|
return errors
|
|
|
|
|
|
def process_results(self, test, path):
|
|
# open raw file for input processing
|
|
raw_file_name = os.path.join(path, self.RAW_FILE_NAME)
|
|
debug.verbose('open %s for raw input' % raw_file_name)
|
|
raw_file = open(raw_file_name, 'r')
|
|
|
|
try:
|
|
results = test.process_data(path, raw_file)
|
|
finally:
|
|
raw_file.close()
|
|
if not results:
|
|
debug.verbose('no results')
|
|
return True # no results, assume success
|
|
|
|
retval = True # everything OK
|
|
|
|
# Process raw.txt and make a bootlog.txt that begins with grubs or
|
|
# Barrelfish's output, avoids having encoding issues when viewing logfiles
|
|
boot_file_name = os.path.join(path, self.BOOT_FILE_NAME)
|
|
if os.path.exists(raw_file_name):
|
|
idx = 0
|
|
with open(raw_file_name, 'r') as rf:
|
|
lines = rf.readlines()
|
|
for idx, line in enumerate(lines):
|
|
if line.strip() == "root (nd)" or \
|
|
"Barrelfish CPU driver starting" in line.strip():
|
|
break
|
|
if idx > 0:
|
|
with open(boot_file_name, 'w') as wf:
|
|
wf.writelines(lines[idx:])
|
|
else:
|
|
debug.verbose('Magic string root (nd) not found, do not write bootlog.txt')
|
|
else:
|
|
debug.verbose('No file named %s exists. Do not create bootlog.txt.' % raw_file_name)
|
|
|
|
# if a single result, turn it into a list
|
|
if not isinstance(results, types.ListType):
|
|
results = [results]
|
|
for result in results:
|
|
# see if it passed
|
|
try:
|
|
passed = result.passed()
|
|
except NotImplementedError:
|
|
passed = None
|
|
if passed is False:
|
|
debug.log('Test %s FAILED %s' % (test.name, '(' + result.reason() + ')') )
|
|
retval = False
|
|
elif passed:
|
|
debug.verbose('Test %s PASSED' % test.name)
|
|
|
|
# write it to a file
|
|
name = result.name if result.name else 'results'
|
|
data_file_name = os.path.join(path, name + '.dat')
|
|
debug.verbose('create %s for processed output' % data_file_name)
|
|
data_file = open(data_file_name, 'w')
|
|
try:
|
|
result.to_file(data_file)
|
|
data_file.close()
|
|
except NotImplementedError:
|
|
debug.verbose('no processed output, remove %s' % data_file_name)
|
|
data_file.close()
|
|
os.remove(data_file_name)
|
|
|
|
return retval
|