blob: 6a7bfa9553948a534cb36d93e822fe0d4e5c7138 [file] [log] [blame]
// Copyright 2018 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.util;
import com.google.common.base.Throwables;
import java.util.logging.Logger;
import javax.annotation.Nullable;
/** Utilities for crashing the jvm. */
public class BazelCrashUtils {
private static final Logger logger = Logger.getLogger(BazelCrashUtils.class.getName());
private static final Object HALT_LOCK = new Object();
@Nullable private static volatile Throwable unprocessedThrowableInTest = null;
private BazelCrashUtils() {}
/**
* In tests, System.exit is disabled. Thus, the main thread should call this method whenever it is
* about to block on thread completion that might hang because of a failed halt below.
*/
public static void maybePropagateUnprocessedThrowableIfInTest() {
synchronized (HALT_LOCK) {
Throwable lastUnprocessedThrowableInTest = unprocessedThrowableInTest;
unprocessedThrowableInTest = null;
if (lastUnprocessedThrowableInTest != null) {
Throwables.throwIfUnchecked(lastUnprocessedThrowableInTest);
throw new RuntimeException(lastUnprocessedThrowableInTest);
}
}
}
/** Terminates the jvm with the exit code {@link ExitCode#BLAZE_INTERNAL_ERROR}. */
public static RuntimeException halt(Throwable cause) {
return halt(cause, ExitCode.BLAZE_INTERNAL_ERROR.getNumericExitCode());
}
/**
* Terminates the jvm with the given {@code exitCode}. Note that, in server mode, the jvm's exit
* code has nothing to do with the exit code picked by the blaze client, except as communicated by
* the 'custom_exit_code_on_abrupt_exit' behavior. If this fails, the exit code picked by the
* client will default to INTERNAL_ERROR.
*/
public static RuntimeException halt(Throwable crash, int exitCode) {
synchronized (HALT_LOCK) {
try {
logCrashDetails(crash);
unprocessedThrowableInTest = crash;
CustomExitCodePublisher.maybeWriteExitStatusFile(exitCode);
} catch (OutOfMemoryError oom) {
// In case we tried to halt with a non-OOM, but OOMed during the lead up to the halt.
Runtime.getRuntime().halt(exitCode);
} finally {
doDie(crash, exitCode);
}
throw new IllegalStateException("impossible");
}
}
private static void logCrashDetails(Throwable crash) {
logger.severe(
"About to call System#exit. Halt requested due to crash. See stderr for details ("
+ crash.getMessage()
+ ")");
System.err.println("About to call System#exit. Halt requested due to crash");
crash.printStackTrace();
System.err.println("... called from");
new Exception().printStackTrace();
}
private static void doDie(Throwable crash, int exitCode) {
try {
// Bypass shutdown hooks if we encountered an OOM, they're likely to take way too long to be
// sensible to run in such a hostile environment.
Throwable cause = crash;
while (cause != null) {
if (cause instanceof OutOfMemoryError) {
Runtime.getRuntime().halt(exitCode);
}
cause = cause.getCause();
}
} finally {
System.exit(exitCode);
}
}
}