001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package org.jikesrvm.adaptive.util;
014
015import java.io.FileOutputStream;
016import java.io.IOException;
017import java.io.PrintStream;
018
019import org.jikesrvm.VM;
020import org.jikesrvm.adaptive.controller.Controller;
021import org.jikesrvm.adaptive.controller.ControllerMemory;
022import org.jikesrvm.adaptive.controller.ControllerPlan;
023import org.jikesrvm.adaptive.controller.HotMethodEvent;
024import org.jikesrvm.adaptive.recompilation.CompilerDNA;
025import org.jikesrvm.classloader.NormalMethod;
026import org.jikesrvm.classloader.RVMMethod;
027import org.jikesrvm.compilers.common.CompiledMethod;
028import org.jikesrvm.compilers.common.RuntimeCompiler;
029import org.jikesrvm.compilers.opt.driver.CompilationPlan;
030import org.jikesrvm.runtime.Time;
031
032/**
033 * This class provides logging functionality for the Adaptive Optimization System.
034 * <p>
035 * Right now this is fairly primitive, an evolving number of events are
036 * defined and log entries are quite unsophisticated.
037 * <p>
038 * Some obvious TODO items:
039 * <ul>
040 *  <li>compact encoding of log entries
041 *  <li>some notion of log format versions
042 *  <li>When is the log file flushed and closed?
043 *  <li>Do we want to put report() information in the log?
044 *  <li> ...
045 * </ul>
046 * <p>
047 * NOTE: All code that writes to the log is synchronized on the PrintStream
048 *      object to avoid interspersed messages, which can happen when the
049 *      compilation thread and the controller thread try to log a message
050 *      "at the same time".
051 * <p>
052 * The current logging levels are:
053 * <ul>
054 *   <li>0  Do no logging
055 *   <li>1  Do minimal logging at startup and VM exit.
056 *      If at all possible, do not log anything during program execution.
057 *      This logging level is supposed to produce minimal performance pertubation.
058 *   <li>2  Log interesting AOS events and controller actions
059 *   <li>3  Exhaustively log pretty much everything that is going on
060 * </ul>
061 */
062public final class AOSLogging {
063
064  /** Singleton instance of the logger */
065  public static final AOSLogging logger = new AOSLogging();
066
067  /*
068   * The output file stream, where all log messages will go
069   */
070  private PrintStream log;
071
072  public PrintStream getLog() {
073    return log;
074  }
075
076  /*
077  * Record that the AOS logging has been booted.
078  * Needed to allow fast exit from reporting to ensure
079  * that when no class is specified to be run but "-help" is specified,
080  * don't want null pointer exception to occur!
081  */
082  private boolean booted = false;
083
084  /**
085   * @return whether AOS logging has booted
086   */
087  public boolean booted() {
088    return booted;
089  }
090
091  /**
092   * @return the current time as a string
093   */
094  private String getTime() {
095    return Controller.controllerClock + ":" + Time.nanoTime();
096  }
097
098  /**
099   * Called from ControllerThread.run to initialize the logging subsystem
100   */
101  public void boot() {
102    if (Controller.options.LOGGING_LEVEL >= 1) {
103      try {
104        log = new PrintStream(new FileOutputStream(Controller.options.LOGFILE_NAME));
105
106        // This statement will force the compilation of println, so it
107        // is needed regardless of the particular content of the message!
108        synchronized (log) {
109          log.println(getTime() + " Logging enabled\n");
110          log.println(Controller.options);
111        }
112      } catch (IOException e) {
113        VM.sysWrite("IOException caught in AOSLogging.java while trying to create and start log file.\n");
114        VM.sysWrite("Please check for file permission problems\n");
115      }
116    }
117    booted = true;
118  }
119
120  ////////////////////////////////////////////////////////////////
121  // Logging level 1
122  ////////////////////////////////////////////////////////////////
123
124  /**
125   * Call this method to dump statistics related to decaying
126   * @param decayCount the number of decay events
127   */
128  public void decayStatistics(int decayCount) {
129    if (!booted) return; // fast exit
130    if (Controller.options.LOGGING_LEVEL >= 1) {
131      synchronized (log) {
132        log.print(getTime() + " Decay Organizer Statistics: \n\t" + " Num of Decay events: " + decayCount + "\n");
133      }
134    }
135  }
136
137  /**
138   * Call this method when one run of the application begins
139   */
140  public void recompilingAllDynamicallyLoadedMethods() {
141    if (Controller.options.LOGGING_LEVEL >= 1) {
142      synchronized (log) {
143        log.println(getTime() + " Recompiling all dynamically loaded methods");
144      }
145    }
146  }
147
148  /**
149   * Dumps lots of controller statistics to the log file
150   */
151  public void printControllerStats() {
152    if (Controller.options.LOGGING_LEVEL >= 1) {
153      int awoken = ControllerMemory.getNumAwoken();
154      int didNothing = ControllerMemory.getNumDidNothing();
155      int numMethodsConsidered = ControllerMemory.getNumMethodsConsidered();
156      int numMethodsScheduledForRecomp = ControllerMemory.getNumMethodsScheduledForRecomp();
157      int numOpt0 = ControllerMemory.getNumOpt0();
158      int numOpt1 = ControllerMemory.getNumOpt1();
159      int numOpt2 = ControllerMemory.getNumOpt2();
160      int numOpt3 = ControllerMemory.getNumOpt3();
161
162      synchronized (log) {
163        log.print(getTime() +
164                  "\n  Num times Controller thread is awoken: " +
165                  awoken +
166                  "\n  Num times did nothing: " +
167                  didNothing +
168                  " (" +
169                  ((int) ((float) didNothing / (float) awoken * 100)) +
170                  "%)\n  Num methods baseline compiled: " +
171                  ControllerMemory.getNumBase() +
172                  "\n  Num methods considered for recompilation: " +
173                  numMethodsConsidered +
174                  "\n  Num methods chosen to recompile: " +
175                  numMethodsScheduledForRecomp +
176                  " (" +
177                  ((int) ((float) numMethodsScheduledForRecomp / numMethodsConsidered * 100)) +
178                  "%)\n  Opt Levels Chosen: " +
179                  "\n\t Opt Level 0: " +
180                  numOpt0 +
181                  " (" +
182                  ((int) ((float) numOpt0 / numMethodsScheduledForRecomp * 100)) +
183                  "%)\n\t Opt Level 1: " +
184                  numOpt1 +
185                  " (" +
186                  ((int) ((float) numOpt1 / numMethodsScheduledForRecomp * 100)) +
187                  "%)\n" +
188                  "\t Opt Level 2: " +
189                  numOpt2 +
190                  " (" +
191                  ((int) ((float) numOpt2 / numMethodsScheduledForRecomp * 100)) +
192                  "%)\n" +
193                  "\t Opt Level 3: " +
194                  numOpt3 +
195                  " (" +
196                  ((int) ((float) numOpt3 / numMethodsScheduledForRecomp * 100)) +
197                  "%)\n\n");
198
199        // Let the controller memory summarize itself to the log file
200        ControllerMemory.printFinalMethodStats(log);
201      }
202    }
203  }
204
205  /**
206   * This method reports the basic speedup rate for a compiler
207   * @param compiler the compiler you are reporting about
208   * @param rate the speedup rate
209   */
210  public void reportSpeedupRate(int compiler, double rate) {
211    if (Controller.options.LOGGING_LEVEL >= 1) {
212      synchronized (log) {
213        log.println(getTime() +
214                    " SpeedupRate for " +
215                    CompilerDNA.getCompilerString(compiler) +
216                    " compiler: " +
217                    rate);
218      }
219    }
220  }
221
222  /**
223   * This method reports the basic compilation rate for a compiler
224   * @param compiler the compiler you are reporting about
225   * @param rate the compilation rate (bytecodes per millisecond)
226   */
227  public void reportCompilationRate(int compiler, double rate) {
228    if (Controller.options.LOGGING_LEVEL >= 1) {
229      synchronized (log) {
230        log.println(getTime() +
231                    " Compilation Rate (bytecode/msec) for " +
232                    CompilerDNA.getCompilerString(compiler) +
233                    " compiler: " +
234                    rate);
235      }
236    }
237  }
238
239  /**
240   *  This method reports the benefit ratio from one compiler to the other
241   *  @param compiler1 the first compiler
242   *  @param compiler2 the second compiler
243   *  @param rate the improvement from going from a compiler1-compiled method
244   *                   to a compiler2-compiled method
245   */
246  public void reportBenefitRatio(int compiler1, int compiler2, double rate) {
247    if (Controller.options.LOGGING_LEVEL >= 1) {
248      synchronized (log) {
249        log.println(getTime() +
250                    " Benefit Ratio from " +
251                    CompilerDNA.getCompilerString(compiler1) +
252                    " compiler to " +
253                    CompilerDNA.getCompilerString(compiler2) +
254                    " compiler: " +
255                    rate);
256      }
257    }
258  }
259
260  /**
261   *  This method reports the compile time ratio from one compiler to
262   *  the other
263   *  @param compiler1 the first compiler
264   *  @param compiler2 the second compiler
265   *  @param rate the ratio of compiler1 compilation rate to
266   *                compiler2 compilation rate
267   */
268  public void reportCompileTimeRatio(int compiler1, int compiler2, double rate) {
269    if (Controller.options.LOGGING_LEVEL >= 1) {
270      synchronized (log) {
271        log.println(getTime() +
272                    " Compile Time Ratio of " +
273                    CompilerDNA.getCompilerString(compiler1) +
274                    " compiler to " +
275                    CompilerDNA.getCompilerString(compiler2) +
276                    " compiler: " +
277                    rate);
278      }
279    }
280  }
281
282  ////////////////////////////////////////////////////////////////
283  // Logging level 2
284  ////////////////////////////////////////////////////////////////
285
286  /**
287   * This method logs the scheduling of a recompilation,
288   * i.e., it being inserted in the compilation queue.
289   * @param plan the Compilation plan being executed.
290   * @param priority a number from 0.0 to 1.0 encoding the plan's priority.
291   */
292  public void recompilationScheduled(CompilationPlan plan, double priority) {
293    if (Controller.options.LOGGING_LEVEL >= 2) {
294      synchronized (log) {
295        log.println(getTime() + " Scheduling level " + plan.options.getOptLevel() + " recompilation of " + plan
296            .method + " (plan has priority " + priority + ")");
297      }
298    }
299  }
300
301  /**
302   * This method logs the beginning of an adaptively selected recompilation
303   * @param plan the Compilation plan being executed.
304   */
305  public void recompilationStarted(CompilationPlan plan) {
306    if (Controller.options.LOGGING_LEVEL >= 2) {
307      synchronized (log) {
308        log.println(getTime() + " Recompiling (at level " + plan.options.getOptLevel() + ") " + plan.method);
309      }
310    }
311  }
312
313  /**
314   * This method logs the successful completion of an adaptively
315   * selected recompilation
316   * @param plan the Compilation plan being executed.
317   */
318  public void recompilationCompleted(CompilationPlan plan) {
319    if (Controller.options.LOGGING_LEVEL >= 2) {
320      synchronized (log) {
321        //        log.println(getTime() +"  Recompiled (at level "+
322        //                    plan.options.getOptLevel() +") " +plan.method);
323        log.println(getTime() + "  Recompiled (at level " + plan.options.getOptLevel() + ") " + plan.method);
324      }
325    }
326  }
327
328  /**
329   * This method logs the abortion of an adaptively selected recompilation
330   * @param plan the Compilation plan being executed.
331   */
332  public void recompilationAborted(CompilationPlan plan) {
333    if (Controller.options.LOGGING_LEVEL >= 2) {
334      synchronized (log) {
335        log.println(getTime() + " Failed recompiling (at level " + plan.options.getOptLevel() + " " + plan.method);
336      }
337    }
338  }
339
340  /**
341   * This method logs the actual compilation time for the given compiled method.
342   * @param cm the compiled method
343   * @param expectedCompilationTime the model-derived expected compilation time
344   */
345  public void recordCompileTime(CompiledMethod cm, double expectedCompilationTime) {
346    if (log != null && Controller.options.LOGGING_LEVEL >= 2) {
347      synchronized (log) {
348        double compTime = cm.getCompilationTime();
349        log.println(getTime() +
350                    " Compiled " +
351                    cm.getMethod() +
352                    " with " +
353                    cm.getCompilerName() +
354                    " in " +
355                    compTime +
356                    " ms" +
357                    ", model estimated: " +
358                    expectedCompilationTime +
359                    " ms" +
360                    ", rate: " +
361                    (((NormalMethod) cm.getMethod()).getBytecodeLength() / compTime));
362      }
363    }
364  }
365
366  /**
367   * this method logs the event when the controller discovers a method that has
368   * been recompiled and the previous version is still regarded as hot,
369   * i.e., still on the stack and significant.
370   *
371   * @param hme the hot method event of the method that was recompiled
372   */
373  public void oldVersionStillHot(HotMethodEvent hme) {
374    if (Controller.options.LOGGING_LEVEL >= 2) {
375      synchronized (log) {
376        log.println(getTime() + " Found a method with an old version still hot " + hme);
377      }
378    }
379  }
380
381  /**
382   * This method logs when the decay organizer runs.
383   */
384  public void decayingCounters() {
385    if (Controller.options.LOGGING_LEVEL >= 2) {
386      synchronized (log) {
387        log.println(getTime() + " Decaying clock and decayable objects");
388      }
389    }
390  }
391
392  /**
393   * This Method logs when the organizer thread has reached its
394   * sampling threshold
395   */
396  public void organizerThresholdReached() {
397    if (Controller.options.LOGGING_LEVEL >= 2) {
398      synchronized (log) {
399        log.println(getTime() + " OrganizerThread reached sample size threshold\n");
400      }
401    }
402  }
403
404  /**
405   * This method logs that the controller is notified of a
406   * candidate to be recompiled due to hotness;
407   * i.e., the method has been inserted in the controller queue.
408   * @param hotMethod   method to be recompiled, and
409   * @param numSamples  number of samples attributed to the method
410   */
411  public void controllerNotifiedForHotness(CompiledMethod hotMethod, double numSamples) {
412    if (Controller.options.LOGGING_LEVEL >= 2) {
413      synchronized (log) {
414        log.println(getTime() +
415                    " Controller notified that method " +
416                    hotMethod.getMethod() +
417                    "(" +
418                    hotMethod.getId() +
419                    ")" +
420                    " has " +
421                    numSamples +
422                    " samples");
423      }
424    }
425  }
426
427  ////////////////////////////////////////////////////////////////
428  // Logging level 3
429  ////////////////////////////////////////////////////////////////
430
431  /**
432   * This method logs a controller cost estimate for doing nothing.
433   * @param method the method of interest
434   * @param optLevel the opt level being estimated, -1 = baseline
435   * @param cost  the computed cost for this method and level
436   */
437  public void recordControllerEstimateCostDoNothing(RVMMethod method, int optLevel, double cost) {
438    if (Controller.options.LOGGING_LEVEL >= 3) {
439      synchronized (log) {
440        log.print(getTime() + "  Estimated cost of doing nothing (leaving at ");
441        if (optLevel == -1) {
442          log.print("baseline");
443        } else {
444          log.print("O" + optLevel);
445        }
446        log.println(") to " + method + " is " + cost);
447      }
448    }
449  }
450
451  /**
452   * This method logs a controller cost estimate.
453   * @param method the method of interest
454   * @param choiceDesc a String describing the choice point
455   * @param compilationTime the computed compilation cost for this method and level
456   * @param futureTime the computed future time, including cost and execution
457   */
458  public void recordControllerEstimateCostOpt(RVMMethod method, String choiceDesc, double compilationTime,
459                                                     double futureTime) {
460    if (Controller.options.LOGGING_LEVEL >= 3) {
461      synchronized (log) {
462        log.println(getTime() +
463                    "  Estimated cost of OPT compiling " +
464                    method +
465                    " at " +
466                    choiceDesc +
467                    " is " +
468                    compilationTime +
469                    ", total future time is " +
470                    futureTime);
471      }
472    }
473  }
474
475  /**
476   * Records lots of details about the online computation of a compilation rate
477   * @param compiler compiler of interest
478   * @param method the method
479   * @param BCLength the number of bytecodes
480   * @param totalBCLength cumulative number of bytecodes
481   * @param MCLength size of machine code
482   * @param totalMCLength cumulative size of machine code
483   * @param compTime compilation time for this method
484   * @param totalCompTime cumulative compilation time for this method
485   * @param totalLogOfRates running sum of the natural logs of the rates
486   * @param totalLogValueMethods number of methods used in the log of rates
487   * @param totalMethods total number of methods
488   */
489  public void recordUpdatedCompilationRates(byte compiler, RVMMethod method, int BCLength, int totalBCLength,
490                                                   int MCLength, int totalMCLength, double compTime,
491                                                   double totalCompTime, double totalLogOfRates,
492                                                   int totalLogValueMethods, int totalMethods) {
493
494    if (Controller.options.LOGGING_LEVEL >= 3) {
495      synchronized (log) {
496        boolean backBranch = false;
497        if (method instanceof NormalMethod) {
498          backBranch = ((NormalMethod) method).hasBackwardsBranch();
499        }
500        log.println(getTime() +
501                    "  Updated compilation rates for " +
502                    RuntimeCompiler.getCompilerName(compiler) +
503                    "compiler");
504        log.println("\tmethod compiled: " + method);
505        log.println("\tbyte code length: " + BCLength + ", Total: " + totalBCLength);
506        log.println("\tmachine code length: " + MCLength + ", Total: " + totalMCLength);
507        log.println("\tbackwards branch: " + (backBranch ? "yes" : "no"));
508        log.println("\tcompilation time: " + compTime + ", Total: " + totalCompTime);
509        log.println("\tRate for this method: " + BCLength / compTime + ", Total of Logs: " + totalLogOfRates);
510        log.println("\tTotal Methods: " + totalMethods);
511        log.println("\tNew Rate: " + Math.exp(totalLogOfRates / totalLogValueMethods));
512      }
513    }
514  }
515
516  public void compileAllMethodsCompleted() {
517    if (Controller.options.LOGGING_LEVEL >= 2) {
518      synchronized (log) {
519        log.println(Controller.controllerClock + "  Compiled all methods finished. ");
520      }
521    }
522  }
523
524  ////////////////////////////////////////////////////////////////
525  // OSR-related code
526  ////////////////////////////////////////////////////////////////
527
528  /**
529   * This method logs the successful completion of an adaptively
530   * selected recompilation
531   * @param plan the Compilation plan being executed.
532   */
533  public void recordOSRRecompilationDecision(ControllerPlan plan) {
534    CompilationPlan cplan = plan.getCompPlan();
535    if (Controller.options.LOGGING_LEVEL >= 1) {
536      synchronized (log) {
537        log.println(getTime() + " recompile with OSR " + "( at level " + cplan.options.getOptLevel() + " ) " + cplan
538            .method);
539      }
540    }
541  }
542
543  public void onStackReplacementStarted(CompilationPlan plan) {
544    if (Controller.options.LOGGING_LEVEL >= 1) {
545      synchronized (log) {
546        log.println(getTime() + " OSR starts " + "( at level " + plan.options.getOptLevel() + " ) " + plan.method);
547      }
548    }
549  }
550
551  public void onStackReplacementCompleted(CompilationPlan plan) {
552    if (Controller.options.LOGGING_LEVEL >= 1) {
553      synchronized (log) {
554        log.println(getTime() + " OSR ends " + "( at level " + plan.options.getOptLevel() + " ) " + plan.method);
555      }
556    }
557  }
558
559  public void onStackReplacementAborted(CompilationPlan plan) {
560    if (Controller.options.LOGGING_LEVEL >= 1) {
561      synchronized (log) {
562        log.println(getTime() + " OSR failed " + "( at level " + plan.options.getOptLevel() + " ) " + plan.method);
563      }
564    }
565  }
566
567  public void logOsrEvent(String s) {
568    if (Controller.options.LOGGING_LEVEL >= 1) {
569      synchronized (log) {
570        log.println(getTime() + " " + s);
571      }
572    }
573  }
574
575  public void debug(String s) {
576    if (Controller.options.LOGGING_LEVEL >= 2) {
577      synchronized (log) {
578        log.println(getTime() + s);
579      }
580    }
581  }
582}