Audit all production calls of Path#getDigest and either replace them with DigestUtils#getDigestWithManualFallback or document why Path#getFastDigest is not useful to them.
This is inspired by cleaning up in preparation for https://github.com/bazelbuild/bazel/pull/11662: currently, some FileSystem implementations call Path#getFastDigest inside Path#getDigest because they have a fast digest implementation and want to guard against expensive digest computations.
After this change, ~all overrides of FileSystem#getDigest can be removed, since the callers will already have called FileSystem#getFastDigest.
PiperOrigin-RevId: 331142210
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ActionCacheChecker.java b/src/main/java/com/google/devtools/build/lib/actions/ActionCacheChecker.java
index b856829..0d069fb 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/ActionCacheChecker.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/ActionCacheChecker.java
@@ -21,7 +21,7 @@
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander;
import com.google.devtools.build.lib.actions.cache.ActionCache;
-import com.google.devtools.build.lib.actions.cache.DigestUtils;
+import com.google.devtools.build.lib.actions.cache.MetadataDigestUtils;
import com.google.devtools.build.lib.actions.cache.MetadataHandler;
import com.google.devtools.build.lib.actions.cache.Protos.ActionCacheStatistics.MissReason;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
@@ -159,7 +159,7 @@
for (Artifact artifact : actionInputs.toList()) {
mdMap.put(artifact.getExecPathString(), getMetadataMaybe(metadataHandler, artifact));
}
- return !Arrays.equals(DigestUtils.fromMetadata(mdMap), entry.getFileDigest());
+ return !Arrays.equals(MetadataDigestUtils.fromMetadata(mdMap), entry.getFileDigest());
}
private void reportCommand(EventHandler handler, Action action) {
@@ -347,7 +347,8 @@
}
Map<String, String> usedEnvironment =
computeUsedEnv(action, clientEnv, remoteDefaultPlatformProperties);
- if (!Arrays.equals(entry.getUsedClientEnvDigest(), DigestUtils.fromEnv(usedEnvironment))) {
+ if (!Arrays.equals(
+ entry.getUsedClientEnvDigest(), MetadataDigestUtils.fromEnv(usedEnvironment))) {
reportClientEnv(handler, action, usedEnvironment);
actionCache.accountMiss(MissReason.DIFFERENT_ENVIRONMENT);
return true;
diff --git a/src/main/java/com/google/devtools/build/lib/actions/BUILD b/src/main/java/com/google/devtools/build/lib/actions/BUILD
index e9299ef..2887382 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/actions/BUILD
@@ -51,7 +51,7 @@
"LocalHostResourceFallback.java",
"MiddlemanType.java",
"ResourceSet.java",
- "cache/DigestUtils.java",
+ "cache/MetadataDigestUtils.java",
],
),
deps = [
@@ -213,14 +213,12 @@
"FileStateValue.java",
"FileValue.java",
"InconsistentFilesystemException.java",
- "cache/DigestUtils.java",
+ "cache/MetadataDigestUtils.java",
],
deps = [
":artifacts",
":has_digest",
- "//src/main/java/com/google/devtools/build/lib/clock",
"//src/main/java/com/google/devtools/build/lib/concurrent",
- "//src/main/java/com/google/devtools/build/lib/profiler",
"//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec",
"//src/main/java/com/google/devtools/build/lib/util",
"//src/main/java/com/google/devtools/build/lib/util:var_int",
diff --git a/src/main/java/com/google/devtools/build/lib/actions/FileArtifactValue.java b/src/main/java/com/google/devtools/build/lib/actions/FileArtifactValue.java
index 5d3369a..aae6b76 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/FileArtifactValue.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/FileArtifactValue.java
@@ -20,12 +20,12 @@
import com.google.common.base.Preconditions;
import com.google.common.hash.HashFunction;
import com.google.common.io.BaseEncoding;
-import com.google.devtools.build.lib.actions.cache.DigestUtils;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.util.BigIntegerFingerprint;
import com.google.devtools.build.lib.util.Fingerprint;
+import com.google.devtools.build.lib.vfs.DigestUtils;
import com.google.devtools.build.lib.vfs.FileStatus;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
diff --git a/src/main/java/com/google/devtools/build/lib/actions/cache/ActionCache.java b/src/main/java/com/google/devtools/build/lib/actions/cache/ActionCache.java
index 2de2969..f7b7b0a 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/cache/ActionCache.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/cache/ActionCache.java
@@ -87,7 +87,7 @@
public Entry(String key, Map<String, String> usedClientEnv, boolean discoversInputs) {
actionKey = key;
- this.usedClientEnvDigest = DigestUtils.fromEnv(usedClientEnv);
+ this.usedClientEnvDigest = MetadataDigestUtils.fromEnv(usedClientEnv);
files = discoversInputs ? new ArrayList<String>() : null;
mdMap = new HashMap<>();
}
@@ -141,7 +141,7 @@
*/
public byte[] getFileDigest() {
if (digest == null) {
- digest = DigestUtils.fromMetadata(mdMap);
+ digest = MetadataDigestUtils.fromMetadata(mdMap);
mdMap = null;
}
return digest;
@@ -182,7 +182,9 @@
.append("\n");
builder.append(" digestKey = ");
if (digest == null) {
- builder.append(formatDigest(DigestUtils.fromMetadata(mdMap))).append(" (from mdMap)\n");
+ builder
+ .append(formatDigest(MetadataDigestUtils.fromMetadata(mdMap)))
+ .append(" (from mdMap)\n");
} else {
builder.append(formatDigest(digest)).append("\n");
}
diff --git a/src/main/java/com/google/devtools/build/lib/actions/cache/CompactPersistentActionCache.java b/src/main/java/com/google/devtools/build/lib/actions/cache/CompactPersistentActionCache.java
index f9fec40..87bacdc 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/cache/CompactPersistentActionCache.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/cache/CompactPersistentActionCache.java
@@ -26,6 +26,7 @@
import com.google.devtools.build.lib.util.PersistentMap;
import com.google.devtools.build.lib.util.StringIndexer;
import com.google.devtools.build.lib.util.VarInt;
+import com.google.devtools.build.lib.vfs.DigestUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.UnixGlob;
import java.io.ByteArrayOutputStream;
@@ -381,14 +382,14 @@
VarInt.putVarInt(actionKeyBytes.length, sink);
sink.write(actionKeyBytes);
- DigestUtils.write(entry.getFileDigest(), sink);
+ MetadataDigestUtils.write(entry.getFileDigest(), sink);
VarInt.putVarInt(entry.discoversInputs() ? files.size() : NO_INPUT_DISCOVERY_COUNT, sink);
for (String file : files) {
VarInt.putVarInt(indexer.getOrCreateIndex(file), sink);
}
- DigestUtils.write(entry.getUsedClientEnvDigest(), sink);
+ MetadataDigestUtils.write(entry.getUsedClientEnvDigest(), sink);
return sink.toByteArray();
} catch (IOException e) {
@@ -410,7 +411,7 @@
source.get(actionKeyBytes);
String actionKey = new String(actionKeyBytes, ISO_8859_1);
- byte[] digest = DigestUtils.read(source);
+ byte[] digest = MetadataDigestUtils.read(source);
int count = VarInt.getVarInt(source);
if (count != NO_INPUT_DISCOVERY_COUNT && count < 0) {
@@ -430,7 +431,7 @@
files = builder.build();
}
- byte[] usedClientEnvDigest = DigestUtils.read(source);
+ byte[] usedClientEnvDigest = MetadataDigestUtils.read(source);
if (source.remaining() > 0) {
throw new IOException("serialized entry data has not been fully decoded");
diff --git a/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataDigestUtils.java b/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataDigestUtils.java
new file mode 100644
index 0000000..4071b63
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataDigestUtils.java
@@ -0,0 +1,85 @@
+// Copyright 2020 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.actions.cache;
+
+import com.google.devtools.build.lib.actions.FileArtifactValue;
+import com.google.devtools.build.lib.util.Fingerprint;
+import com.google.devtools.build.lib.util.VarInt;
+import com.google.devtools.build.lib.vfs.DigestUtils;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.Map;
+import javax.annotation.Nullable;
+
+/** Utility class for digests/metadata relating to the action cache. */
+public final class MetadataDigestUtils {
+ private MetadataDigestUtils() {}
+
+ /**
+ * @param source the byte buffer source.
+ * @return the digest from the given buffer.
+ */
+ public static byte[] read(ByteBuffer source) {
+ int size = VarInt.getVarInt(source);
+ byte[] bytes = new byte[size];
+ source.get(bytes);
+ return bytes;
+ }
+
+ /** Write the digest to the output stream. */
+ public static void write(byte[] digest, OutputStream sink) throws IOException {
+ VarInt.putVarInt(digest.length, sink);
+ sink.write(digest);
+ }
+
+ /**
+ * @param mdMap A collection of (execPath, FileArtifactValue) pairs. Values may be null.
+ * @return an <b>order-independent</b> digest from the given "set" of (path, metadata) pairs.
+ */
+ public static byte[] fromMetadata(Map<String, FileArtifactValue> mdMap) {
+ byte[] result = new byte[1]; // reserve the empty string
+ // Profiling showed that MessageDigest engine instantiation was a hotspot, so create one
+ // instance for this computation to amortize its cost.
+ Fingerprint fp = new Fingerprint();
+ for (Map.Entry<String, FileArtifactValue> entry : mdMap.entrySet()) {
+ result = DigestUtils.xor(result, getDigest(fp, entry.getKey(), entry.getValue()));
+ }
+ return result;
+ }
+
+ /**
+ * @param env A collection of (String, String) pairs.
+ * @return an order-independent digest of the given set of pairs.
+ */
+ public static byte[] fromEnv(Map<String, String> env) {
+ byte[] result = new byte[0];
+ Fingerprint fp = new Fingerprint();
+ for (Map.Entry<String, String> entry : env.entrySet()) {
+ fp.addString(entry.getKey());
+ fp.addString(entry.getValue());
+ result = DigestUtils.xor(result, fp.digestAndReset());
+ }
+ return result;
+ }
+
+ private static byte[] getDigest(Fingerprint fp, String execPath, @Nullable FileArtifactValue md) {
+ fp.addString(execPath);
+ if (md != null) {
+ md.addTo(fp);
+ }
+ return fp.digestAndReset();
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/exec/RunfilesTreeUpdater.java b/src/main/java/com/google/devtools/build/lib/exec/RunfilesTreeUpdater.java
index 188bc2b..3316af9 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/RunfilesTreeUpdater.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/RunfilesTreeUpdater.java
@@ -21,6 +21,7 @@
import com.google.devtools.build.lib.profiler.Profiler;
import com.google.devtools.build.lib.profiler.ProfilerTask;
import com.google.devtools.build.lib.util.io.OutErr;
+import com.google.devtools.build.lib.vfs.DigestUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.io.IOException;
@@ -80,7 +81,9 @@
// symbolic link, it is likely a symbolic link to the input manifest, so we cannot trust it as
// an up-to-date check.
if (!outputManifest.isSymbolicLink()
- && Arrays.equals(outputManifest.getDigest(), inputManifest.getDigest())) {
+ && Arrays.equals(
+ DigestUtils.getDigestWithManualFallbackWhenSizeUnknown(outputManifest),
+ DigestUtils.getDigestWithManualFallbackWhenSizeUnknown(inputManifest))) {
return;
}
} catch (IOException e) {
diff --git a/src/main/java/com/google/devtools/build/lib/exec/SpawnLogContext.java b/src/main/java/com/google/devtools/build/lib/exec/SpawnLogContext.java
index 7223b86..e5116f0 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/SpawnLogContext.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/SpawnLogContext.java
@@ -33,6 +33,7 @@
import com.google.devtools.build.lib.remote.options.RemoteOptions;
import com.google.devtools.build.lib.util.io.MessageOutputStream;
import com.google.devtools.build.lib.vfs.DigestHashFunction;
+import com.google.devtools.build.lib.vfs.DigestUtils;
import com.google.devtools.build.lib.vfs.Dirent;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
@@ -92,7 +93,7 @@
ActionInput input = e.getValue();
Path inputPath = execRoot.getRelative(input.getExecPathString());
if (inputPath.isDirectory()) {
- listDirectoryContents(inputPath, (file) -> builder.addInputs(file), metadataProvider);
+ listDirectoryContents(inputPath, builder::addInputs, metadataProvider);
} else {
Digest digest = computeDigest(input, null, metadataProvider);
builder.addInputsBuilder().setPath(input.getExecPathString()).setDigest(digest);
@@ -110,7 +111,7 @@
for (Map.Entry<Path, ActionInput> e : existingOutputs.entrySet()) {
Path path = e.getKey();
if (path.isDirectory()) {
- listDirectoryContents(path, (file) -> builder.addActualOutputs(file), metadataProvider);
+ listDirectoryContents(path, builder::addActualOutputs, metadataProvider);
} else {
File.Builder outputBuilder = builder.addActualOutputsBuilder();
outputBuilder.setPath(path.relativeTo(execRoot).toString());
@@ -233,9 +234,11 @@
path = execRoot.getRelative(input.getExecPath());
}
// Compute digest manually.
+ long fileSize = path.getFileSize();
return digest
- .setHash(HashCode.fromBytes(path.getDigest()).toString())
- .setSizeBytes(path.getFileSize())
+ .setHash(
+ HashCode.fromBytes(DigestUtils.getDigestWithManualFallback(path, fileSize)).toString())
+ .setSizeBytes(fileSize)
.build();
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/remote/util/DigestUtil.java b/src/main/java/com/google/devtools/build/lib/remote/util/DigestUtil.java
index 1b294af..5066f3c 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/util/DigestUtil.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/util/DigestUtil.java
@@ -21,10 +21,10 @@
import com.google.common.hash.HashCode;
import com.google.common.hash.HashingOutputStream;
import com.google.common.io.BaseEncoding;
-import com.google.devtools.build.lib.actions.cache.DigestUtils;
import com.google.devtools.build.lib.actions.cache.VirtualActionInput;
import com.google.devtools.build.lib.remote.common.RemoteCacheClient.ActionKey;
import com.google.devtools.build.lib.vfs.DigestHashFunction;
+import com.google.devtools.build.lib.vfs.DigestUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.protobuf.Message;
import java.io.ByteArrayOutputStream;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryFunction.java b/src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryFunction.java
index c819b1f..beb0553 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryFunction.java
@@ -19,7 +19,6 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.BaseEncoding;
-import com.google.devtools.build.lib.actions.FileStateValue.RegularFileStateValue;
import com.google.devtools.build.lib.actions.FileValue;
import com.google.devtools.build.lib.analysis.BlazeDirectories;
import com.google.devtools.build.lib.analysis.RuleDefinition;
@@ -251,8 +250,9 @@
public static String fileValueToMarkerValue(FileValue fileValue) throws IOException {
Preconditions.checkArgument(fileValue.isFile() && !fileValue.isSpecialFile());
// Return the file content digest in hex. fileValue may or may not have the digest available.
- byte[] digest = ((RegularFileStateValue) fileValue.realFileStateValue()).getDigest();
+ byte[] digest = fileValue.realFileStateValue().getDigest();
if (digest == null) {
+ // Fast digest not available, or it would have been in the FileValue.
digest = fileValue.realRootedPath().asPath().getDigest();
}
return BaseEncoding.base16().lowerCase().encode(digest);
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/CacheFileDigestsModule.java b/src/main/java/com/google/devtools/build/lib/runtime/CacheFileDigestsModule.java
index 520e02c..ca38d5f 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/CacheFileDigestsModule.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/CacheFileDigestsModule.java
@@ -17,10 +17,10 @@
import com.google.common.base.Preconditions;
import com.google.common.cache.CacheStats;
import com.google.common.flogger.GoogleLogger;
-import com.google.devtools.build.lib.actions.cache.DigestUtils;
import com.google.devtools.build.lib.buildtool.BuildRequest;
import com.google.devtools.build.lib.exec.ExecutionOptions;
import com.google.devtools.build.lib.exec.ExecutorBuilder;
+import com.google.devtools.build.lib.vfs.DigestUtils;
/** Enables the caching of file digests in {@link DigestUtils}. */
public class CacheFileDigestsModule extends BlazeModule {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
index 13c78e1..5499dce 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
@@ -36,9 +36,9 @@
import com.google.devtools.build.lib.actions.FilesetManifest;
import com.google.devtools.build.lib.actions.FilesetManifest.RelativeSymlinkBehavior;
import com.google.devtools.build.lib.actions.FilesetOutputSymlink;
-import com.google.devtools.build.lib.actions.cache.DigestUtils;
import com.google.devtools.build.lib.actions.cache.MetadataHandler;
import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
+import com.google.devtools.build.lib.vfs.DigestUtils;
import com.google.devtools.build.lib.vfs.Dirent;
import com.google.devtools.build.lib.vfs.FileStatus;
import com.google.devtools.build.lib.vfs.FileStatusWithDigest;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java
index 78d2bc9..81a05fe 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java
@@ -29,7 +29,7 @@
import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
import com.google.devtools.build.lib.actions.FileArtifactValue;
import com.google.devtools.build.lib.actions.HasDigest;
-import com.google.devtools.build.lib.actions.cache.DigestUtils;
+import com.google.devtools.build.lib.actions.cache.MetadataDigestUtils;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.SerializationConstant;
import com.google.devtools.build.lib.util.Fingerprint;
@@ -131,7 +131,7 @@
@AutoCodec.VisibleForSerialization
static final TreeArtifactValue EMPTY =
new TreeArtifactValue(
- DigestUtils.fromMetadata(ImmutableMap.of()),
+ MetadataDigestUtils.fromMetadata(ImmutableMap.of()),
ImmutableSortedMap.of(),
/*archivedRepresentation=*/ null,
/*entirelyRemote=*/ false);
diff --git a/src/main/java/com/google/devtools/build/lib/ssd/BUILD b/src/main/java/com/google/devtools/build/lib/ssd/BUILD
index 9069ec7..85fb315 100644
--- a/src/main/java/com/google/devtools/build/lib/ssd/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/ssd/BUILD
@@ -13,7 +13,7 @@
srcs = glob(["*.java"]),
deps = [
"//src/main/java/com/google/devtools/build/lib:runtime",
- "//src/main/java/com/google/devtools/build/lib/actions:file_metadata",
+ "//src/main/java/com/google/devtools/build/lib/vfs",
"//src/main/java/com/google/devtools/common/options",
"//third_party:guava",
],
diff --git a/src/main/java/com/google/devtools/build/lib/ssd/SsdModule.java b/src/main/java/com/google/devtools/build/lib/ssd/SsdModule.java
index 2e73896..219329e 100644
--- a/src/main/java/com/google/devtools/build/lib/ssd/SsdModule.java
+++ b/src/main/java/com/google/devtools/build/lib/ssd/SsdModule.java
@@ -14,9 +14,9 @@
package com.google.devtools.build.lib.ssd;
import com.google.common.collect.ImmutableList;
-import com.google.devtools.build.lib.actions.cache.DigestUtils;
import com.google.devtools.build.lib.runtime.BlazeModule;
import com.google.devtools.build.lib.runtime.CommandEnvironment;
+import com.google.devtools.build.lib.vfs.DigestUtils;
import com.google.devtools.common.options.OptionsBase;
/**
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/BUILD b/src/main/java/com/google/devtools/build/lib/vfs/BUILD
index be8dc58..70a2769 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/vfs/BUILD
@@ -51,7 +51,6 @@
"//src/main/java/com/google/devtools/build/lib/concurrent",
"//src/main/java/com/google/devtools/build/lib/profiler",
"//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec",
- "//src/main/java/com/google/devtools/build/lib/util:TestType",
"//src/main/java/com/google/devtools/build/lib/util:filetype",
"//src/main/java/com/google/devtools/common/options",
"//third_party:guava",
diff --git a/src/main/java/com/google/devtools/build/lib/actions/cache/DigestUtils.java b/src/main/java/com/google/devtools/build/lib/vfs/DigestUtils.java
similarity index 79%
rename from src/main/java/com/google/devtools/build/lib/actions/cache/DigestUtils.java
rename to src/main/java/com/google/devtools/build/lib/vfs/DigestUtils.java
index addd5f0..24edd82 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/cache/DigestUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/DigestUtils.java
@@ -11,7 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.devtools.build.lib.actions.cache;
+package com.google.devtools.build.lib.vfs;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
@@ -19,21 +19,10 @@
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheStats;
import com.google.common.primitives.Longs;
-import com.google.devtools.build.lib.actions.FileArtifactValue;
-import com.google.devtools.build.lib.clock.BlazeClock;
import com.google.devtools.build.lib.profiler.Profiler;
import com.google.devtools.build.lib.profiler.ProfilerTask;
-import com.google.devtools.build.lib.util.Fingerprint;
-import com.google.devtools.build.lib.util.VarInt;
-import com.google.devtools.build.lib.vfs.FileStatus;
-import com.google.devtools.build.lib.vfs.Path;
-import com.google.devtools.build.lib.vfs.PathFragment;
import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
-import javax.annotation.Nullable;
/**
* Utility class for getting digests of files.
@@ -152,7 +141,7 @@
* provide it via extended attribute.
*/
private static byte[] getDigestInExclusiveMode(Path path) throws IOException {
- long startTime = BlazeClock.nanoTime();
+ long startTime = Profiler.nanoTimeMaybe();
synchronized (DIGEST_LOCK) {
Profiler.instance().logSimpleTask(startTime, ProfilerTask.WAIT, path.getPathString());
return getDigestInternal(path);
@@ -160,14 +149,14 @@
}
private static byte[] getDigestInternal(Path path) throws IOException {
- long startTime = BlazeClock.nanoTime();
+ long startTime = System.nanoTime();
byte[] digest = path.getDigest();
// When using multi-threaded digesting, it makes no sense to use the throughput of a single
// digest operation to determine whether a read was abnormally slow (as the scheduler might just
// have preferred other reads).
if (!MULTI_THREADED_DIGEST.get()) {
- long millis = (BlazeClock.nanoTime() - startTime) / 1000000;
+ long millis = (System.nanoTime() - startTime) / 1000000;
if (millis > SLOW_READ_MILLIS && (path.getFileSize() / millis) < SLOW_READ_THROUGHPUT) {
System.err.printf(
"Slow read: a %d-byte read from %s took %d ms.%n", path.getFileSize(), path, millis);
@@ -231,6 +220,20 @@
}
/**
+ * Gets the digest of {@code path}, using a constant-time xattr call if the filesystem supports
+ * it, and calculating the digest manually otherwise.
+ *
+ * <p>Unlike {@link #getDigestWithManualFallback}, will not rate-limit manual digesting of files,
+ * so only use this method if the file size is truly unknown and you don't expect many concurrent
+ * manual digests of large files.
+ *
+ * @param path Path of the file.
+ */
+ public static byte[] getDigestWithManualFallbackWhenSizeUnknown(Path path) throws IOException {
+ return getDigestWithManualFallback(path, -1);
+ }
+
+ /**
* Calculates the digest manually.
*
* @param path Path of the file.
@@ -271,64 +274,8 @@
return digest;
}
- /**
- * @param source the byte buffer source.
- * @return the digest from the given buffer.
- * @throws IOException if the byte buffer is incorrectly formatted.
- */
- public static byte[] read(ByteBuffer source) throws IOException {
- int size = VarInt.getVarInt(source);
- byte[] bytes = new byte[size];
- source.get(bytes);
- return bytes;
- }
-
- /** Write the digest to the output stream. */
- public static void write(byte[] digest, OutputStream sink) throws IOException {
- VarInt.putVarInt(digest.length, sink);
- sink.write(digest);
- }
-
- /**
- * @param mdMap A collection of (execPath, FileArtifactValue) pairs. Values may be null.
- * @return an <b>order-independent</b> digest from the given "set" of (path, metadata) pairs.
- */
- public static byte[] fromMetadata(Map<String, FileArtifactValue> mdMap) {
- byte[] result = new byte[1]; // reserve the empty string
- // Profiling showed that MessageDigest engine instantiation was a hotspot, so create one
- // instance for this computation to amortize its cost.
- Fingerprint fp = new Fingerprint();
- for (Map.Entry<String, FileArtifactValue> entry : mdMap.entrySet()) {
- result = xor(result, getDigest(fp, entry.getKey(), entry.getValue()));
- }
- return result;
- }
-
- /**
- * @param env A collection of (String, String) pairs.
- * @return an order-independent digest of the given set of pairs.
- */
- public static byte[] fromEnv(Map<String, String> env) {
- byte[] result = new byte[0];
- Fingerprint fp = new Fingerprint();
- for (Map.Entry<String, String> entry : env.entrySet()) {
- fp.addString(entry.getKey());
- fp.addString(entry.getValue());
- result = xor(result, fp.digestAndReset());
- }
- return result;
- }
-
- private static byte[] getDigest(Fingerprint fp, String execPath, @Nullable FileArtifactValue md) {
- fp.addString(execPath);
- if (md != null) {
- md.addTo(fp);
- }
- return fp.digestAndReset();
- }
-
/** Compute lhs ^= rhs bitwise operation of the arrays. May clobber either argument. */
- private static byte[] xor(byte[] lhs, byte[] rhs) {
+ public static byte[] xor(byte[] lhs, byte[] rhs) {
int n = rhs.length;
if (lhs.length >= n) {
for (int i = 0; i < n; i++) {
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/Path.java b/src/main/java/com/google/devtools/build/lib/vfs/Path.java
index 58bb000..91e2722 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/Path.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/Path.java
@@ -766,7 +766,11 @@
}
/**
- * Returns the digest of the file denoted by the current path, following symbolic links.
+ * Returns the digest of the file denoted by the current path, following symbolic links. Is not
+ * guaranteed to call {@link #getFastDigest} internally, even if a fast digest is likely
+ * available. Callers should prefer {@link DigestUtils#getDigestWithManualFallback} to this method
+ * unless they know that a fast digest is unavailable and do not need the other features
+ * (disk-read rate-limiting, global cache) that {@link DigestUtils} provides.
*
* @return a new byte array containing the file's digest
* @throws IOException if the digest could not be computed for any reason
@@ -802,7 +806,7 @@
} else {
hasher.putChar('-');
}
- hasher.putBytes(path.getDigest());
+ hasher.putBytes(DigestUtils.getDigestWithManualFallback(path, stat.getSize()));
} else if (stat.isDirectory()) {
hasher.putChar('d').putUnencodedChars(path.getDirectoryDigest());
} else if (stat.isSymbolicLink()) {
@@ -816,7 +820,7 @@
} else {
hasher.putChar('-');
}
- hasher.putBytes(resolved.getDigest());
+ hasher.putBytes(DigestUtils.getDigestWithManualFallbackWhenSizeUnknown(resolved));
} else {
// link to a non-file: include the link itself in the hash
hasher.putChar('l').putUnencodedChars(link.toString());