aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/shell/TerminationStatus.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/shell/TerminationStatus.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/shell/TerminationStatus.java162
1 files changed, 162 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/shell/TerminationStatus.java b/src/main/java/com/google/devtools/build/lib/shell/TerminationStatus.java
new file mode 100644
index 0000000000..73616c4f5f
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/shell/TerminationStatus.java
@@ -0,0 +1,162 @@
+// 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.shell;
+
+/**
+ * Represents the termination status of a command. {@link Process#waitFor} is
+ * not very precisely specified, so this class encapsulates the interpretation
+ * of values returned by it.
+ *
+ * Caveat: due to the lossy encoding, it's not always possible to accurately
+ * distinguish signal and exit cases. In particular, processes that exit with
+ * a value within the interval [129, 191] will be mistaken for having been
+ * terminated by a signal.
+ *
+ * Instances are immutable.
+ */
+public final class TerminationStatus {
+
+ private final int waitResult;
+
+ /**
+ * Values taken from the glibc strsignal(3) function.
+ */
+ private static final String[] SIGNAL_STRINGS = {
+ null,
+ "Hangup",
+ "Interrupt",
+ "Quit",
+ "Illegal instruction",
+ "Trace/breakpoint trap",
+ "Aborted",
+ "Bus error",
+ "Floating point exception",
+ "Killed",
+ "User defined signal 1",
+ "Segmentation fault",
+ "User defined signal 2",
+ "Broken pipe",
+ "Alarm clock",
+ "Terminated",
+ "Stack fault",
+ "Child exited",
+ "Continued",
+ "Stopped (signal)",
+ "Stopped",
+ "Stopped (tty input)",
+ "Stopped (tty output)",
+ "Urgent I/O condition",
+ "CPU time limit exceeded",
+ "File size limit exceeded",
+ "Virtual timer expired",
+ "Profiling timer expired",
+ "Window changed",
+ "I/O possible",
+ "Power failure",
+ "Bad system call",
+ };
+
+ private static String getSignalString(int signum) {
+ return signum > 0 && signum < SIGNAL_STRINGS.length
+ ? SIGNAL_STRINGS[signum]
+ : "Signal " + signum;
+ }
+
+ /**
+ * Construct a TerminationStatus instance from a Process waitFor code.
+ *
+ * @param waitResult the value returned by {@link java.lang.Process#waitFor}.
+ */
+ public TerminationStatus(int waitResult) {
+ this.waitResult = waitResult;
+ }
+
+ /**
+ * Returns the "raw" result returned by Process.waitFor.
+ */
+ int getRawResult() {
+ return waitResult;
+ }
+
+ /**
+ * Returns true iff the process exited with code 0.
+ */
+ public boolean success() {
+ return exited() && getExitCode() == 0;
+ }
+
+ // We're relying on undocumented behaviour of Process.waitFor, specifically
+ // that waitResult is the exit status when the process returns normally, or
+ // 128+signalnumber when the process is terminated by a signal. We further
+ // assume that value signal numbers fall in the interval [1, 63].
+ private static final int SIGNAL_1 = 128 + 1;
+ private static final int SIGNAL_63 = 128 + 63;
+
+ /**
+ * Returns true iff the process exited normally.
+ */
+ public boolean exited() {
+ return waitResult < SIGNAL_1 || waitResult > SIGNAL_63;
+ }
+
+ /**
+ * Returns the exit code of the subprocess. Undefined if exited() is false.
+ */
+ public int getExitCode() {
+ if (!exited()) {
+ throw new IllegalStateException("getExitCode() not defined");
+ }
+ return waitResult;
+ }
+
+ /**
+ * Returns the number of the signal that terminated the process. Undefined
+ * if exited() returns true.
+ */
+ public int getTerminatingSignal() {
+ if (exited()) {
+ throw new IllegalStateException("getTerminatingSignal() not defined");
+ }
+ return waitResult - SIGNAL_1 + 1;
+ }
+
+ /**
+ * Returns a short string describing the termination status.
+ * e.g. "Exit 1" or "Hangup".
+ */
+ public String toShortString() {
+ return exited()
+ ? ("Exit " + getExitCode())
+ : (getSignalString(getTerminatingSignal()));
+ }
+
+ @Override
+ public String toString() {
+ return exited()
+ ? ("Process exited with status " + getExitCode())
+ : ("Process terminated by signal " + getTerminatingSignal());
+ }
+
+ @Override
+ public int hashCode() {
+ return waitResult;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other instanceof TerminationStatus &&
+ ((TerminationStatus) other).waitResult == this.waitResult;
+ }
+}