/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * 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/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.mm.mmtk; import org.jikesrvm.ArchitectureSpecific; import org.jikesrvm.VM; import org.jikesrvm.Constants; import org.jikesrvm.classloader.RVMMethod; import org.jikesrvm.compilers.common.CompiledMethod; import org.jikesrvm.compilers.common.CompiledMethods; import org.jikesrvm.mm.mminterface.Selected; import org.jikesrvm.mm.mminterface.DebugUtil; import org.jikesrvm.mm.mminterface.GCMapIterator; import org.jikesrvm.mm.mminterface.GCMapIteratorGroup; import org.jikesrvm.mm.mminterface.MemoryManager; import org.jikesrvm.runtime.Entrypoints; import org.jikesrvm.runtime.Magic; import org.jikesrvm.runtime.RuntimeEntrypoints; import org.jikesrvm.scheduler.RVMThread; import org.mmtk.plan.TraceLocal; import org.mmtk.utility.Log; import org.vmmagic.pragma.Inline; import org.vmmagic.pragma.Uninterruptible; import org.vmmagic.unboxed.Address; import org.vmmagic.unboxed.ObjectReference; import org.vmmagic.unboxed.Offset; import org.jikesrvm.ArchitectureSpecific.Registers; /** * Class that supports scanning thread stacks for references during * collections. References are located using GCMapIterators and are * inserted into a set of root locations. Optionally, a set of * interior pointer locations associated with the object is created.<p> * * Threads, stacks, jni environments, and register objects have a * complex interaction in terms of scanning. The operation of * scanning the stack reveals not only roots inside the stack but also * the state of the register objects's gprs and the JNI refs array. * They are all associated via the thread object, making it natural * for scanThread to be considered a single operation with the method * directly accessing these objects via the thread object's * fields. <p> * * One pitfall occurs when scanning the thread object (plus * dependents) when not all of the objects have been copied. Then it * may be that the innards of the register object has not been copied * while the stack object has. The result is that an inconsistent set * of slots is reported. In this case, the copied register object may * not be correct if the copy occurs after the root locations are * discovered but before those locations are processed. In essence, * all of these objects form one logical unit but are physically * separated so that sometimes only part of it has been copied causing * the scan to be incorrect. <p> * * The caller of the stack scanning routine must ensure that all of * these components's descendants are consistent (all copied) when * this method is called. <p> * * <i>Code locations:</i> Identifying pointers <i>into</i> code * objects is essential if code objects are allowed to move (and if * the code objects were not otherwise kept alive, it would be * necessary to ensure the liveness of the code objects). A code * pointer is the only case in which we have interior pointers * (pointers into the inside of objects). For such pointers, two * things must occur: first the pointed to object must be kept alive, * and second, if the pointed to object is moved by a copying * collector, the pointer into the object must be adjusted so it now * points into the newly copied object.<p> */ @Uninterruptible public final class ScanThread implements Constants { /*********************************************************************** * * Class variables */ /** quietly validates each ref reported by map iterators */ static final boolean VALIDATE_REFS = VM.VerifyAssertions; /** * debugging options to produce printout during scanStack * MULTIPLE GC THREADS WILL PRODUCE SCRAMBLED OUTPUT so only * use these when running with PROCESSORS=1 */ static final int DEFAULT_VERBOSITY = 0 /*0*/; static final int FAILURE_VERBOSITY = 4; /*********************************************************************** * * Instance variables */ private final GCMapIteratorGroup iteratorGroup = new GCMapIteratorGroup(); private GCMapIterator iterator; private TraceLocal trace; private boolean processCodeLocations; private RVMThread thread; private Address ip, fp, prevFp, initialIPLoc, topFrame; private CompiledMethod compiledMethod; private int compiledMethodType; private boolean failed; /*********************************************************************** * * Thread scanning */ /** * Scan a thread, placing the addresses of pointers into supplied buffers. * * @param thread The thread to be scanned * @param trace The trace instance to use for reporting references. * @param processCodeLocations Should code locations be processed? */ public static void scanThread(RVMThread thread, TraceLocal trace, boolean processCodeLocations) { if (DEFAULT_VERBOSITY>=1) { VM.sysWriteln("scanning ",thread.getThreadSlot()); } /* get the gprs associated with this thread */ Registers regs=thread.getContextRegisters(); Address gprs = Magic.objectAsAddress(regs.gprs); Address ip=regs.getInnermostInstructionAddress(); Address fp=regs.getInnermostFramePointer(); regs.clear(); regs.setInnermost(ip,fp); scanThread(thread, trace, processCodeLocations, gprs, Address.zero()); } /** * Wrapper for {@link TraceLocal#reportDelayedRootEdge(Address)} that allows * sanity checking of the address. */ private static void reportDelayedRootEdge(TraceLocal trace, Address addr) { if (VALIDATE_REFS) checkReference(addr); trace.reportDelayedRootEdge(addr); } /** * A more general interface to thread scanning, which permits the * scanning of stack segments which are dislocated from the thread * structure. * * @param thread The thread to be scanned * @param trace The trace instance to use for reporting references. * @param processCodeLocations Should code locations be processed? * @param gprs The general purpose registers associated with the * stack being scanned (normally extracted from the thread). * @param topFrame The top frame of the stack being scanned, or zero * if this is to be inferred from the thread (normally the case). */ private static void scanThread(RVMThread thread, TraceLocal trace, boolean processCodeLocations, Address gprs, Address topFrame) { // figure out if the thread should be scanned at all; if not, exit if (thread.getExecStatus()==RVMThread.NEW || thread.getIsAboutToTerminate()) { return; } /* establish ip and fp for the stack to be scanned */ Address ip, fp, initialIPLoc; if (topFrame.isZero()) { /* implicit top of stack, inferred from thread */ ip = thread.getContextRegisters().getInnermostInstructionAddress(); fp = thread.getContextRegisters().getInnermostFramePointer(); initialIPLoc = thread.getContextRegisters().getIPLocation(); } else { /* top frame explicitly defined */ ip = Magic.getReturnAddress(topFrame); fp = Magic.getCallerFramePointer(topFrame); initialIPLoc = thread.getContextRegisters().getIPLocation(); // FIXME } /* Registers */ reportDelayedRootEdge(trace,Magic.objectAsAddress(thread).plus(Entrypoints.threadContextRegistersField.getOffset())); reportDelayedRootEdge(trace,Magic.objectAsAddress(thread).plus(Entrypoints.threadContextRegistersSaveField.getOffset())); reportDelayedRootEdge(trace,Magic.objectAsAddress(thread).plus(Entrypoints.threadExceptionRegistersField.getOffset())); /* Scan the JNI Env field */ if (thread.getJNIEnv() != null) { reportDelayedRootEdge(trace,Magic.objectAsAddress(thread).plus(Entrypoints.jniEnvField.getOffset())); reportDelayedRootEdge(trace,Magic.objectAsAddress(thread.getJNIEnv()).plus(Entrypoints.JNIRefsField.getOffset())); reportDelayedRootEdge(trace,Magic.objectAsAddress(thread.getJNIEnv()).plus(Entrypoints.JNIPendingExceptionField.getOffset())); } /* Grab the ScanThread instance associated with this thread */ ScanThread scanner = Magic.threadAsCollectorThread(RVMThread.getCurrentThread()).getThreadScanner(); /* scan the stack */ scanner.startScan(trace, processCodeLocations, thread, gprs, ip, fp, initialIPLoc, topFrame); } /** * Initializes a ScanThread instance, and then scans a stack * associated with a thread, and places references in deques (one for * object pointers, one for interior code pointers). If the thread * scan fails, the thread is rescanned verbosely and a failure * occurs.<p> * * The various state associated with stack scanning is captured by * instance variables of this type, which are initialized here. * * @param trace The trace instance to use for reporting locations. * @param thread Thread for the thread whose stack is being scanned * @param gprs The general purpose registers associated with the * stack being scanned (normally extracted from the thread). * @param ip The instruction pointer for the top frame of the stack * we're about to scan. * @param fp The frame pointer for the top frame of the stack we're * about to scan. */ private void startScan(TraceLocal trace, boolean processCodeLocations, RVMThread thread, Address gprs, Address ip, Address fp, Address initialIPLoc, Address topFrame) { this.trace = trace; this.processCodeLocations = processCodeLocations; this.thread = thread; this.failed = false; this.ip = ip; this.fp = fp; this.initialIPLoc = initialIPLoc; this.topFrame = topFrame; scanThreadInternal(gprs, DEFAULT_VERBOSITY); if (failed) { /* reinitialize and rescan verbosly on failure */ this.ip = ip; this.fp = fp; this.topFrame = topFrame; scanThreadInternal(gprs, FAILURE_VERBOSITY); VM.sysFail("Error encountered while scanning stack"); } } /** * The main stack scanning loop.<p> * * Walk the stack one frame at a time, top (lo) to bottom (hi),<p> * * @param gprs The general purpose registers associated with the * stack being scanned (normally extracted from the thread). * @param verbosity The level of verbosity to be used when * performing the scan. */ private void scanThreadInternal(Address gprs, int verbosity) { if (false) { VM.sysWriteln("Scanning thread ",thread.getThreadSlot()," from thread ",RVMThread.getCurrentThreadSlot()); } if (verbosity >= 2) { Log.writeln("--- Start Of Stack Scan ---\n"); Log.write("Thread #"); Log.writeln(thread.getThreadSlot()); } if (VM.VerifyAssertions) assertImmovableInCurrentCollection(); /* first find any references to exception handlers in the registers */ getHWExceptionRegisters(); /* reinitialize the stack iterator group */ iteratorGroup.newStackWalk(thread, gprs); if (verbosity >= 2) dumpTopFrameInfo(verbosity); /* scan each frame if a non-empty stack */ if (fp.NE(ArchitectureSpecific.StackframeLayoutConstants.STACKFRAME_SENTINEL_FP)) { prevFp = Address.zero(); /* At start of loop: fp -> frame for method invocation being processed ip -> instruction pointer in the method (normally a call site) */ while (Magic.getCallerFramePointer(fp).NE(ArchitectureSpecific.StackframeLayoutConstants.STACKFRAME_SENTINEL_FP)) { if (false) { VM.sysWriteln("Thread ",RVMThread.getCurrentThreadSlot()," at fp = ",fp); } prevFp = scanFrame(verbosity); ip = Magic.getReturnAddress(fp); fp = Magic.getCallerFramePointer(fp); } } /* If a thread started via createVM or attachVM, base may need scaning */ checkJNIBase(); if (verbosity >= 2) Log.writeln("--- End Of Stack Scan ---\n"); } /** * When an exception occurs, registers are saved temporarily. If * the stack being scanned is in this state, we need to scan those * registers for code pointers. If the codeLocations deque is null, * then scanning for code pointers is not required, so we don't need * to do anything. (SB: Why only code pointers?). * * Dave G: The contents of the GPRs of the exceptionRegisters * are handled during normal stack scanning * (@see org.jikesrvm.runtime.compilers.common.HardwareTrapCompiledMethod. * It looks to me like the main goal of this method is to ensure that the * method in which the trap happened isn't treated as dead code and collected * (if it's been marked as obsolete, we are setting its activeOnStackFlag below). * */ private void getHWExceptionRegisters() { ArchitectureSpecific.Registers exReg = thread.getExceptionRegisters(); if (processCodeLocations && exReg.inuse) { Address ip = exReg.ip; CompiledMethod compiledMethod = CompiledMethods.findMethodForInstruction(ip); if (VM.VerifyAssertions) { VM._assert(compiledMethod != null); VM._assert(compiledMethod.containsReturnAddress(ip)); } compiledMethod.setActiveOnStack(); ObjectReference code = ObjectReference.fromObject(compiledMethod.getEntryCodeArray()); Address ipLoc = exReg.getIPLocation(); if (VM.VerifyAssertions) VM._assert(ip == ipLoc.loadAddress()); processCodeLocation(code, ipLoc); } } /** * Push a code pointer location onto the code locations deque, * optionally performing a sanity check first.<p> * * @param code The code object into which this interior pointer points * @param ipLoc The location of the pointer into this code object */ @Inline private void processCodeLocation(ObjectReference code, Address ipLoc) { if (VALIDATE_REFS) { Address ip = ipLoc.loadAddress(); Offset offset = ip.diff(code.toAddress()); if (offset.sLT(Offset.zero()) || offset.sGT(Offset.fromIntZeroExtend(ObjectModel.getObjectSize(code)))) { Log.writeln("ERROR: Suspiciously large offset of interior pointer from object base"); Log.write(" object base = "); Log.writeln(code); Log.write(" interior reference = "); Log.writeln(ip); Log.write(" offset = "); Log.writeln(offset); Log.write(" interior ref loc = "); Log.writeln(ipLoc); if (!failed) failed = true; } } trace.processInteriorEdge(code, ipLoc, true); } /*********************************************************************** * * Frame scanning methods */ /** * Scan the current stack frame.<p> * * First the various iterators are set up, then the frame is scanned * for regular pointers, before scanning for code pointers. The * iterators are then cleaned up, and native frames skipped if * necessary. * * @param verbosity The level of verbosity to be used when * performing the scan. */ private Address scanFrame(int verbosity) { /* set up iterators etc, and skip the frame if appropriate */ if (!setUpFrame(verbosity)) return fp; /* scan the frame for object pointers */ scanFrameForObjects(verbosity); /* scan the frame for pointers to code */ if (processCodeLocations && compiledMethodType != CompiledMethod.TRAP) processFrameForCode(verbosity); iterator.cleanupPointers(); /* skip preceeding native frames if this frame is a native bridge */ if (compiledMethodType != CompiledMethod.TRAP && compiledMethod.getMethod().getDeclaringClass().hasBridgeFromNativeAnnotation()) { fp = RuntimeEntrypoints.unwindNativeStackFrameForGC(fp); if (verbosity >= 2) Log.write("scanFrame skipping native C frames\n"); } return fp; } /** * Set up to scan the current stack frame. This means examining the * frame to discover the method being invoked and then retrieving * the associated metadata (stack maps etc). Certain frames should * not be scanned---these are identified and skipped. * * @param verbosity The level of verbosity to be used when * performing the scan. * @return True if the frame should be scanned, false if it should * be skipped. */ private boolean setUpFrame(int verbosity) { /* get the compiled method ID for this frame */ int compiledMethodId = Magic.getCompiledMethodID(fp); /* skip "invisible" transition frames generated by reflection and JNI) */ if (compiledMethodId == ArchitectureSpecific.ArchConstants.INVISIBLE_METHOD_ID) { if (verbosity >= 2) Log.writeln("\n--- METHOD <invisible method>"); return false; } /* establish the compiled method */ compiledMethod = CompiledMethods.getCompiledMethod(compiledMethodId); compiledMethod.setActiveOnStack(); // prevents code from being collected compiledMethodType = compiledMethod.getCompilerType(); if (verbosity >= 2) printMethodHeader(); /* get the code associated with this frame */ Offset offset = compiledMethod.getInstructionOffset(ip); /* initialize MapIterator for this frame */ iterator = iteratorGroup.selectIterator(compiledMethod); iterator.setupIterator(compiledMethod, offset, fp); if (verbosity >= 3) dumpStackFrame(verbosity); if (verbosity >= 4) Log.writeln("--- Refs Reported By GCMap Iterator ---"); return true; } /** * Identify all the object pointers stored as local variables * associated with (though not necessarily strictly within!) the * current frame. Loop through the GC map iterator, getting the * address of each object pointer, adding them to the root locations * deque.<p> * * NOTE: Because of the callee save policy of the optimizing * compiler, references associated with a given frame may be in * callee stack frames (lower memory), <i>outside</i> the current * frame. So the iterator may return locations that are outside the * frame being scanned. * * @param verbosity The level of verbosity to be used when * performing the scan. */ private void scanFrameForObjects(int verbosity) { for (Address refaddr = iterator.getNextReferenceAddress(); !refaddr.isZero(); refaddr = iterator.getNextReferenceAddress()) { if (VALIDATE_REFS) checkReference(refaddr, verbosity); if (verbosity >= 4) dumpRef(refaddr, verbosity); reportDelayedRootEdge(trace, refaddr); } } /** * Identify all pointers into code pointers associated with a frame. * There are two cases to be considered: a) the instruction pointer * associated with each frame (stored in the thread's metadata for * the top frame and as a return address for all subsequent frames), * and b) local variables on the stack which happen to be pointers * to code.<p> * * FIXME: SB: Why is it that JNI frames are skipped when considering * top of stack frames, while boot image frames are skipped when * considering other frames. Shouldn't they both be considered in * both cases? * * @param verbosity The level of verbosity to be used when * performing the scan. */ private void processFrameForCode(int verbosity) { /* get the code object associated with this frame */ ObjectReference code = ObjectReference.fromObject(compiledMethod.getEntryCodeArray()); pushFrameIP(code, verbosity); scanFrameForCode(code); } /** * Push the instruction pointer associated with this frame onto the * code locations deque.<p> * * A stack frame represents an execution context, and thus has an * instruction pointer associated with it. In the case of the top * frame, the instruction pointer is captured by the IP register, * which is preserved in the thread data structure at thread switch * time. In the case of all non-top frames, the next instruction * pointer is stored as the return address for the <i>previous</i> * frame.<p> * * The address of the code pointer is pushed onto the code locations * deque along with the address of the code object into which it * points (both are required since the former is an internal * pointer).<p> * * The code pointers are updated later (after stack scanning) when * the code locations deque is processed. The pointer from RVMMethod * to the code object is not updated until after stack scanning, so * the pointer to the (uncopied) code object is available throughout * the stack scanning process, which enables interior pointer * offsets to be correctly computed. * * @param verbosity The level of verbosity to be used when * performing the scan. */ private void pushFrameIP(ObjectReference code, int verbosity) { if (prevFp.isZero()) { /* top of stack: IP in thread state */ if (verbosity >= 3) { Log.write(" t.contextRegisters.ip = "); Log.writeln(thread.getContextRegisters().ip); Log.write("*t.contextRegisters.iploc = "); Log.writeln(thread.getContextRegisters().getIPLocation().loadAddress()); } /* skip native code, as it is not (cannot be) moved */ if (compiledMethodType != CompiledMethod.JNI) processCodeLocation(code, initialIPLoc); else if (verbosity >= 4) { Log.writeln("GC Warning: SKIPPING return address for JNI code"); } } else { /* below top of stack: IP is return address, in prev frame */ Address returnAddressLoc = Magic.getReturnAddressLocation(prevFp); Address returnAddress = returnAddressLoc.loadAddress(); if (verbosity >= 4) { Log.write("--- Processing return address "); Log.write(returnAddress); Log.write(" located at "); Log.writeln(returnAddressLoc); } /* skip boot image code, as it is not (cannot be) moved */ if (!DebugUtil.addrInBootImage(returnAddress)) processCodeLocation(code, returnAddressLoc); } } /** * Scan this frame for internal code pointers. The GC map iterator * is used to identify any local variables (stored on the stack) * which happen to be pointers into code.<p> * * @param code The code object associated with this frame. */ private void scanFrameForCode(ObjectReference code) { iterator.reset(); for (Address retaddrLoc = iterator.getNextReturnAddressAddress(); !retaddrLoc.isZero(); retaddrLoc = iterator.getNextReturnAddressAddress()) processCodeLocation(code, retaddrLoc); } /** * AIX-specific code.<p> * * If we are scanning the stack of a thread that entered the VM via * a createVM or attachVM then the "bottom" of the stack had native * C frames instead of the usual java frames. The JNIEnv for the * thread may still contain jniRefs that have been returned to the * native C code, but have not been reported for GC. calling * getNextReferenceAddress without first calling setup... will * report the remaining jniRefs in the current "frame" of the * jniRefs stack. (this should be the bottom frame) * * FIXME: SB: Why is this AIX specific? Why depend on the * preprocessor? * */ private void checkJNIBase() { if (VM.BuildForAix) { GCMapIterator iterator = iteratorGroup.getJniIterator(); Address refaddr = iterator.getNextReferenceAddress(); while(!refaddr.isZero()) { reportDelayedRootEdge(trace, refaddr); refaddr = iterator.getNextReferenceAddress(); } } } /*********************************************************************** * * Debugging etc */ /** * Assert that the stack is immovable.<p> * * Currently we do not allow stacks to be moved within the heap. If * a stack contains native stack frames, then it is impossible for * us to safely move it. Prior to the implementation of JNI, Jikes * RVM did allow the GC system to move thread stacks, and called a * special fixup routine, thread.fixupMovedStack to adjust all of * the special interior pointers (SP, FP). If we implement split C * & Java stacks then we could allow the Java stacks to be moved, * but we can't move the native stack. */ private void assertImmovableInCurrentCollection() { VM._assert(trace.willNotMoveInCurrentCollection(ObjectReference.fromObject(thread.getStack()))); VM._assert(trace.willNotMoveInCurrentCollection(ObjectReference.fromObject(thread))); VM._assert(trace.willNotMoveInCurrentCollection(ObjectReference.fromObject(thread.getStack()))); VM._assert(thread.getJNIEnv() == null || trace.willNotMoveInCurrentCollection(ObjectReference.fromObject(thread.getJNIEnv()))); VM._assert(thread.getJNIEnv() == null || thread.getJNIEnv().refsArray() == null || trace.willNotMoveInCurrentCollection(ObjectReference.fromObject(thread.getJNIEnv().refsArray()))); VM._assert(trace.willNotMoveInCurrentCollection(ObjectReference.fromObject(thread.getContextRegisters()))); VM._assert(trace.willNotMoveInCurrentCollection(ObjectReference.fromObject(thread.getContextRegisters().gprs))); VM._assert(trace.willNotMoveInCurrentCollection(ObjectReference.fromObject(thread.getExceptionRegisters()))); VM._assert(trace.willNotMoveInCurrentCollection(ObjectReference.fromObject(thread.getExceptionRegisters().gprs))); } /** * Print out the basic information associated with the top frame on * the stack. * * @param verbosity The level of verbosity to be used when * performing the scan. */ private void dumpTopFrameInfo(int verbosity) { Log.write(" topFrame = "); Log.writeln(topFrame); Log.write(" ip = "); Log.writeln(ip); Log.write(" fp = "); Log.writeln(fp); Log.write(" registers.ip = "); Log.writeln(thread.getContextRegisters().ip); if (verbosity >= 3 && thread.getJNIEnv() != null) thread.getJNIEnv().dumpJniRefsStack(); } /** * Print out information associated with a reference. * * @param refaddr The address of the reference in question. * @param verbosity The level of verbosity to be used when * performing the scan. */ private void dumpRef(Address refaddr, int verbosity) { ObjectReference ref = refaddr.loadObjectReference(); VM.sysWrite(refaddr); if (verbosity >= 5) { VM.sysWrite(":"); MemoryManager.dumpRef(ref); } else VM.sysWriteln(); } /** * Check that a reference encountered during scanning is valid. If * the reference is invalid, dump stack and die. * * @param refaddr The address of the reference in question. * @param verbosity The level of verbosity to be used when * performing the scan. */ private void checkReference(Address refaddr, int verbosity) { ObjectReference ref = refaddr.loadObjectReference(); if (!MemoryManager.validRef(ref)) { Log.writeln(); Log.writeln("Invalid ref reported while scanning stack"); printMethodHeader(); Log.write(refaddr); Log.write(":"); Log.flush(); MemoryManager.dumpRef(ref); dumpStackFrame(verbosity); Log.writeln(); Log.writeln("Dumping stack starting at frame with bad ref:"); RVMThread.dumpStack(ip, fp); /* dump stack starting at top */ Address top_ip = thread.getContextRegisters().getInnermostInstructionAddress(); Address top_fp = thread.getContextRegisters().getInnermostFramePointer(); RVMThread.dumpStack(top_ip, top_fp); Log.writeln("Failing iterators:"); Offset offset = compiledMethod.getInstructionOffset(ip); iterator = iteratorGroup.selectIterator(compiledMethod); iterator.setupIterator(compiledMethod, offset, fp); int i=0; for (Address addr = iterator.getNextReferenceAddress(); !addr.isZero(); addr = iterator.getNextReferenceAddress()) { ObjectReference ref2 = addr.loadObjectReference(); Log.write("Iterator "); Log.write(i++); Log.write(": "); Log.write(addr); Log.write(": "); Log.flush(); MemoryManager.dumpRef(ref2); } VM.sysFail("\n\nScanStack: Detected bad GC map; exiting RVM with fatal error"); } } /** * Check that a reference encountered during scanning is valid. If * the reference is invalid, dump stack and die. * * @param refaddr The address of the reference in question. */ private static void checkReference(Address refaddr) { ObjectReference ref = refaddr.loadObjectReference(); if (!MemoryManager.validRef(ref)) { Log.writeln(); Log.writeln("Invalid ref reported while scanning stack"); Log.write(refaddr); Log.write(":"); Log.flush(); MemoryManager.dumpRef(ref); Log.writeln(); Log.writeln("Dumping stack:"); RVMThread.dumpStack(); VM.sysFail("\n\nScanStack: Detected bad GC map; exiting RVM with fatal error"); } } /** * Print out the name of a method * * @param m The method to be printed */ private void printMethod(RVMMethod m) { Log.write(m.getMemberRef().getType().getName().toByteArray()); Log.write("."); Log.write(m.getMemberRef().getName().toByteArray()); Log.write(" "); Log.write(m.getMemberRef().getDescriptor().toByteArray()); } /** * Print out the method header for the method associated with the * current frame */ private void printMethodHeader() { RVMMethod method = compiledMethod.getMethod(); Log.write("\n--- METHOD ("); Log.write(CompiledMethod.compilerTypeToString(compiledMethodType)); Log.write(") "); if (method == null) Log.write("null method"); else printMethod(method); Log.writeln(); Log.write("--- fp = "); Log.write(fp); if (compiledMethod.isCompiled()) { ObjectReference codeBase = ObjectReference.fromObject(compiledMethod.getEntryCodeArray()); Log.write(" code base = "); Log.write(codeBase); Log.write(" code offset = "); Log.writeln(ip.diff(codeBase.toAddress())); Log.write(" line number = "); Log.writeln(compiledMethod.findLineNumberForInstruction(ip.diff(codeBase.toAddress()))); } else { Log.write(" Method is uncompiled - ip = "); Log.writeln(ip); } } /** * Dump the contents of a stack frame. Attempts to interpret each * word as an object reference * * @param verbosity The level of verbosity to be used when * performing the scan. */ private void dumpStackFrame(int verbosity) { Address start,end; if (VM.BuildForIA32) { if (prevFp.isZero()) { start = fp.minus(20*BYTES_IN_ADDRESS); Log.writeln("--- 20 words of stack frame with fp = ", fp); } else { start = prevFp; // start at callee fp } end = fp; // end at fp } else { start = fp; // start at fp end = fp.loadAddress(); // stop at callers fp } for (Address loc = start; loc.LT(end); loc = loc.plus(BYTES_IN_ADDRESS)) { Log.write(loc); Log.write(" ("); Log.write(loc.diff(start)); Log.write("): "); ObjectReference value = Selected.Plan.get().loadObjectReference(loc); Log.write(value); Log.write(" "); Log.flush(); if (verbosity >= 4 && MemoryManager.objectInVM(value) && loc.NE(start) && loc.NE(end)) MemoryManager.dumpRef(value); else Log.writeln(); } Log.writeln(); } }