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