001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package org.jikesrvm.runtime;
014
015import org.jikesrvm.VM;
016import org.jikesrvm.architecture.StackFrameLayout;
017import org.jikesrvm.classloader.RVMClass;
018import org.jikesrvm.classloader.RVMMethod;
019import org.jikesrvm.compilers.common.CompiledMethod;
020import org.jikesrvm.compilers.common.CompiledMethods;
021import org.vmmagic.pragma.NoInline;
022import org.vmmagic.unboxed.Address;
023import org.vmmagic.unboxed.Offset;
024
025/**
026 *  Use this class to explore the stack.  It is sometimes necessary to
027 *  find out the current context class loader, and other things like that.
028 */
029public final class StackBrowser {
030
031  /** Method associated with current stack location */
032  private RVMMethod currentMethod;
033  /** Bytecode associated with current stack location */
034  private int currentBytecodeIndex;
035
036  /** The frame pointer for the current stack location */
037  private Address currentFramePointer;
038  /** The offset of the current instruction within its method */
039  private Offset currentInstructionPointer;
040  /** The current compiled method */
041  private CompiledMethod currentCompiledMethod;
042  /** The current inline encoding index for opt compiled methods */
043  private int currentInlineEncodingIndex;
044
045  /** Initialise state of browser */
046  @NoInline
047  public void init() {
048    currentFramePointer = Magic.getFramePointer();
049    upOneFrame();
050  }
051
052  /**
053   * Browse up one frame
054   * @param set should the state of the stack browser be effected?
055   * @return do more frames exist?
056   */
057  private boolean upOneFrameInternal(boolean set) {
058    Address fp;
059    if (currentMethod != null && currentMethod.getDeclaringClass().hasBridgeFromNativeAnnotation()) {
060      // Elide native frames
061      fp = RuntimeEntrypoints.unwindNativeStackFrame(currentFramePointer);
062    } else {
063        fp = currentFramePointer;
064    }
065
066    Address prevFP = fp;
067    Address newFP = Magic.getCallerFramePointer(fp);
068    if (newFP.EQ(StackFrameLayout.getStackFrameSentinelFP())) {
069      return false;
070    }
071    // getReturnAddress has to be put here, consider the case
072    // on ppc, when fp is the frame above SENTINEL FP
073    Address newIP = Magic.getReturnAddress(prevFP);
074
075    int cmid = Magic.getCompiledMethodID(newFP);
076
077    while (cmid == StackFrameLayout.getInvisibleMethodID()) {
078      prevFP = newFP;
079      newFP = Magic.getCallerFramePointer(newFP);
080      if (newFP.EQ(StackFrameLayout.getStackFrameSentinelFP())) {
081        return false;
082      }
083      newIP = Magic.getReturnAddress(prevFP);
084      cmid = Magic.getCompiledMethodID(newFP);
085    }
086
087    if (set) {
088      CompiledMethod cm = CompiledMethods.getCompiledMethod(cmid);
089      currentFramePointer = newFP;
090      currentInstructionPointer = cm.getInstructionOffset(newIP);
091      cm.set(this, currentInstructionPointer);
092    }
093    return true;
094  }
095
096  /** Browse up one frame failing if we fall off the stack */
097  private void upOneFrame() {
098    boolean ok = upOneFrameInternal(true);
099    if (VM.VerifyAssertions) VM._assert(ok, "tried to browse off stack");
100  }
101
102  /** @return whether there are more stack frames */
103  public boolean hasMoreFrames() {
104    return upOneFrameInternal(false);
105  }
106
107  /** Browse up one frame eliding native frames */
108  public void up() {
109    if (!currentCompiledMethod.up(this)) {
110      upOneFrame();
111    }
112  }
113
114  public void setBytecodeIndex(int bytecodeIndex) {
115    currentBytecodeIndex = bytecodeIndex;
116  }
117
118  public void setMethod(RVMMethod method) {
119    currentMethod = method;
120  }
121
122  public void setCompiledMethod(CompiledMethod cm) {
123    currentCompiledMethod = cm;
124  }
125
126  /**
127   * Set the inline encoding. This is only necessary for
128   * opt compiled methods.
129   *
130   * @param index the inline encoding index
131   */
132  public void setInlineEncodingIndex(int index) {
133    currentInlineEncodingIndex = index;
134  }
135
136  /** @return the bytecode index associated with the current stack frame */
137  public int getBytecodeIndex() {
138    return currentBytecodeIndex;
139  }
140
141  /** @return the method associated with the current stack frame */
142  public RVMMethod getMethod() {
143    return currentMethod;
144  }
145
146  /** @return the compiled method associated with the current stack frame */
147  public CompiledMethod getCompiledMethod() {
148    return currentCompiledMethod;
149  }
150
151  /** @return the class of the method associated with the current stack frame */
152  public RVMClass getCurrentClass() {
153    return getMethod().getDeclaringClass();
154  }
155
156  /** @return the class loader of the method associated with the current stack frame */
157  public ClassLoader getClassLoader() {
158    return getCurrentClass().getClassLoader();
159  }
160
161  /**
162   * Get the inline encoding associated with the current stack location.
163   * This method is called only by opt compiled methods.
164   *
165   *  @return the inline encoding associated with the current stack location
166   */
167  public int getInlineEncodingIndex() {
168    return currentInlineEncodingIndex;
169  }
170}