001: /*
002: Copyright (c) 2005-2006, MentorGen, LLC
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without
006: modification, are permitted provided that the following conditions are met:
007:
008: + Redistributions of source code must retain the above copyright notice,
009: this list of conditions and the following disclaimer.
010: + Redistributions in binary form must reproduce the above copyright notice,
011: this list of conditions and the following disclaimer in the documentation
012: and/or other materials provided with the distribution.
013: + Neither the name of MentorGen LLC nor the names of its contributors may be
014: used to endorse or promote products derived from this software without
015: specific prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018: AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
020: ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
021: LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
022: CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
023: SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
024: INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
025: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
026: ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
027: POSSIBILITY OF SUCH DAMAGE.
028: */
029: package com.mentorgen.tools.profile.runtime;
030:
031: import java.io.IOException;
032: import java.util.ArrayList;
033: import java.util.Collections;
034: import java.util.Comparator;
035: import java.util.HashMap;
036: import java.util.List;
037: import java.util.Map;
038:
039: import com.mentorgen.tools.profile.Controller;
040: import com.mentorgen.tools.profile.output.ProfileDump;
041:
042: /**
043: * The <code>Profiler</code> is the class that actually profiles the code.
044: * Code to be profiled is instrumented, when the byte code is loaded,
045: * to make calls to this class:
046: * <ul>
047: * <li>At the beginning of each method (calls <code>Profiler.start()</cdoe>)</li>
048: * <li>At the end of each method (that is, when return is called, which
049: * might not actaully be at the end of the method)
050: * (calls <code>Profiler.end()</cdoe>)</li>
051: * <li>When an exception is thrown (also calls <code>Profiler.end()</cdoe>)</li>
052: * <li>Each time a constructor is called (optional)</li>
053: * </ul>
054: *
055: * <blockquote>
056: * Note: Instrumenting code can interfer with line numbers that are added to
057: * the code for debugging. If you are debugging code and your stack traces
058: * don't have line numbers, don't use the profiler (ie, don't use <code>
059: * -javaagent)</code>
060: * </blockquote>
061: *
062: *
063: * @author Andrew Wilcox
064: *
065: */
066: public final class Profile implements Runnable {
067: private static boolean _debugStart = false;
068: private static boolean _debugException = false;
069:
070: private static ThreadDictionary _threadDictionary;
071: private static List<Frame> _frameList;
072: private static Map<Long, Frame> _threadActiveFrame;
073: private static Map<String, Method> _methodDictionary;
074: private static Object _lock;
075:
076: private static Map<String, ClassAllocation> _allocList;
077:
078: private static Controller _controller;
079: private static Thread _controllerThread;
080:
081: public static void initProfiler() {
082: System.err.println("Java Interactive Profiler: starting");
083:
084: init();
085: Runtime.getRuntime().addShutdownHook(new Thread(new Profile()));
086: _controller = new Controller();
087:
088: if (Controller._remote) {
089: _controllerThread = new Thread(_controller);
090: _controllerThread.start();
091: }
092: }
093:
094: public static void init() {
095: _threadActiveFrame = new HashMap<Long, Frame>(1001);
096: _threadDictionary = new ThreadDictionary();
097: _methodDictionary = new HashMap<String, Method>(2003);
098: _frameList = new ArrayList<Frame>(1001);
099: _lock = new Object();
100:
101: _allocList = new HashMap<String, ClassAllocation>();
102: }
103:
104: //
105: // Methods to programatically manipulate the Profiler
106: //
107:
108: public static void clear() {
109: init();
110: }
111:
112: public static void start() {
113: _controller.start();
114: }
115:
116: public static void stop() {
117: _controller.stop();
118: }
119:
120: public static void setFileName(String fileName) {
121: _controller.setFileName(fileName);
122: }
123:
124: public static void shutdown() {
125: synchronized (_lock) {
126: Controller._profile = false;
127:
128: for (Long threadId : _threadDictionary.keySet()) {
129: Frame f = _threadDictionary
130: .getMostRecentFrame(threadId);
131: f.close();
132: }
133:
134: for (Frame frame : frameList()) {
135: frame.computeNetTime();
136: }
137: } // synchronized
138: }
139:
140: //
141: // Methods called when generating output
142: //
143:
144: public static Iterable<Long> threads() {
145: return _threadDictionary.threads();
146: }
147:
148: public static Iterable<Frame> interactions(long threadId) {
149: return _threadDictionary.interactions((threadId));
150: }
151:
152: public static Iterable<Frame> frameList() {
153: return _frameList;
154: }
155:
156: public static Iterable<ClassAllocation> allocations() {
157: return _allocList.values();
158: }
159:
160: public static long getThreadTotalTime(long threadId) {
161: return _threadDictionary.getThreadTotalTime(threadId);
162: }
163:
164: public static void sortFrameList(Comparator<Frame> comp) {
165: synchronized (_lock) {
166: Collections.sort(_frameList, comp);
167: }
168: }
169:
170: //
171: // Methods that are called by instrumented code
172: //
173:
174: public static void start(String className, String methodName) {
175: long start = System.nanoTime();
176:
177: long threadId = Thread.currentThread().getId();
178:
179: synchronized (_lock) {
180:
181: if (!Controller._profile) {
182: return;
183: }
184:
185: // try to get the method from the method pool
186: //
187: Method method = new Method(className, methodName);
188:
189: if (_methodDictionary.get(method.toString()) == null) {
190: _methodDictionary.put(method.toString(), method);
191: }
192:
193: Frame parent = (Frame) _threadActiveFrame.get(threadId);
194: Frame target = null;
195:
196: if (parent != null) {
197: target = (Frame) parent.getChild(method);
198:
199: if (target == null) {
200: target = new Frame(parent, method, threadId);
201: _frameList.add(target);
202: }
203: } else {
204: target = new Frame(null, method, threadId);
205: _frameList.add(target);
206: _threadDictionary.add(threadId, target);
207: }
208:
209: if (_debugStart) {
210: System.out.print(" (");
211: System.out.print(className);
212: System.out.print(" : ");
213: System.out.print(methodName);
214: System.out.println(')');
215:
216: Frame root = _threadDictionary
217: .getMostRecentFrame(threadId);
218: System.out.println(root);
219: }
220:
221: // "push"
222: _threadActiveFrame.put(threadId, target);
223:
224: target.overhead(System.nanoTime() - start);
225: target.setBeginTime(start);
226: } // synchronized
227: }
228:
229: public static void end(String className, String method) {
230: long start = System.nanoTime();
231:
232: synchronized (_lock) {
233: long threadId = Thread.currentThread().getId();
234: Frame target = findFrame(threadId, className, method);
235:
236: if (target == null) {
237: return;
238: }
239:
240: if (target.getParent() != null) {
241: // "pop"
242: _threadActiveFrame.put(threadId, target.getParent());
243: } else {
244: _threadActiveFrame.put(threadId, null);
245: }
246:
247: target.overhead(System.nanoTime() - start);
248: target.setEndTime(System.nanoTime());
249: } // synchronized
250: }
251:
252: public static void beginWait(String className, String methodName) {
253: long start = System.nanoTime();
254:
255: synchronized (_lock) {
256: Frame target = findFrame(Thread.currentThread().getId(),
257: className, methodName);
258:
259: if (target == null) {
260: return;
261: }
262:
263: target.overhead(System.nanoTime() - start);
264: target.beginWait(System.nanoTime());
265: }
266: }
267:
268: public static void endWait(String className, String methodName) {
269: long start = System.nanoTime();
270:
271: synchronized (_lock) {
272: Frame target = findFrame(Thread.currentThread().getId(),
273: className, methodName);
274:
275: if (target == null) {
276: return;
277: }
278:
279: target.overhead(System.nanoTime() - start);
280: target.endWait(System.nanoTime());
281: }
282: }
283:
284: public static void unwind(String className, String methodName,
285: String exception) {
286: if (_debugException || Controller._debug) {
287: System.out.println("Catch: " + exception);
288: }
289:
290: synchronized (_lock) {
291: long threadId = Thread.currentThread().getId();
292: Frame target = findFrame(threadId, className, methodName);
293:
294: if (target == null) {
295: return;
296: }
297:
298: _threadActiveFrame.put(threadId, target);
299: } // synchronized
300: }
301:
302: // MUST be called from a block that has synchronized on _lock!
303: //
304: private static final Frame findFrame(long threadId,
305: String className, String methodName) {
306: if (!Controller._profile) {
307: return null;
308: }
309:
310: Frame target = (Frame) _threadActiveFrame.get(threadId);
311:
312: if (target == null) {
313: return null;
314: }
315:
316: // The flow of control is interrupted when an exception is
317: // thrown. This code will detect this an unwind the stack
318: // until it figures out where we are.
319: // Note that this method has its problems. Because it tries to figure
320: // out were it is based on the class name method name, it's possible
321: // that it could unwind the call stack to the wrong place. Worse yet,
322: // if the flow of control is transfered to the same method, but at a
323: // different point in the call stack,the exception will not be detected
324: // at all.
325: //
326: boolean detectedException = false;
327:
328: while (true) {
329:
330: if (target.getClassName().equals(className)
331: && target.getMethodName().equals(methodName)) {
332: break;
333: }
334:
335: if (!detectedException) {
336: detectedException = true;
337:
338: if (_debugException || Controller._debug) {
339: System.err.print("Detected an exception at ");
340: System.err.print(className);
341: System.err.print('.');
342: System.err.println(methodName);
343: }
344: } else if (_debugException) {
345: System.err.print("Unwinding ");
346: System.err.print(target.getClassName());
347: System.err.print('.');
348: System.err.println(target.getMethodName());
349: }
350:
351: target.setEndTime(System.nanoTime());
352: target = target.getParent();
353:
354: // the stack has been unwound to pass the point where
355: // we started to profile.
356: //
357: if (target == null) {
358:
359: if (_debugException) {
360: System.err.println("Stack completely unwound.");
361: }
362:
363: return null;
364: }
365: }
366:
367: return target;
368: }
369:
370: // Uses synchronization for thread safety. I thought this
371: // would slow things down a whole bunch, but on Java 5
372: // I haven't seen any major performance problems.
373: //
374: public static void alloc(String className) {
375:
376: synchronized (_lock) {
377:
378: if (!Controller._profile) {
379: return;
380: }
381:
382: // this code guards against a constructor calling another constructor
383: // in the same class
384: long threadId = Thread.currentThread().getId();
385: Frame target = (Frame) _threadActiveFrame.get(threadId);
386:
387: if (target != null
388: && target.getClassName().equals(className)
389: && target.getMethodName().equals("<init>")) {
390: return;
391: }
392:
393: ClassAllocation ca = _allocList.get(className);
394:
395: if (ca == null) {
396: ca = new ClassAllocation(className);
397: _allocList.put(className, ca);
398: }
399:
400: ca.incAllocCount();
401: } // synchronized
402: }
403:
404: /**
405: * ShutdownHook: This will dump the profiling info when the VM shutsdown.
406: */
407: public void run() {
408: try {
409: if (_threadDictionary.size() > 0) {
410: shutdown();
411: ProfileDump.dump();
412: }
413:
414: // fix up the Controller
415: //
416: _controller.close(); //closes the socket
417:
418: if (_controllerThread != null) {
419: _controllerThread.interrupt();
420: }
421:
422: } catch (IOException e) {
423: e.printStackTrace();
424: }
425: }
426:
427: }
|