+
+ def threads(self):
+ output = self.command("info threads")
+ threads = []
+ for line in output.splitlines():
+ m = re.match(
+ r"[\s\*]*(\d+)\s*Thread (\d+)\s*\(Name: ([^\)]+)\s*(.*)",
+ line)
+ if m:
+ threads.append(Thread(*m.groups()))
+ if not threads:
+ threads.append(Thread('1', '1', 'Default', '???'))
+ return threads
+
+ def thread(self, thread):
+ return self.command("thread %s" % thread.id)
+
+def run_all_tests(module, target, parsed):
+ if not os.path.exists(parsed.logs):
+ os.makedirs(parsed.logs)
+
+ overall_start = time.time()
+
+ global gdb_cmd # pylint: disable=global-statement
+ gdb_cmd = parsed.gdb
+
+ todo = []
+ for hart in target.harts:
+ if parsed.misaval:
+ hart.misa = int(parsed.misaval, 16)
+ print "Using $misa from command line: 0x%x" % hart.misa
+ elif hart.misa:
+ print "Using $misa from hart definition: 0x%x" % hart.misa
+ else:
+ todo.append(("ExamineTarget", ExamineTarget, hart))
+
+ for name in dir(module):
+ definition = getattr(module, name)
+ if type(definition) == type and hasattr(definition, 'test') and \
+ (not parsed.test or any(test in name for test in parsed.test)):
+ todo.append((name, definition, None))
+
+ results, count = run_tests(parsed, target, todo)
+
+ header("ran %d tests in %.0fs" % (count, time.time() - overall_start),
+ dash=':')
+
+ return print_results(results)
+
+good_results = set(('pass', 'not_applicable'))
+def run_tests(parsed, target, todo):
+ results = {}
+ count = 0
+
+ for name, definition, hart in todo:
+ log_name = os.path.join(parsed.logs, "%s-%s-%s.log" %
+ (time.strftime("%Y%m%d-%H%M%S"), type(target).__name__, name))
+ log_fd = open(log_name, 'w')
+ print "Running %s > %s ..." % (name, log_name),
+ instance = definition(target, hart)
+ sys.stdout.flush()
+ log_fd.write("Test: %s\n" % name)
+ log_fd.write("Target: %s\n" % type(target).__name__)
+ start = time.time()
+ real_stdout = sys.stdout
+ sys.stdout = log_fd
+ try:
+ result = instance.run()
+ log_fd.write("Result: %s\n" % result)
+ finally:
+ sys.stdout = real_stdout
+ log_fd.write("Time elapsed: %.2fs\n" % (time.time() - start))
+ print "%s in %.2fs" % (result, time.time() - start)
+ if result not in good_results and parsed.print_failures:
+ sys.stdout.write(open(log_name).read())
+ sys.stdout.flush()
+ results.setdefault(result, []).append((name, log_name))
+ count += 1
+ if result not in good_results and parsed.fail_fast:
+ break
+
+ return results, count
+
+def print_results(results):
+ result = 0
+ for key, value in results.iteritems():
+ print "%d tests returned %s" % (len(value), key)
+ if key not in good_results:
+ result = 1
+ for name, log_name in value:
+ print " %s > %s" % (name, log_name)
+
+ return result
+
+def add_test_run_options(parser):
+ parser.add_argument("--logs", default="logs",
+ help="Store logs in the specified directory.")
+ parser.add_argument("--fail-fast", "-f", action="store_true",
+ help="Exit as soon as any test fails.")
+ parser.add_argument("--print-failures", action="store_true",
+ help="When a test fails, print the log file to stdout.")
+ parser.add_argument("test", nargs='*',
+ help="Run only tests that are named here.")
+ parser.add_argument("--gdb",
+ help="The command to use to start gdb.")
+ parser.add_argument("--misaval",
+ help="Don't run ExamineTarget, just assume the misa value which is "
+ "specified.")
+
+def header(title, dash='-', length=78):
+ if title:
+ dashes = dash * (length - 4 - len(title))
+ before = dashes[:len(dashes)/2]
+ after = dashes[len(dashes)/2:]
+ print "%s[ %s ]%s" % (before, title, after)
+ else:
+ print dash * length
+
+def print_log(path):
+ header(path)
+ for l in open(path, "r"):
+ sys.stdout.write(l)
+ print
+
+class BaseTest(object):
+ compiled = {}
+
+ def __init__(self, target, hart=None):
+ self.target = target
+ if hart:
+ self.hart = hart
+ else:
+ self.hart = random.choice(target.harts)
+ self.hart = target.harts[-1] #<<<
+ self.server = None
+ self.target_process = None
+ self.binary = None
+ self.start = 0
+ self.logs = []
+
+ def early_applicable(self):
+ """Return a false value if the test has determined it cannot run
+ without ever needing to talk to the target or server."""
+ # pylint: disable=no-self-use
+ return True
+
+ def setup(self):
+ pass
+
+ def compile(self):
+ compile_args = getattr(self, 'compile_args', None)
+ if compile_args:
+ if compile_args not in BaseTest.compiled:
+ # pylint: disable=star-args
+ BaseTest.compiled[compile_args] = \
+ self.target.compile(self.hart, *compile_args)
+ self.binary = BaseTest.compiled.get(compile_args)
+
+ def classSetup(self):
+ self.compile()
+ self.target_process = self.target.create()
+ if self.target_process:
+ self.logs.append(self.target_process.logname)
+ try:
+ self.server = self.target.server()
+ self.logs.append(self.server.logname)
+ except Exception:
+ for log in self.logs:
+ print_log(log)
+ raise
+
+ def classTeardown(self):
+ del self.server
+ del self.target_process
+
+ def postMortem(self):
+ pass
+
+ def run(self):
+ """
+ If compile_args is set, compile a program and set self.binary.
+
+ Call setup().
+
+ Then call test() and return the result, displaying relevant information
+ if an exception is raised.
+ """
+
+ sys.stdout.flush()
+
+ if not self.early_applicable():
+ return "not_applicable"
+
+ self.start = time.time()
+
+ try:
+ self.classSetup()
+ self.setup()
+ result = self.test() # pylint: disable=no-member
+ except TestNotApplicable:
+ result = "not_applicable"
+ except Exception as e: # pylint: disable=broad-except
+ if isinstance(e, TestFailed):
+ result = "fail"
+ else:
+ result = "exception"
+ if isinstance(e, TestFailed):
+ header("Message")
+ print e.message
+ header("Traceback")
+ traceback.print_exc(file=sys.stdout)
+ self.postMortem()
+ return result
+
+ finally:
+ for log in self.logs:
+ print_log(log)
+ header("End of logs")
+ self.classTeardown()
+
+ if not result:
+ result = 'pass'
+ return result
+
+gdb_cmd = None
+class GdbTest(BaseTest):
+ def __init__(self, target, hart=None):
+ BaseTest.__init__(self, target, hart=hart)
+ self.gdb = None
+
+ def classSetup(self):
+ BaseTest.classSetup(self)
+
+ if gdb_cmd:
+ self.gdb = Gdb(gdb_cmd)
+ else:
+ self.gdb = Gdb()
+
+ self.logs.append(self.gdb.logname)
+
+ if self.binary:
+ self.gdb.command("file %s" % self.binary)
+ if self.target:
+ self.gdb.command("set arch riscv:rv%d" % self.hart.xlen)
+ self.gdb.command("set remotetimeout %d" % self.target.timeout_sec)
+ if self.server.port:
+ self.gdb.command(
+ "target extended-remote localhost:%d" % self.server.port)
+ self.gdb.select_hart(self.hart)
+
+ for cmd in self.target.gdb_setup:
+ self.gdb.command(cmd)
+
+ # FIXME: OpenOCD doesn't handle PRIV now
+ #self.gdb.p("$priv=3")
+
+ def postMortem(self):
+ if not self.gdb:
+ return
+ self.gdb.interrupt()
+ self.gdb.command("info registers all", timeout=10)
+
+ def classTeardown(self):
+ del self.gdb
+ BaseTest.classTeardown(self)
+
+class GdbSingleHartTest(GdbTest):
+ def classSetup(self):
+ GdbTest.classSetup(self)
+
+ for hart in self.target.harts:
+ # Park all harts that we're not using in a safe place.
+ if hart != self.hart:
+ self.gdb.select_hart(hart)
+ self.gdb.p("$pc=loop_forever")
+ self.gdb.select_hart(self.hart)
+
+class ExamineTarget(GdbTest):
+ def test(self):
+ self.hart.misa = self.gdb.p("$misa")
+
+ txt = "RV"
+ if (self.hart.misa >> 30) == 1:
+ txt += "32"
+ elif (self.hart.misa >> 62) == 2:
+ txt += "64"
+ elif (self.hart.misa >> 126) == 3:
+ txt += "128"
+ else:
+ raise TestFailed("Couldn't determine XLEN from $misa (0x%x)" %
+ self.hart.misa)
+
+ for i in range(26):
+ if self.hart.misa & (1<<i):
+ txt += chr(i + ord('A'))
+ print txt,
+
+class TestFailed(Exception):
+ def __init__(self, message):
+ Exception.__init__(self)
+ self.message = message
+
+class TestNotApplicable(Exception):
+ def __init__(self, message):
+ Exception.__init__(self)
+ self.message = message
+
+def assertEqual(a, b):
+ if a != b:
+ raise TestFailed("%r != %r" % (a, b))
+
+def assertNotEqual(a, b):
+ if a == b:
+ raise TestFailed("%r == %r" % (a, b))
+
+def assertIn(a, b):
+ if a not in b:
+ raise TestFailed("%r not in %r" % (a, b))
+
+def assertNotIn(a, b):
+ if a in b:
+ raise TestFailed("%r in %r" % (a, b))
+
+def assertGreater(a, b):
+ if not a > b:
+ raise TestFailed("%r not greater than %r" % (a, b))
+
+def assertLess(a, b):
+ if not a < b:
+ raise TestFailed("%r not less than %r" % (a, b))
+
+def assertTrue(a):
+ if not a:
+ raise TestFailed("%r is not True" % a)
+
+def assertRegexpMatches(text, regexp):
+ if not re.search(regexp, text):
+ raise TestFailed("can't find %r in %r" % (regexp, text))