Fix race when finding the port OpenOCD is using.
[riscv-tests.git] / debug / testlib.py
index a38b99b0ea1d4cffe7a918476eb2a787941d0350..6b01d8df68dad2256e483299974408cd255a2cd0 100644 (file)
@@ -130,11 +130,10 @@ class Openocd(object):
         if debug:
             cmd.append("-d")
 
-        # Assign port
-        self.port = unused_port()
         # This command needs to come before any config scripts on the command
         # line, since they are executed in order.
-        cmd[1:1] = ["--command", "gdb_port %d" % self.port]
+        # Tell OpenOCD to bind to an unused port.
+        cmd[1:1] = ["--command", "gdb_port %d" % 0]
 
         logfile = open(Openocd.logname, "w")
         logfile.write("+ %s\n" % " ".join(cmd))
@@ -158,6 +157,36 @@ class Openocd(object):
                 messaged = True
                 print "Waiting for OpenOCD to examine RISCV core..."
 
+        self.port = self._get_gdb_server_port()
+
+    def _get_gdb_server_port(self):
+        """Get port that OpenOCD's gdb server is listening on."""
+        MAX_ATTEMPTS = 50
+        PORT_REGEX = re.compile(r'(?P<port>\d+) \(LISTEN\)')
+        for _ in range(MAX_ATTEMPTS):
+            with open(os.devnull, 'w') as devnull:
+                try:
+                    output = subprocess.check_output([
+                        'lsof',
+                        '-a',  # Take the AND of the following selectors
+                        '-p{}'.format(self.process.pid),  # Filter on PID
+                        '-iTCP',  # Filter only TCP sockets
+                    ], stderr=devnull)
+                except subprocess.CalledProcessError:
+                    output = ""
+            matches = list(PORT_REGEX.finditer(output))
+            matches = [m for m in matches if m.group('port') not in ('6666', '4444')]
+            if len(matches) > 1:
+                print output
+                raise Exception(
+                    "OpenOCD listening on multiple ports. Cannot uniquely "
+                    "identify gdb server port.")
+            elif matches:
+                [match] = matches
+                return int(match.group('port'))
+            time.sleep(0.1)
+        raise Exception("Timed out waiting for gdb server to obtain port.")
+
     def __del__(self):
         try:
             self.process.kill()
@@ -238,6 +267,13 @@ class Gdb(object):
         value = int(output.split(':')[1].strip(), 0)
         return value
 
+    def p_raw(self, obj):
+        output = self.command("p %s" % obj)
+        m = re.search("Cannot access memory at address (0x[0-9a-f]+)", output)
+        if m:
+            raise CannotAccess(int(m.group(1), 0))
+        return output.split('=')[-1].strip()
+
     def p(self, obj):
         output = self.command("p/x %s" % obj)
         m = re.search("Cannot access memory at address (0x[0-9a-f]+)", output)
@@ -444,6 +480,10 @@ 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)