/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/cpl1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.adaptive.measurements; import java.util.Vector; import org.jikesrvm.ArchitectureSpecific.VM_StackframeLayoutConstants; import org.jikesrvm.adaptive.controller.VM_Controller; import org.jikesrvm.adaptive.measurements.listeners.VM_ContextListener; import org.jikesrvm.adaptive.measurements.listeners.VM_MethodListener; import org.jikesrvm.adaptive.measurements.listeners.VM_NullListener; import org.jikesrvm.adaptive.util.VM_AOSLogging; import org.jikesrvm.compilers.common.VM_CompiledMethod; import org.jikesrvm.compilers.common.VM_CompiledMethods; import org.jikesrvm.runtime.VM_Magic; import org.jikesrvm.scheduler.VM_Scheduler; import org.jikesrvm.scheduler.VM_Thread; import org.vmmagic.pragma.Uninterruptible; import org.vmmagic.unboxed.LocalAddress; /** * RuntimeMeasurements manages listeners, decayable objects, and * reportable objects. * * A listener is installed by an organizer, and activated at thread * switch time by VM_Thread. Depending on the update method that the * listener supports, it can be either a method, context, or a null * listener. Currently we have different registries for different * listeners. An alternative design is to have one register with where * entries are tagged. * * A decayable object implements the VM_Decayable interface. * Anyone can register a decayable object, * The VM_DecayOrganizer periodically decays all objects that have * been registers. * * A reportable object implements the Reportable interface, and * is typically registered and used by the instrumentation subsystem. * A Reporable can be reset and reported. */ public abstract class VM_RuntimeMeasurements { ///////////////////////////////////////////////////////////////////////// // Support for gathering profile data on timer ticks ///////////////////////////////////////////////////////////////////////// /** * listeners on timer ticks for methods */ private static VM_MethodListener[] timerMethodListeners = new VM_MethodListener[0]; /** * listeners on timer ticks for contexts */ private static VM_ContextListener[] timerContextListeners = new VM_ContextListener[0]; /** * listeners on timer ticks for nulls */ private static VM_NullListener[] timerNullListeners = new VM_NullListener[0]; /** * Install a method listener on timer ticks * @param s method listener to be installed */ public static synchronized void installTimerMethodListener(VM_MethodListener s) { int numListeners = timerMethodListeners.length; VM_MethodListener[] tmp = new VM_MethodListener[numListeners + 1]; for (int i = 0; i < numListeners; i++) { tmp[i] = timerMethodListeners[i]; } tmp[numListeners] = s; timerMethodListeners = tmp; } /** * Install a context listener on timer ticks * @param s context listener to be installed */ public static synchronized void installTimerContextListener(VM_ContextListener s) { int numListeners = timerContextListeners.length; VM_ContextListener[] tmp = new VM_ContextListener[numListeners + 1]; for (int i = 0; i < numListeners; i++) { tmp[i] = timerContextListeners[i]; } tmp[numListeners] = s; timerContextListeners = tmp; } /** * Install a null listener on timer ticks * @param s null listener to be installed */ public static synchronized void installTimerNullListener(VM_NullListener s) { int numListeners = timerNullListeners.length; VM_NullListener[] tmp = new VM_NullListener[numListeners + 1]; for (int i = 0; i < numListeners; i++) { tmp[i] = timerNullListeners[i]; } tmp[numListeners] = s; timerNullListeners = tmp; } /** * Called from VM_Thread.yieldpoint every time it is invoked due to * a timer interrupt. */ @Uninterruptible public static void takeTimerSample(int whereFrom, LocalAddress yieldpointServiceMethodFP) { // We use threadswitches as a rough approximation of time. // Every threadswitch is a clock tick. // TODO: kill controller clock in favor of VM_Processor.reportedTimerTicks VM_Controller.controllerClock++; // // "The idle thread is boring, and does not deserve to be sampled" // -- AOS Commandment Number 1 if (!VM_Scheduler.getCurrentThread().isIdleThread()) { LocalAddress ypTakenInFP = VM_Magic.getCallerFramePointer(yieldpointServiceMethodFP); // method that took yieldpoint // Get the cmid for the method in which the yieldpoint was taken. int ypTakenInCMID = VM_Magic.getCompiledMethodID(ypTakenInFP); // Get the cmid for that method's caller. LocalAddress ypTakenInCallerFP = VM_Magic.getCallerFramePointer(ypTakenInFP); int ypTakenInCallerCMID = VM_Magic.getCompiledMethodID(ypTakenInCallerFP); // Determine if ypTakenInCallerCMID corresponds to a real Java stackframe. // If one of the following conditions is detected, set ypTakenInCallerCMID to -1 // Caller is out-of-line assembly (no VM_Method object) or top-of-stack psuedo-frame // Caller is a native method VM_CompiledMethod ypTakenInCM = VM_CompiledMethods.getCompiledMethod(ypTakenInCMID); if (ypTakenInCallerCMID == VM_StackframeLayoutConstants.INVISIBLE_METHOD_ID || ypTakenInCM.getMethod().getDeclaringClass().hasBridgeFromNativeAnnotation()) { ypTakenInCallerCMID = -1; } // Notify all registered listeners for (VM_NullListener aNl : timerNullListeners) { if (aNl.isActive()) { aNl.update(whereFrom); } } for (VM_MethodListener aMl : timerMethodListeners) { if (aMl.isActive()) { aMl.update(ypTakenInCMID, ypTakenInCallerCMID, whereFrom); } } if (ypTakenInCallerCMID != -1) { for (VM_ContextListener aCl : timerContextListeners) { if (aCl.isActive()) { aCl.update(ypTakenInFP, whereFrom); } } } } } ///////////////////////////////////////////////////////////////////////// // Support for gathering profile data on CBS samples ///////////////////////////////////////////////////////////////////////// /** * method listeners that trigger on CBS Method yieldpoints */ private static VM_MethodListener[] cbsMethodListeners = new VM_MethodListener[0]; /** * context listeners that tigger on CBS call yieldpoints */ private static VM_ContextListener[] cbsContextListeners = new VM_ContextListener[0]; /** * Install a method listener on cbs ticks * @param s method listener to be installed */ public static synchronized void installCBSMethodListener(VM_MethodListener s) { int numListeners = cbsMethodListeners.length; VM_MethodListener[] tmp = new VM_MethodListener[numListeners + 1]; for (int i = 0; i < numListeners; i++) { tmp[i] = cbsMethodListeners[i]; } tmp[numListeners] = s; cbsMethodListeners = tmp; } /** * Install a context listener on cbs ticks * @param s context listener to be installed */ public static synchronized void installCBSContextListener(VM_ContextListener s) { int numListeners = cbsContextListeners.length; VM_ContextListener[] tmp = new VM_ContextListener[numListeners + 1]; for (int i = 0; i < numListeners; i++) { tmp[i] = cbsContextListeners[i]; } tmp[numListeners] = s; cbsContextListeners = tmp; } /** * Called from VM_Thread.yieldpoint when it is time to take a CBS method sample. */ @Uninterruptible public static void takeCBSMethodSample(int whereFrom, LocalAddress yieldpointServiceMethodFP) { // // "The idle thread is boring, and does not deserve to be sampled" // -- AOS Commandment Number 1 if (!VM_Scheduler.getCurrentThread().isIdleThread()) { LocalAddress ypTakenInFP = VM_Magic.getCallerFramePointer(yieldpointServiceMethodFP); // method that took yieldpoint // Get the cmid for the method in which the yieldpoint was taken. int ypTakenInCMID = VM_Magic.getCompiledMethodID(ypTakenInFP); // Get the cmid for that method's caller. LocalAddress ypTakenInCallerFP = VM_Magic.getCallerFramePointer(ypTakenInFP); int ypTakenInCallerCMID = VM_Magic.getCompiledMethodID(ypTakenInCallerFP); // Determine if ypTakenInCallerCMID corresponds to a real Java stackframe. // If one of the following conditions is detected, set ypTakenInCallerCMID to -1 // Caller is out-of-line assembly (no VM_Method object) or top-of-stack psuedo-frame // Caller is a native method VM_CompiledMethod ypTakenInCM = VM_CompiledMethods.getCompiledMethod(ypTakenInCMID); if (ypTakenInCallerCMID == VM_StackframeLayoutConstants.INVISIBLE_METHOD_ID || ypTakenInCM.getMethod().getDeclaringClass().hasBridgeFromNativeAnnotation()) { ypTakenInCallerCMID = -1; } // Notify all registered listeners for (VM_MethodListener methodListener : cbsMethodListeners) { if (methodListener.isActive()) { methodListener.update(ypTakenInCMID, ypTakenInCallerCMID, whereFrom); } } } } /** * Called from VM_Thread.yieldpoint when it is time to take a CBS call sample. */ @Uninterruptible public static void takeCBSCallSample(int whereFrom, LocalAddress yieldpointServiceMethodFP) { // // "The idle thread is boring, and does not deserve to be sampled" // -- AOS Commandment Number 1 if (!VM_Scheduler.getCurrentThread().isIdleThread()) { LocalAddress ypTakenInFP = VM_Magic.getCallerFramePointer(yieldpointServiceMethodFP); // method that took yieldpoint // Get the cmid for the method in which the yieldpoint was taken. int ypTakenInCMID = VM_Magic.getCompiledMethodID(ypTakenInFP); // Get the cmid for that method's caller. LocalAddress ypTakenInCallerFP = VM_Magic.getCallerFramePointer(ypTakenInFP); int ypTakenInCallerCMID = VM_Magic.getCompiledMethodID(ypTakenInCallerFP); // Determine if ypTakenInCallerCMID corresponds to a real Java stackframe. // If one of the following conditions is detected, set ypTakenInCallerCMID to -1 // Caller is out-of-line assembly (no VM_Method object) or top-of-stack psuedo-frame // Caller is a native method VM_CompiledMethod ypTakenInCM = VM_CompiledMethods.getCompiledMethod(ypTakenInCMID); if (ypTakenInCallerCMID == VM_StackframeLayoutConstants.INVISIBLE_METHOD_ID || ypTakenInCM.getMethod().getDeclaringClass().hasBridgeFromNativeAnnotation()) { // drop sample } else { // Notify all registered listeners for (VM_ContextListener listener : cbsContextListeners) { if (listener.isActive()) { listener.update(ypTakenInFP, whereFrom); } } } } } ///////////////////////////////////////////////////////////////////////// // Support for decay ///////////////////////////////////////////////////////////////////////// /** * The currently registered decayable objects */ static Vector<VM_Decayable> decayObjects = new Vector<VM_Decayable>(); /** * Counts the number of decay events */ static int decayEventCounter = 0; /** * Register an object that should be decayed. * The passed object will have its decay method called when the * decaying thread decides it is time for the system to decay. */ public static void registerDecayableObject(VM_Decayable obj) { decayObjects.add(obj); } /** * Decay all registered decayable objects. */ public static void decayDecayableObjects() { decayEventCounter++; VM_AOSLogging.decayingCounters(); for (VM_Decayable obj : decayObjects) { obj.decay(); } } ///////////////////////////////////////////////////////////////////////// // Support for reportable objects ///////////////////////////////////////////////////////////////////////// /** * The currently registered reportable objects */ static Vector<VM_Reportable> reportObjects = new Vector<VM_Reportable>(); /** * Register an object that wants to have its report method called * whenever VM_RuntimeMeasurements.report is called */ public static void registerReportableObject(VM_Reportable obj) { reportObjects.add(obj); } /** * Reset to all registered reportable objects */ public static void resetReportableObjects() { for (VM_Reportable obj : reportObjects) { obj.reset(); } } /** * Report to all registered reportable objects */ private static void reportReportableObjects() { for (VM_Reportable obj : reportObjects) { obj.report(); } } /** * Report the current state of runtime measurements */ public static void report() { reportReportableObjects(); VM_AOSLogging.decayStatistics(decayEventCounter); for (int i = 0, n = VM_Scheduler.threads.length; i < n; i++) { VM_Thread t = VM_Scheduler.threads[i]; if (t != null) { VM_AOSLogging.threadExiting(t); } } } /** * Stop the runtime measurement subsystem */ public static synchronized void stop() { timerMethodListeners = new VM_MethodListener[0]; timerContextListeners = new VM_ContextListener[0]; timerNullListeners = new VM_NullListener[0]; cbsMethodListeners = new VM_MethodListener[0]; cbsContextListeners = new VM_ContextListener[0]; } /** * Called from VM_Thread.terminate. */ public static void monitorThreadExit() { VM_AOSLogging.threadExiting(VM_Scheduler.getCurrentThread()); } /** * Called when the VM is booting */ public static void boot() { } }