Bug 1851717 - Use subprocess instead of mozprocess in talos r=perftest-reviewers,sparky

Differential Revision: https://phabricator.services.mozilla.com/D187566
This commit is contained in:
Geoff Brown 2023-09-13 14:31:43 +00:00
parent 66b4b70e4d
commit 6086cf951e

View File

@ -7,13 +7,11 @@ import subprocess
import sys import sys
import time import time
import traceback import traceback
from threading import Event from threading import Timer
import mozcrash import mozcrash
import psutil import psutil
import six
from mozlog import get_proxy_logger from mozlog import get_proxy_logger
from mozprocess import ProcessHandler
from talos.utils import TalosError from talos.utils import TalosError
@ -72,27 +70,23 @@ class ProcessContext(object):
class Reader(object): class Reader(object):
def __init__(self, event): def __init__(self):
self.output = [] self.output = []
self.got_end_timestamp = False self.got_end_timestamp = False
self.got_timeout = False self.got_timeout = False
self.timeout_message = "" self.timeout_message = ""
self.got_error = False self.got_error = False
self.event = event
self.proc = None self.proc = None
def __call__(self, line): def __call__(self, line):
line = six.ensure_str(line) line = line.strip("\n")
if line.find("__endTimestamp") != -1: if line.find("__endTimestamp") != -1:
self.got_end_timestamp = True self.got_end_timestamp = True
self.event.set()
elif line == "TART: TIMEOUT": elif line == "TART: TIMEOUT":
self.got_timeout = True self.got_timeout = True
self.timeout_message = "TART" self.timeout_message = "TART"
self.event.set()
elif line.startswith("TEST-UNEXPECTED-FAIL | "): elif line.startswith("TEST-UNEXPECTED-FAIL | "):
self.got_error = True self.got_error = True
self.event.set()
if not ( if not (
"JavaScript error:" in line "JavaScript error:" in line
@ -132,7 +126,7 @@ def run_browser(
:param on_started: a callback that can be used to do things just after :param on_started: a callback that can be used to do things just after
the browser has been started. The callback must takes the browser has been started. The callback must takes
an argument, which is the psutil.Process instance an argument, which is the psutil.Process instance
:param kwargs: additional keyword arguments for the :class:`ProcessHandler` :param kwargs: additional keyword arguments for the :class:`subprocess.Popen`
instance instance
Returns a ProcessContext instance, with available output and pid used. Returns a ProcessContext instance, with available output and pid used.
@ -148,34 +142,42 @@ def run_browser(
context = ProcessContext(is_launcher) context = ProcessContext(is_launcher)
first_time = int(time.time()) * 1000 first_time = int(time.time()) * 1000
wait_for_quit_timeout = 20 wait_for_quit_timeout = 20
event = Event() reader = Reader()
reader = Reader(event)
LOG.info("Using env: %s" % pprint.pformat(kwargs["env"])) LOG.info("Using env: %s" % pprint.pformat(kwargs["env"]))
kwargs["storeOutput"] = False timed_out = False
kwargs["processOutputLine"] = reader
kwargs["onFinish"] = event.set def timeout_handler():
proc = ProcessHandler(command, **kwargs) nonlocal timed_out
timed_out = True
proc_timer = Timer(timeout, timeout_handler)
proc_timer.start()
proc = subprocess.Popen(
command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, **kwargs
)
reader.proc = proc reader.proc = proc
proc.run()
LOG.process_start(proc.pid, " ".join(command)) LOG.process_start(proc.pid, " ".join(command))
try: try:
context.process = psutil.Process(proc.pid) context.process = psutil.Process(proc.pid)
if on_started: if on_started:
on_started(context.process) on_started(context.process)
# wait until we saw __endTimestamp in the proc output,
# or the browser just terminated - or we have a timeout # read output until the browser terminates or the timeout is hit
if not event.wait(timeout): for line in proc.stdout:
LOG.info("Timeout waiting for test completion; killing browser...") reader(line)
# try to extract the minidump stack if the browser hangs if timed_out:
kill_and_get_minidump(context, minidump_dir) LOG.info("Timeout waiting for test completion; killing browser...")
raise TalosError("timeout") # try to extract the minidump stack if the browser hangs
kill_and_get_minidump(context, minidump_dir)
raise TalosError("timeout")
break
if reader.got_end_timestamp: if reader.got_end_timestamp:
for i in six.moves.range(1, wait_for_quit_timeout): proc.wait(wait_for_quit_timeout)
if proc.wait(1) is not None:
break
if proc.poll() is None: if proc.poll() is None:
LOG.info( LOG.info(
"Browser shutdown timed out after {0} seconds, killing" "Browser shutdown timed out after {0} seconds, killing"
@ -193,6 +195,7 @@ def run_browser(
finally: finally:
# this also handle KeyboardInterrupt # this also handle KeyboardInterrupt
# ensure early the process is really terminated # ensure early the process is really terminated
proc_timer.cancel()
return_code = None return_code = None
try: try:
return_code = context.kill_process() return_code = context.kill_process()