public Frame getLastFramePD(JavaThread thread, Address addr) { Address fp = thread.getLastJavaFP(); if (fp == null) { return null; // no information } return new X86Frame(thread.getLastJavaSP(), fp); }
/** Runs the analysis algorithm */ public void run() { if (VM.getVM().getRevPtrs() != null) { return; // Assume already done } VM vm = VM.getVM(); rp = new ReversePtrs(); vm.setRevPtrs(rp); Universe universe = vm.getUniverse(); CollectedHeap collHeap = universe.heap(); usedSize = collHeap.used(); visitedSize = 0; // Note that an experiment to iterate the heap linearly rather // than in recursive-descent order has been done. It turns out // that the recursive-descent algorithm is nearly twice as fast // due to the fact that it scans only live objects and (currently) // only a fraction of the perm gen, namely the static fields // contained in instanceKlasses. (Iterating the heap linearly // would also change the semantics of the result so that // ReversePtrs.get() would return a non-null value even for dead // objects.) Nonetheless, the reverse pointer computation is still // quite slow and optimization in field iteration of objects // should be done. if (progressThunk != null) { // Get it started progressThunk.heapIterationFractionUpdate(0); } // Allocate mark bits for heap markBits = new MarkBits(collHeap); // Get a hold of the object heap heap = vm.getObjectHeap(); // Do each thread's roots for (JavaThread thread = VM.getVM().getThreads().first(); thread != null; thread = thread.next()) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); thread.printThreadIDOn(new PrintStream(bos)); String threadDesc = " in thread \"" + thread.getThreadName() + "\" (id " + bos.toString() + ")"; doStack(thread, new RootVisitor("Stack root" + threadDesc)); doJNIHandleBlock(thread.activeHandles(), new RootVisitor("JNI handle root" + threadDesc)); } // Do global JNI handles JNIHandles handles = VM.getVM().getJNIHandles(); doJNIHandleBlock(handles.globalHandles(), new RootVisitor("Global JNI handle root")); doJNIHandleBlock(handles.weakGlobalHandles(), new RootVisitor("Weak global JNI handle root")); // Do Java-level static fields SystemDictionary sysDict = VM.getVM().getSystemDictionary(); sysDict.allClassesDo( new SystemDictionary.ClassVisitor() { public void visit(Klass k) { if (k instanceof InstanceKlass) { final InstanceKlass ik = (InstanceKlass) k; ik.iterateStaticFields( new DefaultOopVisitor() { public void doOop(OopField field, boolean isVMField) { Oop next = field.getValue(getObj()); NamedFieldIdentifier nfi = new NamedFieldIdentifier( "Static field \"" + field.getID().getName() + "\" in class \"" + ik.getName().asString() + "\""); LivenessPathElement lp = new LivenessPathElement(null, nfi); rp.put(lp, next); try { markAndTraverse(next); } catch (AddressException e) { System.err.print( "RevPtrs analysis: WARNING: AddressException at 0x" + Long.toHexString(e.getAddress()) + " while traversing static fields of InstanceKlass "); ik.printValueOn(System.err); System.err.println(); } catch (UnknownOopException e) { System.err.println( "RevPtrs analysis: WARNING: UnknownOopException while " + "traversing static fields of InstanceKlass "); ik.printValueOn(System.err); System.err.println(); } } }); } } }); if (progressThunk != null) { progressThunk.heapIterationComplete(); } // Clear out markBits markBits = null; }
public Address getLastJavaFP(Address addr) { return lastJavaFPField.getValue( addr.addOffsetTo(sun.jvm.hotspot.runtime.JavaThread.getAnchorField().getOffset())); }
/** Returns false if not able to find a frame within a reasonable range. */ public boolean run(long regionInBytesToSearch) { Address sp = context.getRegisterAsAddress(AMD64ThreadContext.RSP); Address pc = context.getRegisterAsAddress(AMD64ThreadContext.RIP); Address fp = context.getRegisterAsAddress(AMD64ThreadContext.RBP); if (sp == null) { // Bail out if no last java frame either if (thread.getLastJavaSP() != null) { setValues(thread.getLastJavaSP(), thread.getLastJavaFP(), null); return true; } return false; } Address end = sp.addOffsetTo(regionInBytesToSearch); VM vm = VM.getVM(); setValues(null, null, null); // Assume we're not going to find anything if (vm.isJavaPCDbg(pc)) { if (vm.isClientCompiler()) { // If the topmost frame is a Java frame, we are (pretty much) // guaranteed to have a viable EBP. We should be more robust // than this (we have the potential for losing entire threads' // stack traces) but need to see how much work we really have // to do here. Searching the stack for an (SP, FP) pair is // hard since it's easy to misinterpret inter-frame stack // pointers as base-of-frame pointers; we also don't know the // sizes of C1 frames (not registered in the nmethod) so can't // derive them from ESP. setValues(sp, fp, pc); return true; } else { if (vm.getInterpreter().contains(pc)) { if (DEBUG) { System.out.println( "CurrentFrameGuess: choosing interpreter frame: sp = " + sp + ", fp = " + fp + ", pc = " + pc); } setValues(sp, fp, pc); return true; } // For the server compiler, EBP is not guaranteed to be valid // for compiled code. In addition, an earlier attempt at a // non-searching algorithm (see below) failed because the // stack pointer from the thread context was pointing // (considerably) beyond the ostensible end of the stack, into // garbage; walking from the topmost frame back caused a crash. // // This algorithm takes the current PC as a given and tries to // find the correct corresponding SP by walking up the stack // and repeatedly performing stackwalks (very inefficient). // // FIXME: there is something wrong with stackwalking across // adapter frames...this is likely to be the root cause of the // failure with the simpler algorithm below. for (long offset = 0; offset < regionInBytesToSearch; offset += vm.getAddressSize()) { try { Address curSP = sp.addOffsetTo(offset); Frame frame = new X86Frame(curSP, null, pc); RegisterMap map = thread.newRegisterMap(false); while (frame != null) { if (frame.isEntryFrame() && frame.entryFrameIsFirst()) { // We were able to traverse all the way to the // bottommost Java frame. // This sp looks good. Keep it. if (DEBUG) { System.out.println("CurrentFrameGuess: Choosing sp = " + curSP + ", pc = " + pc); } setValues(curSP, null, pc); return true; } frame = frame.sender(map); } } catch (Exception e) { if (DEBUG) { System.out.println("CurrentFrameGuess: Exception " + e + " at offset " + offset); } // Bad SP. Try another. } } // Were not able to find a plausible SP to go with this PC. // Bail out. return false; /* // Original algorithm which does not work because SP was // pointing beyond where it should have: // For the server compiler, EBP is not guaranteed to be valid // for compiled code. We see whether the PC is in the // interpreter and take care of that, otherwise we run code // (unfortunately) duplicated from AMD64Frame.senderForCompiledFrame. CodeCache cc = vm.getCodeCache(); if (cc.contains(pc)) { CodeBlob cb = cc.findBlob(pc); // See if we can derive a frame pointer from SP and PC // NOTE: This is the code duplicated from AMD64Frame Address saved_fp = null; int llink_offset = cb.getLinkOffset(); if (llink_offset >= 0) { // Restore base-pointer, since next frame might be an interpreter frame. Address fp_addr = sp.addOffsetTo(VM.getVM().getAddressSize() * llink_offset); saved_fp = fp_addr.getAddressAt(0); } setValues(sp, saved_fp, pc); return true; } */ } } else { // If the current program counter was not known to us as a Java // PC, we currently assume that we are in the run-time system // and attempt to look to thread-local storage for saved ESP and // EBP. Note that if these are null (because we were, in fact, // in Java code, i.e., vtable stubs or similar, and the SA // didn't have enough insight into the target VM to understand // that) then we are going to lose the entire stack trace for // the thread, which is sub-optimal. FIXME. if (DEBUG) { System.out.println( "CurrentFrameGuess: choosing last Java frame: sp = " + thread.getLastJavaSP() + ", fp = " + thread.getLastJavaFP()); } if (thread.getLastJavaSP() == null) { return false; // No known Java frames on stack } setValues(thread.getLastJavaSP(), thread.getLastJavaFP(), null); return true; } }