Bug 1400380 - cancel timeout when debugging draft
authorIonut Goldan <igoldan@mozilla.com>
Wed, 15 Nov 2017 15:50:54 +0200
changeset 698342 a4525e9b2189b7acccc5a14bd075c4cc52c8291e
parent 698341 d5f775f4ff41b3e915f3cfdeb86fe28cc9bc0239
child 740348 25e498116860fc97265823f95e1d6997977b4db1
push id89257
push userbmo:igoldan@mozilla.com
push dateWed, 15 Nov 2017 15:09:44 +0000
bugs1400380
milestone59.0a1
Bug 1400380 - cancel timeout when debugging MozReview-Commit-ID: 9JJw3tM0FPp
testing/mozbase/mozprocess/mozprocess/processhandler.py
testing/talos/talos/talos_process.py
testing/talos/talos/ttest.py
testing/talos/talos/utils.py
--- a/testing/mozbase/mozprocess/mozprocess/processhandler.py
+++ b/testing/mozbase/mozprocess/mozprocess/processhandler.py
@@ -662,24 +662,29 @@ falling back to not using job objects fo
                  cwd=None,
                  env=None,
                  ignore_children=False,
                  kill_on_timeout=True,
                  processOutputLine=(),
                  processStderrLine=(),
                  onTimeout=(),
                  onFinish=(),
+                 debuggerInfo=None,
                  **kwargs):
         self.cmd = cmd
         self.args = args
         self.cwd = cwd
         self.didTimeout = False
         self._ignore_children = ignore_children
         self.keywordargs = kwargs
         self.read_buffer = ''
+        self.debuggerInfo = debuggerInfo
+
+        if debuggerInfo is not None:
+            self.cmd = [debuggerInfo.path] + debuggerInfo.args + cmd
 
         if env is None:
             env = os.environ.copy()
         self.env = env
 
         # handlers
         def to_callable_list(arg):
             if callable(arg):
@@ -1065,17 +1070,17 @@ class ProcessHandler(ProcessHandlerMixin
     If storeOutput==True, the output produced by the process will be saved
     as self.output.
 
     If logfile is not None, the output produced by the process will be
     appended to the given file.
     """
 
     def __init__(self, cmd, logfile=None, stream=True, storeOutput=True,
-                 **kwargs):
+                 debuggerInfo=None, **kwargs):
         kwargs.setdefault('processOutputLine', [])
         if callable(kwargs['processOutputLine']):
             kwargs['processOutputLine'] = [kwargs['processOutputLine']]
 
         if logfile:
             logoutput = LogOutput(logfile)
             kwargs['processOutputLine'].append(logoutput)
 
@@ -1088,9 +1093,9 @@ class ProcessHandler(ProcessHandlerMixin
             kwargs['processOutputLine'].append(streamoutput)
 
         self.output = None
         if storeOutput:
             storeoutput = StoreOutput()
             self.output = storeoutput.output
             kwargs['processOutputLine'].append(storeoutput)
 
-        ProcessHandlerMixin.__init__(self, cmd, **kwargs)
+        ProcessHandlerMixin.__init__(self, cmd, debuggerInfo=debuggerInfo, **kwargs)
--- a/testing/talos/talos/talos_process.py
+++ b/testing/talos/talos/talos_process.py
@@ -1,14 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 from __future__ import absolute_import
 
 import pprint
+import signal
 import time
 import traceback
 from threading import Event
 
 import mozcrash
 import psutil
 from mozlog import get_proxy_logger
 from mozprocess import ProcessHandler
@@ -73,17 +74,17 @@ class Reader(object):
 
         if not (line.startswith('JavaScript error:') or
                 line.startswith('JavaScript warning:')):
             LOG.process_output(self.proc.pid, line)
             self.output.append(line)
 
 
 def run_browser(command, minidump_dir, timeout=None, on_started=None,
-                **kwargs):
+                debug=None, debugger=None, debugger_args=None, **kwargs):
     """
     Run the browser using the given `command`.
 
     After the browser prints __endTimestamp, we give it 5
     seconds to quit and kill it if it's still alive at that point.
 
     Note that this method ensure that the process is killed at
     the end. If this is not possible, an exception will be raised.
@@ -97,30 +98,38 @@ def run_browser(command, minidump_dir, t
     :param on_started: a callback that can be used to do things just after
                        the browser has been started. The callback must takes
                        an argument, which is the psutil.Process instance
     :param kwargs: additional keyword arguments for the :class:`ProcessHandler`
                    instance
 
     Returns a ProcessContext instance, with available output and pid used.
     """
+
+    debuggerInfo = find_debugger_info(debug, debugger, debugger_args)
+
     context = ProcessContext()
     first_time = int(time.time()) * 1000
     wait_for_quit_timeout = 5
     event = Event()
     reader = Reader(event)
 
     LOG.info("Using env: %s" % pprint.pformat(kwargs['env']))
 
     kwargs['storeOutput'] = False
     kwargs['processOutputLine'] = reader
     kwargs['onFinish'] = event.set
-    proc = ProcessHandler(command, **kwargs)
+    proc = ProcessHandler(command, debuggerInfo=debuggerInfo, **kwargs)
     reader.proc = proc
     proc.run()
+
+    if debuggerInfo is not None:
+        timeout = None
+        signal.signal(signal.SIGINT, lambda sigid, frame: None)
+
     LOG.process_start(proc.pid, ' '.join(command))
     try:
         context.process = psutil.Process(proc.pid)
         if on_started:
             on_started(context.process)
         # wait until we saw __endTimestamp in the proc output,
         # or the browser just terminated - or we have a timeout
         if not event.wait(timeout):
@@ -160,8 +169,38 @@ def run_browser(command, minidump_dir, t
         % (int(time.time()) * 1000))
 
     if return_code is not None:
         LOG.process_exit(proc.pid, return_code)
     else:
         LOG.debug("Unable to detect exit code of the process %s." % proc.pid)
     context.output = reader.output
     return context
+
+
+def find_debugger_info(debug, debugger, debugger_args):
+    debuggerInfo = None
+    if debug or debugger or debugger_args:
+        import mozdebug
+
+        if not debugger:
+            # No debugger name was provided. Look for the default ones on
+            # current OS.
+            debugger = mozdebug.get_default_debugger_name(mozdebug.DebuggerSearch.KeepLooking)
+
+        debuggerInfo = None
+        if debugger:
+            debuggerInfo = mozdebug.get_debugger_info(debugger, debugger_args)
+
+        if debuggerInfo is None:
+            raise TalosError('Could not find a suitable debugger in your PATH.')
+
+        # Parameters come from the CLI. We need to convert them before
+        # their use.
+        if debugger_args:
+            from mozbuild import shellutil
+            try:
+                shellutil.split(debugger_args)
+            except shellutil.MetaCharacterException as e:
+                raise TalosError(
+                    "The --debugger_args you passed require a real shell to parse them."
+                    "(We can't handle the %r character.)" % e.char)
+    return debuggerInfo
--- a/testing/talos/talos/ttest.py
+++ b/testing/talos/talos/ttest.py
@@ -171,19 +171,16 @@ class TTest(object):
                 # to dump the profile.
                 timeout += 5 * 60
 
             command_args = utils.GenerateBrowserCommandLine(
                 browser_config['browser_path'],
                 browser_config['extra_args'],
                 setup.profile_dir,
                 test_config['url'],
-                browser_config['debug'],
-                browser_config['debugger'],
-                browser_config['debugger_args'],
                 profiling_info=(setup.gecko_profile.profiling_info
                                 if setup.gecko_profile else None)
             )
 
             mainthread_error_count = 0
             if test_config['setup']:
                 # Generate bcontroller.json for xperf
                 talosconfig.generateTalosConfig(command_args,
@@ -205,16 +202,19 @@ class TTest(object):
                 pcontext = run_browser(
                     command_args,
                     minidump_dir,
                     timeout=timeout,
                     env=setup.env,
                     # start collecting counters as soon as possible
                     on_started=(counter_management.start
                                 if counter_management else None),
+                    debug=browser_config['debug'],
+                    debugger=browser_config['debugger'],
+                    debugger_args=browser_config['debugger_args']
                 )
             except:
                 self.check_for_crashes(browser_config, minidump_dir,
                                        test_config['name'])
                 raise
             finally:
                 if counter_management:
                     counter_management.stop()
--- a/testing/talos/talos/utils.py
+++ b/testing/talos/talos/utils.py
@@ -113,51 +113,21 @@ def urlsplit(url, default_scheme='file')
 
 
 def parse_pref(value):
     """parse a preference value from a string"""
     from mozprofile.prefs import Preferences
     return Preferences.cast(value)
 
 
-def GenerateBrowserCommandLine(browser_path, extra_args, profile_dir, url,
-                               debug, debugger, debugger_args, profiling_info=None):
+def GenerateBrowserCommandLine(browser_path, extra_args, profile_dir, url, profiling_info=None):
     # TODO: allow for spaces in file names on Windows
 
     command_args = [browser_path.strip()]
 
-    if debug or debugger or debugger_args:
-        import mozdebug
-
-        if not debugger:
-            # No debugger name was provided. Look for the default ones on
-            # current OS.
-            debugger = mozdebug.get_default_debugger_name(mozdebug.DebuggerSearch.KeepLooking)
-
-        debuggerInfo = None
-        if debugger:
-            debuggerInfo = mozdebug.get_debugger_info(debugger, debugger_args)
-
-        if debuggerInfo is None:
-            raise TalosError('Could not find a suitable debugger in your PATH.')
-
-        # Parameters come from the CLI. We need to convert them before
-        # their use.
-        if debugger_args:
-            from mozbuild import shellutil
-            try:
-                debugger_args = shellutil.split(debugger_args)
-            except shellutil.MetaCharacterException as e:
-                raise TalosError(
-                    "The --debugger_args you passed require a real shell to parse them."
-                    "(We can't handle the %r character.)" % e.char)
-
-        # Prepend the debugger args.
-        command_args = [debuggerInfo.path] + debuggerInfo.args + command_args
-
     if platform.system() == 'Darwin':
         command_args.extend(['-foreground'])
 
     if isinstance(extra_args, list):
         command_args.extend(extra_args)
 
     elif extra_args.strip():
         command_args.extend([extra_args])