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}