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