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.mmtk.utility.statistics;
014
015import org.mmtk.plan.Plan;
016import org.mmtk.utility.Log;
017import org.mmtk.utility.options.Options;
018import org.mmtk.utility.options.PrintPhaseStats;
019import org.mmtk.utility.options.XmlStats;
020
021import org.mmtk.vm.VM;
022
023import org.vmmagic.pragma.*;
024
025/**
026 * This class implements basic statistics functionality.
027 * <p>
028 * In general, statistics are only available if gathering has been enabled.
029 * The sole exception is the count of collections which is always available.
030 */
031@Uninterruptible
032public class Stats {
033
034  /****************************************************************************
035   *
036   * Class variables
037   */
038
039  /**
040   *
041   */
042  public static final boolean GATHER_MARK_CONS_STATS = false;
043
044  /** Maximum number of gc/mutator phases that can be counted */
045  static final int MAX_PHASES = 1 << 12;
046  /** Maximum number of counters that can be in operation */
047  static final int MAX_COUNTERS = 100;
048
049  private static int counters = 0;
050  private static Counter[] counter;
051  static int phase = 0;
052  private static int gcCount = 0;
053  static boolean gatheringStats = false;
054  static boolean exceededPhaseLimit = false;
055
056  /****************************************************************************
057   *
058   * Initialization
059   */
060
061  /**
062   * Class initializer.  This is executed <i>prior</i> to bootstrap
063   * (i.e. at "build" time).  This is where key <i>global</i>
064   * instances are allocated.  These instances will be incorporated
065   * into the boot image by the build process.
066   */
067  static {
068    counter = new Counter[MAX_COUNTERS];
069    Options.printPhaseStats = new PrintPhaseStats();
070    Options.xmlStats = new XmlStats();
071  }
072
073  /**
074   * Add a new counter to the set of managed counters.
075   *
076   * @param ctr The counter to be added.
077   */
078  @Interruptible
079  static void newCounter(Counter ctr) {
080    if (counters < (MAX_COUNTERS - 1)) {
081      counter[counters++] = ctr;
082    } else {
083      Log.writeln("Warning: number of stats counters exceeds maximum");
084    }
085  }
086
087  /**
088   * Start a new GC phase.  This means notifying each counter of the
089   * phase change.
090   */
091  public static void startGC() {
092    gcCount++;
093    if (!gatheringStats) return;
094    if (phase < MAX_PHASES - 1) {
095      for (int c = 0; c < counters; c++) {
096        counter[c].phaseChange(phase);
097      }
098      phase++;
099    } else if (!exceededPhaseLimit) {
100      Log.writeln("Warning: number of GC phases exceeds MAX_PHASES");
101      exceededPhaseLimit = true;
102    }
103  }
104
105  /**
106   * End a GC phase.  This means notifying each counter of the phase
107   * change.
108   */
109  public static void endGC() {
110    if (!gatheringStats) return;
111    if (phase < MAX_PHASES - 1) {
112      for (int c = 0; c < counters; c++) {
113        counter[c].phaseChange(phase);
114      }
115      phase++;
116    } else if (!exceededPhaseLimit) {
117      Log.writeln("Warning: number of GC phases exceeds MAX_PHASES");
118      exceededPhaseLimit = true;
119    }
120  }
121
122  /**
123   * Start all implicitly started counters (i.e. those for whom
124   * <code>start == true</code>).
125   */
126  public static void startAll() {
127    if (gatheringStats) {
128      Log.writeln("Error: calling Stats.startAll() while stats running");
129      Log.writeln("       verbosity > 0 and the harness mechanism may be conflicitng");
130      if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(false);
131    }
132    gatheringStats = true;
133    for (int c = 0; c < counters; c++) {
134      if (counter[c].getStart())
135        counter[c].start();
136    }
137
138    if (Options.xmlStats.getValue()) {
139      Xml.begin();
140      Xml.openTag("mmtk-stats");
141      Xml.end();
142    }
143  }
144
145  /**
146   * Stop all counters
147   */
148  @Interruptible
149  public static void stopAll() {
150    stopAllCounters();
151    Stats.printStats();
152    if (Options.xmlStats.getValue()) {
153      Xml.begin();
154      Xml.closeTag("mmtk-stats");
155      Xml.end();
156    }
157  }
158
159  /**
160   * Stop all counters
161   */
162  private static void stopAllCounters() {
163    for (int c = 0; c < counters; c++) {
164      if (counter[c].getStart())
165        counter[c].stop();
166    }
167    gatheringStats = false;
168  }
169
170  @Interruptible
171  public static void printStats() {
172    if (exceededPhaseLimit) {
173      Log.writeln("Warning: number of GC phases exceeds MAX_PHASES.  Statistics are truncated.");
174    }
175    if (Options.xmlStats.getValue())
176      printStatsXml();
177    else
178      printStatsPlain();
179  }
180
181  /**
182   * Print out statistics
183   */
184  @Interruptible
185  public static void printStatsPlain() {
186    if (Options.printPhaseStats.getValue())
187      printPhases();
188    printTotals();
189  }
190
191  /**
192   * Print out statistics totals
193   */
194  @Interruptible
195  public static void printTotals() {
196    Log.writeln("============================ MMTk Statistics Totals ============================");
197    printColumnNames();
198    Log.write(phase / 2); Log.write("\t");
199    for (int c = 0; c < counters; c++) {
200      if (counter[c].mergePhases()) {
201        counter[c].printTotal(); Log.write("\t");
202      } else {
203        counter[c].printTotal(true); Log.write("\t");
204        counter[c].printTotal(false); Log.write("\t");
205      }
206    }
207    Log.writeln();
208    Log.write("Total time: ");
209    Plan.totalTime.printTotal(); Log.writeln(" ms");
210    Log.writeln("------------------------------ End MMTk Statistics -----------------------------");
211  }
212
213  /**
214   * Print out statistics for each mutator/gc phase
215   */
216  @Interruptible
217  public static void printPhases() {
218    Log.writeln("--------------------- MMTk Statistics Per GC/Mutator Phase ---------------------");
219    printColumnNames();
220    for (int p = 0; p <= phase; p += 2) {
221      Log.write((p / 2) + 1); Log.write("\t");
222      for (int c = 0; c < counters; c++) {
223        if (counter[c].mergePhases()) {
224          counter[c].printCount(p); Log.write("\t");
225        } else {
226          counter[c].printCount(p); Log.write("\t");
227          counter[c].printCount(p + 1); Log.write("\t");
228        }
229      }
230      Log.writeln();
231    }
232  }
233
234  /**
235   * Print out statistics column names
236   */
237  @Interruptible
238  private static void printColumnNames() {
239    Log.write("GC\t");
240    for (int c = 0; c < counters; c++) {
241      if (counter[c].mergePhases()) {
242        Log.write(counter[c].getName());
243        Log.write(counter[c].getColumnSuffix());
244        Log.write("\t");
245      } else {
246        Log.write(counter[c].getName());
247        Log.write(counter[c].getColumnSuffix());
248        Log.write(".mu\t");
249        Log.write(counter[c].getName());
250        Log.write(counter[c].getColumnSuffix());
251        Log.write(".gc\t");
252      }
253    }
254    Log.writeln();
255  }
256
257  /* ****************************************************************
258   *
259   *              Statistics output in xml format
260 */
261
262  /**
263   * Print command-line options and statistics in XML format
264   */
265  @Interruptible
266  public static void printStatsXml() {
267    Xml.begin();
268    Options.set.logXml();
269    VM.config.printConfigXml();
270    if (Options.printPhaseStats.getValue())
271      printPhasesXml();
272    printTotalsXml();
273    Xml.end();
274  }
275
276  private static void openStatXml(String name) {
277    Xml.openMinorTag("stat");
278    Xml.attribute("name", name);
279  }
280
281  private static void closeStatXml() {
282    Xml.closeMinorTag();
283  }
284
285  enum Phase {
286    MUTATOR("mu"), GC("gc"), COMBINED("all");
287
288    private final String name;
289    Phase(String name) {
290      this.name = name;
291    }
292    @Override
293    public String toString() {
294      return name;
295    }
296  }
297
298  /**
299   * Print out statistics totals in Xml format
300   */
301  @Interruptible
302  public static void printTotalsXml() {
303    Xml.openTag("mmtk-stats-totals");
304    Xml.singleValue("gc", phase / 2);
305    for (int c = 0; c < counters; c++) {
306     if (!counter[c].isComplex())
307      if (counter[c].mergePhases()) {
308        printTotalXml(counter[c],Phase.COMBINED);
309      } else {
310        printTotalXml(counter[c],Phase.MUTATOR);
311        printTotalXml(counter[c],Phase.GC);
312      }
313    }
314    Xml.singleValue("total-time",Plan.totalTime.getTotalMillis(),"ms");
315    Xml.closeTag("mmtk-stats-totals");
316  }
317
318  /**
319   * Print a single total in an xml tag
320   *
321   * @param c The counter
322   * @param phase The phase
323   */
324  @Interruptible
325  private static void printTotalXml(Counter c, Phase phase) {
326    openStatXml(c.getName());
327    Xml.attribute("suffix", c.getColumnSuffix());
328    Xml.openAttribute("value");
329    if (phase == Phase.COMBINED) {
330      c.printTotal();
331    } else {
332      c.printTotal(phase == Phase.MUTATOR);
333      Xml.closeAttribute();
334      Xml.openAttribute("phase");
335      Log.write(phase.toString());
336    }
337    Xml.closeAttribute();
338    closeStatXml();
339  }
340
341  /**
342   * Print a single phase counter in an xml tag
343   *
344   * @param c The counter
345   * @param p The phase number
346   * @param phase The phase (null, "mu" or "gc")
347   */
348  @Interruptible
349  private static void printPhaseStatXml(Counter c, int p, Phase phase) {
350    openStatXml(c.getName());
351    Xml.attribute("suffix", c.getColumnSuffix());
352    Xml.openAttribute("value");
353    if (phase == Phase.COMBINED) {
354      c.printCount(p);
355    } else {
356      c.printCount(p);
357      Xml.closeAttribute();
358      Xml.openAttribute("phase");
359      Log.write(phase.name);
360   }
361    Xml.closeAttribute();
362    closeStatXml();
363  }
364
365  /**
366   * Print out statistics for each mutator/gc phase in Xml format
367   */
368  @Interruptible
369  public static void printPhasesXml() {
370    Xml.openTag("mmtk-stats-per-gc");
371    for (int p = 0; p <= phase; p += 2) {
372      Xml.openTag("phase",false);
373      Xml.attribute("gc",(p / 2) + 1);
374      Xml.closeMinorTag();
375      for (int c = 0; c < counters; c++) {
376       if (!counter[c].isComplex())
377        if (counter[c].mergePhases()) {
378          printPhaseStatXml(counter[c],p,Phase.COMBINED);
379        } else {
380          printPhaseStatXml(counter[c],p,Phase.MUTATOR);
381          printPhaseStatXml(counter[c],p,Phase.GC);
382        }
383      }
384      Xml.closeTag("phase");
385    }
386    Xml.closeTag("mmtk-stats-per-gc");
387  }
388
389  /**
390   * Returns the total count of collections.
391   * <p>
392   * Note: This count is available even when gathering of statistics
393   * is disabled.
394   *
395   * @return The GC count (inclusive of any in-progress GC)
396   */
397  public static int gcCount() {
398    return gcCount;
399  }
400
401  /** @return {@code true} if currently gathering stats */
402  public static boolean gatheringStats() {
403    return gatheringStats;
404  }
405}