This is an automated email from the ASF dual-hosted git repository.

mehrdadh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/main by this push:
     new e370ed4597 [Hexagon] Less aggressive adb state clean up (#10909)
e370ed4597 is described below

commit e370ed459739f5312e45a2fb3a446b120f8ec5d1
Author: Chris Sullivan <[email protected]>
AuthorDate: Wed Apr 13 15:19:41 2022 -0700

    [Hexagon] Less aggressive adb state clean up (#10909)
    
    * Only remove port forwarding applied in a session
    to avoid affecting global adb state.
    
    * Send SIGINT to attempt to allow remote
    server to cleanup and undbind port in
    deconstruction
    
    * Only attempt to forward ports not in use by
    adb or the system.
---
 python/tvm/contrib/hexagon/build.py           | 92 +++++++++++++++++++++------
 tests/python/contrib/test_hexagon/conftest.py |  6 +-
 2 files changed, 73 insertions(+), 25 deletions(-)

diff --git a/python/tvm/contrib/hexagon/build.py 
b/python/tvm/contrib/hexagon/build.py
index 16d3a30fd6..776faa9e9f 100644
--- a/python/tvm/contrib/hexagon/build.py
+++ b/python/tvm/contrib/hexagon/build.py
@@ -23,6 +23,7 @@ import multiprocessing as mp
 import os
 import pathlib
 import signal
+import socket
 import stat
 import subprocess
 from typing import Union
@@ -304,6 +305,7 @@ class HexagonLauncherAndroid(HexagonLauncherRPC):
         self._serial_number = serial_number
         adb_socket = rpc_info["adb_server_socket"] if 
rpc_info["adb_server_socket"] else "tcp:5037"
         self._adb_device_sub_cmd = ["adb", "-L", adb_socket, "-s", 
self._serial_number]
+        self.forwarded_ports_ = []
 
         super(HexagonLauncherAndroid, self).__init__(rpc_info, workspace)
 
@@ -356,26 +358,46 @@ class HexagonLauncherAndroid(HexagonLauncherRPC):
         for item in self.ANDROID_HEXAGON_RPC_FILES:
             self._copy_to_remote(lib_dir / item, self._workspace / item)
 
+    def _process_forwarded_ports(self):
+        forwarded_ports = subprocess.check_output(self._adb_device_sub_cmd + 
["forward", "--list"])
+        existing_forwards = []
+        for forward in str(forwarded_ports).split("\\n"):
+            entry = forward.split()
+            if len(entry) == 3:
+                _, local, _ = entry
+                existing_forwards.append(int(local.strip("tcp:")))
+        return existing_forwards
+
+    def _forward_ports(self, rpc_server_port, existing_forwards):
+        # Enable port forward for RPC server. We forward the first ten open 
ports
+        # starting from the rpc_server_port
+        port = rpc_server_port
+        while len(self.forwarded_ports_) < 10:
+            if port not in existing_forwards and not _is_port_in_use(port):
+                subprocess.check_call(
+                    self._adb_device_sub_cmd + ["forward", f"tcp:{port}", 
f"tcp:{port}"]
+                )
+                self.forwarded_ports_.append(port)
+            port += 1
+
+    def _reverse_ports(self, rpc_tracker_port):
+        subprocess.check_call(
+            self._adb_device_sub_cmd
+            + ["reverse", f"tcp:{rpc_tracker_port}", f"tcp:{rpc_tracker_port}"]
+        )
+
     def _run_server_script(self):
         """Setup the ADB connection and execute the server script."""
 
-        # Removed pre-defined forward/reverse rules
-        subprocess.check_call(self._adb_device_sub_cmd + ["forward", 
"--remove-all"])
-        subprocess.check_call(self._adb_device_sub_cmd + ["reverse", 
"--remove-all"])
-
+        # Collect any existing adb port forwarding to avoid duplication
+        # with another running process
+        existing_forwards = self._process_forwarded_ports()
         # Enable port reverse for RPC tracker
         rpc_tracker_port = self._rpc_info["rpc_tracker_port"]
         rpc_server_port = self._rpc_info["rpc_server_port"]
-        subprocess.check_call(
-            self._adb_device_sub_cmd
-            + ["reverse", f"tcp:{rpc_tracker_port}", f"tcp:{rpc_tracker_port}"]
-        )
-        # Enable port forward for RPC server. We forward 9 ports after the 
rpc_server_port.
-        for i in range(0, 10):
-            subprocess.check_call(
-                self._adb_device_sub_cmd
-                + ["forward", f"tcp:{rpc_server_port+i}", 
f"tcp:{rpc_server_port+i}"]
-            )
+
+        self._reverse_ports(rpc_tracker_port)
+        self._forward_ports(rpc_server_port, existing_forwards)
 
         # Run server and connect to tracker
         subprocess.Popen(
@@ -385,13 +407,27 @@ class HexagonLauncherAndroid(HexagonLauncherRPC):
             stderr=subprocess.PIPE,
         )
 
-    def start_server(self):
-        """Abstract method implementation. See description in 
HexagonLauncherRPC."""
-        self._copy_binaries()
-        self._run_server_script()
+    def _cleanup_port_forwarding(self):
+        # Removed pre-defined forward/reverse rules
+        rpc_tracker_port = self._rpc_info["rpc_tracker_port"]
+        subprocess.check_call(
+            self._adb_device_sub_cmd + ["reverse", "--remove", 
f"tcp:{rpc_tracker_port}"]
+        )
+        for port in self.forwarded_ports_:
+            subprocess.check_call(self._adb_device_sub_cmd + ["forward", 
"--remove", f"tcp:{port}"])
 
-    def stop_server(self):
-        """Abstract method implementation. See description in 
HexagonLauncherRPC."""
+    def _terminate_remote(self):
+        # Send interupt to main and child processes
+        subprocess.Popen(
+            self._adb_device_sub_cmd
+            + ["shell", f"pkill -l sigint -P `cat 
{self._workspace}/rpc_pid.txt`"]
+        )
+        subprocess.Popen(
+            self._adb_device_sub_cmd
+            + ["shell", f"kill -s sigint `cat {self._workspace}/rpc_pid.txt`"]
+        )
+        # Wait for processes to destruct cleanly after receiving the intrupt
+        subprocess.Popen(self._adb_device_sub_cmd + ["shell", "sleep", "0.1s"])
         # Kill process children
         subprocess.Popen(
             self._adb_device_sub_cmd + ["shell", f"pkill -P `cat 
{self._workspace}/rpc_pid.txt`"]
@@ -401,6 +437,16 @@ class HexagonLauncherAndroid(HexagonLauncherRPC):
             self._adb_device_sub_cmd + ["shell", f"kill `cat 
{self._workspace}/rpc_pid.txt`"]
         )
 
+    def start_server(self):
+        """Abstract method implementation. See description in 
HexagonLauncherRPC."""
+        self._copy_binaries()
+        self._run_server_script()
+
+    def stop_server(self):
+        """Abstract method implementation. See description in 
HexagonLauncherRPC."""
+        self._cleanup_port_forwarding()
+        self._terminate_remote()
+
 
 class HexagonLauncherSimulator(HexagonLauncherRPC):
     """Hexagon Launcher for Hexagon simulator."""
@@ -501,6 +547,12 @@ class HexagonLauncherSimulator(HexagonLauncherRPC):
         self._server_process.terminate()
 
 
+# https://stackoverflow.com/a/52872579/2689797
+def _is_port_in_use(port: int) -> bool:
+    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+        return s.connect_ex(("localhost", port)) == 0
+
+
 # pylint: disable=invalid-name
 def HexagonLauncher(
     serial_number: str,
diff --git a/tests/python/contrib/test_hexagon/conftest.py 
b/tests/python/contrib/test_hexagon/conftest.py
index 87bb69a349..009150b108 100644
--- a/tests/python/contrib/test_hexagon/conftest.py
+++ b/tests/python/contrib/test_hexagon/conftest.py
@@ -85,10 +85,6 @@ previous_port = None
 
 
 def get_free_port():
-    # https://stackoverflow.com/a/52872579/2689797
-    def is_port_in_use(port: int) -> bool:
-        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
-            return s.connect_ex(("localhost", port)) == 0
 
     global previous_port
     if previous_port is None:
@@ -96,7 +92,7 @@ def get_free_port():
     else:
         port = previous_port + 1
 
-    while is_port_in_use(port):
+    while tvm.contrib.hexagon.build._is_port_in_use(port):
         port = port + 1 if port < listen_port_max else listen_port_min
 
     previous_port = port

Reply via email to