blob: 40a794f04e0d7ef88b0b6694ed093e91ee3d5da2 [file] [log] [blame]
/*
* 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; }
}