1 /* 2 * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2015, 2019, Red Hat Inc. 4 * Copyright (c) 2021, Huawei Technologies Co., Ltd. All rights reserved. 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * This code is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 only, as 9 * published by the Free Software Foundation. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 * 25 */ 26 27 package sun.jvm.hotspot.runtime.riscv64; 28 29 import sun.jvm.hotspot.debugger.*; 30 import sun.jvm.hotspot.debugger.riscv64.*; 31 import sun.jvm.hotspot.code.*; 32 import sun.jvm.hotspot.interpreter.*; 33 import sun.jvm.hotspot.runtime.*; 34 import sun.jvm.hotspot.runtime.riscv64.*; 35 36 /** <P> Should be able to be used on all riscv64 platforms we support 37 (Linux/riscv64) to implement JavaThread's "currentFrameGuess()" 38 functionality. Input is an RISCV64ThreadContext; output is SP, FP, 39 and PC for an RISCV64Frame. Instantiation of the RISCV64Frame is 40 left to the caller, since we may need to subclass RISCV64Frame to 41 support signal handler frames on Unix platforms. </P> 42 43 <P> Algorithm is to walk up the stack within a given range (say, 44 512K at most) looking for a plausible PC and SP for a Java frame, 45 also considering those coming in from the context. If we find a PC 46 that belongs to the VM (i.e., in generated code like the 47 interpreter or CodeCache) then we try to find an associated FP. 48 We repeat this until we either find a complete frame or run out of 49 stack to look at. </P> */ 50 51 public class RISCV64CurrentFrameGuess { 52 private RISCV64ThreadContext context; 53 private JavaThread thread; 54 private Address spFound; 55 private Address fpFound; 56 private Address pcFound; 57 58 private static final boolean DEBUG = System.getProperty("sun.jvm.hotspot.runtime.riscv64.RISCV64Frame.DEBUG") 59 != null; 60 61 public RISCV64CurrentFrameGuess(RISCV64ThreadContext context, 62 JavaThread thread) { 63 this.context = context; 64 this.thread = thread; 65 } 66 67 /** Returns false if not able to find a frame within a reasonable range. */ 68 public boolean run(long regionInBytesToSearch) { 69 Address sp = context.getRegisterAsAddress(RISCV64ThreadContext.SP); 70 Address pc = context.getRegisterAsAddress(RISCV64ThreadContext.PC); 71 Address fp = context.getRegisterAsAddress(RISCV64ThreadContext.FP); 72 if (sp == null) { 73 // Bail out if no last java frame either 74 if (thread.getLastJavaSP() != null) { 75 setValues(thread.getLastJavaSP(), thread.getLastJavaFP(), null); 76 return true; 77 } 78 return false; 79 } 80 Address end = sp.addOffsetTo(regionInBytesToSearch); 81 VM vm = VM.getVM(); 82 83 setValues(null, null, null); // Assume we're not going to find anything 84 85 if (vm.isJavaPCDbg(pc)) { 86 if (vm.isClientCompiler()) { 87 // If the topmost frame is a Java frame, we are (pretty much) 88 // guaranteed to have a viable FP. We should be more robust 89 // than this (we have the potential for losing entire threads' 90 // stack traces) but need to see how much work we really have 91 // to do here. Searching the stack for an (SP, FP) pair is 92 // hard since it's easy to misinterpret inter-frame stack 93 // pointers as base-of-frame pointers; we also don't know the 94 // sizes of C1 frames (not registered in the nmethod) so can't 95 // derive them from SP. 96 97 setValues(sp, fp, pc); 98 return true; 99 } else { 100 if (vm.getInterpreter().contains(pc)) { 101 if (DEBUG) { 102 System.out.println("CurrentFrameGuess: choosing interpreter frame: sp = " + 103 sp + ", fp = " + fp + ", pc = " + pc); 104 } 105 setValues(sp, fp, pc); 106 return true; 107 } 108 109 // For the server compiler, FP is not guaranteed to be valid 110 // for compiled code. In addition, an earlier attempt at a 111 // non-searching algorithm (see below) failed because the 112 // stack pointer from the thread context was pointing 113 // (considerably) beyond the ostensible end of the stack, into 114 // garbage; walking from the topmost frame back caused a crash. 115 // 116 // This algorithm takes the current PC as a given and tries to 117 // find the correct corresponding SP by walking up the stack 118 // and repeatedly performing stackwalks (very inefficient). 119 // 120 // FIXME: there is something wrong with stackwalking across 121 // adapter frames...this is likely to be the root cause of the 122 // failure with the simpler algorithm below. 123 124 for (long offset = 0; 125 offset < regionInBytesToSearch; 126 offset += vm.getAddressSize()) { 127 try { 128 Address curSP = sp.addOffsetTo(offset); 129 Frame frame = new RISCV64Frame(curSP, null, pc); 130 RegisterMap map = thread.newRegisterMap(false); 131 while (frame != null) { 132 if (frame.isEntryFrame() && frame.entryFrameIsFirst()) { 133 // We were able to traverse all the way to the 134 // bottommost Java frame. 135 // This sp looks good. Keep it. 136 if (DEBUG) { 137 System.out.println("CurrentFrameGuess: Choosing sp = " + curSP + ", pc = " + pc); 138 } 139 setValues(curSP, null, pc); 140 return true; 141 } 142 frame = frame.sender(map); 143 } 144 } catch (Exception e) { 145 if (DEBUG) { 146 System.out.println("CurrentFrameGuess: Exception " + e + " at offset " + offset); 147 } 148 // Bad SP. Try another. 149 } 150 } 151 152 // Were not able to find a plausible SP to go with this PC. 153 // Bail out. 154 return false; 155 } 156 } else { 157 // If the current program counter was not known to us as a Java 158 // PC, we currently assume that we are in the run-time system 159 // and attempt to look to thread-local storage for saved SP and 160 // FP. Note that if these are null (because we were, in fact, 161 // in Java code, i.e., vtable stubs or similar, and the SA 162 // didn't have enough insight into the target VM to understand 163 // that) then we are going to lose the entire stack trace for 164 // the thread, which is sub-optimal. FIXME. 165 166 if (DEBUG) { 167 System.out.println("CurrentFrameGuess: choosing last Java frame: sp = " + 168 thread.getLastJavaSP() + ", fp = " + thread.getLastJavaFP()); 169 } 170 if (thread.getLastJavaSP() == null) { 171 return false; // No known Java frames on stack 172 } 173 174 // The runtime has a nasty habit of not saving fp in the frame 175 // anchor, leaving us to grovel about in the stack to find a 176 // plausible address. Fortunately, this only happens in 177 // compiled code; there we always have a valid PC, and we always 178 // push LR and FP onto the stack as a pair, with FP at the lower 179 // address. 180 pc = thread.getLastJavaPC(); 181 fp = thread.getLastJavaFP(); 182 sp = thread.getLastJavaSP(); 183 184 if (fp == null) { 185 CodeCache cc = vm.getCodeCache(); 186 if (cc.contains(pc)) { 187 CodeBlob cb = cc.findBlob(pc); 188 if (DEBUG) { 189 System.out.println("FP is null. Found blob frame size " + cb.getFrameSize()); 190 } 191 // See if we can derive a frame pointer from SP and PC 192 long link_offset = cb.getFrameSize() - 2 * VM.getVM().getAddressSize(); 193 if (link_offset >= 0) { 194 fp = sp.addOffsetTo(link_offset); 195 } 196 } 197 } 198 199 // We found a PC in the frame anchor. Check that it's plausible, and 200 // if it is, use it. 201 if (vm.isJavaPCDbg(pc)) { 202 setValues(sp, fp, pc); 203 } else { 204 setValues(sp, fp, null); 205 } 206 207 return true; 208 } 209 } 210 211 public Address getSP() { return spFound; } 212 public Address getFP() { return fpFound; } 213 /** May be null if getting values from thread-local storage; take 214 care to call the correct RISCV64Frame constructor to recover this if 215 necessary */ 216 public Address getPC() { return pcFound; } 217 218 private void setValues(Address sp, Address fp, Address pc) { 219 spFound = sp; 220 fpFound = fp; 221 pcFound = pc; 222 } 223 }