diff options
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.java | 162 |
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; + } +} |