/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Common Public License (CPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/cpl1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.scheduler;
import org.jikesrvm.ArchitectureSpecific;
import org.jikesrvm.VM;
import org.jikesrvm.VM_SizeConstants;
import org.jikesrvm.annotations.NoSubArchCompile;
import org.jikesrvm.classloader.VM_MemberReference;
import org.jikesrvm.classloader.VM_Method;
import org.jikesrvm.classloader.VM_NormalMethod;
import org.jikesrvm.classloader.VM_TypeReference;
import org.jikesrvm.compilers.common.VM_CompiledMethod;
import org.jikesrvm.compilers.common.VM_CompiledMethods;
import org.jikesrvm.compilers.opt.VM_OptCompiledMethod;
import org.jikesrvm.compilers.opt.VM_OptEncodedCallSiteTree;
import org.jikesrvm.compilers.opt.VM_OptMachineCodeMap;
import org.jikesrvm.memorymanagers.mminterface.MM_Constants;
import org.jikesrvm.memorymanagers.mminterface.MM_Interface;
import org.jikesrvm.runtime.VM_Magic;
import static org.jikesrvm.runtime.VM_SysCall.sysCall;
import org.jikesrvm.scheduler.greenthreads.VM_GreenScheduler;
import org.vmmagic.pragma.Entrypoint;
import org.vmmagic.pragma.Interruptible;
import org.vmmagic.pragma.LogicallyUninterruptible;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.LocalAddress;
import org.vmmagic.unboxed.Offset;
/**
* Global variables used to implement virtual machine thread scheduler.
* - virtual cpus
* - threads
* - locks
*/
@Uninterruptible
public abstract class VM_Scheduler {
private static final VM_Scheduler singleton = new VM_GreenScheduler();
public static class ThreadModel extends org.jikesrvm.scheduler.greenthreads.VM_GreenThread {
public ThreadModel(byte[] stack, String s) {
super(stack, s);
}
public ThreadModel(String s) {
super(s);
}
}
public static final class LockModel extends org.jikesrvm.scheduler.greenthreads.VM_GreenLock {
}
private static VM_Scheduler getScheduler() {
return singleton;
}
/** Toggle display of frame pointer address in stack dump */
private static final boolean SHOW_FP_IN_STACK_DUMP = true;
/** Index of thread in which "VM.boot()" runs */
public static final int PRIMORDIAL_THREAD_INDEX = 1;
/** Maximum number of VM_Thread's that we can support. */
public static final int LOG_MAX_THREADS = 14;
public static final int MAX_THREADS = 1 << LOG_MAX_THREADS;
/** Flag for controlling virtual-to-physical processor binding. */
public static final int NO_CPU_AFFINITY = -1;
/** Scheduling quantum in milliseconds: interruptQuantum * interruptQuantumMultiplier */
public static int schedulingQuantum = 10;
// Virtual cpu's.
//
/**
* Physical cpu to which first virtual processor is bound (remainder are bound
* sequentially)
*/
public static int cpuAffinity = NO_CPU_AFFINITY;
/** VM is terminated, clean up and exit */
public static boolean terminated;
// Thread creation and deletion.
//
/** list of threads that have been created (slot 0 always empty) */
public static final ThreadModel[] threads = new ThreadModel[MAX_THREADS];
/** place to start searching threads[] for next free slot */
protected static int threadAllocationIndex;
/** highest thread index allocated */
private static int threadHighWatermark;
/** number of threads running or waiting to run */
protected static int numActiveThreads;
/** number of "daemon" threads, in the java sense */
protected static int numDaemons;
/**
* guard for serializing access to fields above, also serializes thread
* termination
*/
public static final VM_ProcessorLock threadCreationMutex = new VM_ProcessorLock();
/**
* Flag set by external signal to request debugger activation at next thread switch.
* See also: RunBootImage.C
*/
public static volatile boolean debugRequested;
/** Number of times dump stack has been called recursively */
protected static int inDumpStack = 0;
/** In dump stack and dying */
protected static boolean exitInProgress = false;
/** Extra debug from traces */
protected static final boolean traceDetails = false;
/** Int controlling output. 0 => output can be used, otherwise ID of processor */
@SuppressWarnings({"unused", "UnusedDeclaration"})
@Entrypoint
protected static int outputLock;
////////////////////////////////////////////////
// fields for synchronizing code patching
////////////////////////////////////////////////
/**
* How may processors to be synchronized for code patching, the last one (0)
* will notify the blocked thread. Used only if RVM_FOR_POWERPC is true
*/
public static int toSyncProcessors;
/**
* Synchronize object. Used only if RVM_FOR_POWERPC is true
*/
public static Object syncObj = null;
/**
* Find an empty slot in the {@link VM_Scheduler#threads}[] array and bind
* it to this thread. <br>
* <b>Assumption:</b> call is guarded by threadCreationMutex.
* @return the thread slot assigned this thread
*/
@LogicallyUninterruptible
@NoSubArchCompile
static int assignThreadSlot(VM_Thread thread) {
if (!VM.runningVM) {
// create primordial thread (in boot image)
int threadSlot = VM_Scheduler.PRIMORDIAL_THREAD_INDEX;
VM_Scheduler.threads[threadSlot] = (ThreadModel)thread;
// note that VM_Scheduler.threadAllocationIndex (search hint)
// is out of date
VM_Scheduler.numActiveThreads ++;
return PRIMORDIAL_THREAD_INDEX;
} else {
VM_Scheduler.threadCreationMutex.lock("thread creation mutex");
for (int cnt = threads.length; --cnt >= 1;) {
int index = threadAllocationIndex;
threadAllocationIndex++;
if (threadAllocationIndex == threads.length) {
VM_Scheduler.threadAllocationIndex = 1;
}
if (VM_Scheduler.threads[index] == null) {
/*
* Problem:
*
* We'd like to say "VM_Scheduler.threads[index] = this;"
* but can't do "checkstore" without losing control. Since
* we're using magic for the store, we need to perform an
* explicit write barrier.
*/
if (index > threadHighWatermark) {
threadHighWatermark = index;
}
if (MM_Constants.NEEDS_WRITE_BARRIER) {
MM_Interface.arrayStoreWriteBarrier(VM_Scheduler.threads,
index, thread);
}
VM_Magic.setObjectAtOffset(threads,
Offset.fromIntZeroExtend(index << VM_SizeConstants.LOG_BYTES_IN_ADDRESS), thread);
VM_Scheduler.threadCreationMutex.unlock();
return index;
}
}
VM.sysFail("too many threads"); // !!TODO: grow threads[] array
return -1;
}
}
/**
* Release this thread's threads[] slot.
* Assumption: call is guarded by threadCreationMutex.
* Note that after a thread calls this method, it can no longer
* make JNI calls. This matters when exiting the VM, because it
* implies that this method must be called after the exit callbacks
* are invoked if they are to be able to do JNI.
*/
@NoSubArchCompile
static void releaseThreadSlot(int threadSlot, VM_Thread thread) {
threadCreationMutex.lock("releasing a thread slot");
if (VM.VerifyAssertions) VM._assert(VM_Scheduler.threads[threadSlot] == thread);
/*
* Problem:
*
* We'd like to say "VM_Scheduler.threads[index] = null;" but
* can't do "checkstore" inside dispatcher (with thread switching
* enabled) without losing control to a threadswitch, so we must
* hand code the operation via magic. Since we're using magic
* for the store, we need to perform an explicit write
* barrier. Generational collectors may not care about a null
* store, but a reference counting collector sure does.
*/
if (MM_Constants.NEEDS_WRITE_BARRIER)
MM_Interface.arrayStoreWriteBarrier(VM_Scheduler.threads,
threadSlot, null);
VM_Magic.setObjectAtOffset(VM_Scheduler.threads,
Offset.fromIntZeroExtend(threadSlot << VM_SizeConstants.LOG_BYTES_IN_ADDRESS), null);
if (threadSlot < VM_Scheduler.threadAllocationIndex)
VM_Scheduler.threadAllocationIndex = threadSlot;
threadCreationMutex.unlock();
}
/**
* Scheduler dependent dump of state of virtual machine.
*/
protected abstract void dumpVirtualMachineInternal();
/**
* Dump state of virtual machine.
*/
public static void dumpVirtualMachine() {
getScheduler().dumpVirtualMachineInternal();
}
protected abstract void lockOutputInternal();
public static void lockOutput() {
getScheduler().lockOutputInternal();
}
protected abstract void unlockOutputInternal();
/**
* Unlock output
*/
public static void unlockOutput() {
getScheduler().unlockOutputInternal();
}
protected abstract void suspendDebuggerThreadInternal();
static void suspendDebuggerThread() {
getScheduler().suspendDebuggerThreadInternal();
}
/**
* Schedule another thread
*/
protected abstract void yieldInternal();
/**
* Schedule another thread
*/
public static void yield() {
getScheduler().yieldInternal();
}
/**
* Schedule thread waiting on l to give it a chance to acquire the lock
* @param l the lock to allow other thread chance to acquire
*/
protected abstract void yieldToOtherThreadWaitingOnLockInternal(VM_Lock l);
/**
* Schedule thread waiting on l to give it a chance to acquire the lock
* @param l the lock to allow other thread chance to acquire
*/
static void yieldToOtherThreadWaitingOnLock(VM_Lock l) {
getScheduler().yieldToOtherThreadWaitingOnLockInternal(l);
}
/** Start the debugger thread */
@Interruptible
protected abstract void startDebuggerThreadInternal();
/** Start the debugger thread */
@Interruptible
public static void startDebuggerThread() {
getScheduler().startDebuggerThreadInternal();
}
/** Scheduler specific initialization */
@Interruptible
protected abstract void initInternal();
/** Scheduler specific initialization */
@Interruptible
public static void init() {
getScheduler().initInternal();
}
/** Scheduler specific boot up */
@Interruptible
protected abstract void bootInternal();
/** Scheduler specific boot up */
@Interruptible
public static void boot() {
getScheduler().bootInternal();
}
/** Scheduler specific sysExit shutdown */
@Interruptible
protected abstract void sysExitInternal();
/** Scheduler specific sysExit shutdown */
@Interruptible
public static void sysExit() {
getScheduler().sysExitInternal();
}
/**
* Number of available processors
* @see Runtime#availableProcessors()
*/
protected abstract int availableProcessorsInternal();
/**
* Number of available processors
* @see Runtime#availableProcessors()
*/
public static int availableProcessors() {
return getScheduler().availableProcessorsInternal();
}
/**
* Number of VM_Processors
*/
protected abstract int getNumberOfProcessorsInternal();
/**
* Number of VM_Processors
*/
public static int getNumberOfProcessors() {
return getScheduler().getNumberOfProcessorsInternal();
}
/**
* Get the current executing thread on this VM_Processor
*/
public static VM_Thread getCurrentThread() {
return VM_Magic.objectAsThread(VM_Processor.getCurrentProcessor().activeThread);
}
/*
* MMTk interface
*/
/**
* Returns if the VM is ready for a garbage collection.
*
* @return True if the RVM is ready for GC, false otherwise.
*/
public abstract boolean gcEnabledInternal();
/**
* Returns if the VM is ready for a garbage collection.
*
* @return True if the RVM is ready for GC, false otherwise.
*/
public static boolean gcEnabled() {
return getScheduler().gcEnabledInternal();
}
/**
* Suspend a concurrent worker: it will resume when the garbage collector notifies.
*/
protected abstract void suspendConcurrentCollectorThreadInternal();
/**
* Suspend a concurrent worker: it will resume when the garbage collector notifies.
*/
public static void suspendConcurrentCollectorThread() {
getScheduler().suspendConcurrentCollectorThreadInternal();
}
/**
* Schedule the concurrent workers that are not already running
* @see org.jikesrvm.mm.mmtk.Collection
*/
protected abstract void scheduleConcurrentCollectorThreadsInternal();
/**
* Schedule the concurrent workers that are not already running
* @see org.jikesrvm.mm.mmtk.Collection
*/
public static void scheduleConcurrentCollectorThreads() {
getScheduler().scheduleConcurrentCollectorThreadsInternal();
}
/**
* suspend the finalizer thread: it will resume when the garbage collector
* places objects on the finalizer queue and notifies.
*/
protected abstract void suspendFinalizerThreadInternal();
/**
* suspend the finalizer thread: it will resume when the garbage collector
* places objects on the finalizer queue and notifies.
*/
static void suspendFinalizerThread() {
getScheduler().suspendFinalizerThreadInternal();
}
/**
* Schedule the finalizer thread if its not already running
* @see org.jikesrvm.mm.mmtk.Collection
*/
protected abstract void scheduleFinalizerInternal();
/**
* Schedule the finalizer thread if its not already running
* @see org.jikesrvm.mm.mmtk.Collection
*/
public static void scheduleFinalizer() {
getScheduler().scheduleFinalizerInternal();
}
/**
* Request that all mutators flush their context for gc.
* @see org.jikesrvm.mm.mmtk.Collection
*/
protected abstract void requestMutatorFlushInternal();
/**
* Request that all mutators flush their context for gc.
* @see org.jikesrvm.mm.mmtk.Collection
*/
public static void requestMutatorFlush() {
getScheduler().requestMutatorFlushInternal();
}
/**
* Print out message in format "p[j] (cez#td) who: what", where:
* p = processor id
* j = java thread id
* c* = ava thread id of the owner of threadCreationMutex (if any)
* e* = java thread id of the owner of threadExecutionMutex (if any)
* z* = VM_Processor.getCurrentProcessor().threadSwitchingEnabledCount
* (0 means thread switching is enabled outside of the call to debug)
* t* = numActiveThreads
* d* = numDaemons
*
* * parenthetical values, printed only if traceDetails = true)
*
* We serialize against a mutex to avoid intermingling debug output from multiple threads.
*/
@NoSubArchCompile
public static void trace(String who, String what) {
lockOutput();
VM_Processor.getCurrentProcessor().disableThreadSwitching("disabled for scheduler to trace processor(1)");
VM.sysWriteInt(VM_Processor.getCurrentProcessorId());
VM.sysWrite("[");
VM_Thread t = getCurrentThread();
t.dump();
VM.sysWrite("] ");
if (traceDetails) {
VM.sysWrite("(");
// VM.sysWriteInt(threadCreationMutex.owner);
// VM.sysWrite("-");
// VM.sysWriteInt(-VM_Processor.getCurrentProcessor().threadSwitchingEnabledCount);
// VM.sysWrite("#");
VM.sysWriteInt(numDaemons);
VM.sysWrite("/");
VM.sysWriteInt(numActiveThreads);
VM.sysWrite(") ");
}
VM.sysWrite(who);
VM.sysWrite(": ");
VM.sysWrite(what);
VM.sysWrite("\n");
VM_Processor.getCurrentProcessor().enableThreadSwitching();
unlockOutput();
}
/** @return highest thread index allocated */
public static int getThreadHighWatermark() {
return threadHighWatermark;
}
/**
* Print out message in format "p[j] (cez#td) who: what howmany", where:
* p = processor id
* j = java thread id
* c* = java thread id of the owner of threadCreationMutex (if any)
* e* = java thread id of the owner of threadExecutionMutex (if any)
* z* = VM_Processor.getCurrentProcessor().threadSwitchingEnabledCount
* (0 means thread switching is enabled outside of the call to debug)
* t* = numActiveThreads
* d* = numDaemons
*
* * parenthetical values, printed only if traceDetails = true)
*
* We serialize against a mutex to avoid intermingling debug output from multiple threads.
*/
@NoSubArchCompile
public static void trace(String who, String what, int howmany) {
_trace(who, what, howmany, false);
}
// same as trace, but prints integer value in hex
//
@NoSubArchCompile
public static void traceHex(String who, String what, int howmany) {
_trace(who, what, howmany, true);
}
@NoSubArchCompile
public static void trace(String who, String what, Address addr) {
VM_Processor.getCurrentProcessor().disableThreadSwitching("disabled for scheduler to trace processor(2)");
lockOutput();
VM.sysWriteInt(VM_Processor.getCurrentProcessorId());
VM.sysWrite("[");
getCurrentThread().dump();
VM.sysWrite("] ");
if (traceDetails) {
VM.sysWrite("(");
VM.sysWriteInt(numDaemons);
VM.sysWrite("/");
VM.sysWriteInt(numActiveThreads);
VM.sysWrite(") ");
}
VM.sysWrite(who);
VM.sysWrite(": ");
VM.sysWrite(what);
VM.sysWrite(" ");
VM.sysWriteHex(addr);
VM.sysWrite("\n");
unlockOutput();
VM_Processor.getCurrentProcessor().enableThreadSwitching();
}
@NoSubArchCompile
private static void _trace(String who, String what, int howmany, boolean hex) {
VM_Processor.getCurrentProcessor().disableThreadSwitching("disabled for scheduler to trace processor(3)");
lockOutput();
VM.sysWriteInt(VM_Processor.getCurrentProcessorId());
VM.sysWrite("[");
//VM.sysWriteInt(VM_Thread.getCurrentThread().getIndex());
getCurrentThread().dump();
VM.sysWrite("] ");
if (traceDetails) {
VM.sysWrite("(");
// VM.sysWriteInt(threadCreationMutex.owner);
// VM.sysWrite("-");
// VM.sysWriteInt(-VM_Processor.getCurrentProcessor().threadSwitchingEnabledCount);
// VM.sysWrite("#");
VM.sysWriteInt(numDaemons);
VM.sysWrite("/");
VM.sysWriteInt(numActiveThreads);
VM.sysWrite(") ");
}
VM.sysWrite(who);
VM.sysWrite(": ");
VM.sysWrite(what);
VM.sysWrite(" ");
if (hex) {
VM.sysWriteHex(howmany);
} else {
VM.sysWriteInt(howmany);
}
VM.sysWrite("\n");
unlockOutput();
VM_Processor.getCurrentProcessor().enableThreadSwitching();
}
/**
* Print interesting scheduler information, starting with a stack traceback.
* Note: the system could be in a fragile state when this method
* is called, so we try to rely on as little runtime functionality
* as possible (eg. use no bytecodes that require VM_Runtime support).
*/
@NoSubArchCompile
public static void traceback(String message) {
if (VM.runningVM) {
VM_Processor.getCurrentProcessor().disableThreadSwitching("disabled for scheduler to trace processor(3)");
lockOutput();
}
VM.sysWriteln(message);
tracebackWithoutLock();
if (VM.runningVM) {
unlockOutput();
VM_Processor.getCurrentProcessor().enableThreadSwitching();
}
}
@NoSubArchCompile
public static void traceback(String message, int number) {
if (VM.runningVM) {
VM_Processor.getCurrentProcessor().disableThreadSwitching("disabled for scheduler to trace processor(4)");
lockOutput();
}
VM.sysWriteln(message, number);
tracebackWithoutLock();
if (VM.runningVM) {
unlockOutput();
VM_Processor.getCurrentProcessor().enableThreadSwitching();
}
}
@NoSubArchCompile
static void tracebackWithoutLock() {
if (VM.runningVM) {
dumpStack(VM_Magic.getCallerFramePointer(VM_Magic.getFramePointer()));
} else {
dumpStack();
}
}
/**
* Dump stack of calling thread, starting at callers frame
*/
@LogicallyUninterruptible
@NoSubArchCompile
public static void dumpStack() {
if (VM.runningVM) {
dumpStack(VM_Magic.getFramePointer());
} else {
StackTraceElement[] elements =
(new Throwable("--traceback from Jikes RVM's VM_Scheduler class--")).getStackTrace();
for (StackTraceElement element: elements) {
System.err.println(element.toString());
}
}
}
/**
* Dump state of a (stopped) thread's stack.
* @param fp address of starting frame. first frame output
* is the calling frame of passed frame
*/
@NoSubArchCompile
public static void dumpStack(LocalAddress fp) {
if (VM.VerifyAssertions) {
VM._assert(VM.runningVM);
}
LocalAddress ip = VM_Magic.getReturnAddress(fp);
fp = VM_Magic.getCallerFramePointer(fp);
dumpStack(ip, fp);
}
/**
* Dump state of a (stopped) thread's stack.
* @param ip instruction pointer for first frame to dump
* @param fp frame pointer for first frame to dump
*/
@NoSubArchCompile
public static void dumpStack(LocalAddress ip, LocalAddress fp) {
++inDumpStack;
if (inDumpStack > 1 &&
inDumpStack <= VM.maxSystemTroubleRecursionDepth + VM.maxSystemTroubleRecursionDepthBeforeWeStopVMSysWrite) {
VM.sysWrite("VM_Scheduler.dumpStack(): in a recursive call, ");
VM.sysWrite(inDumpStack);
VM.sysWriteln(" deep.");
}
if (inDumpStack > VM.maxSystemTroubleRecursionDepth) {
VM.dieAbruptlyRecursiveSystemTrouble();
if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
}
VM.sysWriteln();
if (!isAddressValidFramePointer(fp)) {
VM.sysWrite("Bogus looking frame pointer: ", fp);
VM.sysWriteln(" not dumping stack");
} else {
try {
VM.sysWriteln("-- Stack --");
while (VM_Magic.getCallerFramePointer(fp).NE(VM_Magic.addressAsLocalAddress(ArchitectureSpecific.VM_StackframeLayoutConstants.STACKFRAME_SENTINEL_FP))) {
// if code is outside of RVM heap, assume it to be native code,
// skip to next frame
if (!MM_Interface.addressInVM(VM_Magic.localAddressAsAddress(ip))) {
showMethod("native frame", fp);
ip = VM_Magic.getReturnAddress(fp);
fp = VM_Magic.getCallerFramePointer(fp);
} else {
int compiledMethodId = VM_Magic.getCompiledMethodID(fp);
if (compiledMethodId == ArchitectureSpecific.VM_StackframeLayoutConstants.INVISIBLE_METHOD_ID) {
showMethod("invisible method", fp);
} else {
// normal java frame(s)
VM_CompiledMethod compiledMethod = VM_CompiledMethods.getCompiledMethod(compiledMethodId);
if (compiledMethod == null) {
showMethod(compiledMethodId, fp);
} else if (compiledMethod.getCompilerType() == VM_CompiledMethod.TRAP) {
showMethod("hardware trap", fp);
} else {
VM_Method method = compiledMethod.getMethod();
Offset instructionOffset = compiledMethod.getInstructionOffset(ip);
int lineNumber = compiledMethod.findLineNumberForInstruction(instructionOffset);
boolean frameShown = false;
if (VM.BuildForOptCompiler && compiledMethod.getCompilerType() == VM_CompiledMethod.OPT) {
VM_OptCompiledMethod optInfo = (VM_OptCompiledMethod) compiledMethod;
// Opt stack frames may contain multiple inlined methods.
VM_OptMachineCodeMap map = optInfo.getMCMap();
int iei = map.getInlineEncodingForMCOffset(instructionOffset);
if (iei >= 0) {
int[] inlineEncoding = map.inlineEncoding;
int bci = map.getBytecodeIndexForMCOffset(instructionOffset);
for (; iei >= 0; iei = VM_OptEncodedCallSiteTree.getParent(iei, inlineEncoding)) {
int mid = VM_OptEncodedCallSiteTree.getMethodID(iei, inlineEncoding);
method = VM_MemberReference.getMemberRef(mid).asMethodReference().getResolvedMember();
lineNumber = ((VM_NormalMethod)method).getLineNumberForBCIndex(bci);
showMethod(method, lineNumber, fp);
if (iei > 0) {
bci = VM_OptEncodedCallSiteTree.getByteCodeOffset(iei, inlineEncoding);
}
}
frameShown=true;
}
}
if(!frameShown) {
showMethod(method, lineNumber, fp);
}
}
}
ip = VM_Magic.getReturnAddress(fp);
fp = VM_Magic.getCallerFramePointer(fp);
}
if (!isAddressValidFramePointer(fp)) {
VM.sysWrite("Bogus looking frame pointer: ", fp);
VM.sysWriteln(" end of stack dump");
break;
}
} // end while
} catch (Throwable t) {
VM.sysWriteln("Something bad killed the stack dump. The last frame pointer was: ", fp);
}
}
--inDumpStack;
}
/**
* Return true if the supplied address could be a valid frame pointer.
* To check for validity we make sure the frame pointer is in one of the
* spaces;
* <ul>
* <li>LOS (For regular threads)</li>
* <li>Immortal (For threads allocated in immortal space such as collectors)</li>
* <li>Boot (For the boot thread)</li>
* </ul>
*
* <p>or it is {@link ArchitectureSpecific.VM_StackframeLayoutConstants#STACKFRAME_SENTINEL_FP}.
* The STACKFRAME_SENTINEL_FP is possible when the thread has been created but has yet to be
* scheduled.</p>
*
* @param address the address.
* @return true if the address could be a frame pointer, false otherwise.
*/
@NoSubArchCompile
private static boolean isAddressValidFramePointer(final LocalAddress address) {
return address.EQ(VM_Magic.addressAsLocalAddress(ArchitectureSpecific.VM_StackframeLayoutConstants.STACKFRAME_SENTINEL_FP)) ||
MM_Interface.mightBeFP(VM_Magic.localAddressAsAddress(address));
}
@NoSubArchCompile
private static void showPrologue(LocalAddress fp) {
VM.sysWrite(" at ");
if (SHOW_FP_IN_STACK_DUMP) {
VM.sysWrite("[");
VM.sysWrite(fp);
VM.sysWrite("] ");
}
}
/**
* Show a method where getCompiledMethod returns null
*
* @param compiledMethodId
* @param fp
*/
@NoSubArchCompile
private static void showMethod(int compiledMethodId, LocalAddress fp) {
showPrologue(fp);
VM.sysWrite("<unprintable normal Java frame: VM_CompiledMethods.getCompiledMethod(",
compiledMethodId,
") returned null>\n");
}
/**
* Show a method that we can't show (ie just a text description of the
* stack frame
*
* @param name
* @param fp
*/
@NoSubArchCompile
private static void showMethod(String name, LocalAddress fp) {
showPrologue(fp);
VM.sysWrite("<");
VM.sysWrite(name);
VM.sysWrite(">\n");
}
/**
* Helper function for {@link #dumpStack(Address,Address)}. Print a stack
* frame showing the method.
*/
@NoSubArchCompile
private static void showMethod(VM_Method method, int lineNumber, LocalAddress fp) {
showPrologue(fp);
if (method == null) {
VM.sysWrite("<unknown method>");
} else {
VM.sysWrite(method.getDeclaringClass().getDescriptor());
VM.sysWrite(" ");
VM.sysWrite(method.getName());
VM.sysWrite(method.getDescriptor());
}
if (lineNumber > 0) {
VM.sysWrite(" at line ");
VM.sysWriteInt(lineNumber);
}
VM.sysWrite("\n");
}
/**
* Dump state of a (stopped) thread's stack and exit the virtual machine.
* @param fp address of starting frame
* Returned: doesn't return.
* This method is called from RunBootImage.C when something goes horrifically
* wrong with exception handling and we want to die with useful diagnostics.
*/
@Entrypoint
@NoSubArchCompile
public static void dumpStackAndDie(LocalAddress fp) {
if (!exitInProgress) {
// This is the first time I've been called, attempt to exit "cleanly"
exitInProgress = true;
dumpStack(fp);
VM.sysExit(VM.EXIT_STATUS_DUMP_STACK_AND_DIE);
} else {
// Another failure occurred while attempting to exit cleanly.
// Get out quick and dirty to avoid hanging.
sysCall.sysExit(VM.EXIT_STATUS_RECURSIVELY_SHUTTING_DOWN);
}
}
/**
* Is it safe to start forcing garbage collects for stress testing?
*/
protected abstract boolean safeToForceGCsInternal();
/**
* Is it safe to start forcing garbage collects for stress testing?
*/
public static boolean safeToForceGCs() {
return getScheduler().safeToForceGCsInternal();
}
/**
* Set up the initial thread and processors as part of boot image writing
* @return the boot thread
*/
@Interruptible
protected abstract VM_Thread setupBootThreadInternal();
/**
* Set up the initial thread and processors as part of boot image writing
* @return the boot thread
*/
@Interruptible
@NoSubArchCompile
public static VM_Thread setupBootThread() {
if (VM.VerifyAssertions) VM._assert(!VM.runningVM);
return getScheduler().setupBootThreadInternal();
}
/**
* Get the type of the thread (to avoid guarded inlining..)
*/
@Interruptible
protected abstract VM_TypeReference getThreadTypeInternal();
/**
* Get the type of the thread (to avoid guarded inlining..)
*/
@Interruptible
public static VM_TypeReference getThreadType() {
return getScheduler().getThreadTypeInternal();
}
/**
* Get the type of the processor (to avoid guarded inlining..)
*/
@Interruptible
protected abstract VM_TypeReference getProcessorTypeInternal();
/**
* Get the type of the processor (to avoid guarded inlining..)
*/
@Interruptible
public static VM_TypeReference getProcessorType() {
return getScheduler().getProcessorTypeInternal();
}
}