Move target definition into individual files.
[riscv-tests.git] / debug / testlib.py
index 30d49e626dcc4133b5506f790858fe6562cd055c..f511088cf53e81c8fb9607db2c7aa3405e6d2b48 100644 (file)
@@ -1,4 +1,6 @@
+import collections
 import os.path
+import random
 import re
 import shlex
 import subprocess
@@ -82,7 +84,7 @@ class Spike(object):
             os.environ['REMOTE_BITBANG_HOST'] = 'localhost'
         self.infinite_loop = target.compile(
                 "programs/checksum.c", "programs/tiny-malloc.c",
-                "programs/infinite_loop.c", "-DDEFINE_MALLOC", "-DDEFINE_FREE")
+                "programs/infinite_loop.S", "-DDEFINE_MALLOC", "-DDEFINE_FREE")
         cmd.append(self.infinite_loop)
         logfile = open(self.logname, "w")
         logfile.write("+ %s\n" % " ".join(cmd))
@@ -208,6 +210,8 @@ class Openocd(object):
             if "Ready for Remote Connections" in log:
                 break
             if not self.process.poll() is None:
+                header("OpenOCD log")
+                sys.stdout.write(log)
                 raise Exception(
                         "OpenOCD exited before completing riscv_examine()")
             if not messaged and time.time() - start > 1:
@@ -217,7 +221,12 @@ class Openocd(object):
                 raise Exception("ERROR: Timed out waiting for OpenOCD to "
                         "examine RISCV core")
 
-        self.port = self._get_gdb_server_port()
+        try:
+            self.port = self._get_gdb_server_port()
+        except:
+            header("OpenOCD log")
+            sys.stdout.write(log)
+            raise
 
     def _get_gdb_server_port(self):
         """Get port that OpenOCD's gdb server is listening on."""
@@ -252,7 +261,7 @@ class Openocd(object):
         try:
             self.process.kill()
             self.process.wait()
-        except OSError:
+        except (OSError, AttributeError):
             pass
 
 class OpenocdCli(object):
@@ -286,6 +295,9 @@ class CannotAccess(Exception):
         Exception.__init__(self)
         self.address = address
 
+Thread = collections.namedtuple('Thread', ('id', 'target_id', 'name',
+    'frame'))
+
 class Gdb(object):
     logfile = tempfile.NamedTemporaryFile(prefix="gdb", suffix=".log")
     logname = logfile.name
@@ -312,13 +324,17 @@ class Gdb(object):
         self.child.expect(r"\(gdb\)", timeout=timeout)
         return self.child.before.strip()
 
-    def c(self, wait=True, timeout=-1):
+    def c(self, wait=True, timeout=-1, async=False):
+        if async:
+            async = "&"
+        else:
+            async = ""
         if wait:
-            output = self.command("c", timeout=timeout)
+            output = self.command("c%s" % async, timeout=timeout)
             assert "Continuing" in output
             return output
         else:
-            self.child.sendline("c")
+            self.child.sendline("c%s" % async)
             self.child.expect("Continuing")
 
     def interrupt(self):
@@ -372,6 +388,22 @@ class Gdb(object):
         assert "Hardware assisted breakpoint" in output
         return output
 
+    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)
@@ -384,8 +416,9 @@ def run_all_tests(module, target, parsed):
     todo = []
     if parsed.misaval:
         target.misa = int(parsed.misaval, 16)
-        print "Assuming $MISA value of 0x%x. Skipping ExamineTarget." % \
-                target.misa
+        print "Using $misa from command line: 0x%x" % target.misa
+    elif target.misa:
+        print "Using $misa from target definition: 0x%x" % target.misa
     else:
         todo.append(("ExamineTarget", ExamineTarget))
 
@@ -421,12 +454,12 @@ def run_tests(parsed, target, todo):
         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)
         sys.stdout.flush()
-        log_fd.write("Result: %s\n" % result)
-        log_fd.write("Time elapsed: %.2fs\n" % (time.time() - start))
         results.setdefault(result, []).append(name)
         count += 1
         if result not in good_results and parsed.fail_fast:
@@ -506,7 +539,7 @@ class BaseTest(object):
 
     def classSetup(self):
         self.compile()
-        self.target_process = self.target.target()
+        self.target_process = self.target.create()
         self.server = self.target.server()
         self.logs.append(self.server.logname)
 
@@ -584,9 +617,10 @@ class GdbTest(BaseTest):
         if self.server.port:
             self.gdb.command(
                     "target extended-remote localhost:%d" % self.server.port)
-            # Force gdb to discover threads now, otherwise it might interrupt
-            # us at some point when it decides by itself to check.
-            self.gdb.command("info threads")
+            # Select a random thread.
+            # TODO: Allow a command line option to force a specific thread.
+            thread = random.choice(self.gdb.threads())
+            self.gdb.thread(thread)
 
         # FIXME: OpenOCD doesn't handle PRIV now
         #self.gdb.p("$priv=3")