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;
014
015import java.util.Vector;
016
017import org.jikesrvm.adaptive.controller.Controller;
018import org.jikesrvm.adaptive.measurements.listeners.ContextListener;
019import org.jikesrvm.adaptive.measurements.listeners.MethodListener;
020import org.jikesrvm.adaptive.measurements.listeners.NullListener;
021import org.jikesrvm.adaptive.util.AOSLogging;
022import org.jikesrvm.architecture.StackFrameLayout;
023import org.jikesrvm.compilers.common.CompiledMethod;
024import org.jikesrvm.compilers.common.CompiledMethods;
025import org.jikesrvm.runtime.Magic;
026import org.vmmagic.pragma.Uninterruptible;
027import org.vmmagic.unboxed.Address;
028
029/**
030 * RuntimeMeasurements manages listeners, decayable objects, and
031 * reportable objects.<p>
032 *
033 * A listener is installed by an organizer, and activated at thread
034 * switch time by Thread.  Depending on the update method that the
035 * listener supports, it can be either a method, context, or a null
036 * listener.  Currently we have different registries for different
037 * listeners.  An alternative design is to have one register with where
038 * entries are tagged.<p>
039 *
040 * A decayable object implements the Decayable interface.
041 * Anyone can register a decayable object,
042 * The DecayOrganizer periodically decays all objects that have
043 * been registers.<p>
044 *
045 * A reportable object implements the Reportable interface, and
046 * is typically registered and used by the instrumentation subsystem.
047 * A Reporable can be reset and reported.
048 */
049public abstract class RuntimeMeasurements {
050
051  /////////////////////////////////////////////////////////////////////////
052  // Support for gathering profile data on timer ticks
053  /////////////////////////////////////////////////////////////////////////
054
055  /**
056   * listeners on timer ticks for methods
057   */
058  private static MethodListener[] timerMethodListeners = new MethodListener[0];
059
060  /**
061   * listeners on timer ticks for contexts
062   */
063  private static ContextListener[] timerContextListeners = new ContextListener[0];
064
065  /**
066   * listeners on timer ticks for nulls
067   */
068  private static NullListener[] timerNullListeners = new NullListener[0];
069
070  /**
071   * Install a method listener on timer ticks
072   * @param s method listener to be installed
073   */
074  public static synchronized void installTimerMethodListener(MethodListener s) {
075    int numListeners = timerMethodListeners.length;
076    MethodListener[] tmp = new MethodListener[numListeners + 1];
077    for (int i = 0; i < numListeners; i++) {
078      tmp[i] = timerMethodListeners[i];
079    }
080    tmp[numListeners] = s;
081    timerMethodListeners = tmp;
082  }
083
084  /**
085   * Install a context listener on timer ticks
086   * @param s context listener to be installed
087   */
088  public static synchronized void installTimerContextListener(ContextListener s) {
089    int numListeners = timerContextListeners.length;
090    ContextListener[] tmp = new ContextListener[numListeners + 1];
091    for (int i = 0; i < numListeners; i++) {
092      tmp[i] = timerContextListeners[i];
093    }
094    tmp[numListeners] = s;
095    timerContextListeners = tmp;
096  }
097
098  /**
099   * Install a null listener on timer ticks
100   * @param s null listener to be installed
101   */
102  public static synchronized void installTimerNullListener(NullListener s) {
103    int numListeners = timerNullListeners.length;
104    NullListener[] tmp = new NullListener[numListeners + 1];
105    for (int i = 0; i < numListeners; i++) {
106      tmp[i] = timerNullListeners[i];
107    }
108    tmp[numListeners] = s;
109    timerNullListeners = tmp;
110  }
111
112  /**
113   * Called from Thread.yieldpoint every time it is invoked due to
114   * a timer interrupt.
115   *
116   * @param whereFrom source of the yieldpoint (e.g. backedge)
117   * @param yieldpointServiceMethodFP the frame pointer of the service
118   *  method that is responsible for handling the yieldpoint
119   */
120  @Uninterruptible
121  public static void takeTimerSample(int whereFrom, Address yieldpointServiceMethodFP) {
122    // We use timer ticks as a rough approximation of time.
123    // TODO: kill controller clock in favor of reportedTimerTicks
124    // PNT: huh?
125    Controller.controllerClock++;
126
127    Address ypTakenInFP = Magic.getCallerFramePointer(yieldpointServiceMethodFP); // method that took yieldpoint
128
129    // Get the cmid for the method in which the yieldpoint was taken.
130    int ypTakenInCMID = Magic.getCompiledMethodID(ypTakenInFP);
131
132    // Get the cmid for that method's caller.
133    Address ypTakenInCallerFP = Magic.getCallerFramePointer(ypTakenInFP);
134    int ypTakenInCallerCMID = Magic.getCompiledMethodID(ypTakenInCallerFP);
135
136    // Determine if ypTakenInCallerCMID corresponds to a real Java stackframe.
137    // If one of the following conditions is detected, set ypTakenInCallerCMID to -1
138    //    Caller is out-of-line assembly (no RVMMethod object) or top-of-stack psuedo-frame
139    //    Caller is a native method
140    CompiledMethod ypTakenInCM = CompiledMethods.getCompiledMethod(ypTakenInCMID);
141    if (ypTakenInCallerCMID == StackFrameLayout.getInvisibleMethodID() ||
142        ypTakenInCM.getMethod().getDeclaringClass().hasBridgeFromNativeAnnotation()) {
143      ypTakenInCallerCMID = -1;
144    }
145
146    // Notify all registered listeners
147    for (NullListener aNl : timerNullListeners) {
148      if (aNl.isActive()) {
149        aNl.update(whereFrom);
150      }
151    }
152    for (MethodListener aMl : timerMethodListeners) {
153      if (aMl.isActive()) {
154        aMl.update(ypTakenInCMID, ypTakenInCallerCMID, whereFrom);
155      }
156    }
157    if (ypTakenInCallerCMID != -1) {
158      for (ContextListener aCl : timerContextListeners) {
159        if (aCl.isActive()) {
160          aCl.update(ypTakenInFP, whereFrom);
161        }
162      }
163    }
164  }
165
166  /////////////////////////////////////////////////////////////////////////
167  // Support for gathering profile data on CBS samples
168  /////////////////////////////////////////////////////////////////////////
169
170  /**
171   * method listeners that trigger on CBS Method yieldpoints
172   */
173  private static MethodListener[] cbsMethodListeners = new MethodListener[0];
174
175  /**
176   * context listeners that trigger on CBS call yieldpoints
177   */
178  private static ContextListener[] cbsContextListeners = new ContextListener[0];
179
180  /**
181   * Install a method listener on CBS ticks
182   * @param s method listener to be installed
183   */
184  public static synchronized void installCBSMethodListener(MethodListener s) {
185    int numListeners = cbsMethodListeners.length;
186    MethodListener[] tmp = new MethodListener[numListeners + 1];
187    for (int i = 0; i < numListeners; i++) {
188      tmp[i] = cbsMethodListeners[i];
189    }
190    tmp[numListeners] = s;
191    cbsMethodListeners = tmp;
192  }
193
194  /**
195   * Install a context listener on CBS ticks
196   * @param s context listener to be installed
197   */
198  public static synchronized void installCBSContextListener(ContextListener s) {
199    int numListeners = cbsContextListeners.length;
200    ContextListener[] tmp = new ContextListener[numListeners + 1];
201    for (int i = 0; i < numListeners; i++) {
202      tmp[i] = cbsContextListeners[i];
203    }
204    tmp[numListeners] = s;
205    cbsContextListeners = tmp;
206  }
207
208  /**
209   * Called from Thread.yieldpoint when it is time to take a CBS method sample.
210   *
211   * @param whereFrom source of the yieldpoint (e.g. backedge)
212   * @param yieldpointServiceMethodFP the frame pointer of the service
213   *  method that is responsible for handling the yieldpoint
214   */
215  @Uninterruptible
216  public static void takeCBSMethodSample(int whereFrom, Address yieldpointServiceMethodFP) {
217    Address ypTakenInFP = Magic.getCallerFramePointer(yieldpointServiceMethodFP); // method that took yieldpoint
218
219    // Get the cmid for the method in which the yieldpoint was taken.
220    int ypTakenInCMID = Magic.getCompiledMethodID(ypTakenInFP);
221
222    // Get the cmid for that method's caller.
223    Address ypTakenInCallerFP = Magic.getCallerFramePointer(ypTakenInFP);
224    int ypTakenInCallerCMID = Magic.getCompiledMethodID(ypTakenInCallerFP);
225
226    // Determine if ypTakenInCallerCMID corresponds to a real Java stackframe.
227    // If one of the following conditions is detected, set ypTakenInCallerCMID to -1
228    //    Caller is out-of-line assembly (no RVMMethod object) or top-of-stack psuedo-frame
229    //    Caller is a native method
230    CompiledMethod ypTakenInCM = CompiledMethods.getCompiledMethod(ypTakenInCMID);
231    if (ypTakenInCallerCMID == StackFrameLayout.getInvisibleMethodID() ||
232        ypTakenInCM.getMethod().getDeclaringClass().hasBridgeFromNativeAnnotation()) {
233      ypTakenInCallerCMID = -1;
234    }
235
236    // Notify all registered listeners
237    for (MethodListener methodListener : cbsMethodListeners) {
238      if (methodListener.isActive()) {
239        methodListener.update(ypTakenInCMID, ypTakenInCallerCMID, whereFrom);
240      }
241    }
242  }
243
244  /**
245   * Called from Thread.yieldpoint when it is time to take a CBS call sample.
246   *
247   * @param whereFrom source of the yieldpoint (e.g. backedge)
248   * @param yieldpointServiceMethodFP the frame pointer of the service
249   *  method that is responsible for handling the yieldpoint
250   */
251  @Uninterruptible
252  public static void takeCBSCallSample(int whereFrom, Address yieldpointServiceMethodFP) {
253    Address ypTakenInFP = Magic.getCallerFramePointer(yieldpointServiceMethodFP); // method that took yieldpoint
254
255    // Get the cmid for the method in which the yieldpoint was taken.
256    int ypTakenInCMID = Magic.getCompiledMethodID(ypTakenInFP);
257
258    // Get the cmid for that method's caller.
259    Address ypTakenInCallerFP = Magic.getCallerFramePointer(ypTakenInFP);
260    int ypTakenInCallerCMID = Magic.getCompiledMethodID(ypTakenInCallerFP);
261
262    // Determine if ypTakenInCallerCMID corresponds to a real Java stackframe.
263    // If one of the following conditions is detected, set ypTakenInCallerCMID to -1
264    //    Caller is out-of-line assembly (no RVMMethod object) or top-of-stack psuedo-frame
265    //    Caller is a native method
266    CompiledMethod ypTakenInCM = CompiledMethods.getCompiledMethod(ypTakenInCMID);
267    if (ypTakenInCallerCMID == StackFrameLayout.getInvisibleMethodID() ||
268        ypTakenInCM.getMethod().getDeclaringClass().hasBridgeFromNativeAnnotation()) {
269      // drop sample
270    } else {
271      // Notify all registered listeners
272      for (ContextListener listener : cbsContextListeners) {
273        if (listener.isActive()) {
274          listener.update(ypTakenInFP, whereFrom);
275        }
276      }
277    }
278  }
279
280  /////////////////////////////////////////////////////////////////////////
281  // Support for decay
282  /////////////////////////////////////////////////////////////////////////
283
284  /**
285   * The currently registered decayable objects
286   */
287  static final Vector<Decayable> decayObjects = new Vector<Decayable>();
288
289  /**
290   * Counts the number of decay events
291   */
292  static int decayEventCounter = 0;
293
294  /**
295   *  Registers an object that should be decayed.
296   *  The passed object will have its decay method called when the
297   *  decaying thread decides it is time for the system to decay.
298   *
299   *  @param obj the object to decay
300   */
301  public static void registerDecayableObject(Decayable obj) {
302    decayObjects.add(obj);
303  }
304
305  /**
306   * Decays all registered decayable objects.
307   */
308  public static void decayDecayableObjects() {
309    decayEventCounter++;
310    AOSLogging.logger.decayingCounters();
311
312    for (Decayable obj : decayObjects) {
313      obj.decay();
314    }
315  }
316
317  /////////////////////////////////////////////////////////////////////////
318  // Support for reportable objects
319  /////////////////////////////////////////////////////////////////////////
320
321  /**
322   * The currently registered reportable objects
323   */
324  static Vector<Reportable> reportObjects = new Vector<Reportable>();
325
326  /**
327   * Registers an object that wants to have its report method called
328   * whenever RuntimeMeasurements.report is called.
329   *
330   * @param obj the object to report about
331   */
332  public static void registerReportableObject(Reportable obj) {
333    reportObjects.add(obj);
334  }
335
336  /**
337   * Calls {@link Reportable#reset()} on all registered reportable
338   * objects.
339   */
340  public static void resetReportableObjects() {
341    for (Reportable obj : reportObjects) {
342      obj.reset();
343    }
344  }
345
346  /**
347   * Calls {@link Reportable#report()} on all registered reportable
348   * objects.
349   */
350  private static void reportReportableObjects() {
351    for (Reportable obj : reportObjects) {
352      obj.report();
353    }
354  }
355
356  /**
357   * Reports the current state of runtime measurements.
358   */
359  public static void report() {
360    reportReportableObjects();
361
362    AOSLogging.logger.decayStatistics(decayEventCounter);
363  }
364
365  /**
366   * Stop the runtime measurement subsystem
367   */
368  public static synchronized void stop() {
369    timerMethodListeners = new MethodListener[0];
370    timerContextListeners = new ContextListener[0];
371    timerNullListeners = new NullListener[0];
372
373    cbsMethodListeners = new MethodListener[0];
374    cbsContextListeners = new ContextListener[0];
375  }
376
377  /**
378   * Called when the VM is booting
379   */
380  public static void boot() { }
381}
382