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