aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/runtime/BugReport.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/runtime/BugReport.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/runtime/BugReport.java141
1 files changed, 141 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BugReport.java b/src/main/java/com/google/devtools/build/lib/runtime/BugReport.java
new file mode 100644
index 0000000000..ee1e429ac0
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BugReport.java
@@ -0,0 +1,141 @@
+// Copyright 2014 Google Inc. 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.runtime;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.analysis.BlazeVersionInfo;
+import com.google.devtools.build.lib.util.ExitCode;
+import com.google.devtools.build.lib.util.LoggingUtil;
+import com.google.devtools.build.lib.util.io.OutErr;
+
+import java.io.PrintStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Utility methods for sending bug reports.
+ *
+ * <p> Note, code in this class must be extremely robust. There's nothing
+ * worse than a crash-handler that itself crashes!
+ */
+public abstract class BugReport {
+
+ private BugReport() {}
+
+ private static Logger LOG = Logger.getLogger(BugReport.class.getName());
+
+ private static BlazeVersionInfo versionInfo = BlazeVersionInfo.instance();
+
+ private static BlazeRuntime runtime = null;
+
+ public static void setRuntime(BlazeRuntime newRuntime) {
+ Preconditions.checkNotNull(newRuntime);
+ Preconditions.checkState(runtime == null, "runtime already set: %s, %s", runtime, newRuntime);
+ runtime = newRuntime;
+ }
+
+ /**
+ * Logs the unhandled exception with a special prefix signifying that this was a crash.
+ *
+ * @param exception the unhandled exception to display.
+ * @param args additional values to record in the message.
+ * @param values Additional string values to clarify the exception.
+ */
+ public static void sendBugReport(Throwable exception, List<String> args, String... values) {
+ if (!versionInfo.isReleasedBlaze()) {
+ LOG.info("(Not a released binary; not logged.)");
+ return;
+ }
+
+ logException(exception, filterClientEnv(args), values);
+ }
+
+ /**
+ * Print and send a bug report, and exit with the proper Blaze code.
+ */
+ public static void handleCrash(Throwable throwable, String... args) {
+ BugReport.sendBugReport(throwable, Arrays.asList(args));
+ BugReport.printBug(OutErr.SYSTEM_OUT_ERR, throwable);
+ System.err.println("Blaze crash in async thread:");
+ throwable.printStackTrace();
+ int exitCode =
+ (throwable instanceof OutOfMemoryError) ? ExitCode.OOM_ERROR.getNumericExitCode()
+ : ExitCode.BLAZE_INTERNAL_ERROR.getNumericExitCode();
+ if (runtime != null) {
+ runtime.notifyCommandComplete(exitCode);
+ // We don't call runtime#shutDown() here because all it does is shut down the modules, and who
+ // knows if they can be trusted.
+ }
+ System.exit(exitCode);
+ }
+
+ private static void printThrowableTo(OutErr outErr, Throwable e) {
+ PrintStream err = new PrintStream(outErr.getErrorStream());
+ e.printStackTrace(err);
+ err.flush();
+ LOG.log(Level.SEVERE, "Blaze crashed", e);
+ }
+
+ /**
+ * Print user-helpful information about the bug/crash to the output.
+ *
+ * @param outErr where to write the output
+ * @param e the exception thrown
+ */
+ public static void printBug(OutErr outErr, Throwable e) {
+ if (e instanceof OutOfMemoryError) {
+ outErr.printErr(e.getMessage() + "\n\n" +
+ "Blaze ran out of memory and crashed.\n");
+ } else {
+ printThrowableTo(outErr, e);
+ }
+ }
+
+ /**
+ * Filters {@code args} by removing any item that starts with "--client_env",
+ * then returns this as an immutable list.
+ *
+ * <p>The client's environment variables may contain sensitive data, so we filter it out.
+ */
+ private static List<String> filterClientEnv(Iterable<String> args) {
+ if (args == null) {
+ return null;
+ }
+
+ ImmutableList.Builder<String> filteredArgs = ImmutableList.builder();
+ for (String arg : args) {
+ if (arg != null && !arg.startsWith("--client_env")) {
+ filteredArgs.add(arg);
+ }
+ }
+ return filteredArgs.build();
+ }
+
+ // Log the exception. Because this method is only called in a blaze release,
+ // this will result in a report being sent to a remote logging service.
+ private static void logException(Throwable exception, List<String> args, String... values) {
+ // The preamble is used in the crash watcher, so don't change it
+ // unless you know what you're doing.
+ String preamble = exception instanceof OutOfMemoryError
+ ? "Blaze OOMError: "
+ : "Blaze crashed with args: ";
+
+ LoggingUtil.logToRemote(Level.SEVERE, preamble + Joiner.on(' ').join(args), exception,
+ values);
+ }
+}