001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package org.jikesrvm.mm.mmtk;
014
015import static org.jikesrvm.runtime.UnboxedSizeConstants.BYTES_IN_ADDRESS;
016
017import org.jikesrvm.VM;
018import org.jikesrvm.architecture.AbstractRegisters;
019import org.jikesrvm.architecture.StackFrameLayout;
020import org.jikesrvm.classloader.RVMMethod;
021import org.jikesrvm.compilers.common.CompiledMethod;
022import org.jikesrvm.compilers.common.CompiledMethods;
023import org.jikesrvm.mm.mminterface.DebugUtil;
024import org.jikesrvm.mm.mminterface.GCMapIterator;
025import org.jikesrvm.mm.mminterface.GCMapIteratorGroup;
026import org.jikesrvm.mm.mminterface.MemoryManager;
027import org.jikesrvm.mm.mminterface.Selected;
028import org.jikesrvm.runtime.Magic;
029import org.jikesrvm.runtime.RuntimeEntrypoints;
030import org.jikesrvm.scheduler.RVMThread;
031import org.mmtk.plan.TraceLocal;
032import org.mmtk.utility.Log;
033import org.mmtk.utility.options.Options;
034import org.vmmagic.pragma.Inline;
035import org.vmmagic.pragma.Uninterruptible;
036import org.vmmagic.pragma.Untraced;
037import org.vmmagic.unboxed.Address;
038import org.vmmagic.unboxed.ObjectReference;
039import org.vmmagic.unboxed.Offset;
040
041/**
042 * Class that supports scanning thread stacks for references during
043 * collections. References are located using GCMapIterators and are
044 * inserted into a set of root locations.  Optionally, a set of
045 * interior pointer locations associated with the object is created.<p>
046 *
047 * Threads, stacks, jni environments, and register objects have a
048 * complex interaction in terms of scanning.  The operation of
049 * scanning the stack reveals not only roots inside the stack but also
050 * the state of the register objects's gprs and the JNI refs array.
051 * They are all associated via the thread object, making it natural
052 * for scanThread to be considered a single operation with the method
053 * directly accessing these objects via the thread object's
054 * fields. <p>
055 *
056 * One pitfall occurs when scanning the thread object (plus
057 * dependents) when not all of the objects have been copied.  Then it
058 * may be that the innards of the register object has not been copied
059 * while the stack object has.  The result is that an inconsistent set
060 * of slots is reported.  In this case, the copied register object may
061 * not be correct if the copy occurs after the root locations are
062 * discovered but before those locations are processed. In essence,
063 * all of these objects form one logical unit but are physically
064 * separated so that sometimes only part of it has been copied causing
065 * the scan to be incorrect. <p>
066 *
067 * The caller of the stack scanning routine must ensure that all of
068 * these components's descendants are consistent (all copied) when
069 * this method is called. <p>
070 *
071 * <i>Code locations:</i> Identifying pointers <i>into</i> code
072 * objects is essential if code objects are allowed to move (and if
073 * the code objects were not otherwise kept alive, it would be
074 * necessary to ensure the liveness of the code objects). A code
075 * pointer is the only case in which we have interior pointers
076 * (pointers into the inside of objects).  For such pointers, two
077 * things must occur: first the pointed to object must be kept alive,
078 * and second, if the pointed to object is moved by a copying
079 * collector, the pointer into the object must be adjusted so it now
080 * points into the newly copied object
081 */
082@Uninterruptible public final class ScanThread {
083
084  /***********************************************************************
085   *
086   * Class variables
087   */
088
089  /** quietly validates each ref reported by map iterators */
090  static final boolean VALIDATE_REFS = VM.VerifyAssertions;
091
092  /*
093   * debugging options to produce printout during scanStack
094   * MULTIPLE GC THREADS WILL PRODUCE SCRAMBLED OUTPUT so only
095   * use these when running with PROCESSORS=1
096   */
097  private static final int DEFAULT_VERBOSITY = 0 /*0*/;
098  private static final int FAILURE_VERBOSITY = 4;
099
100  /***********************************************************************
101   *
102   * Instance variables
103   */
104
105  /**
106   *
107   */
108  private final GCMapIteratorGroup iteratorGroup = new GCMapIteratorGroup();
109  @Untraced
110  private GCMapIterator iterator;
111  @Untraced
112  private TraceLocal trace;
113  private boolean processCodeLocations;
114  @Untraced
115  private RVMThread thread;
116  private Address ip, fp, prevFp, initialIPLoc, topFrame;
117  @Untraced
118  private CompiledMethod compiledMethod;
119  private int compiledMethodType;
120  private boolean failed;
121  private boolean reinstallReturnBarrier;
122
123  /***********************************************************************
124   *
125   * Thread scanning
126   */
127
128  /**
129   * Scan a thread, placing the addresses of pointers into supplied buffers.
130   *
131   * @param thread The thread to be scanned
132   * @param trace The trace instance to use for reporting references.
133   * @param processCodeLocations Should code locations be processed?
134   * @param newRootsSufficient Is a partial stack scan sufficient, or must we do a full scan?
135   */
136  public static void scanThread(RVMThread thread, TraceLocal trace,
137                                boolean processCodeLocations, boolean newRootsSufficient) {
138    if (DEFAULT_VERBOSITY >= 1) {
139      VM.sysWriteln("scanning ",thread.getThreadSlot());
140    }
141
142    /* get the gprs associated with this thread */
143    AbstractRegisters regs = thread.getContextRegisters();
144    Address gprs = Magic.objectAsAddress(regs.getGPRs());
145
146    Address ip = regs.getInnermostInstructionAddress();
147    Address fp = regs.getInnermostFramePointer();
148    regs.clear();
149    regs.setInnermost(ip,fp);
150
151    scanThread(thread, trace, processCodeLocations, gprs, Address.zero(), newRootsSufficient);
152  }
153
154  /**
155   * Wrapper for {@link TraceLocal#reportDelayedRootEdge(Address)} that allows
156   * sanity checking of the address.
157   *
158   * @param trace the trace on which {@link TraceLocal#reportDelayedRootEdge(Address)}
159   *  will be called
160   * @param addr see JavaDoc of {@link TraceLocal#reportDelayedRootEdge(Address)}
161   */
162  private static void reportDelayedRootEdge(TraceLocal trace, Address addr) {
163    if (VALIDATE_REFS) checkReference(addr);
164    trace.reportDelayedRootEdge(addr);
165  }
166
167  /**
168   * A more general interface to thread scanning, which permits the
169   * scanning of stack segments which are dislocated from the thread
170   * structure.
171   *
172   * @param thread The thread to be scanned
173   * @param trace The trace instance to use for reporting references.
174   * @param processCodeLocations Should code locations be processed?
175   * @param gprs The general purpose registers associated with the
176   * stack being scanned (normally extracted from the thread).
177   * @param topFrame The top frame of the stack being scanned, or zero
178   * if this is to be inferred from the thread (normally the case).
179   * @param newRootsSufficent Is a partial stack scan sufficient, or must we do a full scan?
180   */
181  private static void scanThread(RVMThread thread, TraceLocal trace,
182                                 boolean processCodeLocations,
183                                 Address gprs, Address topFrame, boolean newRootsSufficent) {
184    // figure out if the thread should be scanned at all; if not, exit
185    if (thread.getExecStatus() == RVMThread.NEW || thread.getIsAboutToTerminate()) {
186      return;
187    }
188    /* establish ip and fp for the stack to be scanned */
189    Address ip, fp, initialIPLoc;
190    if (topFrame.isZero()) { /* implicit top of stack, inferred from thread */
191      ip = thread.getContextRegisters().getInnermostInstructionAddress();
192      fp = thread.getContextRegisters().getInnermostFramePointer();
193      initialIPLoc = thread.getContextRegisters().getIPLocation();
194    } else {                 /* top frame explicitly defined */
195      ip = Magic.getReturnAddress(topFrame, thread);
196      fp = Magic.getCallerFramePointer(topFrame);
197      initialIPLoc = thread.getContextRegisters().getIPLocation(); // FIXME
198    }
199
200    /* Grab the ScanThread instance associated with this thread */
201    ScanThread scanner = RVMThread.getCurrentThread().getCollectorThread().getThreadScanner();
202
203    /* Expicitly establish the stopping point for this scan (not necessarily the bottom of stack) */
204    Address sentinalFp = newRootsSufficent && Options.useShortStackScans.getValue() ? thread.getNextUnencounteredFrame() : StackFrameLayout.getStackFrameSentinelFP();
205
206    /* stack trampoline will be freshly reinstalled at end of thread scan */
207    if (Options.useReturnBarrier.getValue() || Options.useShortStackScans.getValue()) {
208      thread.deInstallStackTrampoline();
209    }
210
211    /* scan the stack */
212    scanner.startScan(trace, processCodeLocations, thread, gprs, ip, fp, initialIPLoc, topFrame, sentinalFp);
213  }
214
215  /**
216   * Initializes a ScanThread instance, and then scans a stack
217   * associated with a thread, and places references in deques (one for
218   * object pointers, one for interior code pointers).  If the thread
219   * scan fails, the thread is rescanned verbosely and a failure
220   * occurs.<p>
221   *
222   * The various state associated with stack scanning is captured by
223   * instance variables of this type, which are initialized here.
224   *
225   * @param trace The trace instance to use for reporting locations.
226   * @param processCodeLocations whether to process parts of the thread
227   *  that could point to code (e.g. exception registers).
228   * @param thread Thread for the thread whose stack is being scanned
229   * @param gprs The general purpose registers associated with the
230   * stack being scanned (normally extracted from the thread).
231   * @param ip The instruction pointer for the top frame of the stack
232   * we're about to scan.
233   * @param fp The frame pointer for the top frame of the stack we're
234   * about to scan.
235   * @param initialIPLoc the address of the initial location of the instruction
236   *  pointer
237   * @param topFrame The top frame of the stack being scanned, or zero
238   * if this is to be inferred from the thread (normally the case).
239   * @param sentinelFp The frame pointer at which the stack scan should stop.
240   */
241  private void startScan(TraceLocal trace,
242                         boolean processCodeLocations,
243                         RVMThread thread, Address gprs, Address ip,
244                         Address fp, Address initialIPLoc, Address topFrame,
245                         Address sentinelFp) {
246    this.trace = trace;
247    this.processCodeLocations = processCodeLocations;
248    this.thread = thread;
249    this.failed = false;
250    this.ip = ip;
251    this.fp = fp;
252    this.initialIPLoc = initialIPLoc;
253    this.topFrame = topFrame;
254    scanThreadInternal(gprs, DEFAULT_VERBOSITY, sentinelFp);
255    if (failed) {
256       /* reinitialize and rescan verbosely on failure */
257      this.ip = ip;
258      this.fp = fp;
259      this.topFrame = topFrame;
260      scanThreadInternal(gprs, FAILURE_VERBOSITY, sentinelFp);
261      VM.sysFail("Error encountered while scanning stack");
262    }
263  }
264
265  /**
266   * The main stack scanning loop.<p>
267   *
268   * Walk the stack one frame at a time, top (lo) to bottom (hi),<p>
269   *
270   * @param gprs The general purpose registers associated with the
271   * stack being scanned (normally extracted from the thread).
272   * @param verbosity The level of verbosity to be used when
273   * performing the scan.
274   * @param sentinelFp the frame pointer at which the stack scan should stop
275   */
276  private void scanThreadInternal(Address gprs, int verbosity, Address sentinelFp) {
277    if (false) {
278      VM.sysWriteln("Scanning thread ",thread.getThreadSlot()," from thread ",RVMThread.getCurrentThreadSlot());
279    }
280    if (verbosity >= 2) {
281      Log.writeln("--- Start Of Stack Scan ---\n");
282      Log.write("Thread #");
283      Log.writeln(thread.getThreadSlot());
284    }
285    if (VM.VerifyAssertions) assertImmovableInCurrentCollection();
286
287    /* first find any references to exception handlers in the registers */
288    getHWExceptionRegisters();
289
290    /* reinitialize the stack iterator group */
291    iteratorGroup.newStackWalk(thread, gprs);
292
293    if (verbosity >= 2) dumpTopFrameInfo(verbosity);
294
295    /* scan each frame if a non-empty stack */
296    if (fp.NE(StackFrameLayout.getStackFrameSentinelFP())) {
297      prevFp = Address.zero();
298      reinstallReturnBarrier = Options.useReturnBarrier.getValue() || Options.useShortStackScans.getValue();
299      /* At start of loop:
300         fp -> frame for method invocation being processed
301         ip -> instruction pointer in the method (normally a call site) */
302      while (Magic.getCallerFramePointer(fp).NE(sentinelFp)) {
303        if (false) {
304          VM.sysWriteln("Thread ",RVMThread.getCurrentThreadSlot()," at fp = ",fp);
305        }
306        prevFp = scanFrame(verbosity);
307        ip = Magic.getReturnAddress(fp, thread);
308        fp = Magic.getCallerFramePointer(fp);
309      }
310    }
311
312    /* If a thread started via createVM or attachVM, base may need scaning */
313    // TODO implement this if necessary. It was previously only implemented for
314    // AIX which is no longer supported.
315
316    if (verbosity >= 2) Log.writeln("--- End Of Stack Scan ---\n");
317  }
318
319  /**
320   * When an exception occurs, registers are saved temporarily.  If
321   * the stack being scanned is in this state, we need to scan those
322   * registers for code pointers.  If the codeLocations deque is null,
323   * then scanning for code pointers is not required, so we don't need
324   * to do anything. (SB: Why only code pointers?).
325   * <p>
326   * Dave G:  The contents of the GPRs of the exceptionRegisters
327   * are handled during normal stack scanning
328   * (@see org.jikesrvm.runtime.compilers.common.HardwareTrapCompiledMethod.
329   * It looks to me like the main goal of this method is to ensure that the
330   * method in which the trap happened isn't treated as dead code and collected
331   * (if it's been marked as obsolete, we are setting its activeOnStackFlag below).
332   */
333  private void getHWExceptionRegisters() {
334    AbstractRegisters exReg = thread.getExceptionRegisters();
335    if (processCodeLocations && exReg.getInUse()) {
336      Address ip = exReg.getIP();
337      CompiledMethod compiledMethod = CompiledMethods.findMethodForInstruction(ip);
338      if (VM.VerifyAssertions) {
339        VM._assert(compiledMethod != null);
340        VM._assert(compiledMethod.containsReturnAddress(ip));
341      }
342      compiledMethod.setActiveOnStack();
343      ObjectReference code = ObjectReference.fromObject(compiledMethod.getEntryCodeArray());
344      Address ipLoc = exReg.getIPLocation();
345      if (VM.VerifyAssertions) VM._assert(ip.EQ(ipLoc.loadAddress()));
346      processCodeLocation(code, ipLoc);
347    }
348  }
349
350  /**
351   * Push a code pointer location onto the code locations deque,
352   * optionally performing a sanity check first.<p>
353   *
354   * @param code The code object into which this interior pointer points
355   * @param ipLoc The location of the pointer into this code object
356   */
357  @Inline
358  private void processCodeLocation(ObjectReference code, Address ipLoc) {
359    if (VALIDATE_REFS) {
360      Address ip = ipLoc.loadAddress();
361      Offset offset = ip.diff(code.toAddress());
362
363      if (offset.sLT(Offset.zero()) ||
364          offset.sGT(Offset.fromIntZeroExtend(ObjectModel.getObjectSize(code)))) {
365        Log.writeln("ERROR: Suspiciously large offset of interior pointer from object base");
366        Log.write("       object base = "); Log.writeln(code);
367        Log.write("       interior reference = "); Log.writeln(ip);
368        Log.write("       offset = "); Log.writeln(offset);
369        Log.write("       interior ref loc = "); Log.writeln(ipLoc);
370        if (!failed) failed = true;
371      }
372    }
373    trace.processInteriorEdge(code, ipLoc, true);
374  }
375
376  /***********************************************************************
377   *
378   * Frame scanning methods
379   */
380
381  /**
382   * Scan the current stack frame.<p>
383   *
384   * First the various iterators are set up, then the frame is scanned
385   * for regular pointers, before scanning for code pointers.  The
386   * iterators are then cleaned up, and native frames skipped if
387   * necessary.
388   *
389   * @param verbosity The level of verbosity to be used when
390   * performing the scan.
391   * @return the frame pointer of the frame that was just scanned
392   */
393  private Address scanFrame(int verbosity) {
394    /* set up iterators etc, and skip the frame if appropriate */
395    if (!setUpFrame(verbosity)) return fp;
396
397    /* scan the frame for object pointers */
398    scanFrameForObjects(verbosity);
399
400    /* scan the frame for pointers to code */
401    if (processCodeLocations && compiledMethodType != CompiledMethod.TRAP)
402      processFrameForCode(verbosity);
403
404    iterator.cleanupPointers();
405
406    if (compiledMethodType != CompiledMethod.TRAP) {
407      /* skip preceding native frames if this frame is a native bridge */
408      if (compiledMethod.getMethod().getDeclaringClass().hasBridgeFromNativeAnnotation()) {
409        fp = RuntimeEntrypoints.unwindNativeStackFrameForGC(fp);
410        if (verbosity >= 2) Log.write("scanFrame skipping native C frames\n");
411      }
412
413      /* reinstall the return barrier if necessary (and verbosity indicates that this is a regular scan) */
414      if (reinstallReturnBarrier && verbosity == DEFAULT_VERBOSITY) {
415        thread.installStackTrampolineBridge(fp);
416        reinstallReturnBarrier = false;
417      }
418    }
419    return fp;
420  }
421
422  /**
423   * Set up to scan the current stack frame.  This means examining the
424   * frame to discover the method being invoked and then retrieving
425   * the associated metadata (stack maps etc).  Certain frames should
426   * not be scanned---these are identified and skipped.
427   *
428   * @param verbosity The level of verbosity to be used when
429   * performing the scan.
430   * @return True if the frame should be scanned, false if it should
431   * be skipped.
432   */
433  private boolean setUpFrame(int verbosity) {
434    /* get the compiled method ID for this frame */
435    int compiledMethodId = Magic.getCompiledMethodID(fp);
436
437    /* skip "invisible" transition frames generated by reflection and JNI) */
438    if (compiledMethodId == StackFrameLayout.getInvisibleMethodID()) {
439      if (verbosity >= 2) Log.writeln("\n--- METHOD <invisible method>");
440      return false;
441    }
442
443    /* establish the compiled method */
444    compiledMethod = CompiledMethods.getCompiledMethod(compiledMethodId);
445    compiledMethod.setActiveOnStack();  // prevents code from being collected
446
447    compiledMethodType = compiledMethod.getCompilerType();
448
449    if (verbosity >= 2) printMethodHeader();
450
451    /* get the code associated with this frame */
452    if (RVMThread.DEBUG_STACK_TRAMPOLINE) VM.sysWriteln(thread.getId(), fp, compiledMethod.getMethod());
453    Offset offset = compiledMethod.getInstructionOffset(ip);
454
455    /* initialize MapIterator for this frame */
456    iterator = iteratorGroup.selectIterator(compiledMethod);
457    iterator.setupIterator(compiledMethod, offset, fp);
458
459    if (verbosity >= 3) dumpStackFrame(verbosity);
460    if (verbosity >= 4) Log.writeln("--- Refs Reported By GCMap Iterator ---");
461
462    return true;
463  }
464
465  /**
466   * Identify all the object pointers stored as local variables
467   * associated with (though not necessarily strictly within!) the
468   * current frame.  Loop through the GC map iterator, getting the
469   * address of each object pointer, adding them to the root locations
470   * deque.<p>
471   *
472   * NOTE: Because of the callee save policy of the optimizing
473   * compiler, references associated with a given frame may be in
474   * callee stack frames (lower memory), <i>outside</i> the current
475   * frame.  So the iterator may return locations that are outside the
476   * frame being scanned.
477   *
478   * @param verbosity The level of verbosity to be used when
479   * performing the scan.
480   */
481  private void scanFrameForObjects(int verbosity) {
482    for (Address refaddr = iterator.getNextReferenceAddress();
483         !refaddr.isZero();
484         refaddr = iterator.getNextReferenceAddress()) {
485      if (VALIDATE_REFS) checkReference(refaddr, verbosity);
486      if (verbosity >= 4) dumpRef(refaddr, verbosity);
487      reportDelayedRootEdge(trace, refaddr);
488    }
489  }
490
491  /**
492   * Identify all pointers into code pointers associated with a frame.
493   * There are two cases to be considered: a) the instruction pointer
494   * associated with each frame (stored in the thread's metadata for
495   * the top frame and as a return address for all subsequent frames),
496   * and b) local variables on the stack which happen to be pointers
497   * to code.<p>
498   *
499   * FIXME: SB: Why is it that JNI frames are skipped when considering
500   * top of stack frames, while boot image frames are skipped when
501   * considering other frames.  Shouldn't they both be considered in
502   * both cases?
503   *
504   * @param verbosity The level of verbosity to be used when
505   * performing the scan.
506   */
507  private void processFrameForCode(int verbosity) {
508    /* get the code object associated with this frame */
509    ObjectReference code = ObjectReference.fromObject(compiledMethod.getEntryCodeArray());
510
511    pushFrameIP(code, verbosity);
512    scanFrameForCode(code);
513  }
514
515  /**
516   * Push the instruction pointer associated with this frame onto the
517   * code locations deque.<p>
518   *
519   * A stack frame represents an execution context, and thus has an
520   * instruction pointer associated with it.  In the case of the top
521   * frame, the instruction pointer is captured by the IP register,
522   * which is preserved in the thread data structure at thread switch
523   * time.  In the case of all non-top frames, the next instruction
524   * pointer is stored as the return address for the <i>previous</i>
525   * frame.<p>
526   *
527   * The address of the code pointer is pushed onto the code locations
528   * deque along with the address of the code object into which it
529   * points (both are required since the former is an internal
530   * pointer).<p>
531   *
532   * The code pointers are updated later (after stack scanning) when
533   * the code locations deque is processed. The pointer from RVMMethod
534   * to the code object is not updated until after stack scanning, so
535   * the pointer to the (uncopied) code object is available throughout
536   * the stack scanning process, which enables interior pointer
537   * offsets to be correctly computed.
538   *
539   * @param code start address of the machine code array associated
540   *  with the method
541   * @param verbosity The level of verbosity to be used when
542   * performing the scan.
543   */
544  private void pushFrameIP(ObjectReference code, int verbosity) {
545    if (prevFp.isZero()) {  /* top of stack: IP in thread state */
546      if (verbosity >= 3) {
547        Log.write(" t.contextRegisters.ip    = ");
548        Log.writeln(thread.getContextRegisters().getIP());
549        Log.write("*t.contextRegisters.iploc = ");
550        Log.writeln(thread.getContextRegisters().getIPLocation().loadAddress());
551      }
552      /* skip native code, as it is not (cannot be) moved */
553      if (compiledMethodType != CompiledMethod.JNI)
554        processCodeLocation(code, initialIPLoc);
555      else if (verbosity >= 4) {
556        Log.writeln("GC Warning: SKIPPING return address for JNI code");
557      }
558    } else {  /* below top of stack: IP is return address, in prev frame */
559      Address returnAddressLoc = Magic.getReturnAddressLocation(prevFp);
560      Address returnAddress = returnAddressLoc.loadAddress();
561      if (verbosity >= 4) {
562        Log.write("--- Processing return address "); Log.write(returnAddress);
563        Log.write(" located at "); Log.writeln(returnAddressLoc);
564      }
565      /* skip boot image code, as it is not (cannot be) moved */
566      if (!DebugUtil.addrInBootImage(returnAddress))
567        processCodeLocation(code, returnAddressLoc);
568    }
569  }
570
571  /**
572   * Scan this frame for internal code pointers.  The GC map iterator
573   * is used to identify any local variables (stored on the stack)
574   * which happen to be pointers into code.<p>
575   *
576   * @param code The code object associated with this frame.
577   */
578  private void scanFrameForCode(ObjectReference code) {
579    iterator.reset();
580    for (Address retaddrLoc = iterator.getNextReturnAddressAddress();
581         !retaddrLoc.isZero();
582         retaddrLoc = iterator.getNextReturnAddressAddress())
583      processCodeLocation(code, retaddrLoc);
584  }
585
586  /***********************************************************************
587   *
588   * Debugging etc
589   */
590
591  /**
592   * Assert that the stack is immovable.<p>
593   *
594   * Currently we do not allow stacks to be moved within the heap.  If
595   * a stack contains native stack frames, then it is impossible for
596   * us to safely move it.  Prior to the implementation of JNI, Jikes
597   * RVM did allow the GC system to move thread stacks, and called a
598   * special fixup routine, thread.fixupMovedStack to adjust all of
599   * the special interior pointers (SP, FP).  If we implement split C
600   * &amp; Java stacks then we could allow the Java stacks to be moved,
601   * but we can't move the native stack.
602   */
603  private void assertImmovableInCurrentCollection() {
604    // This method is guarded by VM.VerifyAssertions. Our Checkstyle assertion
605    // plugin does not recognize this because it does not track calls. Therefore,
606    // switch off Checkstyle for this method.
607
608    //CHECKSTYLE:OFF
609    VM._assert(trace.willNotMoveInCurrentCollection(ObjectReference.fromObject(thread.getStack())));
610    VM._assert(trace.willNotMoveInCurrentCollection(ObjectReference.fromObject(thread)));
611    VM._assert(trace.willNotMoveInCurrentCollection(ObjectReference.fromObject(thread.getStack())));
612    VM._assert(thread.getJNIEnv() == null || trace.willNotMoveInCurrentCollection(ObjectReference.fromObject(thread.getJNIEnv())));
613    VM._assert(thread.getJNIEnv() == null || thread.getJNIEnv().refsArray() == null || trace.willNotMoveInCurrentCollection(ObjectReference.fromObject(thread.getJNIEnv().refsArray())));
614    VM._assert(trace.willNotMoveInCurrentCollection(ObjectReference.fromObject(thread.getContextRegisters())));
615    VM._assert(trace.willNotMoveInCurrentCollection(ObjectReference.fromObject(thread.getContextRegisters().getGPRs())));
616    VM._assert(trace.willNotMoveInCurrentCollection(ObjectReference.fromObject(thread.getExceptionRegisters())));
617    VM._assert(trace.willNotMoveInCurrentCollection(ObjectReference.fromObject(thread.getExceptionRegisters().getGPRs())));
618    //CHECKSTYLE:ON
619  }
620
621  /**
622   * Print out the basic information associated with the top frame on
623   * the stack.
624   *
625   * @param verbosity The level of verbosity to be used when
626   * performing the scan.
627   */
628  private void dumpTopFrameInfo(int verbosity) {
629    Log.write("   topFrame = "); Log.writeln(topFrame);
630    Log.write("         ip = "); Log.writeln(ip);
631    Log.write("         fp = "); Log.writeln(fp);
632    Log.write("  registers.ip = "); Log.writeln(thread.getContextRegisters().getIP());
633    if (verbosity >= 3 && thread.getJNIEnv() != null)
634      thread.getJNIEnv().dumpJniRefsStack();
635  }
636
637  /**
638   * Print out information associated with a reference.
639   *
640   * @param refaddr The address of the reference in question.
641   * @param verbosity The level of verbosity to be used when
642   * performing the scan.
643   */
644  private void dumpRef(Address refaddr, int verbosity) {
645    ObjectReference ref = refaddr.loadObjectReference();
646    VM.sysWrite(refaddr);
647    if (verbosity >= 5) {
648      VM.sysWrite(":"); MemoryManager.dumpRef(ref);
649    } else
650      VM.sysWriteln();
651  }
652
653  /**
654   * Check that a reference encountered during scanning is valid.  If
655   * the reference is invalid, dump stack and die.
656   *
657   * @param refaddr The address of the reference in question.
658   * @param verbosity The level of verbosity to be used when
659   * performing the scan.
660   */
661  private void checkReference(Address refaddr, int verbosity) {
662    ObjectReference ref = refaddr.loadObjectReference();
663    if (!MemoryManager.validRef(ref)) {
664      Log.writeln();
665      Log.writeln("Invalid ref reported while scanning stack");
666      printMethodHeader();
667      Log.write(refaddr); Log.write(":"); Log.flush(); MemoryManager.dumpRef(ref);
668      dumpStackFrame(verbosity);
669      Log.writeln();
670      Log.writeln("Dumping stack starting at frame with bad ref:");
671      RVMThread.dumpStack(ip, fp);
672      /* dump stack starting at top */
673      Address top_ip = thread.getContextRegisters().getInnermostInstructionAddress();
674      Address top_fp = thread.getContextRegisters().getInnermostFramePointer();
675      RVMThread.dumpStack(top_ip, top_fp);
676      Log.writeln("Failing iterators:");
677      Offset offset = compiledMethod.getInstructionOffset(ip);
678      iterator = iteratorGroup.selectIterator(compiledMethod);
679      iterator.setupIterator(compiledMethod, offset, fp);
680      int i = 0;
681      for (Address addr = iterator.getNextReferenceAddress();
682           !addr.isZero();
683           addr = iterator.getNextReferenceAddress()) {
684        ObjectReference ref2 = addr.loadObjectReference();
685        Log.write("Iterator "); Log.write(i++); Log.write(": "); Log.write(addr);
686        Log.write(": "); Log.flush(); MemoryManager.dumpRef(ref2);
687      }
688      VM.sysFail("\n\nScanStack: Detected bad GC map; exiting RVM with fatal error");
689    }
690  }
691
692  /**
693   * Check that a reference encountered during scanning is valid.  If
694   * the reference is invalid, dump stack and die.
695   *
696   * @param refaddr The address of the reference in question.
697   */
698  private static void checkReference(Address refaddr) {
699    ObjectReference ref = refaddr.loadObjectReference();
700    if (!MemoryManager.validRef(ref)) {
701      Log.writeln();
702      Log.writeln("Invalid ref reported while scanning stack");
703      Log.write(refaddr); Log.write(":"); Log.flush(); MemoryManager.dumpRef(ref);
704      Log.writeln();
705      Log.writeln("Dumping stack:");
706      RVMThread.dumpStack();
707      VM.sysFail("\n\nScanStack: Detected bad GC map; exiting RVM with fatal error");
708    }
709  }
710  /**
711   * Print out the name of a method
712   *
713   * @param m The method to be printed
714   */
715  private void printMethod(RVMMethod m) {
716    Log.write(m.getMemberRef().getType().getName().toByteArray()); Log.write(".");
717    Log.write(m.getMemberRef().getName().toByteArray()); Log.write(" ");
718    Log.write(m.getMemberRef().getDescriptor().toByteArray());
719  }
720
721  /**
722   * Print out the method header for the method associated with the
723   * current frame
724   */
725  private void printMethodHeader() {
726    RVMMethod method = compiledMethod.getMethod();
727
728    Log.write("\n--- METHOD (");
729    Log.write(CompiledMethod.compilerTypeToString(compiledMethodType));
730    Log.write(") ");
731    if (method == null)
732        Log.write("null method");
733    else
734        printMethod(method);
735    Log.writeln();
736    Log.write("--- fp = ");
737    Log.write(fp);
738    if (compiledMethod.isCompiled()) {
739        ObjectReference codeBase = ObjectReference.fromObject(compiledMethod.getEntryCodeArray());
740        Log.write("     code base = ");
741        Log.write(codeBase);
742        Log.write("     code offset = ");
743        Log.writeln(ip.diff(codeBase.toAddress()));
744        Log.write("     line number = ");
745        Log.writeln(compiledMethod.findLineNumberForInstruction(ip.diff(codeBase.toAddress())));
746    } else {
747      Log.write("   Method is uncompiled - ip = ");
748      Log.writeln(ip);
749    }
750  }
751
752  /**
753   * Dump the contents of a stack frame. Attempts to interpret each
754   * word as an object reference
755   *
756   * @param verbosity The level of verbosity to be used when
757   * performing the scan.
758   */
759  private void dumpStackFrame(int verbosity) {
760    Address start,end;
761    if (VM.BuildForIA32) {
762      if (prevFp.isZero()) {
763        start = fp.minus(20 * BYTES_IN_ADDRESS);
764        Log.writeln("--- 20 words of stack frame with fp = ", fp);
765      } else {
766        start = prevFp;    // start at callee fp
767      }
768      end = fp;            // end at fp
769    } else {
770      start = fp;                         // start at fp
771      end = fp.loadAddress();   // stop at callers fp
772    }
773
774    for (Address loc = start; loc.LT(end); loc = loc.plus(BYTES_IN_ADDRESS)) {
775      Log.write(loc); Log.write(" (");
776      Log.write(loc.diff(start));
777      Log.write("):   ");
778      ObjectReference value = Selected.Plan.get().loadObjectReference(loc);
779      Log.write(value);
780      Log.write(" ");
781      Log.flush();
782      if (verbosity >= 4 && MemoryManager.objectInVM(value) && loc.NE(start) && loc.NE(end))
783        MemoryManager.dumpRef(value);
784      else
785        Log.writeln();
786    }
787    Log.writeln();
788  }
789}