Loading system/blueberry/tests/gd/cert/gd_device.py +63 −8 Original line number Original line Diff line number Diff line Loading @@ -15,6 +15,7 @@ # limitations under the License. # limitations under the License. from abc import ABC from abc import ABC from abc import abstractmethod from datetime import datetime from datetime import datetime import inspect import inspect import logging import logging Loading @@ -22,7 +23,6 @@ import os import pathlib import pathlib import shutil import shutil import signal import signal import socket import subprocess import subprocess import time import time from typing import List from typing import List Loading Loading @@ -141,6 +141,8 @@ class GdDeviceBase(ABC): """ """ WAIT_CHANNEL_READY_TIMEOUT_SECONDS = 10 WAIT_CHANNEL_READY_TIMEOUT_SECONDS = 10 WAIT_SIGINT_TIMEOUT_SECONDS = 5 WAIT_SIGKILL_TIMEOUT_SECONDS = 1 def __init__(self, grpc_port: str, grpc_root_server_port: str, signal_port: str, cmd: List[str], label: str, def __init__(self, grpc_port: str, grpc_root_server_port: str, signal_port: str, cmd: List[str], label: str, type_identifier: str, name: str, verbose_mode: bool): type_identifier: str, name: str, verbose_mode: bool): Loading Loading @@ -279,16 +281,17 @@ class GdDeviceBase(ABC): self.grpc_channel.close() self.grpc_channel.close() if self.grpc_root_server_port != -1: if self.grpc_root_server_port != -1: self.grpc_root_server_channel.close() self.grpc_root_server_channel.close() stop_signal = signal.SIGINT stop_signal = self.gracefully_stop_backing_process() self.backing_process.send_signal(stop_signal) try: try: return_code = self.backing_process.wait(timeout=self.WAIT_CHANNEL_READY_TIMEOUT_SECONDS) if stop_signal == 0: except subprocess.TimeoutExpired: raise RuntimeError("Failed to gracefully shutdown backing process") return_code = self.backing_process.wait(timeout=self.WAIT_SIGINT_TIMEOUT_SECONDS) except (subprocess.TimeoutExpired, RuntimeError): logging.error("[%s] Failed to interrupt backing process via SIGINT, sending SIGKILL" % self.label) logging.error("[%s] Failed to interrupt backing process via SIGINT, sending SIGKILL" % self.label) stop_signal = signal.SIGKILL stop_signal = signal.SIGKILL self.backing_process.kill() self.backing_process.kill() try: try: return_code = self.backing_process.wait(timeout=self.WAIT_CHANNEL_READY_TIMEOUT_SECONDS) return_code = self.backing_process.wait(timeout=self.WAIT_SIGKILL_TIMEOUT_SECONDS) except subprocess.TimeoutExpired: except subprocess.TimeoutExpired: logging.error("Failed to kill backing process") logging.error("Failed to kill backing process") return_code = -65536 return_code = -65536 Loading @@ -303,6 +306,10 @@ class GdDeviceBase(ABC): except grpc.FutureTimeoutError: except grpc.FutureTimeoutError: asserts.fail("[%s] wait channel ready timeout" % self.label) asserts.fail("[%s] wait channel ready timeout" % self.label) @abstractmethod def gracefully_stop_backing_process(self): return NotImplemented class GdHostOnlyDevice(GdDeviceBase): class GdHostOnlyDevice(GdDeviceBase): """ """ Loading Loading @@ -443,12 +450,19 @@ class GdHostOnlyDevice(GdDeviceBase): logging.warning("[%s] Failed to generated coverage summary, cmd result: %r" % (label, result)) logging.warning("[%s] Failed to generated coverage summary, cmd result: %r" % (label, result)) coverage_summary_path.unlink(missing_ok=True) coverage_summary_path.unlink(missing_ok=True) def gracefully_stop_backing_process(self): stop_signal = signal.SIGINT self.backing_process.send_signal(stop_signal) return stop_signal class GdAndroidDevice(GdDeviceBase): class GdAndroidDevice(GdDeviceBase): """Real Android device where the backing process is running on it """Real Android device where the backing process is running on it """ """ WAIT_FOR_DEVICE_TIMEOUT_SECONDS = 180 WAIT_FOR_DEVICE_TIMEOUT_SECONDS = 180 WAIT_FOR_DEVICE_SIGINT_TIMEOUT_SECONDS = 1 ADB_ABORT_EXIT_CODE = 134 def __init__(self, grpc_port: str, grpc_root_server_port: str, signal_port: str, cmd: List[str], label: str, def __init__(self, grpc_port: str, grpc_root_server_port: str, signal_port: str, cmd: List[str], label: str, type_identifier: str, name: str, serial_number: str, verbose_mode: bool): type_identifier: str, name: str, serial_number: str, verbose_mode: bool): Loading Loading @@ -542,14 +556,14 @@ class GdAndroidDevice(GdDeviceBase): stop_signal = signal.SIGINT stop_signal = signal.SIGINT self.logcat_process.send_signal(stop_signal) self.logcat_process.send_signal(stop_signal) try: try: return_code = self.logcat_process.wait(timeout=self.WAIT_CHANNEL_READY_TIMEOUT_SECONDS) return_code = self.logcat_process.wait(timeout=self.WAIT_FOR_DEVICE_SIGINT_TIMEOUT_SECONDS) except subprocess.TimeoutExpired: except subprocess.TimeoutExpired: logging.error("[%s_%s] Failed to interrupt logcat process via SIGINT, sending SIGKILL" % logging.error("[%s_%s] Failed to interrupt logcat process via SIGINT, sending SIGKILL" % (self.label, self.serial_number)) (self.label, self.serial_number)) stop_signal = signal.SIGKILL stop_signal = signal.SIGKILL self.logcat_process.kill() self.logcat_process.kill() try: try: return_code = self.logcat_process.wait(timeout=self.WAIT_CHANNEL_READY_TIMEOUT_SECONDS) return_code = self.logcat_process.wait(timeout=self.WAIT_SIGKILL_TIMEOUT_SECONDS) except subprocess.TimeoutExpired: except subprocess.TimeoutExpired: logging.error("Failed to kill logcat_process %s %s" % (self.label, self.serial_number)) logging.error("Failed to kill logcat_process %s %s" % (self.label, self.serial_number)) return_code = -65536 return_code = -65536 Loading Loading @@ -788,3 +802,44 @@ class GdAndroidDevice(GdDeviceBase): pass pass time.sleep(5) time.sleep(5) asserts.fail(msg='Device %s booting process timed out.' % self.serial_number) asserts.fail(msg='Device %s booting process timed out.' % self.serial_number) def gracefully_stop_backing_process(self): """ Gracefully stops backing process :return: expected backing process exit code on success, 0 on error """ backing_process_pid = None # Since we do not know which segment of self.cmd is the command running # on the Android device. We have to iterate with trial and error. cmd = self.cmd if len(self.cmd) >= 5: # skip adb -s serial shell to speed up the search # we don't know if any environment variables are set up before the # actual command and hence has to try from the 4th argument cmd = self.cmd[4:] + self.cmd[:4] for segment in cmd: try: # pgrep only takes 16 bytes including null terminator # -f cannot be used because that include the full command args current_cmd = pathlib.Path(segment).stem[:15] # -x matches whole command, cannot avoid as short segment may partially match # -n returnes the newest command matched backing_process_pid = int(self.adb.shell("pgrep -n -x {}".format(current_cmd))) logging.debug("Found backing process name on Android as {}, pid is {}".format( segment, backing_process_pid)) except (AdbError, ValueError) as e: logging.debug("Failed to run pgrep {}".format(e)) if backing_process_pid is not None: break if backing_process_pid is None: logging.warning("Failed to get pid for cmd {}".format(self.cmd)) try: logging.debug(self.adb.shell("ps -A | grep bluetooth")) except AdbError: pass return 0 stop_signal = signal.SIGINT self.adb.shell("kill -{} {}".format(stop_signal, backing_process_pid)) logging.debug("Sent SIGINT to backing process at pid {}".format(backing_process_pid)) stop_signal = -self.ADB_ABORT_EXIT_CODE return stop_signal Loading
system/blueberry/tests/gd/cert/gd_device.py +63 −8 Original line number Original line Diff line number Diff line Loading @@ -15,6 +15,7 @@ # limitations under the License. # limitations under the License. from abc import ABC from abc import ABC from abc import abstractmethod from datetime import datetime from datetime import datetime import inspect import inspect import logging import logging Loading @@ -22,7 +23,6 @@ import os import pathlib import pathlib import shutil import shutil import signal import signal import socket import subprocess import subprocess import time import time from typing import List from typing import List Loading Loading @@ -141,6 +141,8 @@ class GdDeviceBase(ABC): """ """ WAIT_CHANNEL_READY_TIMEOUT_SECONDS = 10 WAIT_CHANNEL_READY_TIMEOUT_SECONDS = 10 WAIT_SIGINT_TIMEOUT_SECONDS = 5 WAIT_SIGKILL_TIMEOUT_SECONDS = 1 def __init__(self, grpc_port: str, grpc_root_server_port: str, signal_port: str, cmd: List[str], label: str, def __init__(self, grpc_port: str, grpc_root_server_port: str, signal_port: str, cmd: List[str], label: str, type_identifier: str, name: str, verbose_mode: bool): type_identifier: str, name: str, verbose_mode: bool): Loading Loading @@ -279,16 +281,17 @@ class GdDeviceBase(ABC): self.grpc_channel.close() self.grpc_channel.close() if self.grpc_root_server_port != -1: if self.grpc_root_server_port != -1: self.grpc_root_server_channel.close() self.grpc_root_server_channel.close() stop_signal = signal.SIGINT stop_signal = self.gracefully_stop_backing_process() self.backing_process.send_signal(stop_signal) try: try: return_code = self.backing_process.wait(timeout=self.WAIT_CHANNEL_READY_TIMEOUT_SECONDS) if stop_signal == 0: except subprocess.TimeoutExpired: raise RuntimeError("Failed to gracefully shutdown backing process") return_code = self.backing_process.wait(timeout=self.WAIT_SIGINT_TIMEOUT_SECONDS) except (subprocess.TimeoutExpired, RuntimeError): logging.error("[%s] Failed to interrupt backing process via SIGINT, sending SIGKILL" % self.label) logging.error("[%s] Failed to interrupt backing process via SIGINT, sending SIGKILL" % self.label) stop_signal = signal.SIGKILL stop_signal = signal.SIGKILL self.backing_process.kill() self.backing_process.kill() try: try: return_code = self.backing_process.wait(timeout=self.WAIT_CHANNEL_READY_TIMEOUT_SECONDS) return_code = self.backing_process.wait(timeout=self.WAIT_SIGKILL_TIMEOUT_SECONDS) except subprocess.TimeoutExpired: except subprocess.TimeoutExpired: logging.error("Failed to kill backing process") logging.error("Failed to kill backing process") return_code = -65536 return_code = -65536 Loading @@ -303,6 +306,10 @@ class GdDeviceBase(ABC): except grpc.FutureTimeoutError: except grpc.FutureTimeoutError: asserts.fail("[%s] wait channel ready timeout" % self.label) asserts.fail("[%s] wait channel ready timeout" % self.label) @abstractmethod def gracefully_stop_backing_process(self): return NotImplemented class GdHostOnlyDevice(GdDeviceBase): class GdHostOnlyDevice(GdDeviceBase): """ """ Loading Loading @@ -443,12 +450,19 @@ class GdHostOnlyDevice(GdDeviceBase): logging.warning("[%s] Failed to generated coverage summary, cmd result: %r" % (label, result)) logging.warning("[%s] Failed to generated coverage summary, cmd result: %r" % (label, result)) coverage_summary_path.unlink(missing_ok=True) coverage_summary_path.unlink(missing_ok=True) def gracefully_stop_backing_process(self): stop_signal = signal.SIGINT self.backing_process.send_signal(stop_signal) return stop_signal class GdAndroidDevice(GdDeviceBase): class GdAndroidDevice(GdDeviceBase): """Real Android device where the backing process is running on it """Real Android device where the backing process is running on it """ """ WAIT_FOR_DEVICE_TIMEOUT_SECONDS = 180 WAIT_FOR_DEVICE_TIMEOUT_SECONDS = 180 WAIT_FOR_DEVICE_SIGINT_TIMEOUT_SECONDS = 1 ADB_ABORT_EXIT_CODE = 134 def __init__(self, grpc_port: str, grpc_root_server_port: str, signal_port: str, cmd: List[str], label: str, def __init__(self, grpc_port: str, grpc_root_server_port: str, signal_port: str, cmd: List[str], label: str, type_identifier: str, name: str, serial_number: str, verbose_mode: bool): type_identifier: str, name: str, serial_number: str, verbose_mode: bool): Loading Loading @@ -542,14 +556,14 @@ class GdAndroidDevice(GdDeviceBase): stop_signal = signal.SIGINT stop_signal = signal.SIGINT self.logcat_process.send_signal(stop_signal) self.logcat_process.send_signal(stop_signal) try: try: return_code = self.logcat_process.wait(timeout=self.WAIT_CHANNEL_READY_TIMEOUT_SECONDS) return_code = self.logcat_process.wait(timeout=self.WAIT_FOR_DEVICE_SIGINT_TIMEOUT_SECONDS) except subprocess.TimeoutExpired: except subprocess.TimeoutExpired: logging.error("[%s_%s] Failed to interrupt logcat process via SIGINT, sending SIGKILL" % logging.error("[%s_%s] Failed to interrupt logcat process via SIGINT, sending SIGKILL" % (self.label, self.serial_number)) (self.label, self.serial_number)) stop_signal = signal.SIGKILL stop_signal = signal.SIGKILL self.logcat_process.kill() self.logcat_process.kill() try: try: return_code = self.logcat_process.wait(timeout=self.WAIT_CHANNEL_READY_TIMEOUT_SECONDS) return_code = self.logcat_process.wait(timeout=self.WAIT_SIGKILL_TIMEOUT_SECONDS) except subprocess.TimeoutExpired: except subprocess.TimeoutExpired: logging.error("Failed to kill logcat_process %s %s" % (self.label, self.serial_number)) logging.error("Failed to kill logcat_process %s %s" % (self.label, self.serial_number)) return_code = -65536 return_code = -65536 Loading Loading @@ -788,3 +802,44 @@ class GdAndroidDevice(GdDeviceBase): pass pass time.sleep(5) time.sleep(5) asserts.fail(msg='Device %s booting process timed out.' % self.serial_number) asserts.fail(msg='Device %s booting process timed out.' % self.serial_number) def gracefully_stop_backing_process(self): """ Gracefully stops backing process :return: expected backing process exit code on success, 0 on error """ backing_process_pid = None # Since we do not know which segment of self.cmd is the command running # on the Android device. We have to iterate with trial and error. cmd = self.cmd if len(self.cmd) >= 5: # skip adb -s serial shell to speed up the search # we don't know if any environment variables are set up before the # actual command and hence has to try from the 4th argument cmd = self.cmd[4:] + self.cmd[:4] for segment in cmd: try: # pgrep only takes 16 bytes including null terminator # -f cannot be used because that include the full command args current_cmd = pathlib.Path(segment).stem[:15] # -x matches whole command, cannot avoid as short segment may partially match # -n returnes the newest command matched backing_process_pid = int(self.adb.shell("pgrep -n -x {}".format(current_cmd))) logging.debug("Found backing process name on Android as {}, pid is {}".format( segment, backing_process_pid)) except (AdbError, ValueError) as e: logging.debug("Failed to run pgrep {}".format(e)) if backing_process_pid is not None: break if backing_process_pid is None: logging.warning("Failed to get pid for cmd {}".format(self.cmd)) try: logging.debug(self.adb.shell("ps -A | grep bluetooth")) except AdbError: pass return 0 stop_signal = signal.SIGINT self.adb.shell("kill -{} {}".format(stop_signal, backing_process_pid)) logging.debug("Sent SIGINT to backing process at pid {}".format(backing_process_pid)) stop_signal = -self.ADB_ABORT_EXIT_CODE return stop_signal