001    package org.apache.activemq.util;
002    
003    import java.util.HashMap;
004    import java.util.Map.Entry;
005    
006    import org.apache.commons.logging.Log;
007    import org.apache.commons.logging.LogFactory;
008    
009    /**
010     * Debugging tool to track entry points through code, useful to see runtime call paths
011     * To use, add to a method as follows:<code>
012     *  public void someMethod() {
013     *      ThreadTracker.track("someMethod");
014     *      ...
015     *  }</code>
016     *  and at some stage call <code>result</code> to get a LOG
017     *  output of the callers with an associated call count
018     *      
019     */
020    public class ThreadTracker {
021    
022        static final Log LOG = LogFactory.getLog(ThreadTracker.class);  
023        static HashMap<String, Tracker> trackers = new HashMap<String, Tracker>();
024        
025        /**
026         * track the stack trace of callers
027         * @param name the method being tracked
028         */
029        public static void track(final String name) {
030            Tracker t;
031            final String key = name.intern();
032            synchronized(trackers) {
033                t = trackers.get(key);
034                if (t == null) {
035                    t = new Tracker();
036                    trackers.put(key, t);
037                }
038            }
039            t.track();
040        }
041        
042        /**
043         * output the result of stack trace capture to the log
044         */
045        public static void result() {
046            synchronized(trackers) {
047                for (Entry<String, Tracker> t: trackers.entrySet()) {
048                    LOG.info("Tracker: " + t.getKey() + ", " + t.getValue().size() + " entry points...");
049                    for (Trace trace : t.getValue().values()) {
050                        LOG.info("count: " + trace.count, trace);
051                    }
052                    LOG.info("Tracker: " + t.getKey() + ", done.");
053                }
054            }
055        }
056    
057    }
058    
059    @SuppressWarnings("serial")
060    class Trace extends Throwable {
061        public int count = 1;
062        public final long id;
063        Trace() {
064            super();
065            id = calculateIdentifier();
066        }
067        private long calculateIdentifier() {
068            int len = 0;
069            for (int i=0; i<this.getStackTrace().length; i++) {
070                len += this.getStackTrace()[i].toString().intern().hashCode();
071            }
072            return len;
073        }
074    }
075    
076    @SuppressWarnings("serial")
077    class Tracker extends HashMap<Long, Trace> {
078        public void track() {
079            Trace current = new Trace();
080            synchronized(this) {
081                Trace exist = get(current.id);
082                if (exist != null) {
083                    exist.count++;
084                } else {
085                    put(current.id, current);
086                }
087            }
088        }
089    }