/** * Should be able to be used on all amd64 platforms we support (Linux/amd64) to implement * JavaThread's "currentFrameGuess()" functionality. Input is an AMD64ThreadContext; output is SP, * FP, and PC for an AMD64Frame. Instantiation of the AMD64Frame is left to the caller, since we may * need to subclass AMD64Frame to support signal handler frames on Unix platforms. * * <p>Algorithm is to walk up the stack within a given range (say, 512K at most) looking for a * plausible PC and SP for a Java frame, also considering those coming in from the context. If we * find a PC that belongs to the VM (i.e., in generated code like the interpreter or CodeCache) then * we try to find an associated EBP. We repeat this until we either find a complete frame or run out * of stack to look at. */ public class AMD64CurrentFrameGuess { private AMD64ThreadContext context; private JavaThread thread; private Address spFound; private Address fpFound; private Address pcFound; private static final boolean DEBUG = System.getProperty("sun.jvm.hotspot.runtime.amd64.AMD64Frame.DEBUG") != null; public AMD64CurrentFrameGuess(AMD64ThreadContext context, JavaThread thread) { this.context = context; this.thread = thread; } /** 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; } } public Address getSP() { return spFound; } public Address getFP() { return fpFound; } /** * May be null if getting values from thread-local storage; take care to call the correct * AMD64Frame constructor to recover this if necessary */ public Address getPC() { return pcFound; } private void setValues(Address sp, Address fp, Address pc) { spFound = sp; fpFound = fp; pcFound = pc; } }
static { debug = System.getProperty("sun.jvm.hotspot.utilities.soql.SOQLEngine.debug") != null; }