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