001    /*
002    Galois, a framework to exploit amorphous data-parallelism in irregular
003    programs.
004    
005    Copyright (C) 2010, The University of Texas at Austin. All rights reserved.
006    UNIVERSITY EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES CONCERNING THIS SOFTWARE
007    AND DOCUMENTATION, INCLUDING ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR ANY
008    PARTICULAR PURPOSE, NON-INFRINGEMENT AND WARRANTIES OF PERFORMANCE, AND ANY
009    WARRANTY THAT MIGHT OTHERWISE ARISE FROM COURSE OF DEALING OR USAGE OF TRADE.
010    NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH RESPECT TO THE USE OF THE
011    SOFTWARE OR DOCUMENTATION. Under no circumstances shall University be liable
012    for incidental, special, indirect, direct or consequential damages or loss of
013    profits, interruption of business, or related expenses which may arise from use
014    of Software or Documentation, including but not limited to those resulting from
015    defects in Software and/or Documentation, or loss or inaccuracy of data of any
016    kind.
017    
018    File: StackStatistics.java 
019    
020    */
021    
022    
023    
024    package util;
025    
026    import java.io.PrintStream;
027    import java.util.ArrayList;
028    import java.util.Collections;
029    import java.util.Comparator;
030    import java.util.HashMap;
031    import java.util.HashSet;
032    import java.util.List;
033    import java.util.Map;
034    import java.util.Set;
035    
036    /**
037     * Statistics from stack samples. Computes hot methods from stack samples.
038     * 
039     *
040     */
041    public class StackStatistics extends Statistics {
042      private final Map<Thread, List<StackTraceElement[]>> threadStacks;
043    
044      /**
045       * Creates stack statistics from a list of stack traces.
046       * 
047       * @param threadStacks  lists of stack traces for each thread
048       */
049      public StackStatistics(Map<Thread, List<StackTraceElement[]>> threadStacks) {
050        this.threadStacks = threadStacks;
051      }
052    
053      @Override
054      public void dumpFull(PrintStream out) {
055        printFullHeader(out, "Stacks (top ten)");
056        printTop(out);
057    
058        printFullHeader(out, "Stacks");
059    
060        for (List<StackTraceElement[]> stacks : threadStacks.values()) {
061          for (StackTraceElement[] stack : stacks) {
062            for (int i = 0; i < stack.length; i++) {
063              out.println(stack[i]);
064            }
065            out.println();
066          }
067        }
068      }
069    
070      @Override
071      public void dumpSummary(PrintStream out) {
072        printSummaryHeader(out, "Stacks (top ten)");
073        out.println();
074        printTop(out);
075      }
076    
077      private void printTop(PrintStream out) {
078        // TODO(ddn): Collate by threads and with statistics
079        final Map<String, Integer> selfCounts = new HashMap<String, Integer>();
080        // self + children
081        final Map<String, Integer> totalCounts = new HashMap<String, Integer>();
082        int totalSamples = 0;
083        Set<String> seen = new HashSet<String>();
084    
085        for (List<StackTraceElement[]> stacks : threadStacks.values()) {
086          totalSamples += stacks.size();
087    
088          for (StackTraceElement[] stack : stacks) {
089            int stopIndex = -1;
090            for (int i = stack.length - 1; i >= 0; i--) {
091              String methodName = stack[i].getMethodName();
092    
093              if (methodName.startsWith(StackSampler.includeMethodName)) {
094                stopIndex = i;
095                break;
096              }
097            }
098    
099            if (stopIndex < 0)
100              continue;
101    
102            for (int i = 0; i < stopIndex; i++) {
103              String key = String.format("%s.%s", stack[i].getClassName(), stack[i].getMethodName());
104              // Skip subsequent recursive call frames
105              if (seen.contains(key)) {
106                continue;
107              }
108    
109              if (i == 0) {
110                addOne(selfCounts, key);
111              }
112              addOne(totalCounts, key);
113    
114              seen.add(key);
115            }
116            seen.clear();
117          }
118        }
119    
120        out.println("  Self:");
121        dumpTop(out, selfCounts, totalSamples, "    ");
122    
123        out.println("  Total:");
124        dumpTop(out, totalCounts, totalSamples, "    ");
125      }
126    
127      @Override
128      public void merge(Object obj) {
129        StackStatistics other = (StackStatistics) obj;
130        for (Map.Entry<Thread, List<StackTraceElement[]>> entry : other.threadStacks.entrySet()) {
131          List<StackTraceElement[]> value = threadStacks.get(entry.getKey());
132          if (value == null) {
133            value = new ArrayList<StackTraceElement[]>();
134            threadStacks.put(entry.getKey(), value);
135          }
136          value.addAll(entry.getValue());
137        }
138      }
139    
140      private void dumpTop(PrintStream out, final Map<String, Integer> counts, int totalSamples, String prefix) {
141        int count = 0;
142        for (String method : descendingOrder(counts)) {
143          float ratio = counts.get(method) / (float) totalSamples;
144          if (count++ > 10)
145            break;
146          else if (ratio > 0.05)
147            out.printf("%s%s %.3f\n", prefix, method, ratio);
148          else
149            break;
150        }
151      }
152    
153      private static void addOne(Map<String, Integer> map, String key) {
154        Integer value = map.get(key);
155        if (value == null) {
156          map.put(key, 1);
157        } else {
158          map.put(key, value + 1);
159        }
160      }
161    
162      private static List<String> descendingOrder(final Map<String, Integer> map) {
163        List<String> methods = new ArrayList<String>(map.keySet());
164        Collections.sort(methods, new Comparator<String>() {
165          @Override
166          public int compare(String arg0, String arg1) {
167            return map.get(arg1) - map.get(arg0);
168          }
169        });
170        return methods;
171      }
172    }