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 }