001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.core.blackboard;
028:
029: import java.util.ArrayList;
030: import java.util.Collections;
031: import java.util.Comparator;
032: import java.util.HashMap;
033: import java.util.IdentityHashMap;
034: import java.util.Iterator;
035: import java.util.List;
036: import java.util.Map;
037: import java.util.WeakHashMap;
038:
039: import org.cougaar.util.StackElements;
040: import org.cougaar.util.log.Logger;
041: import org.cougaar.util.log.Logging;
042:
043: /**
044: * A generic object tracker which is currently used to identify
045: * {@link CollectionSubscription}s that could be converted into
046: * {@link DeltaSubscription}s.
047: * <p>
048: * This could be repackaged into "org.cougaar.util".
049: */
050: class ObjectTracker {
051:
052: // TODO make this a weak map? but we want == lookups..
053: private final Map objects = new IdentityHashMap();
054: private final Map stacks = new WeakHashMap();
055:
056: public void add(Object o) {
057: StackElements se = captureStack();
058: synchronized (objects) {
059: objects.put(o, se);
060: }
061: }
062:
063: private StackElements captureStack() {
064: StackElements se = new StackElements(new Throwable());
065: synchronized (stacks) {
066: StackElements cached_se = (StackElements) stacks.get(se);
067: if (cached_se == null) {
068: stacks.put(se, se);
069: } else {
070: se = cached_se;
071: }
072: }
073: return se;
074: }
075:
076: public void remove(Object o) {
077: synchronized (objects) {
078: objects.remove(o);
079: }
080: }
081:
082: /**
083: * @return map from StackElements to Integer count
084: */
085: public Map getObjects() {
086: // map stacks to count
087: Map ret;
088: synchronized (objects) {
089: ret = new HashMap();
090: for (Iterator iter = objects.values().iterator(); iter
091: .hasNext();) {
092: StackElements se = (StackElements) iter.next();
093: Counter c = (Counter) ret.get(se);
094: if (c == null) {
095: c = new Counter();
096: ret.put(se, c);
097: }
098: c.i++;
099: }
100: }
101: // replace Counters with Integers
102: for (Iterator iter = ret.entrySet().iterator(); iter.hasNext();) {
103: Map.Entry me = (Map.Entry) iter.next();
104: me.setValue(new Integer(((Counter) me.getValue()).i));
105: }
106: return ret;
107: }
108:
109: private static class Counter {
110: public int i;
111: }
112:
113: /**
114: * Flatten a "getUsed()" map to just the active stack_element and counter.
115: * <p>
116: * @param m "getUsed()" map
117: * @param ignored_classes optional low-level stack classnames to ignore
118: * @return a list of Entry objects for each lowest-level stack_element
119: */
120: public static List flattenMap(Map m, String[] ignored_classes) {
121: if (m == null || m.isEmpty()) {
122: return Collections.EMPTY_LIST;
123: }
124: // get complete array with duplicate stack_elements
125: List l = new ArrayList();
126: for (Iterator iter = m.entrySet().iterator(); iter.hasNext();) {
127: Map.Entry me = (Map.Entry) iter.next();
128: StackElements se = (StackElements) me.getKey();
129: int count = ((Integer) me.getValue()).intValue();
130: StackTraceElement[] stea = se.getThrowable()
131: .getStackTrace();
132: StackTraceElement ste = null;
133: if (ignored_classes == null) {
134: ste = stea[0];
135: } else {
136: for (int i = 0; i < stea.length; i++) {
137: String s = stea[i].getClassName();
138: boolean skip = false;
139: for (int j = 0; j < ignored_classes.length; j++) {
140: if (s.startsWith(ignored_classes[j])) {
141: skip = true;
142: break;
143: }
144: }
145: if (skip)
146: continue;
147: ste = stea[i];
148: break;
149: }
150: }
151: l.add(new Entry(ste, count));
152: }
153: // sort by stack_element
154: Collections.sort(l);
155: // merge duplicate stack_element counters
156: List l2 = new ArrayList();
157: Entry prev = (Entry) l.get(0);
158: l2.add(prev);
159: for (int i = 1; i < l.size(); i++) {
160: Entry e = (Entry) l.get(i);
161: if (e.getStackTraceElement().equals(
162: prev.getStackTraceElement())) {
163: prev.setCount(prev.getCount() + e.getCount());
164: continue;
165: }
166: prev = e;
167: l2.add(e);
168: }
169: // sort by decreasing count then stack_element
170: Collections.sort(l2, new Comparator() {
171: public int compare(Object o1, Object o2) {
172: Entry e1 = (Entry) o1;
173: Entry e2 = (Entry) o2;
174: int cmp = (e2.getCount() - e1.getCount());
175: if (cmp != 0)
176: return cmp;
177: return e1.compareTo(e2);
178: }
179: });
180: return l2;
181: }
182:
183: public static void appendTo(Map m, boolean asMap, StringBuffer buf) {
184: if (m == null || m.isEmpty()) {
185: return;
186: }
187: boolean first = true;
188: for (Iterator iter = m.entrySet().iterator(); iter.hasNext();) {
189: Map.Entry me = (Map.Entry) iter.next();
190: StackElements se = (StackElements) me.getKey();
191: int count = ((Integer) me.getValue()).intValue();
192: if (!first) {
193: first = true;
194: buf.append("\n");
195: }
196: buf.append("\"stack\" count=").append(count);
197: StackTraceElement[] stea = se.getThrowable()
198: .getStackTrace();
199: for (int i = 0; i < stea.length; i++) {
200: buf.append("\n\tat ").append(stea[i]);
201: }
202: buf.append("\n");
203: }
204: }
205:
206: public static void appendTo(List l, boolean asMap, StringBuffer buf) {
207: int n = (l == null ? 0 : l.size());
208: for (int i = 0; i < n; i++) {
209: Entry e = (Entry) l.get(i);
210: if (i > 0)
211: buf.append("\n");
212: buf.append(e.getCount()).append(",\t").append(
213: e.getStackTraceElement());
214: }
215: }
216:
217: /**
218: * Periodically prints the object table to stdout.
219: */
220: public void startThread(final long period,
221: final String[] ignored_classes) {
222: final Logger log = Logging.getLogger(getClass());
223: Runnable r = new Runnable() {
224: public void run() {
225: while (true) {
226:
227: StringBuffer buf = new StringBuffer();
228: buf
229: .append("############################################################\n");
230: Map m = getObjects();
231: //appendTo(m, false, buf);
232: //buf.append("\n------------------------------------------------------------\n");
233: buf.append("COUNT,\tMETHOD\n");
234: List l = flattenMap(m, ignored_classes);
235: appendTo(l, false, buf);
236: buf
237: .append("\n############################################################");
238: log.shout(buf.toString());
239:
240: try {
241: Thread.sleep(period);
242: } catch (Exception e) {
243: break;
244: }
245: }
246: }
247: };
248: Thread t = new Thread(r, "Object Tracker");
249: t.setDaemon(true);
250: t.start();
251: }
252:
253: public static class Entry implements Comparable {
254:
255: private final StackTraceElement ste;
256: private int count;
257:
258: public Entry(StackTraceElement ste, int count) {
259: this .ste = ste;
260: this .count = count;
261: }
262:
263: public StackTraceElement getStackTraceElement() {
264: return ste;
265: }
266:
267: public int getCount() {
268: return count;
269: }
270:
271: public void setCount(int count) {
272: this .count = count;
273: }
274:
275: public String toString() {
276: return count + ",\t" + ste;
277: }
278:
279: public int compareTo(Object o2) {
280: // first compare by stack
281: StackTraceElement s1 = ste;
282: StackTraceElement s2 = ((Entry) o2).getStackTraceElement();
283: int cmp = s1.getClassName().compareTo(s2.getClassName());
284: if (cmp != 0)
285: return cmp;
286: if (s1.getLineNumber() >= 0 && s2.getLineNumber() >= 0) {
287: cmp = s1.getLineNumber() - s2.getLineNumber();
288: if (cmp != 0)
289: return cmp;
290: }
291: cmp = comp(s1.getMethodName(), s2.getMethodName());
292: if (cmp != 0)
293: return cmp;
294: cmp = comp(s1.getFileName(), s2.getFileName());
295: if (cmp != 0)
296: return cmp;
297:
298: // next compare by decreasing count
299: return count - ((Entry) o2).count;
300: }
301:
302: private int comp(Comparable a, Comparable b) {
303: return (a == b ? 0 : a == null ? -1 : b == null ? 1 : a
304: .compareTo(b));
305: }
306: }
307: }
|