/*
* Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.max.vm.runtime;
import static com.sun.max.vm.MaxineVM.*;
import static com.sun.max.vm.intrinsics.Infopoints.*;
import static com.sun.max.vm.runtime.VMRegister.*;
import static com.sun.max.vm.thread.VmThread.*;
import static com.sun.max.vm.thread.VmThreadLocal.*;
import com.sun.max.annotate.*;
import com.sun.max.program.*;
import com.sun.max.program.ProgramError.Handler;
import com.sun.max.unsafe.*;
import com.sun.max.vm.*;
import com.sun.max.vm.log.*;
import com.sun.max.vm.thread.*;
/**
* A collection of static methods for reporting errors indicating some fatal condition
* that should cause a hard exit of the VM. All errors reported by use of {@link ProgramError}
* are rerouted to use this class.
*
* None of the methods in this class perform any synchronization or heap allocation
* and they should never cause recursive error reporting.
*/
public final class FatalError extends Error {
private static boolean CoreOnError;
private static boolean TrapOnError;
static {
VMOptions.addFieldOption("-XX:", "CoreOnError", FatalError.class, "Generate core dump on fatal error.", MaxineVM.Phase.PRISTINE);
VMOptions.addFieldOption("-XX:", "TrapOnError", FatalError.class, "Issue breakpoint trap on fatal error.", MaxineVM.Phase.PRISTINE);
}
static {
ProgramError.setHandler(new Handler() {
public void handle(String message, Throwable throwable) {
unexpected(message, false, throwable, Pointer.zero());
}
});
}
private static boolean DUMPING_STACKS_IN_FATAL_ERROR;
private static FatalError FATAL_ERROR_WHILE_DUMPING_STACKS = new FatalError(null, null);
static {
FATAL_ERROR_WHILE_DUMPING_STACKS.setStackTrace(new StackTraceElement[0]);
}
/**
* Support for running post-error diagnosis on unexpected error from the vm operation (useful for GC).
*/
private static Runnable runOnVMOpError;
public static void setOnVMOpError(Runnable runOnError) {
if (VmThread.current().isVmOperationThread()) {
FatalError.runOnVMOpError = runOnError;
}
}
/**
* A breakpoint should be set on this method when debugging the VM so that
* fatal errors can be investigated before the VM exits.
*/
@INSPECTED
@NEVER_INLINE
public static void breakpoint() {
}
private FatalError(String msg, Throwable throwable) {
super(msg, throwable);
}
/**
* Reports the occurrence of some error condition.
*
* This method never returns normally.
*
* This method does not perform any synchronization or heap allocation.
*
* @param message a message describing the error condition. This value may be {@code null}.
* @see #unexpected(String, boolean, Throwable, Pointer)
* @return never
*/
@NEVER_INLINE
public static FatalError unexpected(String message) {
throw unexpected(message, false, null, Pointer.zero());
}
/**
* Reports the occurrence of some error condition.
*
* This method never returns normally.
*
* If {@code throwable == null}, this method does not perform any synchronization or heap allocation.
*
* @param message a message describing the error condition. This value may be {@code null}.
* @param throwable an exception given more detail on the cause of the error condition. This value may be {@code null}.
* @see #unexpected(String, boolean, Throwable, Pointer)
* @return never
*/
@INSPECTED
@NEVER_INLINE
public static FatalError unexpected(String message, Throwable throwable) {
throw unexpected(message, false, throwable, Pointer.zero());
}
/**
* Determines if a fatal error is in flight. This can be used to try and prevent recursive fatal errors.
*/
public static boolean inFatalError() {
return recursionCount != 0;
}
/**
* Reports the occurrence of some error condition. Before exiting the VM, this method attempts to print a stack
* trace.
*
* This method never returns normally.
*
* If {@code throwable == null}, this method does not perform any synchronization or heap allocation.
*
* @param message a message describing the trap. This value may be {@code null}.
* @param trappedInNative specifies if this is a fatal error due to a trap in native code. If so, then the native
* code instruction pointer at which the trap occurred can be extracted from {@code trapState}
* @param throwable an exception given more detail on the cause of the error condition. This value may be {@code null}.
* @param trapFrame if non-zero, then this is a pointer to the {@linkplain TrapFrameAccess trap frame} for the trap
* resulting in this fatal error
* @return never
*/
@NEVER_INLINE
public static FatalError unexpected(String message, boolean trappedInNative, Throwable throwable, Pointer trapFrame) {
if (MaxineVM.isHosted()) {
throw new FatalError(message, throwable);
}
final VmThread vmThread = VmThread.current();
if (runOnVMOpError != null && vmThread.isVmOperationThread()) {
runOnVMOpError.run();
}
SafepointPoll.disable();
Throw.TraceExceptions = 0;
Throw.TraceExceptionsRaw = false;
// Try to recover from a fatal error while dumping stacks of the other threads
if (DUMPING_STACKS_IN_FATAL_ERROR && FATAL_ERROR_WHILE_DUMPING_STACKS != null) {
FatalError error = FATAL_ERROR_WHILE_DUMPING_STACKS;
vmThread.stackDumpStackFrameWalker().reset();
// Nulling FATAL_ERROR_WHILE_DUMPING_STACKS makes this is a one shot attempt at recovery
FATAL_ERROR_WHILE_DUMPING_STACKS = null;
throw error;
}
if (recursionCount >= MAX_RECURSION_COUNT) {
Log.println("FATAL VM ERROR: Error occurred while handling previous fatal VM error");
exit(false, Pointer.zero());
}
recursionCount++;
final boolean lockDisabledSafepoints = Log.lock();
if (vmThread != null) {
vmThread.stackDumpStackFrameWalker().reset();
}
Log.println();
Log.print("FATAL VM ERROR[");
Log.print(recursionCount);
Log.print("]: ");
if (message != null) {
Log.println(message);
} else {
Log.println();
}
Log.print("Faulting thread: ");
Log.printThread(vmThread, true);
if (!trapFrame.isZero()) {
Log.print("------ Trap State for thread ");
Log.printThread(vmThread, false);
Log.println(" ------");
vm().trapFrameAccess.logTrapFrame(trapFrame);
}
if (vmThread != null) {
dumpStackAndThreadLocals(currentTLA(), trappedInNative);
if (throwable != null) {
Log.print("------ Cause Exception ------");
throwable.printStackTrace(Log.out);
}
DUMPING_STACKS_IN_FATAL_ERROR = true;
VmThreadMap.ACTIVE.forAllThreadLocals(null, dumpStackOfNonCurrentThread);
DUMPING_STACKS_IN_FATAL_ERROR = false;
}
if (vmThread == null || trappedInNative || Throw.ScanStackOnFatalError) {
final Word highestStackAddress = VmThreadLocal.HIGHEST_STACK_SLOT_ADDRESS.load(currentTLA());
Throw.stackScan("RAW STACK SCAN FOR CODE POINTERS:", VMRegister.getCpuStackPointer(), highestStackAddress.asPointer());
}
Log.unlock(lockDisabledSafepoints);
Pointer ip = Pointer.zero();
if (trappedInNative && !trapFrame.isZero()) {
ip = vm().trapFrameAccess.getPC(trapFrame);
}
exit(trappedInNative, ip);
throw null; // unreachable
}
@NEVER_INLINE
private static void exit(boolean doTrapExit, Pointer instructionPointer) {
if (CoreOnError) {
MaxineVM.core_dump();
}
if (TrapOnError) {
Intrinsics.breakpointTrap();
}
VMLog.vmLog().flush(VMLog.FLUSHMODE_CRASH);
if (doTrapExit) {
MaxineVM.native_trap_exit(11, instructionPointer);
}
MaxineVM.native_exit(11);
}
/**
* Causes the VM to print an error message and exit immediately.
*
* @param message the error message to print
*/
@NEVER_INLINE
public static void crash(String message) {
Log.println(message);
exit(false, Pointer.zero());
}
/**
* Checks a given condition and if it is {@code false}, a fatal error is raised.
*
* @param condition a condition to test
* @param message a message describing the error condition being tested
*/
@INLINE
public static void check(boolean condition, String message) {
if (!condition) {
throw unexpected(message, false, null, Pointer.zero());
}
}
/**
* Reports that an unimplemented piece of VM functionality was encountered.
*
* This method never returns normally.
*
* This method does not perform any synchronization or heap allocation.
*
* @see #unexpected(String, boolean, Throwable, Pointer)
* @return never
*/
@NEVER_INLINE
public static FatalError unimplemented() {
throw unexpected("Unimplemented", false, null, Pointer.zero());
}
/**
* Dumps the stack and thread locals of a given thread to the log stream.
*
* @param tla VM thread locals of a thread
* @param trappedInNative specifies if this is for a thread that trapped in native code
*/
static void dumpStackAndThreadLocals(Pointer tla, boolean trappedInNative) {
final VmThread vmThread = VmThread.fromTLA(tla);
Log.print("------ Stack dump for thread ");
Log.printThread(vmThread, false);
Log.println(" ------");
if (!trappedInNative && tla == currentTLA()) {
Throw.stackDump(null, Pointer.fromLong(here()), getCpuStackPointer(), getCpuFramePointer());
} else {
Throw.stackDump(null, tla);
}
Log.print("------ Thread locals for thread ");
Log.printThread(vmThread, false);
Log.println(" ------");
Log.printThreadLocals(tla, true);
}
static final class DumpStackOfNonCurrentThread implements Pointer.Procedure {
public void run(Pointer tla) {
if (ETLA.load(tla) != ETLA.load(currentTLA())) {
try {
dumpStackAndThreadLocals(tla, false);
} catch (FatalError e) {
Log.println("--- STACK TRACE TERMINATED DUE TO RECURSIVE FATAL ERROR ---");
}
}
}
}
private static final DumpStackOfNonCurrentThread dumpStackOfNonCurrentThread = new DumpStackOfNonCurrentThread();
private static final int MAX_RECURSION_COUNT = 2;
private static int recursionCount;
}