aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/main/java/com/google/devtools/build/lib/worker/RecordingInputStream.java110
-rw-r--r--src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategy.java95
-rw-r--r--src/main/java/com/google/devtools/build/lib/worker/WorkerTestStrategy.java20
3 files changed, 131 insertions, 94 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/worker/RecordingInputStream.java b/src/main/java/com/google/devtools/build/lib/worker/RecordingInputStream.java
new file mode 100644
index 0000000000..1128cea762
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/worker/RecordingInputStream.java
@@ -0,0 +1,110 @@
+// Copyright 2017 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.worker;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.util.regex.Pattern;
+
+/**
+ * An input stream filter that records the first X bytes read from its wrapped stream.
+ *
+ * <p>The number bytes to record can be set via {@link #startRecording(int)}}, which also discards
+ * any already recorded data. The recorded data can be retrieved via {@link
+ * #getRecordedDataAsString(Charset)}.
+ */
+final class RecordingInputStream extends FilterInputStream {
+ private static final Pattern NON_PRINTABLE_CHARS =
+ Pattern.compile("[^\\p{Print}\\t\\r\\n]", Pattern.UNICODE_CHARACTER_CLASS);
+
+ private ByteArrayOutputStream recordedData;
+ private int maxRecordedSize;
+
+ RecordingInputStream(InputStream in) {
+ super(in);
+ }
+
+ /**
+ * Returns the maximum number of bytes that can still be recorded in our buffer (but not more
+ * than {@code size}).
+ */
+ private int getRecordableBytes(int size) {
+ if (recordedData == null) {
+ return 0;
+ }
+ return Math.min(maxRecordedSize - recordedData.size(), size);
+ }
+
+ @Override
+ public int read() throws IOException {
+ int bytesRead = super.read();
+ if (getRecordableBytes(bytesRead) > 0) {
+ recordedData.write(bytesRead);
+ }
+ return bytesRead;
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException {
+ int bytesRead = super.read(b);
+ int recordableBytes = getRecordableBytes(bytesRead);
+ if (recordableBytes > 0) {
+ recordedData.write(b, 0, recordableBytes);
+ }
+ return bytesRead;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int bytesRead = super.read(b, off, len);
+ int recordableBytes = getRecordableBytes(bytesRead);
+ if (recordableBytes > 0) {
+ recordedData.write(b, off, recordableBytes);
+ }
+ return bytesRead;
+ }
+
+ public void startRecording(int maxSize) {
+ recordedData = new ByteArrayOutputStream(maxSize);
+ maxRecordedSize = maxSize;
+ }
+
+ /**
+ * Reads whatever remaining data is available on the input stream if we still have space left in
+ * the recording buffer, in order to maximize the usefulness of the recorded data for the
+ * caller.
+ */
+ public void readRemaining() {
+ try {
+ byte[] dummy = new byte[getRecordableBytes(available())];
+ read(dummy);
+ } catch (IOException e) {
+ // Ignore.
+ }
+ }
+
+ /**
+ * Returns the recorded data as a string, where non-printable characters are replaced with a '?'
+ * symbol.
+ */
+ public String getRecordedDataAsString(Charset charsetName) throws UnsupportedEncodingException {
+ String recordedString = recordedData.toString(charsetName.name());
+ return NON_PRINTABLE_CHARS.matcher(recordedString).replaceAll("?").trim();
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategy.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategy.java
index a81ef6eb52..96021406be 100644
--- a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategy.java
@@ -53,12 +53,8 @@ import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.lib.worker.WorkerProtocol.WorkRequest;
import com.google.devtools.build.lib.worker.WorkerProtocol.WorkResponse;
import com.google.protobuf.ByteString;
-import java.io.ByteArrayOutputStream;
-import java.io.FilterInputStream;
+import com.google.protobuf.InvalidProtocolBufferException;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -78,93 +74,6 @@ import java.util.regex.Pattern;
)
public final class WorkerSpawnStrategy implements SandboxedSpawnActionContext {
- /**
- * An input stream filter that records the first X bytes read from its wrapped stream.
- *
- * <p>The number bytes to record can be set via {@link #startRecording(int)}}, which also discards
- * any already recorded data. The recorded data can be retrieved via {@link
- * #getRecordedDataAsString(Charset)}.
- */
- private static final class RecordingInputStream extends FilterInputStream {
- private static final Pattern NON_PRINTABLE_CHARS =
- Pattern.compile("[^\\p{Print}\\t\\r\\n]", Pattern.UNICODE_CHARACTER_CLASS);
-
- private ByteArrayOutputStream recordedData;
- private int maxRecordedSize;
-
- protected RecordingInputStream(InputStream in) {
- super(in);
- }
-
- /**
- * Returns the maximum number of bytes that can still be recorded in our buffer (but not more
- * than {@code size}).
- */
- private int getRecordableBytes(int size) {
- if (recordedData == null) {
- return 0;
- }
- return Math.min(maxRecordedSize - recordedData.size(), size);
- }
-
- @Override
- public int read() throws IOException {
- int bytesRead = super.read();
- if (getRecordableBytes(bytesRead) > 0) {
- recordedData.write(bytesRead);
- }
- return bytesRead;
- }
-
- @Override
- public int read(byte[] b) throws IOException {
- int bytesRead = super.read(b);
- int recordableBytes = getRecordableBytes(bytesRead);
- if (recordableBytes > 0) {
- recordedData.write(b, 0, recordableBytes);
- }
- return bytesRead;
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- int bytesRead = super.read(b, off, len);
- int recordableBytes = getRecordableBytes(bytesRead);
- if (recordableBytes > 0) {
- recordedData.write(b, off, recordableBytes);
- }
- return bytesRead;
- }
-
- public void startRecording(int maxSize) {
- recordedData = new ByteArrayOutputStream(maxSize);
- maxRecordedSize = maxSize;
- }
-
- /**
- * Reads whatever remaining data is available on the input stream if we still have space left in
- * the recording buffer, in order to maximize the usefulness of the recorded data for the
- * caller.
- */
- public void readRemaining() {
- try {
- byte[] dummy = new byte[getRecordableBytes(available())];
- read(dummy);
- } catch (IOException e) {
- // Ignore.
- }
- }
-
- /**
- * Returns the recorded data as a string, where non-printable characters are replaced with a '?'
- * symbol.
- */
- public String getRecordedDataAsString(Charset charsetName) throws UnsupportedEncodingException {
- String recordedString = recordedData.toString(charsetName.name());
- return NON_PRINTABLE_CHARS.matcher(recordedString).replaceAll("?").trim();
- }
- }
-
public static final String ERROR_MESSAGE_PREFIX =
"Worker strategy cannot execute this %s action, ";
public static final String REASON_NO_FLAGFILE =
@@ -387,7 +296,7 @@ public final class WorkerSpawnStrategy implements SandboxedSpawnActionContext {
recordingStream.startRecording(4096);
try {
response = WorkResponse.parseDelimitedFrom(recordingStream);
- } catch (IOException e2) {
+ } catch (InvalidProtocolBufferException e2) {
// If protobuf couldn't parse the response, try to print whatever the failing worker wrote
// to stdout - it's probably a stack trace or some kind of error message that will help the
// user figure out why the compiler is failing.
diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerTestStrategy.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerTestStrategy.java
index 7e665a1285..3282fe708b 100644
--- a/src/main/java/com/google/devtools/build/lib/worker/WorkerTestStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerTestStrategy.java
@@ -14,6 +14,7 @@
package com.google.devtools.build.lib.worker;
+import com.google.common.base.Charsets;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -40,6 +41,7 @@ import com.google.devtools.build.lib.view.test.TestStatus.TestResultData;
import com.google.devtools.build.lib.worker.WorkerProtocol.WorkRequest;
import com.google.devtools.build.lib.worker.WorkerProtocol.WorkResponse;
import com.google.devtools.common.options.OptionsClassProvider;
+import com.google.protobuf.InvalidProtocolBufferException;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@@ -152,7 +154,23 @@ public class WorkerTestStrategy extends StandaloneTestStrategy {
request.writeDelimitedTo(worker.getOutputStream());
worker.getOutputStream().flush();
- WorkResponse response = WorkResponse.parseDelimitedFrom(worker.getInputStream());
+ RecordingInputStream recordingStream = new RecordingInputStream(worker.getInputStream());
+ recordingStream.startRecording(4096);
+ WorkResponse response;
+ try {
+ response = WorkResponse.parseDelimitedFrom(recordingStream);
+ } catch (InvalidProtocolBufferException e) {
+ // If protobuf couldn't parse the response, try to print whatever the failing worker wrote
+ // to stdout - it's probably a stack trace or some kind of error message that will help the
+ // user figure out why the compiler is failing.
+ recordingStream.readRemaining();
+ String data = recordingStream.getRecordedDataAsString(Charsets.UTF_8);
+ executor
+ .getEventHandler()
+ .handle(Event.warn("Worker process returned an unparseable WorkResponse:\n" + data));
+ throw e;
+ }
+
actionExecutionContext.getFileOutErr().getErrorStream().write(
response.getOutputBytes().toByteArray());