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 }