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.
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.
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.
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 tigger 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