Bug 1563403 - Use 3-tier PGO for local MOZ_PGO=1 builds; r=firefox-build-system-reviewers,dmajor,chmanchester

Local PGO builds now use 3-tier machinery under the hood. Instead of a
single object directory that gets cleaned in between the instrumented
and profile-use builds, now the instrumented build uses a separate
'${objdir}/instrumented' directory. This makes it easier to handle
within mach since we can drive the two builds with environment variables
and keep all build artifacts separate, without needing to do manual
cleanup in between.

Differential Revision: https://phabricator.services.mozilla.com/D50098

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Mike Shal 2019-12-06 01:07:32 +00:00
parent 554a8c6462
commit 25308d30a2
7 changed files with 86 additions and 72 deletions

View File

@ -115,11 +115,8 @@ install-test-files:
include $(topsrcdir)/build/moz-automation.mk
# dist and _tests should be purged during cleaning. However, we don't want them
# purged during PGO builds because they contain some auto-generated files.
ifneq ($(filter-out maybe_clobber_profiledbuild,$(MAKECMDGOALS)),)
# dist and _tests should be purged during cleaning.
GARBAGE_DIRS += dist _tests
endif
# Dummy rule for the cases below where we don't depend on dist/include
recurse_pre-export::
@ -185,37 +182,6 @@ endif
default all::
$(call BUILDSTATUS,TIERS $(TIERS) $(if $(MOZ_AUTOMATION),$(MOZ_AUTOMATION_TIERS)))
# PGO build target.
profiledbuild::
$(call BUILDSTATUS,TIERS pgo_profile_generate pgo_package pgo_profile pgo_clobber pgo_profile_use)
$(call BUILDSTATUS,TIER_START pgo_profile_generate)
$(MAKE) default MOZ_PROFILE_GENERATE=1 MOZ_LTO=
$(call BUILDSTATUS,TIER_FINISH pgo_profile_generate)
$(call BUILDSTATUS,TIER_START pgo_package)
$(MAKE) package
rm -f jarlog/en-US.log
$(call BUILDSTATUS,TIER_FINISH pgo_package)
$(call BUILDSTATUS,TIER_START pgo_profile)
JARLOG_FILE=jarlog/en-US.log $(PYTHON) $(topsrcdir)/build/pgo/profileserver.py
$(call BUILDSTATUS,TIER_FINISH pgo_profile)
$(call BUILDSTATUS,TIER_START pgo_clobber)
$(MAKE) maybe_clobber_profiledbuild
$(call BUILDSTATUS,TIER_FINISH pgo_clobber)
$(call BUILDSTATUS,TIER_START pgo_profile_use)
$(MAKE) default MOZ_PROFILE_USE=1 MOZ_1TIER_PGO=1
$(call BUILDSTATUS,TIER_FINISH pgo_profile_use)
# Change default target to PGO build if PGO is enabled.
ifdef MOZ_PGO
ifdef COMPILE_ENVIRONMENT
# If one of these is already set in addition to PGO we are doing a single phase
# of PGO in isolation, so don't override the default target.
ifeq (,$(MOZ_PROFILE_GENERATE)$(MOZ_PROFILE_USE))
OVERRIDE_DEFAULT_GOAL := profiledbuild
endif
endif
endif
include $(topsrcdir)/config/rules.mk
ifdef SCCACHE_VERBOSE_STATS
@ -281,19 +247,6 @@ update-packaging:
package-generated-sources:
$(call py_action,package_generated_sources,'$(DIST)/$(PKG_PATH)$(GENERATED_SOURCE_FILE_PACKAGE)')
# PGO support, but we can't do this test in client.mk
# No point in clobbering if PGO has been explicitly disabled.
ifdef NO_PROFILE_GUIDED_OPTIMIZE
maybe_clobber_profiledbuild:
else
maybe_clobber_profiledbuild: clean
ifneq (,$(findstring clang,$(CC_TYPE)))
$(LLVM_PROFDATA) merge -o $(DEPTH)/merged.profdata $(DEPTH)/*.profraw
endif
endif # NO_PROFILE_GUIDED_OPTIMIZE
.PHONY: maybe_clobber_profiledbuild
ifdef JS_STANDALONE
# Delegate js-specific rules to js
check-%:

View File

@ -23,17 +23,6 @@ Then::
This is roughly equivalent to::
#. Perform a build with *MOZ_PROFILE_GENERATE=1*
#. Performing a run of the instrumented binaries
#. $ make maybe_clobber_profiledbuild
#. Perform a build with *MOZ_PROFILE_USE=1*
Differences between toolchains
==============================
There are some implementation differences depending on the compiler
toolchain being used.
The *maybe_clobber_profiledbuild* step gets its name because of a
difference. On Windows, this step merely moves some *.pgc* files around.
Using GCC or Clang, it is equivalent to a *make clean*.
#. Perform a build with *--enable-profile-generate* in $topobjdir/instrumented
#. Perform a run of the instrumented binaries with build/pgo/profileserver.py
#. Perform a build with *--enable-profile-use* in $topobjdir

View File

@ -11,6 +11,7 @@ llvm_profdata = check_prog('LLVM_PROFDATA', ['llvm-profdata'],
paths=toolchain_search_path)
js_option('--enable-profile-generate',
env='MOZ_PROFILE_GENERATE',
nargs='?',
choices=('cross',),
help='Build a PGO instrumented binary')
@ -25,6 +26,7 @@ set_define('MOZ_PROFILE_GENERATE',
depends_if('--enable-profile-generate')(lambda _: True))
js_option('--enable-profile-use',
env='MOZ_PROFILE_USE',
nargs='?',
choices=('cross',),
help='Use a generated profile during the build')
@ -51,7 +53,7 @@ def pgo_profile_path(path, pgo_use, profdata, build_env):
topobjdir = topobjdir[:-7]
if not path:
return os.path.join(topobjdir, 'merged.profdata')
return os.path.join(topobjdir, 'instrumented', 'merged.profdata')
if path and not pgo_use:
die('Pass --enable-profile-use to use --with-pgo-profile-path.')
if path and not profdata:

View File

@ -80,6 +80,10 @@ if __name__ == '__main__':
port=PORT,
options='primary,privileged')
old_profraw_files = glob.glob('*.profraw')
for f in old_profraw_files:
os.remove(f)
with TemporaryDirectory() as profilePath:
# TODO: refactor this into mozprofile
profile_data_dir = os.path.join(build.topsrcdir, 'testing', 'profiles')
@ -151,6 +155,8 @@ if __name__ == '__main__':
if jarlog:
env["MOZ_JAR_LOG_FILE"] = os.path.abspath(jarlog)
print("jarlog: %s" % env["MOZ_JAR_LOG_FILE"])
if os.path.exists(jarlog):
os.remove(jarlog)
if 'UPLOAD_PATH' in env:
process_args['logfile'] = os.path.join(env['UPLOAD_PATH'], 'profile-run-2.log')

View File

@ -6,6 +6,7 @@ from __future__ import absolute_import, print_function, unicode_literals
import argparse
import os
import subprocess
from mach.decorators import (
CommandArgument,
@ -14,6 +15,9 @@ from mach.decorators import (
)
from mozbuild.base import MachCommandBase
from mozbuild.util import ensure_subprocess_env
from mozbuild.mozconfig import MozconfigLoader
import mozpack.path as mozpath
from mozbuild.backend import (
backends,
@ -73,6 +77,54 @@ class Build(MachCommandBase):
self.log_manager.enable_all_structured_loggers()
loader = MozconfigLoader(self.topsrcdir)
mozconfig = loader.read_mozconfig(loader.AUTODETECT)
doing_pgo = 'MOZ_PGO=1' in mozconfig['configure_args']
append_env = None
if doing_pgo:
if what:
raise Exception('Cannot specify targets (%s) in MOZ_PGO=1 builds' %
what)
instr = self._spawn(BuildDriver)
orig_topobjdir = instr._topobjdir
instr._topobjdir = mozpath.join(instr._topobjdir, 'instrumented')
append_env = {'MOZ_PROFILE_GENERATE': '1'}
status = instr.build(
what=what,
disable_extra_make_dependencies=disable_extra_make_dependencies,
jobs=jobs,
directory=directory,
verbose=verbose,
keep_going=keep_going,
mach_context=self._mach_context,
append_env=append_env)
if status != 0:
return status
# Packaging the instrumented build is required to get the jarlog
# data.
status = instr._run_make(
directory=".", target='package',
silent=not verbose, ensure_exit_code=False,
append_env=append_env)
if status != 0:
return status
pgo_env = os.environ.copy()
pgo_env['LLVM_PROFDATA'] = instr.config_environment.substs.get('LLVM_PROFDATA')
pgo_env['JARLOG_FILE'] = mozpath.join(orig_topobjdir, 'jarlog/en-US.log')
pgo_cmd = [
instr.virtualenv_manager.python_path,
mozpath.join(self.topsrcdir, 'build/pgo/profileserver.py'),
]
subprocess.check_call(pgo_cmd, cwd=instr.topobjdir,
env=ensure_subprocess_env(pgo_env))
# Set the default build to MOZ_PROFILE_USE
append_env = {'MOZ_PROFILE_USE': '1'}
driver = self._spawn(BuildDriver)
return driver.build(
what=what,
@ -81,7 +133,8 @@ class Build(MachCommandBase):
directory=directory,
verbose=verbose,
keep_going=keep_going,
mach_context=self._mach_context)
mach_context=self._mach_context,
append_env=append_env)
@Command('configure', category='build',
description='Configure the tree (run configure and config.status).')

View File

@ -996,7 +996,8 @@ class BuildDriver(MozbuildObject):
self.mach_context = None
def build(self, what=None, disable_extra_make_dependencies=None, jobs=0,
directory=None, verbose=False, keep_going=False, mach_context=None):
directory=None, verbose=False, keep_going=False, mach_context=None,
append_env=None):
"""Invoke the build backend.
``what`` defines the thing to build. If not defined, the default
@ -1065,7 +1066,8 @@ class BuildDriver(MozbuildObject):
print(" Config object not found by mach.")
config_rc = self.configure(buildstatus_messages=True,
line_handler=output.on_line)
line_handler=output.on_line,
append_env=append_env)
if config_rc != 0:
return config_rc
@ -1158,12 +1160,13 @@ class BuildDriver(MozbuildObject):
# could potentially be fixed if the build monitor were more
# intelligent about encountering undefined state.
no_build_status = b'1' if make_dir is not None else b''
tgt_env = dict(append_env or {})
tgt_env['NO_BUILDSTATUS_MESSAGES'] = no_build_status
status = self._run_make(
directory=make_dir, target=make_target,
line_handler=output.on_line, log=False, print_directory=False,
ensure_exit_code=False, num_jobs=jobs, silent=not verbose,
append_env={
b'NO_BUILDSTATUS_MESSAGES': no_build_status},
append_env=tgt_env,
keep_going=keep_going)
if status != 0:
@ -1175,7 +1178,8 @@ class BuildDriver(MozbuildObject):
status = self._run_client_mk(line_handler=output.on_line,
jobs=jobs,
verbose=verbose,
keep_going=keep_going)
keep_going=keep_going,
append_env=append_env)
self.log(logging.WARNING, 'warning_summary',
{'count': len(monitor.warnings_database)},
@ -1338,7 +1342,7 @@ class BuildDriver(MozbuildObject):
return status
def configure(self, options=None, buildstatus_messages=False,
line_handler=None):
line_handler=None, append_env=None):
# Disable indexing in objdir because it is not necessary and can slow
# down builds.
mkdir(self.topobjdir, not_indexed=True)
@ -1350,12 +1354,13 @@ class BuildDriver(MozbuildObject):
line_handler = line_handler or on_line
options = ' '.join(shell_quote(o) for o in options or ())
append_env = {b'CONFIGURE_ARGS': options.encode('utf-8')}
append_env = dict(append_env or {})
append_env['CONFIGURE_ARGS'] = options
# Only print build status messages when we have an active
# monitor.
if not buildstatus_messages:
append_env[b'NO_BUILDSTATUS_MESSAGES'] = b'1'
append_env['NO_BUILDSTATUS_MESSAGES'] = b'1'
status = self._run_client_mk(target='configure',
line_handler=line_handler,
append_env=append_env)

View File

@ -349,6 +349,12 @@ class MozconfigLoader(object):
if name == 'MOZ_OBJDIR':
result['topobjdir'] = value
if parsed['env_before'].get('MOZ_PROFILE_GENERATE') == '1':
# If MOZ_OBJDIR is specified in the mozconfig, we need to
# make sure that the '/instrumented' directory gets appended
# for the first build to avoid an objdir mismatch when
# running 'mach package' on Windows.
result['topobjdir'] = mozpath.join(result['topobjdir'], 'instrumented')
continue
result['make_extra'].append(o)