/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.mmtk.utility.statistics; import org.mmtk.plan.Plan; import org.mmtk.utility.Log; import org.mmtk.utility.options.Options; import org.mmtk.utility.options.PrintPhaseStats; import org.mmtk.utility.options.XmlStats; import org.mmtk.vm.VM; import org.vmmagic.pragma.*; /** * This class implements basic statistics functionality */ @Uninterruptible public class Stats { /**************************************************************************** * * Class variables */ public static final boolean GATHER_MARK_CONS_STATS = false; /** Maximum number of gc/mutator phases that can be counted */ static final int MAX_PHASES = 1 << 12; /** Maximum number of counters that can be in operation */ static final int MAX_COUNTERS = 100; private static int counters = 0; private static Counter[] counter; static int phase = 0; private static int gcCount = 0; static boolean gatheringStats = false; static boolean exceededPhaseLimit = false; /**************************************************************************** * * Initialization */ /** * Class initializer. This is executed <i>prior</i> to bootstrap * (i.e. at "build" time). This is where key <i>global</i> * instances are allocated. These instances will be incorporated * into the boot image by the build process. */ static { counter = new Counter[MAX_COUNTERS]; Options.printPhaseStats = new PrintPhaseStats(); Options.xmlStats = new XmlStats(); } /** * Add a new counter to the set of managed counters. * * @param ctr The counter to be added. */ @Interruptible static void newCounter(Counter ctr) { if (counters < (MAX_COUNTERS - 1)) { counter[counters++] = ctr; } else { Log.writeln("Warning: number of stats counters exceeds maximum"); } } /** * Start a new GC phase. This means notifying each counter of the * phase change. */ public static void startGC() { gcCount++; if (!gatheringStats) return; if (phase < MAX_PHASES - 1) { for (int c = 0; c < counters; c++) { counter[c].phaseChange(phase); } phase++; } else if (!exceededPhaseLimit) { Log.writeln("Warning: number of GC phases exceeds MAX_PHASES"); exceededPhaseLimit = true; } } /** * End a GC phase. This means notifying each counter of the phase * change. */ public static void endGC() { if (!gatheringStats) return; if (phase < MAX_PHASES - 1) { for (int c = 0; c < counters; c++) { counter[c].phaseChange(phase); } phase++; } else if (!exceededPhaseLimit) { Log.writeln("Warning: number of GC phases exceeds MAX_PHASES"); exceededPhaseLimit = true; } } /** * Start all implicitly started counters (i.e. those for whom * <code>start == true</code>). */ public static void startAll() { if (gatheringStats) { Log.writeln("Error: calling Stats.startAll() while stats running"); Log.writeln(" verbosity > 0 and the harness mechanism may be conflicitng"); if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(false); } gatheringStats = true; for (int c = 0; c < counters; c++) { if (counter[c].getStart()) counter[c].start(); } if (Options.xmlStats.getValue()) { Xml.begin(); Xml.openTag("mmtk-stats"); Xml.end(); } } /** * Stop all counters */ @Interruptible public static void stopAll() { stopAllCounters(); Stats.printStats(); if (Options.xmlStats.getValue()) { Xml.begin(); Xml.closeTag("mmtk-stats"); Xml.end(); } } /** * Stop all counters */ private static void stopAllCounters() { for (int c = 0; c < counters; c++) { if (counter[c].getStart()) counter[c].stop(); } gatheringStats = false; } @Interruptible public static void printStats() { if (exceededPhaseLimit) { Log.writeln("Warning: number of GC phases exceeds MAX_PHASES. Statistics are truncated."); } if (Options.xmlStats.getValue()) printStatsXml(); else printStatsPlain(); } /** * Print out statistics */ @Interruptible public static void printStatsPlain() { if (Options.printPhaseStats.getValue()) printPhases(); printTotals(); } /** * Print out statistics totals */ @Interruptible public static void printTotals() { Log.writeln("============================ MMTk Statistics Totals ============================"); printColumnNames(); Log.write(phase/2); Log.write("\t"); for (int c = 0; c < counters; c++) { if (counter[c].mergePhases()) { counter[c].printTotal(); Log.write("\t"); } else { counter[c].printTotal(true); Log.write("\t"); counter[c].printTotal(false); Log.write("\t"); } } Log.writeln(); Log.write("Total time: "); Plan.totalTime.printTotal(); Log.writeln(" ms"); Log.writeln("------------------------------ End MMTk Statistics -----------------------------"); } /** * Print out statistics for each mutator/gc phase */ @Interruptible public static void printPhases() { Log.writeln("--------------------- MMTk Statistics Per GC/Mutator Phase ---------------------"); printColumnNames(); for (int p = 0; p <= phase; p += 2) { Log.write((p/2)+1); Log.write("\t"); for (int c = 0; c < counters; c++) { if (counter[c].mergePhases()) { counter[c].printCount(p); Log.write("\t"); } else { counter[c].printCount(p); Log.write("\t"); counter[c].printCount(p+1); Log.write("\t"); } } Log.writeln(); } } /** * Print out statistics column names */ @Interruptible private static void printColumnNames() { Log.write("GC\t"); for (int c = 0; c < counters; c++) { if (counter[c].mergePhases()) { Log.write(counter[c].getName()); Log.write(counter[c].getColumnSuffix()); Log.write("\t"); } else { Log.write(counter[c].getName()); Log.write(counter[c].getColumnSuffix()); Log.write(".mu\t"); Log.write(counter[c].getName()); Log.write(counter[c].getColumnSuffix()); Log.write(".gc\t"); } } Log.writeln(); } /* **************************************************************** * * Statistics output in xml format */ /** * Print command-line options and statistics in XML format */ @Interruptible public static void printStatsXml() { Xml.begin(); Options.set.logXml(); VM.config.printConfigXml(); if (Options.printPhaseStats.getValue()) printPhasesXml(); printTotalsXml(); Xml.end(); } private static void openStatXml(String name) { Xml.openMinorTag("stat"); Xml.attribute("name", name); } private static void closeStatXml() { Xml.closeMinorTag(); } enum Phase { MUTATOR("mu"), GC("gc"), COMBINED("all"); private final String name; Phase(String name) { this.name = name; } public String toString() { return name; } } /** * Print out statistics totals in Xml format */ @Interruptible public static void printTotalsXml() { Xml.openTag("mmtk-stats-totals"); Xml.singleValue("gc", phase/2); for (int c = 0; c < counters; c++) { if (!counter[c].isComplex()) if (counter[c].mergePhases()) { printTotalXml(counter[c],Phase.COMBINED); } else { printTotalXml(counter[c],Phase.MUTATOR); printTotalXml(counter[c],Phase.GC); } } Xml.singleValue("total-time",Plan.totalTime.getTotalMillis(),"ms"); Xml.closeTag("mmtk-stats-totals"); } /** * Print a single total in an xml tag * * @param c The counter * @param phase The phase */ @Interruptible private static void printTotalXml(Counter c, Phase phase) { openStatXml(c.getName()); Xml.attribute("suffix", c.getColumnSuffix()); Xml.openAttribute("value"); if (phase == Phase.COMBINED) { c.printTotal(); } else { c.printTotal(phase == Phase.MUTATOR); Xml.closeAttribute(); Xml.openAttribute("phase"); Log.write(phase.toString()); } Xml.closeAttribute(); closeStatXml(); } /** * Print a single phase counter in an xml tag * * @param c The counter * @param p The phase number * @param phase The phase (null, "mu" or "gc") */ @Interruptible private static void printPhaseStatXml(Counter c, int p, Phase phase) { openStatXml(c.getName()); Xml.attribute("suffix", c.getColumnSuffix()); Xml.openAttribute("value"); if (phase == Phase.COMBINED) { c.printCount(p); } else { c.printCount(p); Xml.closeAttribute(); Xml.openAttribute("phase"); Log.write(phase.name); } Xml.closeAttribute(); closeStatXml(); } /** * Print out statistics for each mutator/gc phase in Xml format */ @Interruptible public static void printPhasesXml() { Xml.openTag("mmtk-stats-per-gc"); for (int p = 0; p <= phase; p += 2) { Xml.openTag("phase",false); Xml.attribute("gc",(p/2)+1); Xml.closeMinorTag(); for (int c = 0; c < counters; c++) { if (!counter[c].isComplex()) if (counter[c].mergePhases()) { printPhaseStatXml(counter[c],p,Phase.COMBINED); } else { printPhaseStatXml(counter[c],p,Phase.MUTATOR); printPhaseStatXml(counter[c],p,Phase.GC); } } Xml.closeTag("phase"); } Xml.closeTag("mmtk-stats-per-gc"); } /** @return The GC count (inclusive of any in-progress GC) */ public static int gcCount() { return gcCount; } /** @return True if currently gathering stats */ public static boolean gatheringStats() { return gatheringStats; } }