aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/actions
diff options
context:
space:
mode:
authorGravatar Shreya Bhattarai <shreyax@google.com>2016-08-22 22:00:24 +0000
committerGravatar Philipp Wollermann <philwo@google.com>2016-08-23 09:29:38 +0000
commit141b6c2a48353a0a4e2c0debe8f040adb41ab349 (patch)
tree2473d7e4b1fd9be372e11bcaa8390c645938c67d /src/main/java/com/google/devtools/build/lib/actions
parentcf847b82e87340cac005ad1a5f32a59bb8815662 (diff)
Make Digest (renamed Md5Digest) a little more multi-purpose.
-- MOS_MIGRATED_REVID=130986194
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/actions')
-rw-r--r--src/main/java/com/google/devtools/build/lib/actions/ActionCacheChecker.java4
-rw-r--r--src/main/java/com/google/devtools/build/lib/actions/cache/ActionCache.java32
-rw-r--r--src/main/java/com/google/devtools/build/lib/actions/cache/CompactPersistentActionCache.java17
-rw-r--r--src/main/java/com/google/devtools/build/lib/actions/cache/Digest.java138
-rw-r--r--src/main/java/com/google/devtools/build/lib/actions/cache/DigestUtils.java75
-rw-r--r--src/main/java/com/google/devtools/build/lib/actions/cache/Md5Digest.java59
-rw-r--r--src/main/java/com/google/devtools/build/lib/actions/cache/Metadata.java1
-rw-r--r--src/main/java/com/google/devtools/build/lib/actions/cache/MetadataHandler.java5
8 files changed, 161 insertions, 170 deletions
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 b96a9e4111..5b913ad1c3 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
@@ -19,7 +19,7 @@ import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.ActionAnalysisMetadata.MiddlemanType;
import com.google.devtools.build.lib.actions.cache.ActionCache;
import com.google.devtools.build.lib.actions.cache.ActionCache.Entry;
-import com.google.devtools.build.lib.actions.cache.Digest;
+import com.google.devtools.build.lib.actions.cache.DigestUtils;
import com.google.devtools.build.lib.actions.cache.Metadata;
import com.google.devtools.build.lib.actions.cache.MetadataHandler;
import com.google.devtools.build.lib.events.Event;
@@ -108,7 +108,7 @@ public class ActionCacheChecker {
for (Artifact artifact : artifacts) {
mdMap.put(artifact.getExecPathString(), metadataHandler.getMetadataMaybe(artifact));
}
- return !Digest.fromMetadata(mdMap).equals(entry.getFileDigest());
+ return !DigestUtils.fromMetadata(mdMap).equals(entry.getFileDigest());
}
private void reportCommand(EventHandler handler, Action action) {
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 b87dd846ab..5dc88cf0ff 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
@@ -19,7 +19,6 @@ import com.google.common.collect.Lists;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadCompatible;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.vfs.PathFragment;
-
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
@@ -28,7 +27,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-
import javax.annotation.Nullable;
/**
@@ -73,9 +71,9 @@ public interface ActionCache {
@Nullable
// Null iff the corresponding action does not do input discovery.
private final List<String> files;
- // If null, digest is non-null and the entry is immutable.
+ // If null, md5Digest is non-null and the entry is immutable.
private Map<String, Metadata> mdMap;
- private Digest digest;
+ private Md5Digest md5Digest;
public Entry(String key, boolean discoversInputs) {
actionKey = key;
@@ -83,10 +81,10 @@ public interface ActionCache {
mdMap = new HashMap<>();
}
- public Entry(String key, @Nullable List<String> files, Digest digest) {
+ public Entry(String key, @Nullable List<String> files, Md5Digest md5Digest) {
actionKey = key;
this.files = files;
- this.digest = digest;
+ this.md5Digest = md5Digest;
mdMap = null;
}
@@ -97,7 +95,7 @@ public interface ActionCache {
public void addFile(PathFragment relativePath, Metadata md) {
Preconditions.checkState(mdMap != null);
Preconditions.checkState(!isCorrupted());
- Preconditions.checkState(digest == null);
+ Preconditions.checkState(md5Digest == null);
String execPath = relativePath.getPathString();
if (discoversInputs()) {
@@ -114,17 +112,17 @@ public interface ActionCache {
}
/**
- * Returns the combined digest of the action's inputs and outputs.
+ * Returns the combined md5Digest of the action's inputs and outputs.
*
- * This may compresses the data into a more compact representation, and
- * makes the object immutable.
+ * <p>This may compresses the data into a more compact representation, and makes the object
+ * immutable.
*/
- public Digest getFileDigest() {
- if (digest == null) {
- digest = Digest.fromMetadata(mdMap);
+ public Md5Digest getFileDigest() {
+ if (md5Digest == null) {
+ md5Digest = DigestUtils.fromMetadata(mdMap);
mdMap = null;
}
- return digest;
+ return md5Digest;
}
/**
@@ -153,10 +151,10 @@ public interface ActionCache {
StringBuilder builder = new StringBuilder();
builder.append(" actionKey = ").append(actionKey).append("\n");
builder.append(" digestKey = ");
- if (digest == null) {
- builder.append(Digest.fromMetadata(mdMap)).append(" (from mdMap)\n");
+ if (md5Digest == null) {
+ builder.append(DigestUtils.fromMetadata(mdMap)).append(" (from mdMap)\n");
} else {
- builder.append(digest).append("\n");
+ builder.append(md5Digest).append("\n");
}
if (discoversInputs()) {
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 8cbfd7705b..f0b47f857d 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,7 +26,6 @@ import com.google.devtools.build.lib.util.StringIndexer;
import com.google.devtools.build.lib.util.VarInt;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.UnixGlob;
-
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -352,14 +351,18 @@ public class CompactPersistentActionCache implements ActionCache {
// + 16 bytes for the digest
// + 5 bytes max for the file list length
// + 5 bytes max for each file id
- int maxSize = VarInt.MAX_VARINT_SIZE + actionKeyBytes.length + Digest.MD5_SIZE
- + VarInt.MAX_VARINT_SIZE + files.size() * VarInt.MAX_VARINT_SIZE;
+ int maxSize =
+ VarInt.MAX_VARINT_SIZE
+ + actionKeyBytes.length
+ + Md5Digest.MD5_SIZE
+ + VarInt.MAX_VARINT_SIZE
+ + files.size() * VarInt.MAX_VARINT_SIZE;
ByteArrayOutputStream sink = new ByteArrayOutputStream(maxSize);
VarInt.putVarInt(actionKeyBytes.length, sink);
sink.write(actionKeyBytes);
- entry.getFileDigest().write(sink);
+ DigestUtils.write(entry.getFileDigest(), sink);
VarInt.putVarInt(entry.discoversInputs() ? files.size() : NO_INPUT_DISCOVERY_COUNT, sink);
for (String file : files) {
@@ -385,7 +388,7 @@ public class CompactPersistentActionCache implements ActionCache {
source.get(actionKeyBytes);
String actionKey = new String(actionKeyBytes, ISO_8859_1);
- Digest digest = Digest.read(source);
+ Md5Digest md5Digest = DigestUtils.read(source);
int count = VarInt.getVarInt(source);
ImmutableList.Builder<String> builder = new ImmutableList.Builder<>();
@@ -400,8 +403,8 @@ public class CompactPersistentActionCache implements ActionCache {
if (source.remaining() > 0) {
throw new IOException("serialized entry data has not been fully decoded");
}
- return new Entry(actionKey,
- count == NO_INPUT_DISCOVERY_COUNT ? null : builder.build(), digest);
+ return new Entry(
+ actionKey, count == NO_INPUT_DISCOVERY_COUNT ? null : builder.build(), md5Digest);
} catch (BufferUnderflowException e) {
throw new IOException("encoded entry data is incomplete", e);
}
diff --git a/src/main/java/com/google/devtools/build/lib/actions/cache/Digest.java b/src/main/java/com/google/devtools/build/lib/actions/cache/Digest.java
deleted file mode 100644
index 06989198d2..0000000000
--- a/src/main/java/com/google/devtools/build/lib/actions/cache/Digest.java
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright 2014 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.common.hash.HashCode;
-import com.google.devtools.build.lib.util.Fingerprint;
-import com.google.devtools.build.lib.util.VarInt;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.Map;
-
-/**
- * A value class for capturing and comparing MD5-based digests.
- *
- * <p>Note that this class is responsible for digesting file metadata in an
- * order-independent manner. Care must be taken to do this properly. The
- * digest must be a function of the set of (path, metadata) tuples. While the
- * order of these pairs must not matter, it would <b>not</b> be safe to make
- * the digest be a function of the set of paths and the set of metadata.
- *
- * <p>Note that the (path, metadata) tuples must be unique, otherwise the
- * XOR-based approach will fail.
- */
-public class Digest {
-
- static final int MD5_SIZE = 16;
-
- private final byte[] digest;
-
- /**
- * Construct the digest from the given bytes.
- * @param digest an MD5 digest. Must be sized properly.
- */
- private Digest(byte[] digest) {
- this.digest = 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 Digest read(ByteBuffer source) throws IOException {
- int size = VarInt.getVarInt(source);
- if (size != MD5_SIZE) {
- throw new IOException("Unexpected digest length: " + size);
- }
- byte[] bytes = new byte[size];
- source.get(bytes);
- return new Digest(bytes);
- }
-
- /**
- * Write the digest to the output stream.
- */
- public void write(OutputStream sink) throws IOException {
- VarInt.putVarInt(digest.length, sink);
- sink.write(digest);
- }
-
- /**
- * @param mdMap A collection of (execPath, Metadata) pairs.
- * Values may be null.
- * @return an <b>order-independent</b> digest from the given "set" of
- * (path, metadata) pairs.
- */
- public static Digest fromMetadata(Map<String, Metadata> mdMap) {
- byte[] result = new byte[MD5_SIZE];
- // Profiling showed that MD5 engine instantiation was a hotspot, so create one instance for
- // this computation to amortize its cost.
- Fingerprint fp = new Fingerprint();
- for (Map.Entry<String, Metadata> entry : mdMap.entrySet()) {
- xorWith(result, getDigest(fp, entry.getKey(), entry.getValue()));
- }
- return new Digest(result);
- }
-
- /**
- * @return this Digest as a Metadata with no mtime.
- */
- public Metadata asMetadata() {
- return new Metadata(digest);
- }
-
- @Override
- public int hashCode() {
- return Arrays.hashCode(digest);
- }
-
- @Override
- public boolean equals(Object obj) {
- return (obj instanceof Digest) && Arrays.equals(digest, ((Digest) obj).digest);
- }
-
- @Override
- public String toString() {
- return HashCode.fromBytes(digest).toString();
- }
-
- private static byte[] getDigest(Fingerprint fp, String execPath, Metadata md) {
- fp.addStringLatin1(execPath);
-
- if (md == null) {
- // Move along, nothing to see here.
- } else if (md.digest == null) {
- // Use the timestamp if the digest is not present, but not both.
- // Modifying a timestamp while keeping the contents of a file the
- // same should not cause rebuilds.
- fp.addLong(md.mtime);
- } else {
- fp.addBytes(md.digest);
- }
- return fp.digestAndReset();
- }
-
- /**
- * Compute lhs ^= rhs bitwise operation of the arrays.
- */
- private static void xorWith(byte[] lhs, byte[] rhs) {
- for (int i = 0; i < lhs.length; i++) {
- lhs[i] ^= rhs[i];
- }
- }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/actions/cache/DigestUtils.java b/src/main/java/com/google/devtools/build/lib/actions/cache/DigestUtils.java
index c61e943eda..ef4d9dc4d9 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/cache/DigestUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/cache/DigestUtils.java
@@ -17,21 +17,33 @@ import com.google.common.io.BaseEncoding;
import com.google.devtools.build.lib.profiler.Profiler;
import com.google.devtools.build.lib.profiler.ProfilerTask;
import com.google.devtools.build.lib.util.BlazeClock;
+import com.google.devtools.build.lib.util.Fingerprint;
import com.google.devtools.build.lib.util.LoggingUtil;
import com.google.devtools.build.lib.util.Preconditions;
+import com.google.devtools.build.lib.util.VarInt;
import com.google.devtools.build.lib.vfs.Path;
-
import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
-
import javax.annotation.Nullable;
/**
* Utility class for getting md5 digests of files.
+ *
+ * <p>Note that this class is responsible for digesting file metadata in an order-independent
+ * manner. Care must be taken to do this properly. The digest must be a function of the set of
+ * (path, metadata) tuples. While the order of these pairs must not matter, it would <b>not</b> be
+ * safe to make the digest be a function of the set of paths and the set of metadata.
+ *
+ * <p>Note that the (path, metadata) tuples must be unique, otherwise the XOR-based approach will
+ * fail.
*/
public class DigestUtils {
+
// Object to synchronize on when serializing large file reads.
private static final Object MD5_LOCK = new Object();
private static final AtomicBoolean MULTI_THREADED_DIGEST = new AtomicBoolean(false);
@@ -122,4 +134,63 @@ public class DigestUtils {
return getDigestInternal(path);
}
}
+
+ /**
+ * @param source the byte buffer source.
+ * @return the digest from the given buffer.
+ * @throws IOException if the byte buffer is incorrectly formatted.
+ */
+ public static Md5Digest read(ByteBuffer source) throws IOException {
+ int size = VarInt.getVarInt(source);
+ if (size != Md5Digest.MD5_SIZE) {
+ throw new IOException("Unexpected digest length: " + size);
+ }
+ byte[] bytes = new byte[size];
+ source.get(bytes);
+ return new Md5Digest(bytes);
+ }
+
+ /** Write the digest to the output stream. */
+ public static void write(Md5Digest digest, OutputStream sink) throws IOException {
+ VarInt.putVarInt(digest.getDigestBytesUnsafe().length, sink);
+ sink.write(digest.getDigestBytesUnsafe());
+ }
+
+ /**
+ * @param mdMap A collection of (execPath, Metadata) pairs. Values may be null.
+ * @return an <b>order-independent</b> digest from the given "set" of (path, metadata) pairs.
+ */
+ public static Md5Digest fromMetadata(Map<String, Metadata> mdMap) {
+ byte[] result = new byte[Md5Digest.MD5_SIZE];
+ // Profiling showed that MD5 engine instantiation was a hotspot, so create one instance for
+ // this computation to amortize its cost.
+ Fingerprint fp = new Fingerprint();
+ for (Map.Entry<String, Metadata> entry : mdMap.entrySet()) {
+ xorWith(result, getDigest(fp, entry.getKey(), entry.getValue()));
+ }
+ return new Md5Digest(result);
+ }
+
+ private static byte[] getDigest(Fingerprint fp, String execPath, Metadata md) {
+ fp.addStringLatin1(execPath);
+
+ if (md == null) {
+ // Move along, nothing to see here.
+ } else if (md.digest == null) {
+ // Use the timestamp if the digest is not present, but not both.
+ // Modifying a timestamp while keeping the contents of a file the
+ // same should not cause rebuilds.
+ fp.addLong(md.mtime);
+ } else {
+ fp.addBytes(md.digest);
+ }
+ return fp.digestAndReset();
+ }
+
+ /** Compute lhs ^= rhs bitwise operation of the arrays. */
+ private static void xorWith(byte[] lhs, byte[] rhs) {
+ for (int i = 0; i < lhs.length; i++) {
+ lhs[i] ^= rhs[i];
+ }
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/actions/cache/Md5Digest.java b/src/main/java/com/google/devtools/build/lib/actions/cache/Md5Digest.java
new file mode 100644
index 0000000000..75441b52e7
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/actions/cache/Md5Digest.java
@@ -0,0 +1,59 @@
+// Copyright 2014 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.common.hash.HashCode;
+import com.google.devtools.build.lib.util.Preconditions;
+import java.util.Arrays;
+
+/** A value class for capturing and comparing MD5-based digests. */
+public class Md5Digest {
+
+ static final int MD5_SIZE = 16;
+ private final byte[] digest;
+
+ /**
+ * Construct the digest from the given bytes.
+ *
+ * @param digest an MD5 digest. Must be sized properly.
+ */
+ public Md5Digest(byte[] digest) {
+ Preconditions.checkState(digest.length == MD5_SIZE);
+ this.digest = digest;
+ }
+
+ @Override
+ public int hashCode() {
+ // We are already dealing with the digest so we can just use portion of it as a hash code.
+ return digest[0] + (digest[1] << 8) + (digest[2] << 16) + (digest[3] << 24);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (obj instanceof Md5Digest) && Arrays.equals(digest, ((Md5Digest) obj).digest);
+ }
+
+ @Override
+ public String toString() {
+ return HashCode.fromBytes(digest).toString();
+ }
+
+ /**
+ * Warning: This is a mutable wrapper and does not necessarily own the underlying byte[]. Don't
+ * modify it unless you are sure about it.
+ */
+ public byte[] getDigestBytesUnsafe() {
+ return digest;
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/actions/cache/Metadata.java b/src/main/java/com/google/devtools/build/lib/actions/cache/Metadata.java
index 21891300b0..316fd234e5 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/cache/Metadata.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/cache/Metadata.java
@@ -18,7 +18,6 @@ import com.google.common.io.BaseEncoding;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import com.google.devtools.build.lib.util.Preconditions;
-
import java.util.Arrays;
import java.util.Date;
diff --git a/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataHandler.java b/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataHandler.java
index 6fcb964840..5e74180014 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataHandler.java
@@ -18,7 +18,6 @@ import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
import com.google.devtools.build.lib.actions.MiddlemanAction;
import com.google.devtools.build.lib.vfs.FileStatus;
-
import java.io.IOException;
/** Retrieves {@link Metadata} of {@link Artifact}s, and inserts virtual metadata as well. */
@@ -46,8 +45,8 @@ public interface MetadataHandler {
*/
Metadata getMetadata(Artifact artifact) throws IOException;
- /** Sets digest for virtual artifacts (e.g. middlemen). {@code digest} must not be null. */
- void setDigestForVirtualArtifact(Artifact artifact, Digest digest);
+ /** Sets digest for virtual artifacts (e.g. middlemen). {@code md5Digest} must not be null. */
+ void setDigestForVirtualArtifact(Artifact artifact, Md5Digest md5Digest);
/**
* Registers the given output as contents of a TreeArtifact, without injecting its digest.