2013-05-28 23:33:57 +04:00
|
|
|
#!/usr/bin/python
|
2008-02-22 00:08:39 +03:00
|
|
|
#
|
2012-05-21 15:12:37 +04:00
|
|
|
# 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/.
|
2008-02-22 00:08:39 +03:00
|
|
|
|
2018-05-07 16:43:12 +03:00
|
|
|
import json
|
2008-02-22 00:08:39 +03:00
|
|
|
import os
|
2019-03-05 02:36:59 +03:00
|
|
|
import sys
|
2019-09-11 00:56:15 +03:00
|
|
|
import glob
|
|
|
|
import subprocess
|
2017-08-25 04:25:42 +03:00
|
|
|
|
2019-11-21 21:44:10 +03:00
|
|
|
import mozcrash
|
2017-08-25 04:25:42 +03:00
|
|
|
from mozbuild.base import MozbuildObject
|
2017-08-25 04:32:05 +03:00
|
|
|
from mozfile import TemporaryDirectory
|
2017-08-25 04:25:42 +03:00
|
|
|
from mozhttpd import MozHttpd
|
2019-03-05 02:36:59 +03:00
|
|
|
from mozprofile import FirefoxProfile, Preferences
|
|
|
|
from mozprofile.permissions import ServerLocations
|
|
|
|
from mozrunner import FirefoxRunner, CLI
|
2018-04-20 18:03:38 +03:00
|
|
|
from six import string_types
|
2008-02-22 00:08:39 +03:00
|
|
|
|
|
|
|
PORT = 8888
|
|
|
|
|
2017-08-25 20:33:11 +03:00
|
|
|
PATH_MAPPINGS = {
|
2019-11-14 22:27:26 +03:00
|
|
|
'/webkit/PerformanceTests': 'third_party/webkit/PerformanceTests',
|
|
|
|
# It is tempting to map to `testing/talos/talos/tests` instead, to avoid
|
|
|
|
# writing `tests/` in every path, but we can't do that because some files
|
|
|
|
# refer to scripts located in `../..`.
|
|
|
|
'/talos': 'testing/talos/talos',
|
2017-08-25 20:33:11 +03:00
|
|
|
}
|
|
|
|
|
2019-03-05 02:36:59 +03:00
|
|
|
|
2019-11-21 21:44:10 +03:00
|
|
|
def get_crashreports(directory, name=None):
|
|
|
|
rc = 0
|
|
|
|
upload_path = os.environ.get('UPLOAD_PATH')
|
|
|
|
if upload_path:
|
|
|
|
# For automation, log the minidumps with stackwalk and get them moved to
|
|
|
|
# the artifacts directory.
|
|
|
|
fetches_dir = os.environ.get('MOZ_FETCHES_DIR')
|
|
|
|
if not fetches_dir:
|
|
|
|
raise Exception("Unable to process minidump in automation because "
|
|
|
|
"$MOZ_FETCHES_DIR is not set in the environment")
|
|
|
|
stackwalk_binary = os.path.join(fetches_dir, 'minidump_stackwalk', 'minidump_stackwalk')
|
|
|
|
if sys.platform == 'win32':
|
|
|
|
stackwalk_binary += '.exe'
|
|
|
|
minidump_path = os.path.join(directory, "minidumps")
|
|
|
|
rc = mozcrash.check_for_crashes(
|
|
|
|
minidump_path,
|
|
|
|
symbols_path=fetches_dir,
|
|
|
|
stackwalk_binary=stackwalk_binary,
|
|
|
|
dump_save_path=upload_path,
|
|
|
|
test_name=name,
|
|
|
|
)
|
|
|
|
return rc
|
|
|
|
|
|
|
|
|
2008-02-22 00:08:39 +03:00
|
|
|
if __name__ == '__main__':
|
2017-08-25 04:30:18 +03:00
|
|
|
cli = CLI()
|
|
|
|
debug_args, interactive = cli.debugger_arguments()
|
2018-11-26 20:16:40 +03:00
|
|
|
runner_args = cli.runner_args()
|
2013-05-28 23:33:57 +04:00
|
|
|
|
2017-08-25 04:30:18 +03:00
|
|
|
build = MozbuildObject.from_environment()
|
2018-11-26 20:16:40 +03:00
|
|
|
|
|
|
|
binary = runner_args.get('binary')
|
|
|
|
if not binary:
|
|
|
|
binary = build.get_binary_path(where="staged-package")
|
2019-02-20 17:36:35 +03:00
|
|
|
binary = os.path.normpath(os.path.abspath(binary))
|
2018-11-26 20:16:40 +03:00
|
|
|
|
2017-08-25 20:33:11 +03:00
|
|
|
path_mappings = {
|
|
|
|
k: os.path.join(build.topsrcdir, v)
|
|
|
|
for k, v in PATH_MAPPINGS.items()
|
|
|
|
}
|
2019-03-05 02:36:59 +03:00
|
|
|
httpd = MozHttpd(port=PORT,
|
|
|
|
docroot=os.path.join(build.topsrcdir, "build", "pgo"),
|
|
|
|
path_mappings=path_mappings)
|
2017-08-25 04:30:18 +03:00
|
|
|
httpd.start(block=False)
|
2013-05-28 23:33:57 +04:00
|
|
|
|
2019-03-05 02:36:59 +03:00
|
|
|
locations = ServerLocations()
|
|
|
|
locations.add_host(host='127.0.0.1',
|
|
|
|
port=PORT,
|
|
|
|
options='primary,privileged')
|
|
|
|
|
2019-12-06 04:07:32 +03:00
|
|
|
old_profraw_files = glob.glob('*.profraw')
|
|
|
|
for f in old_profraw_files:
|
|
|
|
os.remove(f)
|
|
|
|
|
2017-08-25 04:32:05 +03:00
|
|
|
with TemporaryDirectory() as profilePath:
|
2017-08-25 04:30:18 +03:00
|
|
|
# TODO: refactor this into mozprofile
|
2018-05-07 16:43:12 +03:00
|
|
|
profile_data_dir = os.path.join(build.topsrcdir, 'testing', 'profiles')
|
|
|
|
with open(os.path.join(profile_data_dir, 'profiles.json'), 'r') as fh:
|
|
|
|
base_profiles = json.load(fh)['profileserver']
|
|
|
|
|
2019-03-05 02:36:59 +03:00
|
|
|
prefpaths = [os.path.join(profile_data_dir, profile, 'user.js')
|
|
|
|
for profile in base_profiles]
|
2017-10-11 17:29:39 +03:00
|
|
|
|
2017-08-25 04:30:18 +03:00
|
|
|
prefs = {}
|
2018-05-07 16:43:12 +03:00
|
|
|
for path in prefpaths:
|
|
|
|
prefs.update(Preferences.read_prefs(path))
|
2017-10-11 17:29:39 +03:00
|
|
|
|
2019-05-31 17:18:05 +03:00
|
|
|
interpolation = {"server": "%s:%d" % httpd.httpd.server_address}
|
2018-04-20 18:03:38 +03:00
|
|
|
for k, v in prefs.items():
|
|
|
|
if isinstance(v, string_types):
|
|
|
|
v = v.format(**interpolation)
|
|
|
|
prefs[k] = Preferences.cast(v)
|
2017-10-11 17:29:39 +03:00
|
|
|
|
2019-05-31 17:18:05 +03:00
|
|
|
# Enforce e10s. This isn't in one of the user.js files because those
|
|
|
|
# are shared with android, which doesn't want this on. We can't
|
|
|
|
# interpolate because the formatting code only works for strings,
|
|
|
|
# and this is a bool pref.
|
|
|
|
prefs["browser.tabs.remote.autostart"] = True
|
|
|
|
|
2019-03-05 02:36:59 +03:00
|
|
|
profile = FirefoxProfile(profile=profilePath,
|
|
|
|
preferences=prefs,
|
|
|
|
addons=[os.path.join(
|
|
|
|
build.topsrcdir, 'tools', 'quitter',
|
|
|
|
'quitter@mozilla.org.xpi')],
|
|
|
|
locations=locations)
|
|
|
|
|
|
|
|
env = os.environ.copy()
|
|
|
|
env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
|
|
|
|
env["XPCOM_DEBUG_BREAK"] = "warn"
|
2019-05-31 17:18:05 +03:00
|
|
|
# We disable sandboxing to make writing profiling data actually work
|
|
|
|
# Bug 1553850 considers fixing this.
|
|
|
|
env["MOZ_DISABLE_CONTENT_SANDBOX"] = "1"
|
|
|
|
|
|
|
|
# Ensure different pids write to different files
|
|
|
|
env["LLVM_PROFILE_FILE"] = "default_%p_random_%m.profraw"
|
2014-03-14 23:48:59 +04:00
|
|
|
|
2019-01-08 20:28:36 +03:00
|
|
|
# Write to an output file if we're running in automation
|
2019-03-05 02:36:59 +03:00
|
|
|
process_args = {}
|
2019-01-08 20:28:36 +03:00
|
|
|
if 'UPLOAD_PATH' in env:
|
2019-03-05 02:36:59 +03:00
|
|
|
process_args['logfile'] = os.path.join(env['UPLOAD_PATH'], 'profile-run-1.log')
|
2019-01-08 20:28:36 +03:00
|
|
|
|
2017-08-25 04:30:18 +03:00
|
|
|
# Run Firefox a first time to initialize its profile
|
2019-03-05 02:36:59 +03:00
|
|
|
runner = FirefoxRunner(profile=profile,
|
|
|
|
binary=binary,
|
|
|
|
cmdargs=['data:text/html,<script>Quitter.quit()</script>'],
|
|
|
|
env=env,
|
|
|
|
process_args=process_args)
|
|
|
|
runner.start()
|
|
|
|
ret = runner.wait()
|
|
|
|
if ret:
|
|
|
|
print("Firefox exited with code %d during profile initialization"
|
|
|
|
% ret)
|
|
|
|
logfile = process_args.get('logfile')
|
|
|
|
if logfile:
|
|
|
|
print("Firefox output (%s):" % logfile)
|
|
|
|
with open(logfile) as f:
|
|
|
|
print(f.read())
|
|
|
|
httpd.stop()
|
2019-11-21 21:44:10 +03:00
|
|
|
get_crashreports(profilePath, name='Profile initialization')
|
2019-03-05 02:36:59 +03:00
|
|
|
sys.exit(ret)
|
|
|
|
|
|
|
|
jarlog = os.getenv("JARLOG_FILE")
|
|
|
|
if jarlog:
|
|
|
|
env["MOZ_JAR_LOG_FILE"] = os.path.abspath(jarlog)
|
|
|
|
print("jarlog: %s" % env["MOZ_JAR_LOG_FILE"])
|
2019-12-06 04:07:32 +03:00
|
|
|
if os.path.exists(jarlog):
|
|
|
|
os.remove(jarlog)
|
2019-03-05 02:36:59 +03:00
|
|
|
|
|
|
|
if 'UPLOAD_PATH' in env:
|
|
|
|
process_args['logfile'] = os.path.join(env['UPLOAD_PATH'], 'profile-run-2.log')
|
|
|
|
cmdargs = ["http://localhost:%d/index.html" % PORT]
|
|
|
|
runner = FirefoxRunner(profile=profile,
|
|
|
|
binary=binary,
|
|
|
|
cmdargs=cmdargs,
|
|
|
|
env=env,
|
|
|
|
process_args=process_args)
|
|
|
|
runner.start(debug_args=debug_args, interactive=interactive)
|
|
|
|
ret = runner.wait()
|
2017-08-25 04:30:18 +03:00
|
|
|
httpd.stop()
|
2019-03-05 02:36:59 +03:00
|
|
|
if ret:
|
|
|
|
print("Firefox exited with code %d during profiling" % ret)
|
|
|
|
logfile = process_args.get('logfile')
|
|
|
|
if logfile:
|
|
|
|
print("Firefox output (%s):" % logfile)
|
|
|
|
with open(logfile) as f:
|
|
|
|
print(f.read())
|
2019-11-21 21:44:10 +03:00
|
|
|
get_crashreports(profilePath, name='Profiling run')
|
2019-03-05 02:36:59 +03:00
|
|
|
sys.exit(ret)
|
2019-09-11 00:56:15 +03:00
|
|
|
|
2019-11-21 21:44:10 +03:00
|
|
|
# Try to move the crash reports to the artifacts even if Firefox appears
|
|
|
|
# to exit successfully, in case there's a crash that doesn't set the
|
|
|
|
# return code to non-zero for some reason.
|
|
|
|
if get_crashreports(profilePath, name='Firefox exited successfully?') != 0:
|
|
|
|
print("Firefox exited successfully, but produced a crashreport")
|
|
|
|
sys.exit(1)
|
|
|
|
|
2019-09-11 00:56:15 +03:00
|
|
|
llvm_profdata = env.get('LLVM_PROFDATA')
|
|
|
|
if llvm_profdata:
|
|
|
|
profraw_files = glob.glob('*.profraw')
|
|
|
|
if not profraw_files:
|
|
|
|
print('Could not find profraw files in the current directory: %s' % os.getcwd())
|
|
|
|
sys.exit(1)
|
|
|
|
merge_cmd = [
|
|
|
|
llvm_profdata,
|
|
|
|
'merge',
|
|
|
|
'-o',
|
|
|
|
'merged.profdata',
|
|
|
|
] + profraw_files
|
|
|
|
rc = subprocess.call(merge_cmd)
|
|
|
|
if rc != 0:
|
|
|
|
print('INFRA-ERROR: Failed to merge profile data. Corrupt profile?')
|
|
|
|
# exit with TBPL_RETRY
|
|
|
|
sys.exit(4)
|