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.runtime;
014
015import java.util.Enumeration;
016
017import org.jikesrvm.VM;
018import org.jikesrvm.classloader.Atom;
019import org.jikesrvm.classloader.RVMClass;
020import org.jikesrvm.classloader.RVMMethod;
021import org.jikesrvm.classloader.RVMType;
022import org.jikesrvm.scheduler.RVMThread;
023
024/**
025 * A class for managing various callbacks from the VM.
026 *
027 * <p>Consumers should register an implementation of the needed interface with
028 * a given callback method, and will get notified when the event happens.
029 *
030 * <p>Note: callback consumers should not rely on any particular order of
031 * callback invocation.
032 *
033 * <p>TODO: allow limited control over callback order.
034 *
035 * <p>
036 * The following events are currently implemented.  See code for exact
037 * invocation syntax.
038 * <ul>
039 * <li> ClassLoaded       - called after a RVMClass is loaded
040 * <li> ClassResolved     - called after a RVMClass is resolved
041 * <li> ClassInstantiated - called after a RVMClass is instantiated
042 * <li> ClassInitialized  - called after a RVMClass is initialized
043 * <li> MethodOverride    - called when a method in a newly loaded class
044 *                          overrides a method in an existing class
045 * <li> MethodCompile     - called before a method is compiled
046 * <li> ForName           - called when java.lang.Class.forName() is invoked
047 * <li> BootImageWriting  - called when boot image writing is started
048 * <li> Startup           - called when the VM has completed booting
049 * <li> Exit              - called when the VM is about to exit  (note: this is very fragile; TODO: remove???)
050 * <li> AppStart          - called before the application starts executing
051 *                          all runs -- needs application support)
052 * <li> AppComplete       - called after the application completes executing
053 *                          all runs --- needs application support)
054 * <li> AppRunStart       - called before the application starts a run
055 *                          (many applications have several runs -- needs
056 *                          application support)
057 * <li> AppRunComplete    - called after the application completes a run
058 *                          (many applications have several runs --- needs
059 *                          application support)
060 * <li> RecompileAllDynamicallyLoadedMethods - called when the application
061 *                          wants to recompile all methods that were previously
062 *                          dynamically compiled.  Could be useful for
063 *                          studying the the impact of how much of
064 *                          class hierarchy being loaded effects compilation
065 *                          performance
066 *                          (application must call this explicitly for anything
067 *                           to happen)
068 * </ul>
069 */
070public final class Callbacks {
071  ///////////////
072  // INTERFACE //
073  ///////////////
074
075  /**
076   * Interface for monitoring class loading.
077   */
078  public interface ClassLoadedMonitor {
079    /**
080     * Notify the monitor that a class has been loaded.
081     * @param klass the class that was loaded
082     */
083    void notifyClassLoaded(RVMClass klass);
084  }
085
086  /**
087   * Class loading callback list.
088   */
089  private static CallbackList classLoadedCallbacks = null;
090  private static final Object classLoadedLock = new Object();
091  private static boolean classLoadedEnabled = true;
092
093  /**
094   * Register a callback for class loading.
095   * @param cb the object to notify when event happens
096   */
097  public static void addClassLoadedMonitor(ClassLoadedMonitor cb) {
098    synchronized (classLoadedLock) {
099      if (TRACE_ADDMONITOR || TRACE_CLASSLOADED) {
100        VM.sysWrite("adding class loaded monitor: ");
101        VM.sysWrite(getClass(cb));
102        VM.sysWrite("\n");
103      }
104      classLoadedCallbacks = new CallbackList(cb, classLoadedCallbacks);
105    }
106  }
107
108  /**
109   * Notify the callback manager that a class has been loaded.
110   * @param klass the class that was loaded
111   */
112  public static void notifyClassLoaded(RVMClass klass) {
113    // NOTE: will need synchronization if allowing unregistering
114    if (!classLoadedEnabled) return;
115    classLoadedEnabled = false;
116    if (TRACE_CLASSLOADED) {
117      //VM.sysWrite(getThread(), false);
118      //VM.sysWrite(": ");
119      VM.sysWrite("invoking class loaded monitors: ");
120      VM.sysWrite(klass.getDescriptor());
121      VM.sysWrite("\n");
122      //printStack("From: ");
123    }
124    for (CallbackList l = classLoadedCallbacks; l != null; l = l.next) {
125      if (TRACE_CLASSLOADED) {
126        VM.sysWrite("    ");
127        VM.sysWrite(getClass(l.callback));
128        VM.sysWrite("\n");
129      }
130      ((ClassLoadedMonitor) l.callback).notifyClassLoaded(klass);
131    }
132    classLoadedEnabled = true;
133  }
134
135  /**
136   * Interface for monitoring class resolution.
137   */
138  public interface ClassResolvedMonitor {
139    /**
140     * Notify the monitor that a class has been resolved.
141     * @param klass the class that was resolved
142     */
143    void notifyClassResolved(RVMClass klass);
144  }
145
146  /**
147   * Class resolution callback list.
148   */
149  private static CallbackList classResolvedCallbacks = null;
150  private static final Object classResolvedLock = new Object();
151  private static boolean classResolvedEnabled = true;
152
153  /**
154   * Register a callback for class resolution.
155   * @param cb the object to notify when event happens
156   */
157  public static void addClassResolvedMonitor(ClassResolvedMonitor cb) {
158    synchronized (classResolvedLock) {
159      if (TRACE_ADDMONITOR || TRACE_CLASSRESOLVED) {
160        VM.sysWrite("adding class resolved monitor: ");
161        VM.sysWrite(getClass(cb));
162        VM.sysWrite("\n");
163      }
164      classResolvedCallbacks = new CallbackList(cb, classResolvedCallbacks);
165    }
166  }
167
168  /**
169   * Notify the callback manager that a class has been resolved.
170   * @param klass the class that was resolved
171   */
172  public static void notifyClassResolved(RVMClass klass) {
173    // NOTE: will need synchronization if allowing unregistering
174    if (!classResolvedEnabled) return;
175    classResolvedEnabled = false;
176    if (TRACE_CLASSRESOLVED) {
177      //VM.sysWrite(getThread(), false);
178      //VM.sysWrite(": ");
179      VM.sysWrite("invoking class resolved monitors: ");
180      VM.sysWrite(klass.getDescriptor());
181      VM.sysWrite("\n");
182      //printStack("From: ");
183    }
184    for (CallbackList l = classResolvedCallbacks; l != null; l = l.next) {
185      if (TRACE_CLASSRESOLVED) {
186        VM.sysWrite("    ");
187        VM.sysWrite(getClass(l.callback));
188        VM.sysWrite("\n");
189      }
190      ((ClassResolvedMonitor) l.callback).notifyClassResolved(klass);
191    }
192    classResolvedEnabled = true;
193  }
194
195  /**
196   * Interface for monitoring class instantiation.
197   */
198  public interface ClassInstantiatedMonitor {
199    /**
200     * Notify the monitor that a class has been instantiated.
201     * @param klass the class that was instantiated
202     */
203    void notifyClassInstantiated(RVMClass klass);
204  }
205
206  /**
207   * Class instantiation callback list.
208   */
209  private static CallbackList classInstantiatedCallbacks = null;
210  private static final Object classInstantiatedLock = new Object();
211  private static boolean classInstantiatedEnabled = true;
212
213  /**
214   * Register a callback for class instantiation.
215   * @param cb the object to notify when event happens
216   */
217  public static void addClassInstantiatedMonitor(ClassInstantiatedMonitor cb) {
218    synchronized (classInstantiatedLock) {
219      if (TRACE_ADDMONITOR || TRACE_CLASSINSTANTIATED) {
220        VM.sysWrite("adding class instantiated monitor: ");
221        VM.sysWrite(getClass(cb));
222        VM.sysWrite("\n");
223      }
224      classInstantiatedCallbacks = new CallbackList(cb, classInstantiatedCallbacks);
225    }
226  }
227
228  /**
229   * Notify the callback manager that a class has been instantiated.
230   * @param klass the class that was instantiated
231   */
232  public static void notifyClassInstantiated(RVMClass klass) {
233    // NOTE: will need synchronization if allowing unregistering
234    if (!classInstantiatedEnabled) return;
235    classInstantiatedEnabled = false;
236    if (TRACE_CLASSINSTANTIATED) {
237      //VM.sysWrite(getThread(), false);
238      //VM.sysWrite(": ");
239      VM.sysWrite("invoking class instantiated monitors: ");
240      VM.sysWrite(klass.getDescriptor());
241      VM.sysWrite("\n");
242      //printStack("From: ");
243    }
244    for (CallbackList l = classInstantiatedCallbacks; l != null; l = l.next) {
245      if (TRACE_CLASSINSTANTIATED) {
246        VM.sysWrite("    ");
247        VM.sysWrite(getClass(l.callback));
248        VM.sysWrite("\n");
249      }
250      ((ClassInstantiatedMonitor) l.callback).notifyClassInstantiated(klass);
251    }
252    classInstantiatedEnabled = true;
253  }
254
255  /**
256   * Interface for monitoring class initialization.
257   */
258  public interface ClassInitializedMonitor {
259    /**
260     * Notify the monitor that a class has been initialized.
261     * @param klass the class that was initialized
262     */
263    void notifyClassInitialized(RVMClass klass);
264  }
265
266  /**
267   * Class initialization callback list.
268   */
269  private static CallbackList classInitializedCallbacks = null;
270  private static final Object classInitializedLock = new Object();
271  private static boolean classInitializedEnabled = true;
272
273  /**
274   * Register a callback for class initialization.
275   * @param cb the object to notify when event happens
276   */
277  public static void addClassInitializedMonitor(ClassInitializedMonitor cb) {
278    synchronized (classInitializedLock) {
279      if (TRACE_ADDMONITOR || TRACE_CLASSINITIALIZED) {
280        VM.sysWrite("adding class initialized monitor: ");
281        VM.sysWrite(getClass(cb));
282        VM.sysWrite("\n");
283      }
284      classInitializedCallbacks = new CallbackList(cb, classInitializedCallbacks);
285    }
286  }
287
288  /**
289   * Notify the callback manager that a class has been initialized.
290   * @param klass the class that was initialized
291   */
292  public static void notifyClassInitialized(RVMClass klass) {
293    // NOTE: will need synchronization if allowing unregistering
294    if (!classInitializedEnabled) return;
295    classInitializedEnabled = false;
296    if (TRACE_CLASSINITIALIZED) {
297      //VM.sysWrite(getThread(), false);
298      //VM.sysWrite(": ");
299      VM.sysWrite("invoking class initialized monitors: ");
300      VM.sysWrite(klass.getDescriptor());
301      VM.sysWrite("\n");
302      //printStack("From: ");
303    }
304    for (CallbackList l = classInitializedCallbacks; l != null; l = l.next) {
305      if (TRACE_CLASSINITIALIZED) {
306        VM.sysWrite("    ");
307        VM.sysWrite(getClass(l.callback));
308        VM.sysWrite("\n");
309      }
310      ((ClassInitializedMonitor) l.callback).notifyClassInitialized(klass);
311    }
312    classInitializedEnabled = true;
313  }
314
315  /**
316   * Interface for monitoring method override.
317   */
318  public interface MethodOverrideMonitor {
319    /**
320     * Notify the monitor that a method has been overridden.
321     * @param method the method that was loaded
322     * @param parent the method that it overrides (null if none)
323     */
324    void notifyMethodOverride(RVMMethod method, RVMMethod parent);
325  }
326
327  /**
328   * Method override callback list.
329   */
330  private static CallbackList methodOverrideCallbacks = null;
331  private static final Object methodOverrideLock = new Object();
332  private static boolean methodOverrideEnabled = true;
333
334  /**
335   * Register a callback for method override.
336   * @param cb the object to notify when event happens
337   */
338  public static void addMethodOverrideMonitor(MethodOverrideMonitor cb) {
339    synchronized (methodOverrideLock) {
340      if (TRACE_ADDMONITOR || TRACE_METHODOVERRIDE) {
341        VM.sysWrite("adding method override monitor: ");
342        VM.sysWrite(getClass(cb));
343        VM.sysWrite("\n");
344      }
345      methodOverrideCallbacks = new CallbackList(cb, methodOverrideCallbacks);
346    }
347  }
348
349  /**
350   * Notify the callback manager that a method has been overridden.
351   * @param method the method that was loaded
352   * @param parent the method that it overrides (null if none)
353   */
354  public static void notifyMethodOverride(RVMMethod method, RVMMethod parent) {
355    // NOTE: will need synchronization if allowing unregistering
356    if (!methodOverrideEnabled) return;
357    methodOverrideEnabled = false;
358    if (TRACE_METHODOVERRIDE) {
359      //VM.sysWrite(getThread(), false);
360      //VM.sysWrite(": ");
361      VM.sysWrite("invoking method override monitors: ");
362      VM.sysWrite(method);
363      VM.sysWrite(":");
364      if (parent != null) {
365        VM.sysWrite(parent);
366      } else {
367        VM.sysWrite("null");
368      }
369      VM.sysWrite("\n");
370      //printStack("From: ");
371    }
372    for (CallbackList l = methodOverrideCallbacks; l != null; l = l.next) {
373      if (TRACE_METHODOVERRIDE) {
374        VM.sysWrite("    ");
375        VM.sysWrite(getClass(l.callback));
376        VM.sysWrite("\n");
377      }
378      ((MethodOverrideMonitor) l.callback).notifyMethodOverride(method, parent);
379    }
380    methodOverrideEnabled = true;
381  }
382
383  /**
384   * Interface for monitoring method compile.
385   */
386  public interface MethodCompileMonitor {
387    /**
388     * Notify the monitor that a method is about to be compiled.
389     * NOTE: use VM.runningVM and VM.writingBootImage to determine
390     *       whether the VM is running
391     * @param method the method that will be compiled
392     * @param compiler the compiler that will be invoked.
393     *        Values are constants in CompiledMethod
394     */
395    void notifyMethodCompile(RVMMethod method, int compiler);
396  }
397
398  /**
399   * Method compile callback list.
400   */
401  private static CallbackList methodCompileCallbacks = null;
402  private static final Object methodCompileLock = new Object();
403  private static boolean methodCompileEnabled = true;
404
405  /**
406   * Register a callback for method compile.
407   * @param cb the object to notify when event happens
408   */
409  public static void addMethodCompileMonitor(MethodCompileMonitor cb) {
410    synchronized (methodCompileLock) {
411      if (TRACE_ADDMONITOR || TRACE_METHODCOMPILE) {
412        VM.sysWrite("adding method compile monitor: ");
413        VM.sysWrite(getClass(cb));
414        VM.sysWrite("\n");
415      }
416      methodCompileCallbacks = new CallbackList(cb, methodCompileCallbacks);
417    }
418  }
419
420  /**
421   * Notify the callback manager that a method is about to be compiled.
422   * NOTE: use VM.runningVM and VM.writingBootImage to determine
423   *       whether the VM is running
424   * @param method the method that will be compiled
425   * @param compiler the compiler that will be invoked
426   *        Values are constants in CompiledMethod
427   */
428  public static void notifyMethodCompile(RVMMethod method, int compiler) {
429    // NOTE: will need synchronization if allowing unregistering
430    if (!methodCompileEnabled) return;
431    methodCompileEnabled = false;
432    if (TRACE_METHODCOMPILE) {
433      //VM.sysWrite(getThread(), false);
434      //VM.sysWrite(": ");
435      VM.sysWrite("invoking method compile monitors: ");
436      VM.sysWrite(method);
437      VM.sysWrite(":");
438      VM.sysWrite(compiler);
439      VM.sysWrite("\n");
440      //printStack("From: ");
441    }
442    for (CallbackList l = methodCompileCallbacks; l != null; l = l.next) {
443      if (TRACE_METHODCOMPILE) {
444        VM.sysWrite("    ");
445        VM.sysWrite(getClass(l.callback));
446        VM.sysWrite("\n");
447      }
448      ((MethodCompileMonitor) l.callback).notifyMethodCompile(method, compiler);
449    }
450    methodCompileEnabled = true;
451  }
452
453  /**
454   * Interface for monitoring forName calls.
455   */
456  public interface ForNameMonitor {
457    /**
458     * Notify the monitor that java.lang.Class.forName was called.
459     * @param type the type that will be returned
460     */
461    void notifyForName(RVMType type);
462  }
463
464  /**
465   * forName call callback list.
466   */
467  private static CallbackList forNameCallbacks = null;
468  private static final Object forNameLock = new Object();
469  private static boolean forNameEnabled = true;
470
471  /**
472   * Register a callback for forName call.
473   * @param cb the object to notify when event happens
474   */
475  public static void addForNameMonitor(ForNameMonitor cb) {
476    synchronized (forNameLock) {
477      if (TRACE_ADDMONITOR || TRACE_FORNAME) {
478        VM.sysWrite("adding forName monitor: ");
479        VM.sysWrite(getClass(cb));
480        VM.sysWrite("\n");
481      }
482      forNameCallbacks = new CallbackList(cb, forNameCallbacks);
483    }
484  }
485
486  /**
487   * Notify the monitor that java.lang.Class.forName was called.
488   * @param type the type that will be returned
489   */
490  public static void notifyForName(RVMType type) {
491    // NOTE: will need synchronization if allowing unregistering
492    if (!forNameEnabled) return;
493    forNameEnabled = false;
494    if (TRACE_FORNAME) {
495      //VM.sysWrite(getThread(), false);
496      //VM.sysWrite(": ");
497      VM.sysWrite("invoking forName monitors: ");
498      VM.sysWrite(type.getDescriptor());
499      VM.sysWrite("\n");
500      //printStack("From: ");
501    }
502    for (CallbackList l = forNameCallbacks; l != null; l = l.next) {
503      if (TRACE_FORNAME) {
504        VM.sysWrite("    ");
505        VM.sysWrite(getClass(l.callback));
506        VM.sysWrite("\n");
507      }
508      ((ForNameMonitor) l.callback).notifyForName(type);
509    }
510    forNameEnabled = true;
511  }
512
513  /**
514   * Interface for monitoring defineClass calls.
515   */
516  public interface DefineClassMonitor {
517    /**
518     * Notify the monitor that java.lang.Class.defineclass was called.
519     * @param type the type that will be returned
520     */
521    void notifyDefineClass(RVMType type);
522  }
523
524  /**
525   * defineclass call callback list.
526   */
527  private static CallbackList defineClassCallbacks = null;
528  private static final Object defineClassLock = new Object();
529  private static boolean defineClassEnabled = true;
530
531  /**
532   * Register a callback for defineClass call.
533   * @param cb the object to notify when event happens
534   */
535  public static void addDefineClassMonitor(DefineClassMonitor cb) {
536    synchronized (defineClassLock) {
537      if (TRACE_ADDMONITOR || TRACE_DEFINECLASS) {
538        VM.sysWrite("adding defineclass monitor: ");
539        VM.sysWrite(getClass(cb));
540        VM.sysWrite("\n");
541      }
542      defineClassCallbacks = new CallbackList(cb, defineClassCallbacks);
543    }
544  }
545
546  /**
547   * Notify the monitor that java.lang.Class.defineclass was called.
548   * @param type the type that will be returned
549   */
550  public static void notifyDefineClass(RVMType type) {
551    // NOTE: will need synchronization if allowing unregistering
552    if (!defineClassEnabled) return;
553    defineClassEnabled = false;
554    if (TRACE_DEFINECLASS) {
555      //VM.sysWrite(getThread(), false);
556      //VM.sysWrite(": ");
557      VM.sysWrite("invoking defineclass monitors: ");
558      VM.sysWrite(type.getDescriptor());
559      VM.sysWrite("\n");
560      //printStack("From: ");
561    }
562    for (CallbackList l = defineClassCallbacks; l != null; l = l.next) {
563      if (TRACE_DEFINECLASS) {
564        VM.sysWrite("    ");
565        VM.sysWrite(getClass(l.callback));
566        VM.sysWrite("\n");
567      }
568      ((DefineClassMonitor) l.callback).notifyDefineClass(type);
569    }
570    defineClassEnabled = true;
571  }
572
573  /**
574   * Interface for monitoring loadClass calls.
575   */
576  public interface LoadClassMonitor {
577    /**
578     * Notify the monitor that java.lang.Class.loadclass was called.
579     * @param type the type that will be returned
580     */
581    void notifyLoadClass(RVMType type);
582  }
583
584  /**
585   * loadclass call callback list.
586   */
587  private static CallbackList loadClassCallbacks = null;
588  private static final Object loadClassLock = new Object();
589  private static boolean loadClassEnabled = true;
590
591  /**
592   * Register a callback for loadClass call.
593   * @param cb the object to notify when event happens
594   */
595  public static void addLoadClassMonitor(LoadClassMonitor cb) {
596    synchronized (loadClassLock) {
597      if (TRACE_ADDMONITOR || TRACE_LOADCLASS) {
598        VM.sysWrite("adding loadclass monitor: ");
599        VM.sysWrite(getClass(cb));
600        VM.sysWrite("\n");
601      }
602      loadClassCallbacks = new CallbackList(cb, loadClassCallbacks);
603    }
604  }
605
606  /**
607   * Notify the monitor that java.lang.Class.loadclass was called.
608   * @param type the type that will be returned
609   */
610  public static void notifyLoadClass(RVMType type) {
611    // NOTE: will need synchronization if allowing unregistering
612    if (!loadClassEnabled) return;
613    loadClassEnabled = false;
614    if (TRACE_LOADCLASS) {
615      //VM.sysWrite(getThread(), false);
616      //VM.sysWrite(": ");
617      VM.sysWrite("invoking loadclass monitors: ");
618      VM.sysWrite(type.getDescriptor());
619      VM.sysWrite("\n");
620      //printStack("From: ");
621    }
622    for (CallbackList l = loadClassCallbacks; l != null; l = l.next) {
623      if (TRACE_LOADCLASS) {
624        VM.sysWrite("    ");
625        VM.sysWrite(getClass(l.callback));
626        VM.sysWrite("\n");
627      }
628      ((LoadClassMonitor) l.callback).notifyLoadClass(type);
629    }
630    loadClassEnabled = true;
631  }
632
633  /**
634   * Interface for monitoring boot image writing.
635   */
636  public interface BootImageMonitor {
637    /**
638     * Notify the monitor that boot image writing is in progress.
639     * @param types the types that are included in the boot image
640     */
641    void notifyBootImage(Enumeration<String> types);
642  }
643
644  /**
645   * Boot image writing callback list.
646   */
647  private static CallbackList bootImageCallbacks = null;
648  private static final Object bootImageLock = new Object();
649  private static boolean bootImageEnabled = true;
650
651  /**
652   * Register a callback for boot image writing.
653   * @param cb the object to notify when event happens
654   */
655  public static void addBootImageMonitor(BootImageMonitor cb) {
656    synchronized (bootImageLock) {
657      if (TRACE_ADDMONITOR || TRACE_BOOTIMAGE) {
658        VM.sysWrite("adding boot image writing monitor: ");
659        VM.sysWrite(getClass(cb));
660        VM.sysWrite("\n");
661      }
662      bootImageCallbacks = new CallbackList(cb, bootImageCallbacks);
663    }
664  }
665
666  /**
667   * Notify the monitor that boot image writing is in progress.
668   * @param types the types that are included in the boot image
669   */
670  public static void notifyBootImage(Enumeration<String> types) {
671    // NOTE: will need synchronization if allowing unregistering
672    if (!bootImageEnabled) return;
673    bootImageEnabled = false;
674    if (TRACE_BOOTIMAGE) {
675      //VM.sysWrite(getThread(), false);
676      //VM.sysWrite(": ");
677      VM.sysWrite("invoking boot image writing monitors\n");
678    }
679    for (CallbackList l = bootImageCallbacks; l != null; l = l.next) {
680      if (TRACE_BOOTIMAGE) {
681        VM.sysWrite("    ");
682        VM.sysWrite(getClass(l.callback));
683        VM.sysWrite("\n");
684      }
685      ((BootImageMonitor) l.callback).notifyBootImage(types);
686    }
687    bootImageEnabled = true;
688  }
689
690  /**
691   * Interface for monitoring VM startup.
692   */
693  public interface StartupMonitor {
694    /**
695     * Notify the monitor that the VM has started up.
696     */
697    void notifyStartup();
698  }
699
700  /**
701   * VM startup callback list.
702   */
703  private static CallbackList startupCallbacks = null;
704  private static final Object startupLock = new Object();
705  private static boolean startupEnabled = true;
706
707  /**
708   * Register a callback for VM startup.
709   * @param cb the object to notify when event happens
710   */
711  public static void addStartupMonitor(StartupMonitor cb) {
712    synchronized (startupLock) {
713      if (TRACE_ADDMONITOR || TRACE_STARTUP) {
714        VM.sysWrite("adding startup monitor: ");
715        VM.sysWrite(getClass(cb));
716        VM.sysWrite("\n");
717      }
718      startupCallbacks = new CallbackList(cb, startupCallbacks);
719    }
720  }
721
722  /**
723   * Notify the callback manager that the VM has started up.
724   * NOTE: Runs in the main thread!
725   */
726  public static void notifyStartup() {
727    // NOTE: will need synchronization if allowing unregistering
728    if (!startupEnabled) return;
729    startupEnabled = false;
730    if (TRACE_STARTUP) {
731      //VM.sysWrite(getThread(), false);
732      //VM.sysWrite(": ");
733      VM.sysWrite("invoking startup monitors\n");
734    }
735    for (CallbackList l = startupCallbacks; l != null; l = l.next) {
736      if (TRACE_STARTUP) {
737        VM.sysWrite("    ");
738        VM.sysWrite(getClass(l.callback));
739        VM.sysWrite("\n");
740      }
741      ((StartupMonitor) l.callback).notifyStartup();
742    }
743    startupEnabled = true;
744  }
745
746  /**
747   * Interface for monitoring VM exit.
748   */
749  public interface ExitMonitor {
750    /**
751     * Notify the monitor that the VM is about to exit.
752     * @param value the exit value
753     */
754    void notifyExit(int value);
755  }
756
757  /**
758   * VM exit callback list.
759   */
760  private static CallbackList exitCallbacks = null;
761  private static final Object exitLock = new Object();
762  private static boolean exitCallbacksStarted = false;
763
764  /**
765   * Register a callback for VM exit.
766   * @param cb the object to notify when event happens
767   */
768  public static void addExitMonitor(ExitMonitor cb) {
769    synchronized (exitLock) {
770      if (TRACE_ADDMONITOR || TRACE_EXIT) {
771        VM.sysWrite("adding exit monitor: ");
772        VM.sysWrite(getClass(cb));
773        VM.sysWrite("\n");
774      }
775      exitCallbacks = new CallbackList(cb, exitCallbacks);
776    }
777  }
778
779  /**
780   * Notify the callback manager that the VM is about to exit.
781   * Will return once all the callbacks are invoked.
782   * @param value the exit value
783   */
784  public static void notifyExit(final int value) {
785    synchronized (exitLock) {
786      if (exitCallbacksStarted) return;
787      if (exitCallbacks == null) return;
788      exitCallbacksStarted = true;
789      if (TRACE_EXIT) {
790        //VM.sysWrite(Callbacks.getThread(), false);
791        //VM.sysWrite(": ");
792        VM.sysWrite("invoking exit monitors: ");
793        VM.sysWriteln(value);
794        //printStack("From: ");
795      }
796      for (CallbackList l = exitCallbacks; l != null; l = l.next) {
797        if (TRACE_EXIT) {
798          VM.sysWrite("    ");
799          VM.sysWrite(Callbacks.getClass(l.callback));
800          VM.sysWrite("\n");
801        }
802        ((ExitMonitor) l.callback).notifyExit(value);
803      }
804    }
805  }
806
807  /**
808   * Interface for monitoring when an application starts executing
809   */
810  public interface AppStartMonitor {
811    /**
812     * Notify the monitor that the application has started executing
813     * @param app application name
814     */
815    void notifyAppStart(String app);
816  }
817
818  /**
819   * Application Start executing callback list.
820   */
821  private static CallbackList appStartCallbacks = null;
822  private static final Object appStartLock = new Object();
823
824  /**
825   * Register a callback for when the application starts executing
826   * @param cb the object to notify when event happens
827   */
828  public static void addAppStartMonitor(AppStartMonitor cb) {
829    synchronized (appStartLock) {
830      if (TRACE_ADDMONITOR || TRACE_APP_START) {
831        VM.sysWrite("adding application start monitor: ");
832        VM.sysWrite(getClass(cb));
833        VM.sysWrite("\n");
834      }
835      appStartCallbacks = new CallbackList(cb, appStartCallbacks);
836    }
837  }
838
839  /**
840   * Notify the callback manager that the application started executing
841   * Will return once all the callbacks are invoked.
842   * @param app name of application
843   */
844  public static void notifyAppStart(String app) {
845    synchronized (appStartLock) {
846      if (appStartCallbacks == null) return;
847      if (TRACE_APP_START) {
848        VM.sysWrite("invoking application start monitors\n");
849      }
850      for (CallbackList l = appStartCallbacks; l != null; l = l.next) {
851        if (TRACE_APP_START) {
852          VM.sysWrite("    ");
853          VM.sysWrite(Callbacks.getClass(l.callback));
854          VM.sysWrite("\n");
855        }
856        ((AppStartMonitor) l.callback).notifyAppStart(app);
857      }
858    }
859  }
860
861  /**
862   * Interface for monitoring when an application completes executing
863   */
864  public interface AppCompleteMonitor {
865    /**
866     * Notify the monitor that the application has completed executing
867     * @param app  name of application
868     */
869    void notifyAppComplete(String app);
870  }
871
872  /**
873   * Application Execution Complete callback list.
874   */
875  private static CallbackList appCompleteCallbacks = null;
876  private static final Object appCompleteLock = new Object();
877
878  /**
879   * Register a callback for when the application completes executing
880   * @param cb the object to notify when event happens
881   */
882  public static void addAppCompleteMonitor(AppCompleteMonitor cb) {
883    synchronized (appCompleteLock) {
884      if (TRACE_ADDMONITOR || TRACE_APP_COMPLETE) {
885        VM.sysWrite("adding application complete monitor: ");
886        VM.sysWrite(getClass(cb));
887        VM.sysWrite("\n");
888      }
889      appCompleteCallbacks = new CallbackList(cb, appCompleteCallbacks);
890    }
891  }
892
893  /**
894   * Notify the callback manager that the application completed executing
895   * Will return once all the callbacks are invoked.
896   * @param app name of application
897   */
898  public static void notifyAppComplete(String app) {
899    synchronized (appCompleteLock) {
900      if (appCompleteCallbacks == null) return;
901      if (TRACE_APP_COMPLETE) {
902        VM.sysWrite("invoking application complete monitors for application ");
903        VM.sysWrite(app);
904        VM.sysWrite("\n");
905      }
906      for (CallbackList l = appCompleteCallbacks; l != null; l = l.next) {
907        if (TRACE_APP_COMPLETE) {
908          VM.sysWrite("    ");
909          VM.sysWrite(Callbacks.getClass(l.callback));
910          VM.sysWrite("\n");
911        }
912        ((AppCompleteMonitor) l.callback).notifyAppComplete(app);
913      }
914    }
915  }
916
917  /**
918   * Interface for monitoring when an application starts a run
919   */
920  public interface AppRunStartMonitor {
921    /**
922     * Notify the monitor that the application has started a run
923     * @param app application name
924     * @param run run number
925     */
926    void notifyAppRunStart(String app, int run);
927  }
928
929  /**
930   * Application Run Start callback list.
931   */
932  private static CallbackList appRunStartCallbacks = null;
933  private static final Object appRunStartLock = new Object();
934
935  /**
936   * Register a callback for when the application starts a run
937   * @param cb the object to notify when event happens
938   */
939  public static void addAppRunStartMonitor(AppRunStartMonitor cb) {
940    synchronized (appRunStartLock) {
941      if (TRACE_ADDMONITOR || TRACE_APP_RUN_START) {
942        VM.sysWrite("adding application run start monitor: ");
943        VM.sysWrite(getClass(cb));
944        VM.sysWrite("\n");
945      }
946      appRunStartCallbacks = new CallbackList(cb, appRunStartCallbacks);
947    }
948  }
949
950  /**
951   * Notify the callback manager that the application started a run
952   * Will return once all the callbacks are invoked.
953   * @param app application name
954   * @param run run number
955   */
956  public static void notifyAppRunStart(String app, int run) {
957    synchronized (appRunStartLock) {
958      if (appRunStartCallbacks == null) return;
959      if (TRACE_APP_RUN_START) {
960        //VM.sysWrite(getThread(), false);
961        //VM.sysWrite(": ");
962        VM.sysWrite("invoking the start monitor for application ");
963        VM.sysWrite(app);
964        VM.sysWrite(" at run ");
965        VM.sysWrite(run);
966        VM.sysWrite("\n");
967      }
968      for (CallbackList l = appRunStartCallbacks; l != null; l = l.next) {
969        if (TRACE_APP_RUN_START) {
970          VM.sysWrite("    ");
971          VM.sysWrite(Callbacks.getClass(l.callback));
972          VM.sysWrite("\n");
973        }
974        ((AppRunStartMonitor) l.callback).notifyAppRunStart(app, run);
975      }
976    }
977  }
978
979  /**
980   * Interface for monitoring when an application completes a run
981   */
982  public interface AppRunCompleteMonitor {
983    /**
984     * Notify the monitor that the application has completed a run
985     * @param app name of application
986     * @param run run number
987     */
988    void notifyAppRunComplete(String app, int run);
989  }
990
991  /**
992   * Application Run Complete callback list.
993   */
994  private static CallbackList appRunCompleteCallbacks = null;
995  private static final Object appRunCompleteLock = new Object();
996
997  /**
998   * Register a callback for when the application completes a run
999   * @param cb the object to notify when event happens
1000   */
1001  public static void addAppRunCompleteMonitor(AppRunCompleteMonitor cb) {
1002    synchronized (appRunCompleteLock) {
1003      if (TRACE_ADDMONITOR || TRACE_APP_RUN_COMPLETE) {
1004        VM.sysWrite("adding application run complete monitor: ");
1005        VM.sysWrite(getClass(cb));
1006        VM.sysWrite("\n");
1007      }
1008      appRunCompleteCallbacks = new CallbackList(cb, appRunCompleteCallbacks);
1009    }
1010  }
1011
1012  /**
1013   * Notify the callback manager that the application completed a run
1014   * Will return once all the callbacks are invoked.
1015   * @param app name of application
1016   * @param run run number
1017   */
1018  public static void notifyAppRunComplete(String app, int run) {
1019    synchronized (appRunCompleteLock) {
1020      if (appRunCompleteCallbacks == null) return;
1021      if (TRACE_APP_RUN_COMPLETE) {
1022        //VM.sysWrite(getThread(), false);
1023        //VM.sysWrite(": ");
1024        VM.sysWrite("invoking the complete monitor for application ", app);
1025        VM.sysWriteln(" at run ", run);
1026      }
1027      for (CallbackList l = appRunCompleteCallbacks; l != null; l = l.next) {
1028        if (TRACE_APP_RUN_COMPLETE) {
1029          VM.sysWrite("    ");
1030          VM.sysWrite(Callbacks.getClass(l.callback));
1031          VM.sysWrite("\n");
1032        }
1033        ((AppRunCompleteMonitor) l.callback).notifyAppRunComplete(app, run);
1034      }
1035    }
1036  }
1037
1038  /**
1039   * Interface for requesting VM to recompile all previously dynamically compiled methods
1040   */
1041  public interface RecompileAllDynamicallyLoadedMethodsMonitor {
1042    /**
1043     * Notify the monitor that the application has requested the recompile
1044     */
1045    void notifyRecompileAll();
1046  }
1047
1048  /**
1049   * Recompile all callback list.
1050   */
1051  private static CallbackList recompileAllCallbacks = null;
1052  private static final Object recompileAllLock = new Object();
1053
1054  /**
1055   * Register a callback for when the application requests to recompile all
1056   *  dynamically loaded classes
1057   * @param cb the object to notify when event happens
1058   */
1059  public static void addRecompileAllDynamicallyLoadedMethodsMonitor(RecompileAllDynamicallyLoadedMethodsMonitor cb) {
1060    synchronized (recompileAllLock) {
1061      if (TRACE_ADDMONITOR || TRACE_RECOMPILE_ALL) {
1062        VM.sysWrite("adding recompile all monitor: ");
1063        VM.sysWrite(getClass(cb));
1064        VM.sysWrite("\n");
1065      }
1066      recompileAllCallbacks = new CallbackList(cb, recompileAllCallbacks);
1067    }
1068  }
1069
1070  /**
1071   * Notify the callback manager that the application requested a recompile all
1072   * Will return once all the callbacks are invoked.
1073   */
1074  public static void recompileAllDynamicallyLoadedMethods() {
1075    synchronized (recompileAllLock) {
1076      if (recompileAllCallbacks == null) return;
1077      if (TRACE_RECOMPILE_ALL) {
1078        VM.sysWriteln("invoking the recompile all monitor");
1079      }
1080      for (CallbackList l = recompileAllCallbacks; l != null; l = l.next) {
1081        if (TRACE_RECOMPILE_ALL) {
1082          VM.sysWrite("    ");
1083          VM.sysWrite(Callbacks.getClass(l.callback));
1084          VM.sysWrite("\n");
1085        }
1086        ((RecompileAllDynamicallyLoadedMethodsMonitor) l.callback).notifyRecompileAll();
1087      }
1088    }
1089  }
1090
1091  ////////////////////
1092  // IMPLEMENTATION //
1093  ////////////////////
1094
1095  /**
1096   * Initialize callbacks.
1097   */
1098  public static void init() { }
1099
1100  /**
1101   * Perform boot-time actions.
1102   */
1103  public static void boot() { }
1104
1105  /**
1106   * Linked list of callbacks.
1107   */
1108  private static class CallbackList {
1109    CallbackList(Object cb, CallbackList n) {
1110      callback = cb;
1111      next = n;
1112    }
1113
1114    public final Object callback;
1115    public final CallbackList next;
1116  }
1117
1118  private static final boolean TRACE_ADDMONITOR = false;
1119  private static final boolean TRACE_CLASSLOADED = false;
1120  private static final boolean TRACE_CLASSRESOLVED = false;
1121  private static final boolean TRACE_CLASSINITIALIZED = false;
1122  private static final boolean TRACE_CLASSINSTANTIATED = false;
1123  private static final boolean TRACE_METHODOVERRIDE = false;
1124  private static final boolean TRACE_METHODCOMPILE = false;
1125  private static final boolean TRACE_FORNAME = false;
1126  private static final boolean TRACE_DEFINECLASS = false;
1127  private static final boolean TRACE_LOADCLASS = false;
1128  private static final boolean TRACE_BOOTIMAGE = false;
1129  private static final boolean TRACE_STARTUP = false;
1130  private static final boolean TRACE_EXIT = false;
1131  private static final boolean TRACE_APP_RUN_START = false;
1132  private static final boolean TRACE_APP_RUN_COMPLETE = false;
1133  private static final boolean TRACE_APP_START = false;
1134  private static final boolean TRACE_APP_COMPLETE = false;
1135  private static final boolean TRACE_RECOMPILE_ALL = false;
1136
1137  /**
1138   * Return class name of the object.
1139   * @param o the object
1140   * @return class name of the object
1141   */
1142  private static Atom getClass(Object o) {
1143    if (VM.runningVM) {
1144      return java.lang.JikesRVMSupport.getTypeForClass(o.getClass()).getDescriptor();
1145    } else {
1146      return Atom.findOrCreateAsciiAtom(o.getClass().getName());
1147    }
1148  }
1149
1150  /**
1151   * Return current thread id.
1152   * @return current thread id
1153   */
1154  @SuppressWarnings("unused")
1155  private static int getThread() {
1156    if (VM.runningVM) {
1157      return RVMThread.getCurrentThread().getThreadSlot();
1158    } else {
1159      return System.identityHashCode(Thread.currentThread());
1160    }
1161  }
1162
1163  /**
1164   * Print current stack trace.
1165   * @param message error message
1166   */
1167  @SuppressWarnings("unused")
1168  private static void printStack(String message) {
1169    if (VM.runningVM) {
1170      RVMThread.traceback(message);
1171    } else {
1172      new Throwable(message).printStackTrace();
1173    }
1174  }
1175}
1176