001    /*
002    Copyright (c) 2008 Evan Jones, Yang Zhang
003    
004    Permission is hereby granted, free of charge, to any person obtaining a copy
005    of this software and associated documentation files (the "Software"), to deal
006    in the Software without restriction, including without limitation the rights
007    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
008    copies of the Software, and to permit persons to whom the Software is
009    furnished to do so, subject to the following conditions:
010    
011    The above copyright notice and this permission notice shall be included in
012    all copies or substantial portions of the Software.
013    
014    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
015    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
016    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
017    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
018    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
019    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
020    THE SOFTWARE.
021     */
022    
023    package util;
024    
025    import java.util.ArrayDeque;
026    import java.util.ArrayList;
027    import java.util.Deque;
028    import java.util.HashMap;
029    import java.util.List;
030    import java.util.Map;
031    
032    /**
033     * A minimal pure Java sampling profiler.
034     * 
035     * Due to biased placement of yield points. Samples will be
036     * biased towards code that contains many such points [1]. Can't
037     * do much about this problem with a pure Java sampler. 
038     * 
039     * [1] Mytkowicz, Diwan, Hauswirth, Sweeney. "Evaluating the Accuracy of Java Profilers." PLDI 2010
040     */
041    public class StackSampler extends Sampler {
042      private final Deque<Map<Thread, StackTraceElement[]>> samples;
043    
044      /**
045       * Only include stack traces that contain this method name.
046       */
047      public static final String includeMethodName = "__stackSamplerRecordMe";
048    
049      protected StackSampler(int intervalMillis) {
050        super(intervalMillis);
051    
052        samples = new ArrayDeque<Map<Thread, StackTraceElement[]>>();
053      }
054    
055      @Override
056      protected void sample() {
057        samples.add(Thread.getAllStackTraces());
058      }
059    
060      private Map<Thread, List<StackTraceElement[]>> getSamples() {
061        Map<Thread, List<StackTraceElement[]>> retval = new HashMap<Thread, List<StackTraceElement[]>>();
062        for (Map<Thread, StackTraceElement[]> sample : samples) {
063          for (Map.Entry<Thread, StackTraceElement[]> entry : sample.entrySet()) {
064            Thread t = entry.getKey();
065    
066            StackTraceElement[] stack = entry.getValue();
067            boolean keep = false;
068            for (int i = 0; i < stack.length; i++) {
069              if (stack[i].getMethodName().startsWith(includeMethodName)) {
070                keep = true;
071              }
072            }
073    
074            if (!keep)
075              continue;
076    
077            List<StackTraceElement[]> list = retval.get(t);
078            if (list == null) {
079              list = new ArrayList<StackTraceElement[]>();
080              retval.put(t, list);
081            }
082    
083            list.add(stack);
084          }
085        }
086        return retval;
087      }
088    
089      @Override
090      protected StackStatistics dumpSamples() {
091        return new StackStatistics(getSamples());
092      }
093    
094      /**
095       * Start sampling with this sampler.
096       * 
097       * @param interval  sample interval in milliseconds
098       * @return          a sampler
099       */
100      public static Sampler start(int interval) {
101        return start(new StackSampler(interval));
102      }
103    }