/* * This file is part of the X10 project (http://x10-lang.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 * * This file was originally derived from the Polyglot extensible compiler framework. * * (C) Copyright 2000-2007 Polyglot project group, Cornell University * (C) Copyright IBM Corporation 2007-2012. */ package polyglot.frontend; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Collections; import java.util.Stack; import polyglot.main.Reporter; import polyglot.util.CollectionUtil; import x10.util.CollectionFactory; /** * Statistics collection and reporting object. Extensions can override this to * collect more stats or to change reporting. */ public class Stats { protected static class Counts { public long count; public Counter counter; } /*** Count accumulator for an object */ private class Counter { /** Map from Objects to counts for the Object */ public Map<Object, Counts> counts; /** List of Objects used as keys to counts. */ public List<Object> keys; public Counter() { counts = CollectionFactory.newHashMap(); keys = new ArrayList<Object>(50); } /** Accumulate counts for a key */ public void accumulate(Object key, long count) { Counts t = counts.get(key); if (t == null) { keys.add(key); t = new Counts(); counts.put(key, t); } t.count += count; } } private Reporter reporter; private Counter phase, site, freq; private long startTime, totalTime, reportTimeThreshold; private int currDepth, maxDepth; private boolean t2; private String name; /*** Stack of phase names for timing nested phases (goals) */ private class stackStruct { long startTime; Object phaseName; Object siteName; Counter phaseCounter; stackStruct(long sTime, Object pName, Object sName, Counter pCounter) { startTime = sTime; phaseName = pName; siteName = sName; phaseCounter = pCounter; } } private Stack<stackStruct> start; /*** * Initialize statistics if reporting is requested. Subsequently, we just * need to check for non-null pointers to see if we need to do the statistic * gathering work. * * @param startTime */ public void initialize(ExtensionInfo ext, long startTime) { this.startTime = startTime; this.name = ext.compilerName(); reporter = ext.getOptions().reporter; if (reporter.should_report(Reporter.time, 1)) { t2 = reporter.should_report(Reporter.time, 2); reporter.removeTopic(Reporter.time); if (reporter.should_report(Reporter.threshold, 1)) { reportTimeThreshold = reporter.level(Reporter.threshold); reporter.removeTopic(Reporter.threshold); } phase = new Counter(); site = new Counter(); start = new Stack<stackStruct>(); } if (reporter.should_report(Reporter.frequency, 1)) { reporter.removeTopic(Reporter.frequency); freq = new Counter(); } } /*** Increment frequency counter */ public void incrFrequency(Object key, long count) { if (freq == null) return; freq.accumulate(key, count); } /*** * Start timing a phase This should be paired with stopTiming * * @param phaseName * @param siteName */ public void startTiming(Object phaseName, Object siteName) { if (phase == null) return; Counter c; if (start.empty()) { c = phase; } else { stackStruct s = start.peek(); Counts t = s.phaseCounter.counts.get(s.phaseName); if (t == null) { s.phaseCounter.keys.add(s.phaseName); t = new Counts(); s.phaseCounter.counts.put(s.phaseName, t); } if (t.counter == null) { t.counter = new Counter(); } c = t.counter; } start.push(new stackStruct(System.nanoTime(), phaseName, siteName, c)); currDepth++; maxDepth = (currDepth > maxDepth) ? currDepth : maxDepth; } /*** * Stop timing a phase. This should be paired with startTiming. */ public void stopTiming() { if (phase == null) return; stackStruct s = start.pop(); long elapsed = System.nanoTime() - s.startTime; s.phaseCounter.accumulate(s.phaseName, elapsed); site.accumulate(s.siteName, elapsed); currDepth--; } /** Reporter the frequency counts. */ public void reportFrequency() { if (freq == null) return; reporter.report(1, "\nFrequency Statistics for "+name); reporter.report(1, String.format("%16s", "Count") + " Name"); reporter.report(1, String.format("%16s", "-----") + " ----"); for (Iterator<Object> i = freq.keys.iterator(); i.hasNext();) { Object key = i.next(); Counts t = freq.counts.get(key); reporter.report(1, String.format("%16d", t.count) + " " + key.toString()); } } /** Reporter the times. */ public void reportTime() { totalTime = System.nanoTime() - startTime; // Scale the threshold to a percentage of total time reportTimeThreshold *= totalTime; reportTimeThreshold /= 100; if (phase != null) reportTiming(); if (reporter.should_report(Reporter.verbose, 1) || phase != null) reporter.report(1, "Total time=" + String.format("%.3f", totalTime / 1e9) + " seconds"); } /** Reporter the times. */ private void reportTiming() { if (currDepth != 0) reporter.report(1, "\nWarning: mismatched start/stop times"); reporter.report(1, "\nPhase Statistics for "+name); String pad = ""; for (int i = t2 ? maxDepth : 1; i > 0; i--) pad += " "; reporter.report(1, "Percent" + pad + " Seconds Name"); reporter.report(1, "-------" + pad + "--------- ----"); reportPhase(0, phase); long unattributedTime = totalTime; for (Iterator<Object> i = phase.keys.iterator(); i.hasNext();) { Object key = i.next(); Counts t = phase.counts.get(key); unattributedTime -= t.count; } reporter.report(1, String.format("%6.3f%%", (unattributedTime * 100) / (double) totalTime) + pad + String.format("%9.3f", unattributedTime / 1e9) + " Unattributed"); reporter.report(1, "\nSite Statistics for x10c"); reporter.report(1, " Seconds Name"); reporter.report(1, " ------- ----"); for (Iterator<Object> i = sortByCount(site.counts).iterator(); i.hasNext();) { Object key = i.next(); Counts t = site.counts.get(key); if (t.count >= reportTimeThreshold) { reporter.report(1, String.format("%9.3f", t.count / 1e9) + " " + key.toString()); } } } /*** This handles reporting nestedphases (goals) */ private void reportPhase(int depth, Counter c) { int d; String indent = ""; for (d = 0; d < depth; d++) indent += " "; String pad = " "; if (t2) { for (d = depth; d < maxDepth - 1; d++) { pad += " "; } } for (Iterator<Object> i = sortByCount(c.counts).iterator(); i.hasNext();) { Object key = i.next(); Counts t = c.counts.get(key); if (t.count > reportTimeThreshold) { reporter.report(1, indent + String.format("%6.3f%%", (t.count * 100) / (double) totalTime) + pad + String.format("%9.3f", t.count / 1e9) + " " + key.toString()); if (t2 && t.counter != null) { reportPhase(depth + 1, t.counter); } } } } /*** Sort the hash maps by frequency */ private static List<Object> sortByCount(final Map<Object, Counts> m) { List<Object> keys = new ArrayList<Object>(); keys.addAll(m.keySet()); Collections.sort(keys, new Comparator<Object>() { public int compare(Object k1, Object k2) { Counts c1 = (Counts) m.get(k1); Counts c2 = (Counts) m.get(k2); return ((Long) c2.count).compareTo(c1.count); } }); return keys; } }