1 /** 2 ******************************************************************************* 3 * Copyright (C) 2012 International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 */ 7 8 package org.unicode.cldr.util; 9 10 import java.util.Hashtable; 11 import java.util.Iterator; 12 import java.util.Map; 13 14 /** 15 * Debugging utility. 16 * 17 * A StackTracker tracks which stack frame various objects were created from. 18 * For example, call add() and remove() alongside some cache, and then StackTracker's toString() will 19 * print out the stack frame of all adds() not balanced by remove(). 20 * 21 * Objects must be Comparable. 22 * 23 * Example use is in the main() at the bottom. Outputs: 24 * 25 * "{StackTracker: 26 * Held Obj #1/2: the 27 * org.unicode.cldr.util.StackTracker.currentStack(StackTracker.java:92) 28 * org.unicode.cldr.util.StackTracker.add(StackTracker.java:34) 29 * org.unicode.cldr.util.StackTracker.main(StackTracker.java:118) 30 * ...}" 31 * 32 * @author srl 33 */ 34 @CLDRTool(alias = "test.stacktracker", description = "Test for StackTracker", hidden = "test") 35 public class StackTracker implements Iterable<Object> { 36 private Hashtable<Object, String> stacks = new Hashtable<>(); 37 38 /** 39 * Add object (i.e. added to cache) 40 * 41 * @param o 42 */ add(Object o)43 public void add(Object o) { 44 String stack = currentStack(); 45 stacks.put(o, stack); 46 } 47 48 /** 49 * remove obj (i.e. removed from cache) 50 * 51 * @param o 52 */ remove(Object o)53 public void remove(Object o) { 54 stacks.remove(o); 55 } 56 57 /** 58 * internal - convert a stack to string 59 * 60 * @param stackTrace 61 * @param skip 62 * start at this index (skip the top stuff) 63 * @return 64 */ stackToString(StackTraceElement[] stackTrace, int skip)65 public static String stackToString(StackTraceElement[] stackTrace, int skip) { 66 StringBuffer sb = new StringBuffer(); 67 for (int i = skip; i < stackTrace.length; i++) { 68 sb.append(stackTrace[i].toString() + "\n"); 69 } 70 return sb.toString(); 71 } 72 73 /** 74 * Get this tracker as a string. Prints any leaked objects, and the stack frame of where they were constructed. 75 */ 76 @Override toString()77 public String toString() { 78 if (stacks.isEmpty()) { 79 return "{StackTracker: empty}"; 80 } 81 StringBuffer sb = new StringBuffer(); 82 83 sb.append("{StackTracker:\n"); 84 int n = 0; 85 for (Map.Entry<Object, String> e : stacks.entrySet()) { 86 sb.append("Held Obj #" + (++n) + "/" + stacks.size() + ": " + e.getKey() + "\n"); 87 sb.append(e.getValue() + "\n"); 88 } 89 sb.append("}"); 90 return sb.toString(); 91 } 92 93 /** 94 * Purges all held objects. 95 */ clear()96 public void clear() { 97 stacks.clear(); 98 } 99 100 /** 101 * Convenience function, gets the current stack trace. 102 * 103 * @return current stack trace 104 */ currentStack()105 public static String currentStack() { 106 return stackToString(Thread.currentThread().getStackTrace(), 2); 107 } 108 109 /** 110 * Convenience function, gets the current element 111 * 112 * @param stacks 113 * to skip - 0 for immediate caller, 1, etc 114 */ currentElement(int skip)115 public static StackTraceElement currentElement(int skip) { 116 return Thread.currentThread().getStackTrace()[3 + skip]; 117 } 118 119 /** 120 * 121 * @return true if there are no held objects 122 */ isEmpty()123 public boolean isEmpty() { 124 return stacks.isEmpty(); 125 } 126 127 /** 128 * Iterate over held objects. 129 */ 130 @Override iterator()131 public Iterator<Object> iterator() { 132 return stacks.keySet().iterator(); 133 } 134 135 /** 136 * Example use. 137 * 138 * @param args 139 * ignored 140 */ main(String args[])141 public static void main(String args[]) { 142 StackTracker tracker = new StackTracker(); 143 System.out.println("At first: " + tracker); 144 145 tracker.add("Now"); 146 tracker.add("is"); 147 tracker.add("the"); 148 tracker.add("time"); 149 tracker.add("for"); 150 tracker.add("time"); 151 tracker.remove("Now"); 152 tracker.remove("for"); 153 tracker.remove("time"); 154 155 // any leaks? 156 System.out.println("At end: " + tracker); 157 } 158 } 159