Bug 1425308 - Make it easier to view talos gecko profiles in perf-html.io when running locally draft
authorRob Wood <rwood@mozilla.com>
Fri, 12 Jan 2018 14:02:08 -0500
changeset 750251 f88b469271930b0c0d9f1498d115fd9e1df99d1f
parent 749804 39a50040a93aa1102692f810830e71d391febdc8
push id97597
push userrwood@mozilla.com
push dateThu, 01 Feb 2018 20:30:09 +0000
bugs1425308
milestone60.0a1
Bug 1425308 - Make it easier to view talos gecko profiles in perf-html.io when running locally MozReview-Commit-ID: 4F3RQcUXfMg
testing/mozharness/mozharness/mozilla/testing/talos.py
testing/talos/mach_commands.py
testing/talos/talos/cmdline.py
testing/talos/talos/gecko_profile.py
--- a/testing/mozharness/mozharness/mozilla/testing/talos.py
+++ b/testing/mozharness/mozharness/mozilla/testing/talos.py
@@ -10,35 +10,41 @@ run talos tests in a virtualenv
 
 import os
 import sys
 import pprint
 import copy
 import re
 import shutil
 import subprocess
+import tempfile
 import json
 
+import mozinfo
+import mozrunner
 import mozharness
+
 from mozharness.base.config import parse_config_file
 from mozharness.base.errors import PythonErrorList
 from mozharness.base.log import OutputParser, DEBUG, ERROR, CRITICAL
 from mozharness.base.log import INFO, WARNING
 from mozharness.base.python import Python3Virtualenv
 from mozharness.mozilla.blob_upload import BlobUploadMixin, blobupload_config_options
 from mozharness.mozilla.testing.testbase import TestingMixin, testing_config_options
 from mozharness.base.vcs.vcsbase import MercurialScript
 from mozharness.mozilla.testing.errors import TinderBoxPrintRe
 from mozharness.mozilla.buildbot import TBPL_SUCCESS, TBPL_WORST_LEVEL_TUPLE
 from mozharness.mozilla.buildbot import TBPL_RETRY, TBPL_FAILURE, TBPL_WARNING
 from mozharness.mozilla.tooltool import TooltoolMixin
 from mozharness.mozilla.testing.codecoverage import (
     CodeCoverageMixin,
     code_coverage_config_options
 )
+from mozprocess import processhandler
+from mozprofile.profile import Profile
 
 
 scripts_path = os.path.abspath(os.path.dirname(os.path.dirname(mozharness.__file__)))
 external_tools_path = os.path.join(scripts_path, 'external_tools')
 
 TalosErrorList = PythonErrorList + [
     {'regex': re.compile(r'''run-as: Package '.*' is unknown'''), 'level': DEBUG},
     {'substr': r'''FAIL: Graph server unreachable''', 'level': CRITICAL},
@@ -175,24 +181,26 @@ class Talos(TestingMixin, MercurialScrip
         kwargs.setdefault('all_actions', ['clobber',
                                           'read-buildbot-config',
                                           'download-and-extract',
                                           'populate-webroot',
                                           'create-virtualenv',
                                           'install',
                                           'setup-mitmproxy',
                                           'run-tests',
+                                          'start-gecko-profile-viewer',
                                           ])
         kwargs.setdefault('default_actions', ['clobber',
                                               'download-and-extract',
                                               'populate-webroot',
                                               'create-virtualenv',
                                               'install',
                                               'setup-mitmproxy',
                                               'run-tests',
+                                              'start-gecko-profile-viewer',
                                               ])
         kwargs.setdefault('config', {})
         super(Talos, self).__init__(**kwargs)
 
         self.workdir = self.query_abs_dirs()['abs_work_dir']  # convenience
 
         self.run_local = self.config.get('run_local')
         self.installer_url = self.config.get("installer_url")
@@ -748,16 +756,71 @@ class Talos(TestingMixin, MercurialScrip
                 if not self.run_local:
                     # copy results to upload dir so they are included as an artifact
                     dest = os.path.join(env['MOZ_UPLOAD_DIR'], 'perfherder-data.json')
                     self._artifact_perf_data(dest)
 
         self.buildbot_status(parser.worst_tbpl_status,
                              level=parser.worst_log_level)
 
+    def start_gecko_profile_viewer(self):
+        # if running locally and gecko profiling is on, start a new
+        # webserver and serve the talos gecko profiles output folder
+        if not self.run_local or ("--geckoProfile" not in self.config.get('talos_extra_options')):
+            self.info("Skipping view gecko profile step")
+            return
+
+        # if the user has set the 'TALOS_DISABLE_PROF_AUTO_VIEW=1' env var then don't launch the viewer
+        if os.getenv('TALOS_DISABLE_PERFHTML_AUTO_LAUNCH') == '1':
+            self.info("TALOS_DISABLE_PERFHTML_AUTO_LAUNCH is set, skipping view gecko profile step")
+            return
+
+        gecko_profiles_out_dir = os.path.join(self.repo_path,
+                                              'testing/talos/talos/gecko_profiles_out')
+
+        # make sure some gecko profiles exist
+        if not self._local_gecko_profiles_exist(gecko_profiles_out_dir):
+            # no local gecko profile found; nothing to display
+            self.critical("Couldn't find any gecko profiles in: %s" % gecko_profiles_out_dir)
+            return
+
+        # start Firefox with gecko viewer url
+        self._start_perfhtmlio(gecko_profiles_out_dir)
+
+    def _local_gecko_profiles_exist(self, gecko_profiles_out_dir):
+        for root, dirs, files in os.walk(gecko_profiles_out_dir):
+            for name in files:
+                self.info(name)
+                if ".profile" in name:
+                    return True
+        return False
+
+    def _start_perfhtmlio(self, gecko_profiles_out_dir):
+        load_url = "https://perf-html.io/"
+        
+        # make a new Firefox profile (in caes Firefox is already running elsewhere)
+        profile_dir = os.path.join(tempfile.mkdtemp(), 'profile')
+        profile = Profile(profile_dir)
+
+        # Firefox cmd line
+        command = [self.config['binary_path'], '-profile', profile_dir, load_url]
+        self.info(' '.join(command))
+        self.info("Starting Firefox and loading %s" % load_url)
+        perfhtml_proc = processhandler.ProcessHandler(command)
+        perfhtml_proc.run()
+
+        self.info("Started prcoess %d: %s" % (perfhtml_proc.pid, command))
+        self.info("*" * 75)
+        self.info("   In perf-html.io, click the 'Browse' button and navigate to:")
+        self.info("   %s" % gecko_profiles_out_dir)
+        self.info("*" * 75)
+
+        # give a few sec for it to startup etc. then leave it running but let talos finish up
+        perfhtml_proc.wait(timeout=3)
+
     def fetch_python3(self):
         manifest_file = os.path.join(
             self.talos_path,
             'talos',
             'mitmproxy',
             self.config.get('python3_manifest')[self.platform_name()])
         output_dir = self.query_abs_dirs()['abs_work_dir']
         # Slowdown: The unzipped Python3 installation gets deleted every time
--- a/testing/talos/mach_commands.py
+++ b/testing/talos/mach_commands.py
@@ -49,16 +49,17 @@ class TalosRunner(MozbuildObject):
 
     def make_config(self):
         default_actions = ['populate-webroot']
         if not os.path.exists(self.virtualenv_path):
             default_actions.append('create-virtualenv')
         default_actions.extend([
             'setup-mitmproxy',
             'run-tests',
+            'start-gecko-profile-viewer',
         ])
         self.config = {
             'run_local': True,
             'talos_json': self.talos_json,
             'binary_path': self.binary_path,
             'repo_path': self.topsrcdir,
             'obj_path': self.topobjdir,
             'log_name': 'talos',
--- a/testing/talos/talos/cmdline.py
+++ b/testing/talos/talos/cmdline.py
@@ -76,27 +76,23 @@ def create_parser(mach_interface=False):
     add_arg("--mozAfterPaint", action='store_true', dest="tpmozafterpaint",
             help="wait for MozAfterPaint event before recording the time")
     add_arg("--firstPaint", action='store_true', dest="firstpaint",
             help="Also report the first paint value in supported tests")
     add_arg("--useHero", action='store_true', dest="tphero",
             help="use Hero elementtiming attribute to record the time")
     add_arg("--userReady", action='store_true', dest="userready",
             help="Also report the user ready value in supported tests")
-    add_arg('--spsProfile', action="store_true", dest="gecko_profile",
-            help="(Deprecated - Use --geckoProfile instead.) Profile the "
-                 "run and output the results in $MOZ_UPLOAD_DIR.")
-    add_arg('--spsProfileInterval', dest='gecko_profile_interval', type=float,
-            help="(Deprecated - Use --geckoProfileInterval instead.) How "
-                 "frequently to take samples (ms)")
-    add_arg('--spsProfileEntries', dest="gecko_profile_entries", type=int,
-            help="(Deprecated - Use --geckoProfileEntries instead.) How "
-                 "many samples to take with the profiler")
     add_arg('--geckoProfile', action="store_true", dest="gecko_profile",
-            help="Profile the run and output the results in $MOZ_UPLOAD_DIR.")
+            help="Profile the talos run and output the results in $MOZ_UPLOAD_DIR. "
+                 "When running talos locally, profiles will also be available at: "
+                 "'/testing/talos/talos/gecko_profiles_out'. "
+                 "After talos is finished, Perf-html.io will be launched in Firefox so you can "
+                 "analyze the local profiles. To disable auto-launching of Perf-html.io, set "
+                 "the TALOS_DISABLE_PERFHTML_AUTO_LAUNCH=1 env var.")
     add_arg('--geckoProfileInterval', dest='gecko_profile_interval', type=float,
             help="How frequently to take samples (ms)")
     add_arg('--geckoProfileEntries', dest="gecko_profile_entries", type=int,
             help="How many samples to take with the profiler")
     add_arg('--extension', dest='extensions', action='append',
             default=['${talos}/talos-powers',
                      '${talos}/pageloader'],
             help="Extension to install while running")
--- a/testing/talos/talos/gecko_profile.py
+++ b/testing/talos/talos/gecko_profile.py
@@ -66,16 +66,18 @@ class GeckoProfile(object):
             "gecko_profile_threads": gecko_profile_threads
         }
 
         # when running locally, profiles will be zipped up, and also stored in a
         # talos gecko profiles output folder, so they can easily be viewed later
         if self.browser_config['develop']:
             if os.path.exists(self.browser_config['gecko_profiles_out_dir']):
                 mozfile.remove(self.browser_config['gecko_profiles_out_dir'])
+        # make path available as env var (used later if automatically displaying profile)
+        os.environ["GECKO_PROFILES_OUT_DIR"] = self.browser_config['gecko_profiles_out_dir']
 
     def option(self, name):
         return self.profiling_info["gecko_profile_" + name]
 
     def update_env(self, env):
         """
         update the given env to update some env vars if required.
         """