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.adaptive.measurements.listeners;
014
015import org.jikesrvm.VM;
016import org.jikesrvm.adaptive.AosEntrypoints;
017import org.jikesrvm.architecture.StackFrameLayout;
018import org.jikesrvm.compilers.common.CompiledMethod;
019import org.jikesrvm.compilers.common.CompiledMethods;
020import org.jikesrvm.runtime.Magic;
021import org.jikesrvm.scheduler.Synchronization;
022import org.jikesrvm.scheduler.RVMThread;
023import org.vmmagic.pragma.Uninterruptible;
024import org.vmmagic.unboxed.Address;
025import org.vmmagic.unboxed.Offset;
026
027/**
028 * A EdgeListener defines a listener
029 * that computes a call graph edge from the call stack.
030 * After a parameterized number of edges are collected,
031 * it notifies its organizer that the threshold is reached.
032 * <p>
033 * Defines update's interface.
034 * <p>
035 * EdgeListener communicates with an organizer through a
036 * integer array, buffer.  Each time this listener is called,
037 * it places a triple of integers in buffer that correspond to
038 * the callee, caller, and machine code offset of the call site
039 */
040@Uninterruptible
041public class EdgeListener extends ContextListener {
042
043  protected static final boolean DEBUG = false;
044
045  /**
046   * buffer provides the communication channel between the listener and the
047   * organizer.
048   * The buffer contains an array of triples &lt;callee, caller, address&gt; where
049   * the caller and callee are CompiledMethodID's.
050   * Initially, buffer contains zeros.  The listener adds triples.
051   * When the listener hits the end of the buffer, notify the organizer.
052   */
053  private int[] buffer;
054
055  /**
056   * Number of samples to be taken before issuing callback to controller
057   */
058  private int desiredSamples;
059
060  /**
061   * Number of samples taken so far
062   */
063  protected int samplesTaken;
064
065  /**
066   * Number of times update is called
067   */
068  protected int updateCalled;
069
070  /**
071   * Constructor
072   */
073  public EdgeListener() {
074    buffer = null;
075    desiredSamples = 0;
076  }
077
078  /**
079   * @return the number of times that update has been called
080   */
081  int getTimesUpdateCalled() {
082    return updateCalled;
083  }
084
085  /**
086   * Setup buffer and buffer size.
087   * This method must be called before any data can be written to
088   * the buffer.
089   *
090   * @param buffer the allocated buffer to contain the samples, size should
091   *      be a muliple of 3
092   */
093  public void setBuffer(int[] buffer) {
094    // ensure buffer is proper length
095    if (VM.VerifyAssertions) {
096      VM._assert(buffer.length % 3 == 0);
097    }
098
099    if (DEBUG) {
100      VM.sysWrite("EdgeListener.setBuffer(", buffer.length, "): enter\n");
101    }
102
103    this.buffer = buffer;
104    desiredSamples = buffer.length / 3;
105    resetBuffer();
106  }
107
108  /**
109   * This method is called when a call stack edge needs to be
110   * sampled.  Expect the sfp argument to point to the stack frame that
111   * contains the target of the edge to be sampled.
112   * <p>
113   * NOTE: This method is uninterruptible, therefore we don't need to disable
114   *       thread switching during stackframe inspection.
115   *
116   * @param sfp  a pointer to the stack frame that corresponds to the callee of
117   *             the call graph edge that is to be sampled.
118   * @param whereFrom Was this a yieldpoint in a PROLOGUE, BACKEDGE, or
119   *         EPILOGUE?
120   */
121  @Override
122  public final void update(Address sfp, int whereFrom) {
123    if (DEBUG) {
124      VM.sysWrite("EdgeListener.update(", sfp, ",", whereFrom);
125      VM.sysWriteln("): enter ", samplesTaken);
126    }
127
128    Synchronization.fetchAndAdd(this, AosEntrypoints.edgeListenerUpdateCalledField.getOffset(), 1);
129
130    // don't take a sample for back edge yield points
131    if (whereFrom == RVMThread.BACKEDGE) return;
132
133    int calleeCMID = 0;
134    int callerCMID = 0;
135    Address returnAddress = Address.zero();
136
137    if (sfp.loadAddress().EQ(StackFrameLayout.getStackFrameSentinelFP())) {
138      if (DEBUG) VM.sysWrite(" Walking off end of stack!\n");
139      return;
140    }
141
142    calleeCMID = Magic.getCompiledMethodID(sfp);
143    if (calleeCMID == StackFrameLayout.getInvisibleMethodID()) {
144      if (DEBUG) {
145        VM.sysWrite(" INVISIBLE_METHOD_ID  (assembler code) ");
146        VM.sysWrite(calleeCMID);
147        VM.sysWrite("\n");
148      }
149      return;
150    }
151
152    returnAddress = Magic.getReturnAddress(sfp); // return address in caller
153    sfp = Magic.getCallerFramePointer(sfp);      // caller's frame pointer
154    if (sfp.loadAddress().EQ(StackFrameLayout.getStackFrameSentinelFP())) {
155      if (DEBUG) VM.sysWrite(" Walking off end of stack\n");
156      return;
157    }
158    callerCMID = Magic.getCompiledMethodID(sfp);
159    if (callerCMID == StackFrameLayout.getInvisibleMethodID()) {
160      if (DEBUG) {
161        VM.sysWrite(" INVISIBLE_METHOD_ID  (assembler code) ");
162        VM.sysWrite(callerCMID);
163        VM.sysWrite("\n");
164      }
165      return;
166    }
167
168    // store the offset of the return address from the beginning of the
169    // instruction
170    CompiledMethod callerCM = CompiledMethods.getCompiledMethod(callerCMID);
171    if (callerCM.getCompilerType() == CompiledMethod.TRAP) {
172      if (DEBUG) {
173        VM.sysWriteln(" HARDWARE TRAP FRAME ");
174      }
175      return;
176    }
177    Offset callSite = callerCM.getInstructionOffset(returnAddress);
178
179    if (DEBUG) {
180      VM.sysWrite("  <");
181      VM.sysWrite(calleeCMID);
182      VM.sysWrite(",");
183      VM.sysWrite(callerCMID);
184      VM.sysWrite(",");
185      VM.sysWrite(returnAddress);
186      VM.sysWrite(">\n");
187    }
188
189    // Find out what sample we are.
190    int sampleNumber =
191        Synchronization.fetchAndAdd(this, AosEntrypoints.edgeListenerSamplesTakenField.getOffset(), 1);
192    int idx = 3 * sampleNumber;
193
194    // If we got buffer slots that are beyond the end of the buffer, that means
195    // that we're actually not supposed to take the sample at all (the system
196    // is in the process of activating our organizer and processing the buffer).
197    if (idx < buffer.length) {
198      buffer[idx + 1] = callerCMID;
199      buffer[idx + 2] = callSite.toInt();
200      Magic.sync();
201      buffer[idx + 0] = calleeCMID;
202
203      // If we are the last sample, we need to activate the organizer.
204      if (sampleNumber + 1 == desiredSamples) {
205        activateOrganizer();
206      }
207    }
208  }
209
210  /**
211   *  No-op.
212   */
213  @Override
214  public final void report() {}
215
216  @Override
217  public void reset() {
218    if (DEBUG) VM.sysWrite("EdgeListener.reset(): enter\n");
219    samplesTaken = 0;
220    updateCalled = 0;
221    resetBuffer();
222  }
223
224  /**
225   *  Reset the buffer
226   */
227  private void resetBuffer() {
228    for (int i = 0; i < buffer.length; i++) {
229      buffer[i] = 0;
230    }
231  }
232}