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