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    }