1 /* 2 * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 package sun.jvm.hotspot.runtime.amd64; 26 27 import sun.jvm.hotspot.debugger.*; 28 import sun.jvm.hotspot.debugger.amd64.*; 29 import sun.jvm.hotspot.code.*; 30 import sun.jvm.hotspot.interpreter.*; 31 import sun.jvm.hotspot.runtime.*; 32 import sun.jvm.hotspot.runtime.x86.*; 33 34 /** <P> Should be able to be used on all amd64 platforms we support 35 (Linux/amd64) to implement JavaThread's 36 "currentFrameGuess()" functionality. Input is an AMD64ThreadContext; 37 output is SP, FP, and PC for an AMD64Frame. Instantiation of the 38 AMD64Frame is left to the caller, since we may need to subclass 39 AMD64Frame to support signal handler frames on Unix platforms. </P> 40 41 <P> Algorithm is to walk up the stack within a given range (say, 42 512K at most) looking for a plausible PC and SP for a Java frame, 43 also considering those coming in from the context. If we find a PC 44 that belongs to the VM (i.e., in generated code like the 45 interpreter or CodeCache) then we try to find an associated EBP. 46 We repeat this until we either find a complete frame or run out of 47 stack to look at. </P> */ 48 49 public class AMD64CurrentFrameGuess { 50 private AMD64ThreadContext context; 51 private JavaThread thread; 52 private Address spFound; 53 private Address fpFound; 54 private Address pcFound; 55 56 private static final boolean DEBUG = System.getProperty("sun.jvm.hotspot.runtime.amd64.AMD64Frame.DEBUG") 57 != null; 58 59 public AMD64CurrentFrameGuess(AMD64ThreadContext context, 60 JavaThread thread) { 61 this.context = context; 62 this.thread = thread; 63 } 64 65 /** Returns false if not able to find a frame within a reasonable range. */ 66 public boolean run(long regionInBytesToSearch) { 67 Address sp = context.getRegisterAsAddress(AMD64ThreadContext.RSP); 68 Address pc = context.getRegisterAsAddress(AMD64ThreadContext.RIP); 69 Address fp = context.getRegisterAsAddress(AMD64ThreadContext.RBP); 70 if (sp == null) { 71 // Bail out if no last java frame either 72 if (thread.getLastJavaSP() != null) { 73 setValues(thread.getLastJavaSP(), thread.getLastJavaFP(), null); 74 return true; 75 } 76 return false; 77 } 78 Address end = sp.addOffsetTo(regionInBytesToSearch); 79 VM vm = VM.getVM(); 80 81 setValues(null, null, null); // Assume we're not going to find anything 82 83 if (vm.isJavaPCDbg(pc)) { 84 if (vm.isClientCompiler()) { 85 // If the topmost frame is a Java frame, we are (pretty much) 86 // guaranteed to have a viable EBP. We should be more robust 87 // than this (we have the potential for losing entire threads' 88 // stack traces) but need to see how much work we really have 89 // to do here. Searching the stack for an (SP, FP) pair is 90 // hard since it's easy to misinterpret inter-frame stack 91 // pointers as base-of-frame pointers; we also don't know the 92 // sizes of C1 frames (not registered in the nmethod) so can't 93 // derive them from ESP. 94 95 setValues(sp, fp, pc); 96 return true; 97 } else { 98 if (vm.getInterpreter().contains(pc)) { 99 if (DEBUG) { 100 System.out.println("CurrentFrameGuess: choosing interpreter frame: sp = " + 101 sp + ", fp = " + fp + ", pc = " + pc); 102 } 103 setValues(sp, fp, pc); 104 return true; 105 } 106 107 // For the server compiler, EBP is not guaranteed to be valid 108 // for compiled code. In addition, an earlier attempt at a 109 // non-searching algorithm (see below) failed because the 110 // stack pointer from the thread context was pointing 111 // (considerably) beyond the ostensible end of the stack, into 112 // garbage; walking from the topmost frame back caused a crash. 113 // 114 // This algorithm takes the current PC as a given and tries to 115 // find the correct corresponding SP by walking up the stack 116 // and repeatedly performing stackwalks (very inefficient). 117 // 118 // FIXME: there is something wrong with stackwalking across 119 // adapter frames...this is likely to be the root cause of the 120 // failure with the simpler algorithm below. 121 122 for (long offset = 0; 123 offset < regionInBytesToSearch; 124 offset += vm.getAddressSize()) { 125 try { 126 Address curSP = sp.addOffsetTo(offset); 127 Frame frame = new X86Frame(curSP, null, pc); 128 RegisterMap map = thread.newRegisterMap(false); 129 while (frame != null) { 130 if (frame.isEntryFrame() && frame.entryFrameIsFirst()) { 131 // We were able to traverse all the way to the 132 // bottommost Java frame. 133 // This sp looks good. Keep it. 134 if (DEBUG) { 135 System.out.println("CurrentFrameGuess: Choosing sp = " + curSP + ", pc = " + pc); 136 } 137 setValues(curSP, null, pc); 138 return true; 139 } 140 Frame oldFrame = frame; 141 frame = frame.sender(map); 142 if (frame.getSP().lessThanOrEqual(oldFrame.getSP())) { 143 // Frame points to itself or to a location in the wrong direction. 144 // Break the loop and move on to next offset. 145 if (DEBUG) { 146 System.out.println("AMD64CurrentFrameGuess.run: frame <= oldFrame: " + frame); 147 } 148 break; 149 } 150 } 151 } catch (Exception e) { 152 if (DEBUG) { 153 System.out.println("CurrentFrameGuess: Exception " + e + " at offset " + offset); 154 } 155 // Bad SP. Try another. 156 } 157 } 158 159 // Were not able to find a plausible SP to go with this PC. 160 // Bail out. 161 return false; 162 163 /* 164 // Original algorithm which does not work because SP was 165 // pointing beyond where it should have: 166 171 172 CodeCache cc = vm.getCodeCache(); 173 if (cc.contains(pc)) { 174 CodeBlob cb = cc.findBlob(pc); 175 176 // See if we can derive a frame pointer from SP and PC 177 // NOTE: This is the code duplicated from AMD64Frame 178 Address saved_fp = null; 179 int llink_offset = cb.getLinkOffset(); 180 if (llink_offset >= 0) { 181 // Restore base-pointer, since next frame might be an interpreter frame. 182 Address fp_addr = sp.addOffsetTo(VM.getVM().getAddressSize() * llink_offset); 183 saved_fp = fp_addr.getAddressAt(0); 184 } 185 186 setValues(sp, saved_fp, pc); 187 return true; 188 } 189 */ 190 } 191 } else { 192 // If the current program counter was not known to us as a Java 193 // PC, we currently assume that we are in the run-time system 194 // and attempt to look to thread-local storage for saved ESP and 195 // EBP. Note that if these are null (because we were, in fact, 196 // in Java code, i.e., vtable stubs or similar, and the SA 197 // didn't have enough insight into the target VM to understand 198 // that) then we are going to lose the entire stack trace for 199 // the thread, which is sub-optimal. FIXME. 200 201 if (DEBUG) { 202 System.out.println("CurrentFrameGuess: choosing last Java frame: sp = " + 203 thread.getLastJavaSP() + ", fp = " + thread.getLastJavaFP()); 204 } 205 if (thread.getLastJavaSP() == null) { 206 return false; // No known Java frames on stack 207 } 208 setValues(thread.getLastJavaSP(), thread.getLastJavaFP(), null); 209 return true; 210 } 211 } 212 213 public Address getSP() { return spFound; } 214 public Address getFP() { return fpFound; } 215 /** May be null if getting values from thread-local storage; take 216 care to call the correct AMD64Frame constructor to recover this if 217 necessary */ 218 public Address getPC() { return pcFound; } 219 220 private void setValues(Address sp, Address fp, Address pc) { 221 spFound = sp; 222 fpFound = fp; 223 pcFound = pc; 224 } 225 } | 1 /* 2 * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 package sun.jvm.hotspot.runtime.amd64; 26 27 import sun.jvm.hotspot.debugger.*; 28 import sun.jvm.hotspot.debugger.amd64.*; 29 import sun.jvm.hotspot.code.*; 30 import sun.jvm.hotspot.interpreter.*; 31 import sun.jvm.hotspot.oops.*; 32 import sun.jvm.hotspot.runtime.*; 33 import sun.jvm.hotspot.runtime.x86.*; 34 import sun.jvm.hotspot.types.*; 35 import sun.jvm.hotspot.utilities.*; 36 37 /** <P> Should be able to be used on all amd64 platforms we support 38 (Linux/amd64) to implement JavaThread's 39 "currentFrameGuess()" functionality. Input is an AMD64ThreadContext; 40 output is SP, FP, and PC for an AMD64Frame. Instantiation of the 41 AMD64Frame is left to the caller, since we may need to subclass 42 AMD64Frame to support signal handler frames on Unix platforms. </P> 43 44 <P> Algorithm is to walk up the stack within a given range (say, 45 512K at most) looking for a plausible PC and SP for a Java frame, 46 also considering those coming in from the context. If we find a PC 47 that belongs to the VM (i.e., in generated code like the 48 interpreter or CodeCache) then we try to find an associated EBP. 49 We repeat this until we either find a complete frame or run out of 50 stack to look at. </P> */ 51 52 public class AMD64CurrentFrameGuess { 53 private AMD64ThreadContext context; 54 private JavaThread thread; 55 private Address spFound; 56 private Address fpFound; 57 private Address pcFound; 58 59 private static final boolean DEBUG = System.getProperty("sun.jvm.hotspot.runtime.amd64.AMD64Frame.DEBUG") 60 != null; 61 62 public AMD64CurrentFrameGuess(AMD64ThreadContext context, 63 JavaThread thread) { 64 this.context = context; 65 this.thread = thread; 66 } 67 68 private boolean validateInterpreterFrame(Address sp, Address fp, Address pc) { 69 VM vm = VM.getVM(); 70 X86Frame f = new X86Frame(sp, fp, pc); 71 72 // First validate that frame->method is really a Method* 73 Method method = null; 74 try { 75 method = f.getInterpreterFrameMethod(); 76 } catch (WrongTypeException | AddressException | NullPointerException e) { 77 // This just means frame->method is not valid. 78 if (DEBUG) { 79 System.out.println("CurrentFrameGuess: frame->method is invalid"); 80 } 81 } 82 83 // Next make sure frame->bcp is really in the method's bytecodes 84 if (method != null && f.getInterpreterFrameBCP() != null) { 85 if (method.getConstMethod().isAddressInMethod(f.getInterpreterFrameBCP())) { 86 // All's good. This is the normal path when the PC is in the interpreter. 87 // The cases below are all exceptionally rare. 88 setValues(sp, fp, pc); 89 return true; 90 } else { 91 if (DEBUG) { 92 System.out.println("CurrentFrameGuess: frame->bcp is invalid"); 93 } 94 } 95 } 96 97 // Either frame->method is not a Method* or frame->bcp is not valid. That means either 98 // we have pushed the new interpreter frame, but have not intialized it yet, or 99 // we have yet to push the new interpreter frame, and the "current" frame is not an 100 // interpreter frame. Figure out which is the case. 101 102 // Try to find the return address in RAX or on the stack. If we can't 103 // find what appears to be a valid codecache address in either of these 104 // two locations, then we canot determine the frame. 105 Address returnAddress = context.getRegisterAsAddress(AMD64ThreadContext.RAX); 106 CodeCache c = VM.getVM().getCodeCache(); 107 if (returnAddress == null || !c.contains(returnAddress)) { 108 returnAddress = sp.getAddressAt(0); // check top of stack 109 if (returnAddress == null || !c.contains(returnAddress)) { 110 if (DEBUG) { 111 System.out.println("CurrentFrameGuess: Cannot find valid returnAddress"); 112 } 113 setValues(sp, fp, pc); 114 return false; // couldn't find a valid PC for frame. 115 } else { 116 if (DEBUG) { 117 System.out.println("CurrentFrameGuess: returnAddress found on stack: " + returnAddress); 118 } 119 } 120 } else { 121 if (DEBUG) { 122 System.out.println("CurrentFrameGuess: returnAddress found in RAX: " + returnAddress); 123 } 124 } 125 126 // See what return address is stored in the frame. Most likely it is not valid, but 127 // it's validity will help us determine the state of the new frame push. 128 Address returnAddress2 = null; 129 try { 130 returnAddress2 = f.getSenderPC(); 131 } catch (AddressException e) { 132 // Just ignore. This is expected sometimes. 133 if (DEBUG) { 134 System.out.println("CurrentFrameGuess: senderPC is invalid"); 135 } 136 } 137 if (DEBUG) { 138 System.out.println("CurrentFrameGuess: returnAddress2: " + returnAddress2); 139 } 140 141 if (returnAddress.equals(returnAddress2)) { 142 // If these two ways of fetching the return address produce the same address, 143 // then that means we have pushed the new frame, but have not finished 144 // initializing it yet. Otherwise we would also have found a valid frame->method 145 // and frame->bcp. Because this frame is incomplete, we instead use 146 // the previous frame as the current frame. 147 if (DEBUG) { 148 System.out.println("CurrentFrameGuess: frame pushed but not initaliazed."); 149 } 150 sp = f.getSenderSP(); 151 fp = f.getLink(); 152 setValues(sp, fp, returnAddress); 153 // If this previous frame is interpreted, then we are done and setValues() has been 154 // called with a valid interpreter frame. Otherwise return false and the caller will 155 // need to determine frame. 156 if (vm.getInterpreter().contains(returnAddress)) { 157 if (DEBUG) { 158 System.out.println("CurrentFrameGuess: Interpreted: using previous frame."); 159 } 160 return true; 161 } else { 162 if (DEBUG) { 163 System.out.println("CurrentFrameGuess: Not Interpreted: using previous frame."); 164 } 165 return false; 166 } 167 } else { 168 // We haven't even pushed the new frame yet. sp and fp are for the previous 169 // frame that is making the call to the interpreter. Since this frame is 170 // not a valid interpreter frame (we know either frame->method or frame->bcp 171 // are not valid), it must be something else. Assume compiled or native and 172 // let the caller figure it out. 173 setValues(sp, fp, returnAddress); 174 if (DEBUG) { 175 System.out.println("CurrentFrameGuess: Frame not yet pushed. Previous frame not interpreted."); 176 } 177 return false; 178 } 179 } 180 181 /** Returns false if not able to find a frame within a reasonable range. */ 182 public boolean run(long regionInBytesToSearch) { 183 Address sp = context.getRegisterAsAddress(AMD64ThreadContext.RSP); 184 Address pc = context.getRegisterAsAddress(AMD64ThreadContext.RIP); 185 Address fp = context.getRegisterAsAddress(AMD64ThreadContext.RBP); 186 if (sp == null) { 187 return checkLastJavaSP(); 188 } 189 Address end = sp.addOffsetTo(regionInBytesToSearch); 190 VM vm = VM.getVM(); 191 192 setValues(null, null, null); // Assume we're not going to find anything 193 194 if (!vm.isJavaPCDbg(pc)) { 195 return checkLastJavaSP(); 196 } else { 197 if (vm.isClientCompiler()) { 198 // If the topmost frame is a Java frame, we are (pretty much) 199 // guaranteed to have a viable EBP. We should be more robust 200 // than this (we have the potential for losing entire threads' 201 // stack traces) but need to see how much work we really have 202 // to do here. Searching the stack for an (SP, FP) pair is 203 // hard since it's easy to misinterpret inter-frame stack 204 // pointers as base-of-frame pointers; we also don't know the 205 // sizes of C1 frames (not registered in the nmethod) so can't 206 // derive them from ESP. 207 208 setValues(sp, fp, pc); 209 return true; 210 } else { 211 if (vm.getInterpreter().contains(pc)) { 212 // pc points into the interpreter, but that doesn't necessarily mean the current 213 // frame is interpreted. We may be in interpreter method entry code before the frame 214 // has been pushed, or possibly after it has been pushed but before it has been 215 // initialized. See TemplateInterpreterGenerator::generate_normal_entry(). So we 216 // need to do a few sanity checks here, and try to correct the situation if 217 // we are in the middle of a frame push. 218 if (validateInterpreterFrame(sp, fp, pc)) { 219 if (DEBUG) { 220 System.out.println("CurrentFrameGuess: choosing interpreter frame: sp = " + 221 spFound + ", fpFound = " + fp + ", pcFound = " + pc); 222 } 223 return true; // We're done. setValues() has been called for valid interpreter frame. 224 } else { 225 // This does not appear to be a valid interpreter frame. Possibly we are in the 226 // middle of pushing a new frame. Update the frame values to those suggested 227 // by validateInterpreterFrame() and then fall through to check if it is compiled. 228 sp = spFound; 229 fp = fpFound; 230 pc = pcFound; 231 setValues(null, null, null); 232 if (pcFound == null) { 233 return false; 234 } 235 // pc may have changed, so we need to redo the isJavaPCDbg(pc) check before 236 // falling into code below that assumes the frame is compiled. 237 if (!vm.isJavaPCDbg(pc)) { 238 return checkLastJavaSP(); 239 } 240 } 241 } 242 243 // For the server compiler, EBP is not guaranteed to be valid 244 // for compiled code. In addition, an earlier attempt at a 245 // non-searching algorithm (see below) failed because the 246 // stack pointer from the thread context was pointing 247 // (considerably) beyond the ostensible end of the stack, into 248 // garbage; walking from the topmost frame back caused a crash. 249 // 250 // This algorithm takes the current PC as a given and tries to 251 // find the correct corresponding SP by walking up the stack 252 // and repeatedly performing stackwalks (very inefficient). 253 // 254 // FIXME: there is something wrong with stackwalking across 255 // adapter frames...this is likely to be the root cause of the 256 // failure with the simpler algorithm below. 257 258 if (DEBUG) { 259 System.out.println("CurrentFrameGuess: sp = " + sp + ", pc = " + pc); 260 } 261 for (long offset = 0; 262 offset < regionInBytesToSearch; 263 offset += vm.getAddressSize()) { 264 try { 265 Address curSP = sp.addOffsetTo(offset); 266 Frame frame = new X86Frame(curSP, null, pc); 267 RegisterMap map = thread.newRegisterMap(false); 268 while (frame != null) { 269 if (frame.isEntryFrame() && frame.entryFrameIsFirst()) { 270 // We were able to traverse all the way to the 271 // bottommost Java frame. 272 // This sp looks good. Keep it. 273 if (DEBUG) { 274 System.out.println("CurrentFrameGuess: Choosing sp = " + curSP + ", pc = " + pc); 275 } 276 setValues(curSP, null, pc); 277 return true; 278 } 279 Frame oldFrame = frame; 280 frame = frame.sender(map); 281 if (frame.getSP().lessThanOrEqual(oldFrame.getSP())) { 282 // Frame points to itself or to a location in the wrong direction. 283 // Break the loop and move on to next offset. 284 if (DEBUG) { 285 System.out.println("CurrentFrameGuess: frame <= oldFrame: " + frame); 286 } 287 break; 288 } 289 } 290 } catch (Exception e) { 291 if (DEBUG) { 292 System.out.println("CurrentFrameGuess: Exception " + e + " at offset " + offset); 293 } 294 // Bad SP. Try another. 295 } 296 } 297 298 // Were not able to find a plausible SP to go with this PC. 299 // Bail out. 300 return false; 301 302 /* 303 // Original algorithm which does not work because SP was 304 // pointing beyond where it should have: 305 310 311 CodeCache cc = vm.getCodeCache(); 312 if (cc.contains(pc)) { 313 CodeBlob cb = cc.findBlob(pc); 314 315 // See if we can derive a frame pointer from SP and PC 316 // NOTE: This is the code duplicated from AMD64Frame 317 Address saved_fp = null; 318 int llink_offset = cb.getLinkOffset(); 319 if (llink_offset >= 0) { 320 // Restore base-pointer, since next frame might be an interpreter frame. 321 Address fp_addr = sp.addOffsetTo(VM.getVM().getAddressSize() * llink_offset); 322 saved_fp = fp_addr.getAddressAt(0); 323 } 324 325 setValues(sp, saved_fp, pc); 326 return true; 327 } 328 */ 329 } 330 } 331 } 332 333 private boolean checkLastJavaSP() { 334 // If the current program counter was not known to us as a Java 335 // PC, we currently assume that we are in the run-time system 336 // and attempt to look to thread-local storage for saved ESP and 337 // EBP. Note that if these are null (because we were, in fact, 338 // in Java code, i.e., vtable stubs or similar, and the SA 339 // didn't have enough insight into the target VM to understand 340 // that) then we are going to lose the entire stack trace for 341 // the thread, which is sub-optimal. FIXME. 342 343 if (DEBUG) { 344 System.out.println("CurrentFrameGuess: choosing last Java frame: sp = " + 345 thread.getLastJavaSP() + ", fp = " + thread.getLastJavaFP()); 346 } 347 if (thread.getLastJavaSP() == null) { 348 return false; // No known Java frames on stack 349 } 350 setValues(thread.getLastJavaSP(), thread.getLastJavaFP(), null); 351 return true; 352 } 353 354 public Address getSP() { return spFound; } 355 public Address getFP() { return fpFound; } 356 /** May be null if getting values from thread-local storage; take 357 care to call the correct AMD64Frame constructor to recover this if 358 necessary */ 359 public Address getPC() { return pcFound; } 360 361 private void setValues(Address sp, Address fp, Address pc) { 362 spFound = sp; 363 fpFound = fp; 364 pcFound = pc; 365 } 366 } |