aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupport.java
diff options
context:
space:
mode:
authorGravatar Lukacs Berki <lberki@google.com>2016-04-06 12:31:07 +0000
committerGravatar Lukacs Berki <lberki@google.com>2016-04-07 11:45:11 +0000
commitc0e5bc50f946c6b127485aeee133c149283ae352 (patch)
treecd382125be5cc2a5132646312c164f2c95be90c1 /src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupport.java
parentc23ba45553699b5d8b8ac1520adb307fd7e4e7ee (diff)
Move FDO support to the analysis phase by wrapping FdoSupport in its own SkyFunction.
This removes one of the two reasons for the existence of BuildConfiguration#prepareToBuild() which makes implementing dynamic configurations impossible and also makes FDO support halfway sane; now FDO is exactly as ugly as remote repositories, that is to say, reasonably okay. Ideally, we'd implement the zip extraction as an Action and make it a TreeArtifact, but support for TreeArtifacts is not mature yet enough, so it's not possible at the moment. -- MOS_MIGRATED_REVID=119150223
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupport.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupport.java246
1 files changed, 132 insertions, 114 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupport.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupport.java
index b182c8cf04..18f2cf18df 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupport.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupport.java
@@ -25,7 +25,7 @@ import com.google.devtools.build.lib.actions.Root;
import com.google.devtools.build.lib.analysis.AnalysisEnvironment;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.cmdline.Label;
-import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadHostile;
+import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
import com.google.devtools.build.lib.skyframe.FileValue;
@@ -47,20 +47,13 @@ import java.util.zip.ZipException;
* Support class for FDO (feedback directed optimization) and LIPO (lightweight inter-procedural
* optimization).
*
- * <p>There is a 1:1 relationship between {@link CppConfiguration} objects and {@code FdoSupport}
- * objects. The FDO support of a build configuration can be retrieved using {@link
- * CppConfiguration#getFdoSupport()}.
- *
- * <p>With respect to thread-safety, the {@link #prepareToBuild} method is not thread-safe, and must
- * not be called concurrently with other methods on this class.
- *
* <p>Here follows a quick run-down of how FDO/LIPO builds work (for non-FDO/LIPO builds, none
* of this applies):
*
- * <p>{@link CppConfiguration#prepareHook} is called before the analysis phase, which calls
- * {@link #prepareToBuild}, which extracts the FDO .zip (in case we work with an explicitly
- * generated FDO profile file) or analyzes the .afdo.imports file next to the .afdo file (if
- * AutoFDO is in effect).
+ * <p>{@link FdoSupport#create} is called from {@link FdoSupportFunction} (a {@link SkyFunction}),
+ * which is requested from Skyframe by the {@code cc_toolchain} rule. It extracts the FDO .zip (in
+ * case we work with an explicitly generated FDO profile file) or analyzes the .afdo.imports file
+ * next to the .afdo file (if AutoFDO is in effect).
*
* <p>.afdo.imports files contain one import a line. A line is two paths separated by a colon,
* with functions in the second path being referenced by functions in the first path. These are
@@ -108,9 +101,30 @@ import java.util.zip.ZipException;
* {@code CachingAnalysisEnvironment.allowRegisteringActions}, which causes actions to be silently
* discarded after configured targets are created.
*/
+@Immutable
public class FdoSupport {
/**
+ * The FDO mode we are operating in.
+ *
+ * LIPO can only be active if this is not <code>OFF</code>, but all of the modes below can work
+ * with LIPO either off or on.
+ */
+ private enum FdoMode {
+ /** FDO is turned off. */
+ OFF,
+
+ /** Profiling-based FDO using an explicitly recorded profile. */
+ VANILLA,
+
+ /** FDO based on automatically collected data. */
+ AUTO_FDO,
+
+ /** Instrumentation-based FDO implemented on LLVM. */
+ LLVM_FDO,
+ }
+
+ /**
* Path within profile data .zip files that is considered the root of the
* profile information directory tree.
*/
@@ -170,24 +184,15 @@ public class FdoSupport {
private final LipoMode lipoMode;
/**
- * Flag indicating whether to use AutoFDO (as opposed to
- * instrumentation-based FDO).
+ * FDO mode.
*/
- private final boolean useAutoFdo;
-
- /**
- * Flag indicating whether to use LLVM instrumentation-based FDO (as
- * opposed to GCC instrumentation-based FDO).
- */
- private final boolean useLLVMFdo;
+ private final FdoMode fdoMode;
/**
* The {@code .gcda} files that have been extracted from the ZIP file,
* relative to the root of the ZIP file.
- *
- * <p>Set only in {@link #prepareToBuild}.
*/
- private ImmutableSet<PathFragment> gcdaFiles = ImmutableSet.of();
+ private final ImmutableSet<PathFragment> gcdaFiles;
/**
* Multimap from .gcda file base names to auxiliary input files.
@@ -198,10 +203,8 @@ public class FdoSupport {
*
* <p>The contents of the multimap are copied verbatim from the .gcda.imports
* files and not yet checked for validity.
- *
- * <p>Set only in {@link #prepareToBuild}.
*/
- private ImmutableMultimap<PathFragment, PathFragment> imports;
+ private final ImmutableMultimap<PathFragment, PathFragment> imports;
/**
* Creates an FDO support object.
@@ -210,83 +213,122 @@ public class FdoSupport {
* @param fdoProfile path to the profile file passed to --fdo_optimize option
* @param lipoMode value of the --lipo_mode option
*/
- public FdoSupport(PathFragment fdoInstrument, Path fdoProfile, LipoMode lipoMode, Path execRoot) {
+ private FdoSupport(FdoMode fdoMode, LipoMode lipoMode, Root fdoRoot, PathFragment fdoRootExecPath,
+ PathFragment fdoInstrument, Path fdoProfile, FdoZipContents fdoZipContents) {
this.fdoInstrument = fdoInstrument;
this.fdoProfile = fdoProfile;
- this.fdoRoot = (fdoProfile == null)
- ? null
- : Root.asDerivedRoot(execRoot, execRoot.getRelative("blaze-fdo"));
- this.fdoRootExecPath = fdoProfile == null
- ? null
- : fdoRoot.getExecPath().getRelative(FileSystemUtils.removeExtension(
- new PathFragment("_fdo").getChild(fdoProfile.getBaseName())));
+ this.fdoRoot = fdoRoot;
+ this.fdoRootExecPath = fdoRootExecPath;
this.fdoPath = fdoProfile == null
? null
: FileSystemUtils.removeExtension(new PathFragment("_fdo").getChild(
fdoProfile.getBaseName()));
this.lipoMode = lipoMode;
- this.useAutoFdo = fdoProfile != null && isAutoFdo(fdoProfile.getBaseName());
- this.useLLVMFdo = fdoProfile != null && isLLVMFdo(fdoProfile.getBaseName());
+ this.fdoMode = fdoMode;
+ this.gcdaFiles = fdoZipContents.gcdaFiles;
+ this.imports = fdoZipContents.imports;
}
public Root getFdoRoot() {
return fdoRoot;
}
- public void declareSkyframeDependencies(SkyFunction.Environment env, Path execRoot) {
+ /**
+ * Creates an initialized {@link FdoSupport} instance.
+ */
+ static FdoSupport create(SkyFunction.Environment env, PathFragment fdoInstrument,
+ Path fdoProfile, LipoMode lipoMode, Path execRoot) throws IOException, FdoException {
+ FdoMode fdoMode;
+ if (fdoProfile != null && isAutoFdo(fdoProfile.getBaseName())) {
+ fdoMode = FdoMode.AUTO_FDO;
+ } else if (fdoProfile != null && isLLVMFdo(fdoProfile.getBaseName())) {
+ fdoMode = FdoMode.LLVM_FDO;
+ } else if (fdoProfile != null) {
+ fdoMode = FdoMode.VANILLA;
+ } else {
+ fdoMode = FdoMode.OFF;
+ }
+
+ if (fdoProfile == null) {
+ lipoMode = LipoMode.OFF;
+ }
+
+ Root fdoRoot = (fdoProfile == null)
+ ? null
+ : Root.asDerivedRoot(execRoot, execRoot.getRelative("blaze-fdo"));
+
+ PathFragment fdoRootExecPath = fdoProfile == null
+ ? null
+ : fdoRoot.getExecPath().getRelative(FileSystemUtils.removeExtension(
+ new PathFragment("_fdo").getChild(fdoProfile.getBaseName())));
+
if (fdoProfile != null) {
- if (isLipoEnabled()) {
+ if (lipoMode != LipoMode.OFF) {
// Incrementality is not supported for LIPO builds, see FdoSupport#scannables.
// Ensure that the Skyframe value containing the configuration will not be reused to avoid
// incrementality issues.
PrecomputedValue.dependOnBuildId(env);
- return;
- }
-
- // IMPORTANT: Keep the following in sync with #prepareToBuild.
- Path path;
- if (useAutoFdo) {
- path = fdoProfile.getParentDirectory().getRelative(
- fdoProfile.getBaseName() + ".imports");
} else {
- path = fdoProfile;
+ Path path = fdoMode == FdoMode.AUTO_FDO ? getAutoFdoImportsPath(fdoProfile) : fdoProfile;
+ env.getValue(FileValue.key(RootedPath.toRootedPathMaybeUnderRoot(path,
+ ImmutableList.of(execRoot))));
}
- env.getValue(FileValue.key(RootedPath.toRootedPathMaybeUnderRoot(path,
- ImmutableList.of(execRoot))));
+ }
+
+ if (env.valuesMissing()) {
+ return null;
+ }
+
+ FdoZipContents fdoZipContents = extractFdoZip(
+ fdoMode, lipoMode, execRoot, fdoProfile, fdoRootExecPath);
+ return new FdoSupport(
+ fdoMode, lipoMode, fdoRoot, fdoRootExecPath, fdoInstrument, fdoProfile, fdoZipContents);
+ }
+
+ @Immutable
+ private static class FdoZipContents {
+ private final ImmutableSet<PathFragment> gcdaFiles;
+ private final ImmutableMultimap<PathFragment, PathFragment> imports;
+
+ private FdoZipContents(ImmutableSet<PathFragment> gcdaFiles,
+ ImmutableMultimap<PathFragment, PathFragment> imports) {
+ this.gcdaFiles = gcdaFiles;
+ this.imports = imports;
}
}
/**
- * Prepares the FDO support for building.
+ * Extracts the FDO zip file and collects data from it that's needed during analysis.
*
* <p>When an {@code --fdo_optimize} compile is requested, unpacks the given
* FDO gcda zip file into a clean working directory under execRoot.
*
* @throws FdoException if the FDO ZIP contains a file of unknown type
*/
- @ThreadHostile // must be called before starting the build
- public void prepareToBuild(Path execRoot)
+ private static FdoZipContents extractFdoZip(FdoMode fdoMode, LipoMode lipoMode, Path execRoot,
+ Path fdoProfile, PathFragment fdoRootExecPath)
throws IOException, FdoException {
// The execRoot != null case is only there for testing. We cannot provide a real ZIP file in
// tests because ZipFileSystem does not work with a ZIP on an in-memory file system.
// IMPORTANT: Keep in sync with #declareSkyframeDependencies to avoid incrementality issues.
+ ImmutableSet<PathFragment> gcdaFiles = ImmutableSet.of();
+ ImmutableMultimap<PathFragment, PathFragment> imports = ImmutableMultimap.of();
+
if (fdoProfile != null && execRoot != null) {
Path fdoDirPath = execRoot.getRelative(fdoRootExecPath);
FileSystemUtils.deleteTreesBelow(fdoDirPath);
FileSystemUtils.createDirectoryAndParents(fdoDirPath);
- if (useAutoFdo) {
- Path fdoImports = fdoProfile.getParentDirectory().getRelative(
- fdoProfile.getBaseName() + ".imports");
- if (isLipoEnabled()) {
- imports = readAutoFdoImports(fdoImports);
+ if (fdoMode == FdoMode.AUTO_FDO) {
+ if (lipoMode != LipoMode.OFF) {
+ imports = readAutoFdoImports(getAutoFdoImportsPath(fdoProfile));
}
FileSystemUtils.ensureSymbolicLink(
- execRoot.getRelative(getAutoProfilePath()), fdoProfile);
- } else if (useLLVMFdo) {
+ execRoot.getRelative(getAutoProfilePath(fdoProfile, fdoRootExecPath)), fdoProfile);
+ } else if (fdoMode == FdoMode.LLVM_FDO) {
FileSystemUtils.ensureSymbolicLink(
- execRoot.getRelative(getLLVMProfilePath()), fdoProfile);
+ execRoot.getRelative(getLLVMProfilePath(fdoProfile, fdoRootExecPath)), fdoProfile);
} else {
Path zipFilePath = new ZipFileSystem(fdoProfile).getRootDirectory();
if (!zipFilePath.getRelative("blaze-out").isDirectory()) {
@@ -296,11 +338,13 @@ public class FdoSupport {
ImmutableSet.Builder<PathFragment> gcdaFilesBuilder = ImmutableSet.builder();
ImmutableMultimap.Builder<PathFragment, PathFragment> importsBuilder =
ImmutableMultimap.builder();
- extractFdoZip(zipFilePath, fdoDirPath, gcdaFilesBuilder, importsBuilder);
+ extractFdoZipDirectory(zipFilePath, fdoDirPath, gcdaFilesBuilder, importsBuilder);
gcdaFiles = gcdaFilesBuilder.build();
imports = importsBuilder.build();
}
}
+
+ return new FdoZipContents(gcdaFiles, imports);
}
/**
@@ -320,7 +364,7 @@ public class FdoSupport {
* @throws IOException if any of the I/O operations failed
* @throws FdoException if the FDO ZIP contains a file of unknown type
*/
- private void extractFdoZip(Path sourceDir, Path targetDir,
+ private static void extractFdoZipDirectory(Path sourceDir, Path targetDir,
ImmutableSet.Builder<PathFragment> gcdaFilesBuilder,
ImmutableMultimap.Builder<PathFragment, PathFragment> importsBuilder)
throws IOException, FdoException {
@@ -328,7 +372,7 @@ public class FdoSupport {
Path targetFile = targetDir.getRelative(sourceFile.getBaseName());
if (sourceFile.isDirectory()) {
targetFile.createDirectory();
- extractFdoZip(sourceFile, targetFile, gcdaFilesBuilder, importsBuilder);
+ extractFdoZipDirectory(sourceFile, targetFile, gcdaFilesBuilder, importsBuilder);
} else {
if (CppFileTypes.COVERAGE_DATA.matches(sourceFile)) {
FileSystemUtils.copyFile(sourceFile, targetFile);
@@ -343,12 +387,13 @@ public class FdoSupport {
}
}
+
/**
* Reads a .gcda.imports file and stores the imports information.
*
* @throws FdoException if an auxiliary LIPO input was not found
*/
- private void readCoverageImports(Path importsFile,
+ private static void readCoverageImports(Path importsFile,
ImmutableMultimap.Builder<PathFragment, PathFragment> importsBuilder)
throws IOException, FdoException {
PathFragment key = importsFile.asFragment().relativeTo(ZIP_ROOT);
@@ -373,7 +418,7 @@ public class FdoSupport {
/**
* Reads a .afdo.imports file and stores the imports information.
*/
- private ImmutableMultimap<PathFragment, PathFragment> readAutoFdoImports(Path importsFile)
+ private static ImmutableMultimap<PathFragment, PathFragment> readAutoFdoImports(Path importsFile)
throws IOException, FdoException {
ImmutableMultimap.Builder<PathFragment, PathFragment> importBuilder =
ImmutableMultimap.builder();
@@ -396,6 +441,10 @@ public class FdoSupport {
return importBuilder.build();
}
+ private static Path getAutoFdoImportsPath(Path fdoProfile) {
+ return fdoProfile.getParentDirectory().getRelative(fdoProfile.getBaseName() + ".imports");
+ }
+
/**
* Returns the imports from the .afdo.imports file of a source file.
*
@@ -403,7 +452,7 @@ public class FdoSupport {
*/
private Collection<Artifact> getAutoFdoImports(RuleContext ruleContext,
PathFragment sourceExecPath, LipoContextProvider lipoContextProvider) {
- Preconditions.checkState(isLipoEnabled());
+ Preconditions.checkState(lipoMode != LipoMode.OFF);
ImmutableCollection<PathFragment> afdoImports = imports.get(sourceExecPath);
Preconditions.checkState(afdoImports != null,
"AutoFDO import data missing for %s", sourceExecPath);
@@ -427,7 +476,7 @@ public class FdoSupport {
* @param objectName the object file
*/
private Iterable<PathFragment> getImports(PathFragment objDirectory, PathFragment objectName) {
- Preconditions.checkState(isLipoEnabled());
+ Preconditions.checkState(lipoMode != LipoMode.OFF);
Preconditions.checkState(imports != null,
"Tried to look up imports of uninitialized FDOSupport");
PathFragment key = objDirectory.getRelative(FileSystemUtils.removeExtension(objectName));
@@ -449,7 +498,7 @@ public class FdoSupport {
// It is a bug if this method is called with useLipo if lipo is disabled. However, it is legal
// if is is called with !useLipo, even though lipo is enabled.
LipoContextProvider lipoInputProvider = CppHelper.getLipoContextProvider(ruleContext);
- Preconditions.checkArgument(lipoInputProvider == null || isLipoEnabled());
+ Preconditions.checkArgument(lipoInputProvider == null || lipoMode != LipoMode.OFF);
// FDO is disabled -> do nothing.
if ((fdoInstrument == null) && (fdoRoot == null)) {
@@ -474,12 +523,12 @@ public class FdoSupport {
if (!Iterables.isEmpty(auxiliaryInputs)) {
if (featureConfiguration.isEnabled(CppRuleClasses.AUTOFDO)) {
buildVariables.addVariable("fdo_profile_path",
- getAutoProfilePath().getPathString());
+ getAutoProfilePath(fdoProfile, fdoRootExecPath).getPathString());
}
if (featureConfiguration.isEnabled(CppRuleClasses.FDO_OPTIMIZE)) {
- if (useLLVMFdo) {
+ if (fdoMode == FdoMode.LLVM_FDO) {
buildVariables.addVariable("fdo_profile_path",
- getLLVMProfilePath().getPathString());
+ getLLVMProfilePath(fdoProfile, fdoRootExecPath).getPathString());
} else {
buildVariables.addVariable("fdo_profile_path",
fdoRootExecPath.getPathString());
@@ -498,12 +547,12 @@ public class FdoSupport {
// If --fdo_optimize was not specified, we don't have any additional inputs.
if (fdoProfile == null) {
return ImmutableSet.of();
- } else if (useAutoFdo || useLLVMFdo) {
+ } else if (fdoMode == FdoMode.AUTO_FDO || fdoMode == FdoMode.LLVM_FDO) {
ImmutableSet.Builder<Artifact> auxiliaryInputs = ImmutableSet.builder();
- PathFragment profileRootRelativePath = (useLLVMFdo)
- ? getLLVMProfileRootRelativePath()
- : getAutoProfileRootRelativePath();
+ PathFragment profileRootRelativePath = fdoMode == FdoMode.LLVM_FDO
+ ? getLLVMProfileRootRelativePath(fdoProfile)
+ : getAutoProfileRootRelativePath(fdoProfile);
Artifact artifact = env.getDerivedArtifact(
fdoPath.getRelative(profileRootRelativePath), fdoRoot);
env.registerAction(new FdoStubAction(ruleContext.getActionOwner(), artifact));
@@ -607,37 +656,29 @@ public class FdoSupport {
}
- private PathFragment getAutoProfilePath() {
- return fdoRootExecPath.getRelative(getAutoProfileRootRelativePath());
+ private static PathFragment getAutoProfilePath(Path fdoProfile, PathFragment fdoRootExecPath) {
+ return fdoRootExecPath.getRelative(getAutoProfileRootRelativePath(fdoProfile));
}
- private PathFragment getAutoProfileRootRelativePath() {
+ private static PathFragment getAutoProfileRootRelativePath(Path fdoProfile) {
return new PathFragment(fdoProfile.getBaseName());
}
- private PathFragment getLLVMProfilePath() {
- return fdoRootExecPath.getRelative(getLLVMProfileRootRelativePath());
+ private static PathFragment getLLVMProfilePath(Path fdoProfile, PathFragment fdoRootExecPath) {
+ return fdoRootExecPath.getRelative(getLLVMProfileRootRelativePath(fdoProfile));
}
- private PathFragment getLLVMProfileRootRelativePath() {
+ private static PathFragment getLLVMProfileRootRelativePath(Path fdoProfile) {
return new PathFragment(fdoProfile.getBaseName());
}
/**
- * Returns whether LIPO is enabled.
- */
- @ThreadSafe
- public boolean isLipoEnabled() {
- return fdoProfile != null && lipoMode != LipoMode.OFF;
- }
-
- /**
* Returns whether AutoFDO is enabled.
*/
@ThreadSafe
public boolean isAutoFdoEnabled() {
- return useAutoFdo;
+ return fdoMode == FdoMode.AUTO_FDO;
}
/**
@@ -664,29 +705,6 @@ public class FdoSupport {
}
/**
- * Returns the path of the FDO zip containing the .gcda profile files, or null
- * if FDO is not enabled.
- */
- @VisibleForTesting
- public Path getFdoOptimizeProfile() {
- return fdoProfile;
- }
-
- /**
- * Returns the path fragment of the instrumentation output dir for gcc when
- * FDO is enabled, or null if FDO is not enabled.
- */
- @ThreadSafe
- public PathFragment getFdoInstrument() {
- return fdoInstrument;
- }
-
- @VisibleForTesting
- public void setGcdaFilesForTesting(ImmutableSet<PathFragment> gcdaFiles) {
- this.gcdaFiles = gcdaFiles;
- }
-
- /**
* An exception indicating an issue with FDO coverage files.
*/
public static final class FdoException extends Exception {