зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1628073 - Rewrite view_gecko_profile.py to open a URL; r=perftest-reviewers,sparky
The current implementation opens the built Firefox in the objdir. This is not optimal as the built Firefox is not really great for viewing files in. The build could be broken. With this patch, the profiles will instead be opened in the users default browser. Differential Revision: https://phabricator.services.mozilla.com/D70089
This commit is contained in:
Родитель
723f5d5bba
Коммит
01b425b6d5
|
@ -32,7 +32,7 @@ from cmdline import parse_args, CHROMIUM_DISTROS
|
|||
from logger.logger import RaptorLogger
|
||||
from manifest import get_raptor_test_list
|
||||
from signal_handler import SignalHandler
|
||||
from utils import view_gecko_profile
|
||||
from utils import view_gecko_profile_from_raptor
|
||||
from webextension import (
|
||||
WebExtensionFirefox,
|
||||
WebExtensionDesktopChrome,
|
||||
|
@ -194,7 +194,7 @@ def main(args=sys.argv[1:]):
|
|||
"Not launching profiler.firefox.com because DISABLE_PROFILE_LAUNCH=1"
|
||||
)
|
||||
else:
|
||||
view_gecko_profile(args.binary)
|
||||
view_gecko_profile_from_raptor()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -60,20 +60,13 @@ def transform_subtest(str_to_transform, subtest_name):
|
|||
return str_to_transform.replace('{subtest}', subtest_name)
|
||||
|
||||
|
||||
def view_gecko_profile(ffox_bin):
|
||||
# automatically load the latest talos gecko-profile archive in profiler.firefox.com
|
||||
def view_gecko_profile_from_raptor():
|
||||
# automatically load the latest raptor gecko-profile archive in profiler.firefox.com
|
||||
LOG_GECKO = RaptorLogger(component='raptor-view-gecko-profile')
|
||||
|
||||
if sys.platform.startswith('win') and not ffox_bin.endswith(".exe"):
|
||||
ffox_bin = ffox_bin + ".exe"
|
||||
|
||||
if not os.path.exists(ffox_bin):
|
||||
LOG_GECKO.info("unable to find Firefox bin, cannot launch view-gecko-profile")
|
||||
return
|
||||
|
||||
profile_zip = os.environ.get('RAPTOR_LATEST_GECKO_PROFILE_ARCHIVE', None)
|
||||
if profile_zip is None or not os.path.exists(profile_zip):
|
||||
LOG_GECKO.info("No local talos gecko profiles were found so not "
|
||||
LOG_GECKO.info("No local raptor gecko profiles were found so not "
|
||||
"launching profiler.firefox.com")
|
||||
return
|
||||
|
||||
|
@ -91,18 +84,15 @@ def view_gecko_profile(ffox_bin):
|
|||
|
||||
command = [sys.executable,
|
||||
view_gp,
|
||||
'-b', ffox_bin,
|
||||
'-p', profile_zip]
|
||||
|
||||
LOG_GECKO.info('Auto-loading this profile in perfhtml.io: %s' % profile_zip)
|
||||
LOG_GECKO.info(command)
|
||||
|
||||
# if the view-gecko-profile tool fails to launch for some reason, we don't
|
||||
# want to crash talos! just dump error and finsh up talos as usual
|
||||
# want to crash talos! just dump error and finish up talos as usual
|
||||
try:
|
||||
view_profile = subprocess.Popen(command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
view_profile = subprocess.Popen(command)
|
||||
# that will leave it running in own instance and let talos finish up
|
||||
except Exception as e:
|
||||
LOG_GECKO.info("failed to launch view-gecko-profile tool, exeption: %s" % e)
|
||||
|
|
|
@ -340,22 +340,14 @@ function FindProxyForURL(url, host) {
|
|||
if os.environ.get('DISABLE_PROFILE_LAUNCH', '0') == '1':
|
||||
LOG.info("Not launching profiler.firefox.com because DISABLE_PROFILE_LAUNCH=1")
|
||||
else:
|
||||
view_gecko_profile(config['browser_path'])
|
||||
view_gecko_profile_from_talos()
|
||||
|
||||
# we will stop running tests on a failed test, or we will return 0 for
|
||||
# green
|
||||
return 0
|
||||
|
||||
|
||||
def view_gecko_profile(ffox_bin):
|
||||
# automatically load the latest talos gecko-profile archive in profiler.firefox.com
|
||||
if sys.platform.startswith('win') and not ffox_bin.endswith(".exe"):
|
||||
ffox_bin = ffox_bin + ".exe"
|
||||
|
||||
if not os.path.exists(ffox_bin):
|
||||
LOG.info("unable to find Firefox bin, cannot launch view-gecko-profile")
|
||||
return
|
||||
|
||||
def view_gecko_profile_from_talos():
|
||||
profile_zip = os.environ.get('TALOS_LATEST_GECKO_PROFILE_ARCHIVE', None)
|
||||
if profile_zip is None or not os.path.exists(profile_zip):
|
||||
LOG.info("No local talos gecko profiles were found so not launching profiler.firefox.com")
|
||||
|
@ -375,18 +367,15 @@ def view_gecko_profile(ffox_bin):
|
|||
|
||||
command = ['python',
|
||||
view_gp,
|
||||
'-b', ffox_bin,
|
||||
'-p', profile_zip]
|
||||
|
||||
LOG.info('Auto-loading this profile in perfhtml.io: %s' % profile_zip)
|
||||
LOG.info(command)
|
||||
LOG.info('Auto-loading this profile in profiler.firefox.com: %s' % profile_zip)
|
||||
LOG.info(' '.join(command))
|
||||
|
||||
# if the view-gecko-profile tool fails to launch for some reason, we don't
|
||||
# want to crash talos! just dump error and finsh up talos as usual
|
||||
try:
|
||||
view_profile = subprocess.Popen(command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
view_profile = subprocess.Popen(command)
|
||||
# that will leave it running in own instance and let talos finish up
|
||||
except Exception as e:
|
||||
LOG.info("failed to launch view-gecko-profile tool, exeption: %s" % e)
|
||||
|
|
|
@ -10,12 +10,8 @@ pip install -r requirements.txt
|
|||
|
||||
Then the command line:
|
||||
|
||||
python view_gecko_profile.py -b <path to browser binary> -p <path to gecko_profile.zip>
|
||||
python view_gecko_profile.py -p <path to gecko_profile.zip>
|
||||
|
||||
i.e. For Firefox:
|
||||
i.e.:
|
||||
|
||||
python view_gecko_profile.py -b "/Users/rwood/mozilla-unified/obj-x86_64-apple-darwin17.4.0/dist/Nightly.app/Contents/MacOS/firefox" -p /Users/rwood/mozilla-unified/testing/mozharness/build/blobber_upload_dir/profile_damp.zip
|
||||
|
||||
i.e. For Chrome:
|
||||
|
||||
python view_gecko_profile.py -b "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" -p /Users/rwood/mozilla-unified/testing/mozharness/build/blobber_upload_dir/profile_damp.zip
|
||||
python view_gecko_profile.py -p /Users/rwood/mozilla-unified/testing/mozharness/build/blobber_upload_dir/profile_damp.zip
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
mozrunner ~= 7.0
|
||||
wptserve ~= 1.4.0
|
||||
mozlog==6.0
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
from __future__ import absolute_import
|
||||
import sys
|
||||
|
||||
|
||||
import argparse
|
||||
import json
|
||||
|
@ -17,134 +19,70 @@ from threading import Thread
|
|||
|
||||
from mozlog import commandline, get_default_logger
|
||||
from mozlog.commandline import add_logging_group
|
||||
from mozrunner import runners
|
||||
|
||||
from wptserve import server, handlers
|
||||
import SocketServer
|
||||
import SimpleHTTPServer
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
class ProfileServingHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
"""Extends the basic SimpleHTTPRequestHandler (which serves a directory
|
||||
of files) to include request headers required by profiler.firefox.com"""
|
||||
def end_headers(self):
|
||||
self.send_header("Access-Control-Allow-Origin", "https://profiler.firefox.com")
|
||||
SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self)
|
||||
|
||||
|
||||
class ViewGeckoProfile(object):
|
||||
"""Container class for ViewGeckoProfile"""
|
||||
|
||||
def __init__(self, browser_binary, gecko_profile_zip):
|
||||
def __init__(self, gecko_profile_data_path):
|
||||
self.log = get_default_logger(component='view-gecko-profile')
|
||||
self.browser_bin = browser_binary
|
||||
self.gecko_profile_zip = gecko_profile_zip
|
||||
self.gecko_profile_dir = os.path.dirname(gecko_profile_zip)
|
||||
self.perfhtmlio_url = "https://profiler.firefox.com/from-url/"
|
||||
self.gecko_profile_data_path = gecko_profile_data_path
|
||||
self.gecko_profile_dir = os.path.dirname(gecko_profile_data_path)
|
||||
self.profiler_url = "https://profiler.firefox.com/from-url/"
|
||||
self.httpd = None
|
||||
self.host = None
|
||||
self.host = '127.0.0.1'
|
||||
self.port = None
|
||||
|
||||
# Create the runner
|
||||
self.output_handler = OutputHandler()
|
||||
process_args = {
|
||||
'processOutputLine': [self.output_handler],
|
||||
}
|
||||
runner_cls = runners['firefox']
|
||||
self.runner = runner_cls(
|
||||
browser_binary, profile=None, process_args=process_args)
|
||||
|
||||
def start_http_server(self):
|
||||
self.write_server_headers()
|
||||
|
||||
def setup_http_server(self):
|
||||
# pick a free port
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.bind(('', 0))
|
||||
port = sock.getsockname()[1]
|
||||
self.port = sock.getsockname()[1]
|
||||
sock.close()
|
||||
_webserver = '127.0.0.1:%d' % port
|
||||
|
||||
self.httpd = self.setup_webserver(_webserver)
|
||||
self.httpd.start()
|
||||
os.chdir(self.gecko_profile_dir)
|
||||
self.httpd = SocketServer.TCPServer((self.host, self.port), ProfileServingHTTPRequestHandler)
|
||||
self.log.info("File server started at: %s:%s" % (self.host, self.port))
|
||||
|
||||
def write_server_headers(self):
|
||||
# to add specific headers for serving files via wptserve, write out a headers dir file
|
||||
# see http://wptserve.readthedocs.io/en/latest/handlers.html#file-handlers
|
||||
self.log.info("writing wptserve headers file")
|
||||
headers_file = os.path.join(self.gecko_profile_dir, '__dir__.headers')
|
||||
file = open(headers_file, 'w')
|
||||
file.write("Access-Control-Allow-Origin: *")
|
||||
file.close()
|
||||
self.log.info("wrote wpt headers file: %s" % headers_file)
|
||||
|
||||
def setup_webserver(self, webserver):
|
||||
self.log.info("starting webserver on %r" % webserver)
|
||||
self.host, self.port = webserver.split(':')
|
||||
|
||||
return server.WebTestHttpd(port=int(self.port), doc_root=self.gecko_profile_dir,
|
||||
routes=[("GET", "*", handlers.file_handler)])
|
||||
def handle_single_request(self):
|
||||
self.httpd.handle_request()
|
||||
|
||||
def encode_url(self):
|
||||
# Encode url i.e.: https://profiler.firefox.com/from-url/http... (the profile_zip served locally)
|
||||
url = 'http://' + self.host + ':' + self.port + '/' + os.path.basename(self.gecko_profile_zip)
|
||||
url = "http://{}:{}/{}".format(self.host, self.port,
|
||||
os.path.basename(self.path_to_gecko_profile_data))
|
||||
self.log.info("raw url is:")
|
||||
self.log.info(url)
|
||||
encoded_url = urllib.quote(url, safe='')
|
||||
self.log.info('encoded url is:')
|
||||
self.log.info(encoded_url)
|
||||
self.perfhtmlio_url = self.perfhtmlio_url + encoded_url
|
||||
self.profiler_url = self.profiler_url + encoded_url
|
||||
self.log.info('full url is:')
|
||||
self.log.info(self.perfhtmlio_url)
|
||||
self.log.info(self.profiler_url)
|
||||
|
||||
def start_browser_perfhtml(self):
|
||||
self.log.info("Starting browser and opening the gecko profile zip in profiler.firefox.com...")
|
||||
|
||||
self.runner.cmdargs.append(self.perfhtmlio_url)
|
||||
self.runner.start()
|
||||
|
||||
first_time = int(time.time()) * 1000
|
||||
proc = self.runner.process_handler
|
||||
self.output_handler.proc = proc
|
||||
|
||||
try:
|
||||
self.runner.wait(timeout=None)
|
||||
except Exception:
|
||||
self.log.info("Failed to start browser")
|
||||
|
||||
|
||||
class OutputHandler(object):
|
||||
def __init__(self):
|
||||
self.proc = None
|
||||
self.kill_thread = Thread(target=self.wait_for_quit)
|
||||
self.kill_thread.daemon = True
|
||||
self.log = get_default_logger(component='view_gecko_profile')
|
||||
|
||||
def __call__(self, line):
|
||||
if not line.strip():
|
||||
return
|
||||
line = line.decode('utf-8', errors='replace')
|
||||
|
||||
try:
|
||||
data = json.loads(line)
|
||||
except ValueError:
|
||||
self.process_output(line)
|
||||
return
|
||||
|
||||
if isinstance(data, dict) and 'action' in data:
|
||||
self.log.log_raw(data)
|
||||
else:
|
||||
self.process_output(json.dumps(data))
|
||||
|
||||
def process_output(self, line):
|
||||
self.log.process_output(self.proc.pid, line)
|
||||
|
||||
def wait_for_quit(self, timeout=5):
|
||||
"""Wait timeout seconds for the process to exit. If it hasn't
|
||||
exited by then, kill it.
|
||||
"""
|
||||
time.sleep(timeout)
|
||||
if self.proc.poll() is None:
|
||||
self.proc.kill()
|
||||
def open_profile_in_browser(self):
|
||||
# Open the file in the user's preferred browser.
|
||||
self.log.info("Opening the profile data in profiler.firefox.com...")
|
||||
import webbrowser
|
||||
webbrowser.open_new_tab(self.profiler_url)
|
||||
|
||||
|
||||
def create_parser(mach_interface=False):
|
||||
parser = argparse.ArgumentParser()
|
||||
add_arg = parser.add_argument
|
||||
|
||||
add_arg('-b', '--binary', required=True, dest='binary',
|
||||
help="path to browser executable")
|
||||
add_arg('-p', '--profile-zip', required=True, dest='profile_zip',
|
||||
help="path to the gecko profiles zip file to open in profiler.firefox.com")
|
||||
|
||||
|
@ -155,9 +93,6 @@ def create_parser(mach_interface=False):
|
|||
def verify_options(parser, args):
|
||||
ctx = vars(args)
|
||||
|
||||
if not os.path.isfile(args.binary):
|
||||
parser.error("{binary} does not exist!".format(**ctx))
|
||||
|
||||
if not os.path.isfile(args.profile_zip):
|
||||
parser.error("{profile_zip} does not exist!".format(**ctx))
|
||||
|
||||
|
@ -174,12 +109,12 @@ def main(args=sys.argv[1:]):
|
|||
commandline.setup_logging('view-gecko-profile', args, {'tbpl': sys.stdout})
|
||||
LOG = get_default_logger(component='view-gecko-profile')
|
||||
|
||||
view_gecko_profile = ViewGeckoProfile(args.binary, args.profile_zip)
|
||||
view_gecko_profile = ViewGeckoProfile(args.profile_zip)
|
||||
|
||||
view_gecko_profile.start_http_server()
|
||||
view_gecko_profile.setup_http_server()
|
||||
view_gecko_profile.encode_url()
|
||||
view_gecko_profile.start_browser_perfhtml()
|
||||
|
||||
view_gecko_profile.open_profile_in_browser()
|
||||
view_gecko_profile.handle_single_request()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
Загрузка…
Ссылка в новой задаче