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       * @param app name of application
895       */
896      public static void notifyAppComplete(String app) {
897        synchronized (appCompleteLock) {
898          if (appCompleteCallbacks == null) return;
899          if (TRACE_APP_COMPLETE) {
900            VM.sysWrite("invoking application complete monitors for application ");
901            VM.sysWrite(app);
902            VM.sysWrite("\n");
903          }
904          for (CallbackList l = appCompleteCallbacks; l != null; l = l.next) {
905            if (TRACE_APP_COMPLETE) {
906              VM.sysWrite("    ");
907              VM.sysWrite(Callbacks.getClass(l.callback));
908              VM.sysWrite("\n");
909            }
910            ((AppCompleteMonitor) l.callback).notifyAppComplete(app);
911          }
912        }
913      }
914    
915      /**
916       * Interface for monitoring when an application starts a run
917       */
918      public interface AppRunStartMonitor {
919        /**
920         * Notify the monitor that the application has started a run
921         * @param app application name
922         * @param run run number
923         */
924        void notifyAppRunStart(String app, int run);
925      }
926    
927      /**
928       * Application Run Start callback list.
929       */
930      private static CallbackList appRunStartCallbacks = null;
931      private static final Object appRunStartLock = new Object();
932    
933      /**
934       * Register a callback for when the application starts a run
935       * @param cb the object to notify when event happens
936       */
937      public static void addAppRunStartMonitor(AppRunStartMonitor cb) {
938        synchronized (appRunStartLock) {
939          if (TRACE_ADDMONITOR || TRACE_APP_RUN_START) {
940            VM.sysWrite("adding application run start monitor: ");
941            VM.sysWrite(getClass(cb));
942            VM.sysWrite("\n");
943          }
944          appRunStartCallbacks = new CallbackList(cb, appRunStartCallbacks);
945        }
946      }
947    
948      /**
949       * Notify the callback manager that the application started a run
950       * Will return once all the callbacks are invoked.
951       * @param app application name
952       * @param run run number
953       */
954      public static void notifyAppRunStart(String app, int run) {
955        synchronized (appRunStartLock) {
956          if (appRunStartCallbacks == null) return;
957          if (TRACE_APP_RUN_START) {
958            //VM.sysWrite(getThread(), false);
959            //VM.sysWrite(": ");
960            VM.sysWrite("invoking the start monitor for application ");
961            VM.sysWrite(app);
962            VM.sysWrite(" at run ");
963            VM.sysWrite(run);
964            VM.sysWrite("\n");
965          }
966          for (CallbackList l = appRunStartCallbacks; l != null; l = l.next) {
967            if (TRACE_APP_RUN_START) {
968              VM.sysWrite("    ");
969              VM.sysWrite(Callbacks.getClass(l.callback));
970              VM.sysWrite("\n");
971            }
972            ((AppRunStartMonitor) l.callback).notifyAppRunStart(app, run);
973          }
974        }
975      }
976    
977      /**
978       * Interface for monitoring when an application completes a run
979       */
980      public interface AppRunCompleteMonitor {
981        /**
982         * Notify the monitor that the application has completed a run
983         * @param app name of application
984         * @param run run number
985         */
986        void notifyAppRunComplete(String app, int run);
987      }
988    
989      /**
990       * Application Run Complete callback list.
991       */
992      private static CallbackList appRunCompleteCallbacks = null;
993      private static final Object appRunCompleteLock = new Object();
994    
995      /**
996       * Register a callback for when the application completes a run
997       * @param cb the object to notify when event happens
998       */
999      public static void addAppRunCompleteMonitor(AppRunCompleteMonitor cb) {
1000        synchronized (appRunCompleteLock) {
1001          if (TRACE_ADDMONITOR || TRACE_APP_RUN_COMPLETE) {
1002            VM.sysWrite("adding application run complete monitor: ");
1003            VM.sysWrite(getClass(cb));
1004            VM.sysWrite("\n");
1005          }
1006          appRunCompleteCallbacks = new CallbackList(cb, appRunCompleteCallbacks);
1007        }
1008      }
1009    
1010      /**
1011       * Notify the callback manager that the application completed a run
1012       * Will return once all the callbacks are invoked.
1013       * @param app name of application
1014       * @param run run number
1015       */
1016      public static void notifyAppRunComplete(String app, int run) {
1017        synchronized (appRunCompleteLock) {
1018          if (appRunCompleteCallbacks == null) return;
1019          if (TRACE_APP_RUN_COMPLETE) {
1020            //VM.sysWrite(getThread(), false);
1021            //VM.sysWrite(": ");
1022            VM.sysWrite("invoking the complete monitor for application ", app);
1023            VM.sysWriteln(" at run ", run);
1024          }
1025          for (CallbackList l = appRunCompleteCallbacks; l != null; l = l.next) {
1026            if (TRACE_APP_RUN_COMPLETE) {
1027              VM.sysWrite("    ");
1028              VM.sysWrite(Callbacks.getClass(l.callback));
1029              VM.sysWrite("\n");
1030            }
1031            ((AppRunCompleteMonitor) l.callback).notifyAppRunComplete(app, run);
1032          }
1033        }
1034      }
1035    
1036      /**
1037       * Interface for requesting VM to recompile all previously dynamically compiled methods
1038       */
1039      public interface RecompileAllDynamicallyLoadedMethodsMonitor {
1040        /**
1041         * Notify the monitor that the application has requested the recompile
1042         */
1043        void notifyRecompileAll();
1044      }
1045    
1046      /**
1047       * Recompile all callback list.
1048       */
1049      private static CallbackList recompileAllCallbacks = null;
1050      private static final Object recompileAllLock = new Object();
1051    
1052      /**
1053       * Register a callback for when the application requests to recompile all
1054       *  dynamically loaded classes
1055       * @param cb the object to notify when event happens
1056       */
1057      public static void addRecompileAllDynamicallyLoadedMethodsMonitor(RecompileAllDynamicallyLoadedMethodsMonitor cb) {
1058        synchronized (recompileAllLock) {
1059          if (TRACE_ADDMONITOR || TRACE_RECOMPILE_ALL) {
1060            VM.sysWrite("adding recompile all monitor: ");
1061            VM.sysWrite(getClass(cb));
1062            VM.sysWrite("\n");
1063          }
1064          recompileAllCallbacks = new CallbackList(cb, recompileAllCallbacks);
1065        }
1066      }
1067    
1068      /**
1069       * Notify the callback manager that the application requested a recompile all
1070       * Will return once all the callbacks are invoked.
1071       */
1072      public static void recompileAllDynamicallyLoadedMethods() {
1073        synchronized (recompileAllLock) {
1074          if (recompileAllCallbacks == null) return;
1075          if (TRACE_RECOMPILE_ALL) {
1076            VM.sysWriteln("invoking the recompile all monitor");
1077          }
1078          for (CallbackList l = recompileAllCallbacks; l != null; l = l.next) {
1079            if (TRACE_RECOMPILE_ALL) {
1080              VM.sysWrite("    ");
1081              VM.sysWrite(Callbacks.getClass(l.callback));
1082              VM.sysWrite("\n");
1083            }
1084            ((RecompileAllDynamicallyLoadedMethodsMonitor) l.callback).notifyRecompileAll();
1085          }
1086        }
1087      }
1088    
1089      ////////////////////
1090      // IMPLEMENTATION //
1091      ////////////////////
1092    
1093      /**
1094       * Initialize callbacks.
1095       */
1096      public static void init() { }
1097    
1098      /**
1099       * Perform boot-time actions.
1100       */
1101      public static void boot() { }
1102    
1103      /**
1104       * Linked list of callbacks.
1105       */
1106      private static class CallbackList {
1107        public CallbackList(Object cb, CallbackList n) {
1108          callback = cb;
1109          next = n;
1110        }
1111    
1112        public final Object callback;
1113        public final CallbackList next;
1114      }
1115    
1116      private static final boolean TRACE_ADDMONITOR = false;
1117      private static final boolean TRACE_CLASSLOADED = false;
1118      private static final boolean TRACE_CLASSRESOLVED = false;
1119      private static final boolean TRACE_CLASSINITIALIZED = false;
1120      private static final boolean TRACE_CLASSINSTANTIATED = false;
1121      private static final boolean TRACE_METHODOVERRIDE = false;
1122      private static final boolean TRACE_METHODCOMPILE = false;
1123      private static final boolean TRACE_FORNAME = false;
1124      private static final boolean TRACE_DEFINECLASS = false;
1125      private static final boolean TRACE_LOADCLASS = false;
1126      private static final boolean TRACE_BOOTIMAGE = false;
1127      private static final boolean TRACE_STARTUP = false;
1128      private static final boolean TRACE_EXIT = false;
1129      private static final boolean TRACE_APP_RUN_START = false;
1130      private static final boolean TRACE_APP_RUN_COMPLETE = false;
1131      private static final boolean TRACE_APP_START = false;
1132      private static final boolean TRACE_APP_COMPLETE = false;
1133      private static final boolean TRACE_RECOMPILE_ALL = false;
1134    
1135      /**
1136       * Return class name of the object.
1137       * @param o the object
1138       * @return class name of the object
1139       */
1140      private static Atom getClass(Object o) {
1141        if (VM.runningVM) {
1142          return java.lang.JikesRVMSupport.getTypeForClass(o.getClass()).getDescriptor();
1143        } else {
1144          return Atom.findOrCreateAsciiAtom(o.getClass().getName());
1145        }
1146      }
1147    
1148      /**
1149       * Return current thread id.
1150       * @return current thread id
1151       */
1152      @SuppressWarnings("unused")
1153      private static int getThread() {
1154        if (VM.runningVM) {
1155          return RVMThread.getCurrentThread().getThreadSlot();
1156        } else {
1157          return System.identityHashCode(Thread.currentThread());
1158        }
1159      }
1160    
1161      /**
1162       * Print current stack trace.
1163       * @param message error message
1164       */
1165      @SuppressWarnings("unused")
1166      private static void printStack(String message) {
1167        if (VM.runningVM) {
1168          RVMThread.traceback(message);
1169        } else {
1170          new Throwable(message).printStackTrace();
1171        }
1172      }
1173    }
1174