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;
030:
031: import java.io.BufferedInputStream;
032: import java.io.FileInputStream;
033: import java.io.IOException;
034: import java.io.InputStream;
035: import java.lang.reflect.Method;
036: import java.net.ServerSocket;
037: import java.net.Socket;
038: import java.net.SocketException;
039: import java.util.ArrayList;
040: import java.util.Properties;
041: import java.util.StringTokenizer;
042:
043: import net.sourceforge.jiprof.instrument.clfilter.GenericClassLoaderFilter;
044:
045: import com.mentorgen.tools.profile.instrument.clfilter.ClassLoaderFilter;
046: import com.mentorgen.tools.profile.instrument.clfilter.StandardClassLoaderFilter;
047: import com.mentorgen.tools.profile.output.ProfileDump;
048: import com.mentorgen.tools.profile.runtime.Profile;
049:
050: /**
051: * <code>Controller</code> reads the properties file that controls
052: * how the profiler operates, both when the byte code is instrumented
053: * as well as at runtime. It also opens a server socket to receive
054: * remote commands to modify the profiler's behavior, like turning
055: * the profiler on and off, but only if <code>remote=on</code>
056: * has been specified in the profile properties. Here's a short
057: * description of all of the properties that are supported:
058: <ul>
059: <li><a href="#profiler">profiler</a></li>
060: <li><a href="#remote">remote</a></li>
061: <li><a href="#port">port</a></li>
062: <li><a href="#classloader">ClassLoaderFilter.x</a></li>
063: <li><a href="#thread-depth">thread-depth</a></li>
064: <li><a href="#thread-threshold">thread.compact.threshold.ms</a></li>
065: <li><a href="#max-method-count">max-method-count</a></li>
066: <li><a href="#method-threshold">method.compact.threshold.ms</a></li>
067: <li><a href="#file">file</a></li>
068: <li><a href="#exlcude">exclude</a></li>
069: <li><a href="#include">include</a></li>
070: <li><a href="#alloc">track.object.alloc</a></li>
071: <li><a href="#output">output</a></li>
072: <li><a href="#debug">debug</a></li>
073: <li><a href="#profiler-class">profiler-class</a></li>
074: <li><a href="#output-method-signatures">output-method-signatures</a></li>
075: <li><a href="#clock-resolution">clock-resolution</a></li>
076: <li><a href="#output-summary-only">output-summary-only</li>
077: <li><a href="#accept-class-loaders">accept-class-loaders</li>
078: </ul>
079:
080: <A NAME="profiler"/>
081: <h3>profiler</h3>
082: <blockquote>
083: <b>Values</b>: on, off<br/>
084: <b>Default</b>: on<br/>
085: <b>Description</b>: controls whether or not profiling information
086: is gathered when the VM starts. Usually you'll want this to
087: be on for command-line apps but off if you're profiling a web
088: app.
089: </blockquote>
090:
091: <a name="remote"/>
092: <h3>remote</h3>
093: <blockquote>
094: <b>Values</b>: on, off<br/>
095: <b>Default</b>: off<br/>
096: <b>Description</b>: controls whether of not the remote interface
097: is enabled or not. The remote interface allows you to turn the
098: profiler on and off at runtime. This lets you take multiple
099: measurements without having to stop and start the application.
100: Usually you'll want this to be <code>on</code> for webapps
101: but <code>off</code> for command-line apps.
102: </blockquote>
103:
104: <a name="port"/>
105: <h3>port</h3>
106: <blockquote>
107: <b>Values</b>: any valid TCP port number<br/>
108: <b>Default</b>: 15599<br/>
109: <b>Description</b>: this controls which port the remote interface
110: listens on.
111: </blockquote>
112:
113: <a name="classloader"/>
114: <h3>ClassLoaderFilter.x</h3>
115: <blockquote>
116: <b>Values</b>: any valid implemtation of
117: <code>com.mentorgen.tools.profile.ClassLoaderFilter</code><br/>
118: <b>Default</b>: If no class loader filters a specificed then
119: <code>net.sourceforge.jiprof.instrument.clfilter.GenericClassLoaderFilter</code>
120: is used (see also: <a href="#accept-class-loaders">accept-class-loaders</a>).<br/>
121: <b>Description</b>: JIP has to know which classloader will be
122: loading the classes to be profiled. With command-line
123: apps we know what this is. However webapps and other
124: kinds of apps that run in a container use different classloaders.
125: The solution to this was to defined an interface:
126: <code>ClassLoaderFilter</code> to use in a chain of responsilbility
127: pattern to determine which classloader should be "hooked"
128: for profiling. The way this works is that you can define a number
129: of realizations of this interface, one of each different env.
130: You specify the search order by appending a number to the end
131: of the property. For exmaple, the standard setup is:<pre><code>
132: ClassLoaderFilter.1=com.mentorgen.tools.profile.instrument.clfilter.WebAppClassLoaderFilter
133: ClassLoaderFilter.2=com.mentorgen.tools.profile.instrument.clfilter.StandardClassLoaderFilter
134: </code></pre>
135: This indicates that the <code>WebAppClassLoaderFilter</code>
136: should be called to determine if we're running in Tomcat. If that
137: fails, call the <code>StandardClassLoaderFilter</code>. Note
138: that currently only the Java 5(tm) and Tomcat 5.5 environments
139: are supported. People who would like to add support for other
140: environments are encouraged to do so.
141: </blockquote>
142:
143: <a name="thread-depth"/>
144: <h3>thread-depth</h3>
145: <blockquote>
146: <b>Values</b>: any positive integer, -1 or <i>compact</i><br/>
147: <b>Default</b>: -1<br/>
148: <b>Description</b>: Call stacks can get really deep and sometimes
149: you only want to see a certain number of levels. This parameter
150: controls the number of levels you will see. The default is -1
151: which means that there is no limit. Another option that can
152: be used is <i>compact</i>. This will limit the call stacks
153: to items that have a gross time that is at least 10 ms (this
154: can be <a href="#hread-threshold">changed</a>). Using
155: <i>compact</i> is nice way to limit what you see while not
156: imposing an arbitrary limit on the thread-depth.
157: </blockquote>
158:
159: <a name="thread-threshold"/>
160: <h3>thread.compact.threshold.ms</h3>
161: <blockquote>
162: <b>Values</b>: any positive integer<br/>
163: <b>Default</b>: 10<br/>
164: <b>Description</b>: Modifies the call stack output to
165: only show nodes with the given gross time. Only works when
166: <code>thread-depth</code> is set to <i>compact</i>
167: </blockquote>
168:
169: <a name="max-method-count"/>
170: <h3>max-method-count</h3>
171: <blockquote>
172: <b>Values</b>: any positive integer, -1 or <i>compact</i><br/>
173: <b>Default</b>: -1<br/>
174: <b>Description</b>: This property modifieds the section
175: of the profiler output that shows the most expensive method.
176: Giving a number greater than -1 will limit the number of methods
177: that are shown. -1 means no limit. <i>compact</i> can be usd to
178: show only methods with a creatin minimum gross time (the
179: default is 10ms but can be changed by using
180: <a href="#method-threshold">method.compact.threshold.ms</a>
181: </blockquote>
182:
183: <a name="method-threshold"/>
184: <h3>method.compact.threshold.ms</h3>
185: <blockquote>
186: <b>Values</b>: any positive integer<br/>
187: <b>Default</b>: 10<br/>
188: <b>Description</b>: Modifies the method output to
189: only show methods with the given gross time. Only works when
190: <code>max-method-count</code> is set to <i>compact</i>.
191: </blockquote>
192:
193: <a name="file"/>
194: <h3>file</h3>
195: <blockquote>
196: <b>Values</b>: the name of any valid file or directory.<br/>
197: <b>Default</b>: ./profile.txt<br/>
198: <b>Description</b>: Names the file that the profile is
199: sent to. If this is a directory, JIP will auto-generate
200: file names and
201: put the files in that directory. The format for the
202: generated file name is <code>yyyyMMdd-HHmmss</code>.
203: </blockquote>
204:
205: <a name="exlcude"/>
206: <h3>exclude</h3>
207: <blockquote>
208: <b>Values</b>: a comman spearated list of package or class
209: names (class names must be fully qualified).<br/>
210: <b>Default</b>: <i>no default</i><br/>
211: <b>Description</b>: the values for this property name
212: packages or classes to be excluded from the profile. This
213: is handy when you have a chatty package or class that you
214: just don't want to see all over the place. Note that only
215: classes that are loaded by the "app" class loader
216: are profiled to start with.
217: </blockquote>
218:
219: <a name="include"/>
220: <h3>include</h3>
221: <blockquote>
222: <b>Values</b>: a comman spearated list of package or class
223: names (class names must be fully qualified).<br/>
224: <b>Default</b>: <i>no default</i><br/>
225: <b>Description</b>: the values for this property name
226: packages or classes to be explicitly included in the profile.
227: Normally, you wouldn't use this, you'd let the <code><a href="#classloader">ClassLoaderFilter</a></code>
228: determine which classes to include. If you don't want to see something,
229: use <code><a href="#exlcude">exclude</a></code>. However, there
230: are situations where you want to exclude so much stuff, that it's easier
231: just to say what you want to be included. When using both exclude and include,
232: the include list is applied, then the exclude list is applied.
233: </blockquote>
234:
235: <a name="alloc"/>
236: <h3>track.object.alloc</h3>
237: <blockquote>
238: <b>Values</b>: <code>on</code> or <code>off</code><br/>
239: <b>Default</b>: <code>off</code><br/>
240: <b>Description</b>: control whether or not JIP tracks
241: object allocation.
242: </blockquote>
243:
244: <a name="output"/>
245: <h3>output</h3>
246: <blockquote>
247: <b>Values</b>: <code>text</code>, <code>xml</code> or <code>both</code><br/>
248: <b>Default</b>: <code>text</code><br/>
249: <b>Description</b>: in addition to the standard human readable
250: profiles, this option allows you to output the profile information
251: in a raw XML format.
252: </blockquote>
253:
254: <a name="debug"/>
255: <h3>debug</h3>
256: <blockquote>
257: <b>Values</b>: <code>on</code> or <code>off</code><br/>
258: <b>Default</b>: <code>off</code><br/>
259: <b>Description</b>: when debug is turned on, text will be sent to
260: standard out each time a class is classloaded and inspected by
261: the profiler for possbile instrumentation (see <code>
262: com.mentorgen.tools.profile.instrument.Transformer</code>). If the
263: class is instrumented, <code>INST</code>, plus the class name
264: plus the classloader name will be sent to stddout. If the class
265: is not instrumented, <code>skip</code>, plus the class name
266: plus the classloader name will be sent to stddout. This is a
267: helpful tool when the profile you're getting (or not getting)
268: doesn't match what you're expecting.<p/>
269: In addition, text will be sent to standard error when an exception is
270: detected and when the profile for a method has not been completed
271: when the profiler terminates. <br/>
272: Exceptions are usually handled gracefully.
273: However, there are some cases where they skew the timings and therefore
274: the output is incorrect. Knowing that an exception is being thrown is a great
275: help in diagnosing problems like this.<br/>
276: Needing to "fixup" the profile
277: for one or two methods is also not that unusual. However, if the timing
278: for a method seems to be incorrect, knowing if the profiler needed to
279: fixup that method can be useful from a diagnosics perspective.
280: </blockquote>
281:
282: <a name="profiler-class"/>
283: <h3>profiler-class</h3>
284: <blockquote>
285: <b>Values</b>: any class name<br/>
286: <b>Default</b>: <code>com.mentorgen.tools.profile.runtime.Profile</code><br/>
287: <b>Description</b>: allows the another profiling backend to be used.
288: </blockquote>
289:
290: <a name="output-method-signatures"/>
291: <h3>output-method-signatures</h3>
292: <blockquote>
293: <b>Values:</b> <code>yes</code> or <code>no</code></br>
294: <b>Default:</b> <code>no</code></br>
295: <b>Description:</b> When set to <code>yes</code>, outputs the signature
296: of methods. By default, the method signature is omitted from the output
297: to save space. However, if you're dealing with methods that have been overloaded
298: you need to be able to see the method signature.</br>
299: </blockquote>
300:
301: <a name="clock-resolution"/>
302: <h3>clock-resolution</h3>
303: <blockquote>
304: <b>Values:</b> <code>ms</code> or <code>ns</code></br>
305: <b>Default:</b> <code>ns</code></br>
306: <b>Description:</b> Sets the resolution of the TimeLineProfiler's clock to either milliseconds
307: (<code>ms</code>) or nanoseconds (<code>ns</code>). Only valid when using the <code>
308: TimeLineProfiler</code>.</br>
309: </blockquote>
310:
311: <a name="output-summary-only"/>
312: <h3>output-summary-only</h3>
313: <blockquote>
314: <b>Values:</b> <code>yes</code> or <code>no</code></br>
315: <b>Default:</b> <code>no</code></br>
316: <b>Description:</b> When set to <code>yes</code> the top most section of the profiler output
317: (the section that contains thread + call stack information) is omitted. The section can be
318: quite large so it is sometime desirable to not have to page through it to get to the
319: summary information. </br>
320: </blockquote>
321:
322: <a name="accept-class-loaders"/>
323: <h3>accept-class-loaders</h3>
324: <blockquote>
325: <b>Values:</b> A comma separated list of classloader names (you can also specify
326: interface names)</br>
327: <b>Default:</b> If no values are given, <code>java.lang.ClassLoader.getSystemClassLoader()</code>
328: is used.</br>
329: <b>Description:</b> A list of <code>Class Loaders</code> whose classes will be instrumented
330: when using <code>net.sourceforge.jiprof.instrument.clfilter.GenericClassLoaderFilter</code>
331: as the classloader filter. Note that when looking to determine if profiling should be applied
332: to a classloader, <code>instanceof</code> is used as the mode of comparison. This means, for
333: example, that when profiling Tomcat, you can specify <code>org.apache.catalina.loader.Reloader</code>
334: which is an interface rather than a subclass of <code>java.lang.ClassLoader</code>.
335: </br>
336: </blockquote>
337: *
338: *
339: *
340: * @author Andrew Wilcox
341: *
342: */
343: public class Controller implements Runnable {
344: private static final String DEFAULT_PROFILE = "on";
345: private static final String DEFAULT_REMOVE = "off";
346: private static final String DEFAULT_PORT = "15599";
347: private static final String DEFAULT_MAX_THREAD_DEPTH = "-1";
348: private static final String DEFAULT_THREAD_COMPACT_THRESHOLD = "10";
349: private static final String DEFAULT_MAX_METHOD_COUNT = "-1";
350: private static final String DEFAULT_METHOD_COMPACT_THRESHOLD = "10";
351: private static final String DEFAULT_FILE = "profile.txt";
352: private static final String DEFAULT_OBJECT_ALLOC = "off";
353: private static final String DEFAULT_PROFILER_CLASS = "com.mentorgen.tools.profile.runtime.Profile";
354:
355: private static final String ON = "on";
356:
357: public static final int UNLIMITED = -1;
358:
359: private static final String START = "start";
360: private static final String STOP = "stop";
361: private static final String DUMP = "dump";
362: private static final String FILE = "file";
363: private static final String CLEAR = "clear";
364: private static final String FINISH = "finish";
365:
366: public static enum OutputType {
367: Text, XML, Both
368: };
369:
370: public static enum TimeResolution {
371: ms, ns
372: };
373:
374: public static boolean _profile;
375: public static boolean _remote;
376: public static int _port;
377: public static int _threadDepth;
378: public static int _methodCount;
379: public static String _fileName;
380: public static String[] _excludeList;
381: public static String[] _includeList;
382: public static boolean _compactThreadDepth = false;
383: public static boolean _compactMethodCount = false;
384: public static int _compactThreadThreshold;
385: public static int _compactMethodThreshold;
386: public static boolean _trackObjectAlloc = false;
387: public static ClassLoaderFilter _filter;
388: public static OutputType _outputType = OutputType.Text;
389: public static boolean _debug = false;
390: public static String _profiler;
391: public static boolean _outputMethodSignatures = false;
392: public static TimeResolution _timeResoltion;
393: public static boolean _outputSummaryOnly = false;
394: public static Class[] _acceptClassLoaders;
395:
396: public static int _instrumentCount = 0;
397:
398: private ServerSocket _socket;
399:
400: static {
401: Properties props = new Properties();
402: String propsFile = System.getProperty("profile.properties");
403:
404: if (propsFile != null) {
405: try {
406: props.load(new FileInputStream(propsFile));
407: } catch (IOException e) {
408: System.err.print("Unable to open ");
409: System.err.print(propsFile);
410: System.err.println(". Using the defaults.");
411: }
412: }
413:
414: String profile = getProperty(props, "profiler", DEFAULT_PROFILE);
415: String remote = getProperty(props, "remote", DEFAULT_REMOVE);
416: String port = getProperty(props, "port", DEFAULT_PORT);
417: String threadDepth = getProperty(props, "thread-depth",
418: DEFAULT_MAX_THREAD_DEPTH);
419: String threadCompactThreshold = getProperty(props,
420: "thread.compact.threshold.ms",
421: DEFAULT_THREAD_COMPACT_THRESHOLD);
422: String maxMethodCount = getProperty(props, "max-method-count",
423: DEFAULT_MAX_METHOD_COUNT);
424: String methodCompactThreshold = getProperty(props,
425: "method.compact.threshold.ms",
426: DEFAULT_METHOD_COMPACT_THRESHOLD);
427: String file = getProperty(props, "file", DEFAULT_FILE);
428: String objectAlloc = getProperty(props, "track.object.alloc",
429: DEFAULT_OBJECT_ALLOC);
430: String outputType = getProperty(props, "output", "text");
431: String debug = getProperty(props, "debug", "off");
432: String profiler = getProperty(props, "profiler-class",
433: DEFAULT_PROFILER_CLASS);
434: String methodSigs = getProperty(props,
435: "output-method-signatures", "no");
436: String clockResolution = getProperty(props, "clock-resolution",
437: "ms");
438: String outputSummaryOnly = getProperty(props,
439: "output-summary-only", "no");
440:
441: Controller._profile = profile.equals(ON);
442: Controller._remote = remote.equals(ON);
443: Controller._port = Integer.parseInt(port);
444: Controller._compactThreadThreshold = Integer
445: .parseInt(threadCompactThreshold);
446: Controller._compactMethodThreshold = Integer
447: .parseInt(methodCompactThreshold);
448:
449: if ("compact".equals(threadDepth.trim())) {
450: Controller._compactThreadDepth = true;
451: } else {
452: Controller._threadDepth = Integer.parseInt(threadDepth);
453: }
454:
455: if ("compact".equals(maxMethodCount.trim())) {
456: Controller._compactMethodCount = true;
457: } else {
458: Controller._methodCount = Integer.parseInt(maxMethodCount);
459: }
460:
461: if ("on".equalsIgnoreCase(objectAlloc.trim())) {
462: _trackObjectAlloc = true;
463: }
464:
465: if ("on".equalsIgnoreCase(debug.trim())) {
466: _debug = true;
467: }
468:
469: if ("yes".equalsIgnoreCase(methodSigs)) {
470: _outputMethodSignatures = true;
471: }
472:
473: if ("text".equalsIgnoreCase(outputType.trim())) {
474: _outputType = OutputType.Text;
475: } else if ("xml".equalsIgnoreCase(outputType.trim())) {
476: _outputType = OutputType.XML;
477: } else if ("both".equalsIgnoreCase(outputType.trim())) {
478: _outputType = OutputType.Both;
479: }
480:
481: if ("ms".equalsIgnoreCase(clockResolution)) {
482: _timeResoltion = TimeResolution.ms;
483: } else {
484: _timeResoltion = TimeResolution.ns;
485: }
486:
487: if ("yes".equalsIgnoreCase(outputSummaryOnly)) {
488: _outputSummaryOnly = true;
489: }
490:
491: Controller._fileName = file;
492:
493: String excludeList = props.getProperty("exclude");
494: String includeList = props.getProperty("include");
495:
496: System.out.print("exclude:");
497: System.out.println(excludeList);
498:
499: if (includeList != null && includeList.length() > 0) {
500: System.out.print("include:");
501: System.out.println(includeList);
502: }
503:
504: Controller._excludeList = parseList(excludeList, true);
505: Controller._includeList = parseList(includeList, true);
506:
507: String[] classLoaderNames = parseList(props.getProperty(
508: "accept-class-loaders", ClassLoader
509: .getSystemClassLoader().getClass().getName()),
510: false);
511:
512: Controller._acceptClassLoaders = new Class[classLoaderNames.length];
513:
514: for (int i = 0; i < classLoaderNames.length; i++) {
515: try {
516: Controller._acceptClassLoaders[i] = Class
517: .forName(classLoaderNames[i]);
518: System.out.println("Accept ClassLoader: "
519: + Controller._acceptClassLoaders[i].getName());
520: } catch (ClassNotFoundException e) {
521: System.err.println("UNKNOWN CLASSLOADER: "
522: + classLoaderNames[i]);
523: System.err
524: .println("Using the system classloader instead");
525: Controller._acceptClassLoaders[i] = ClassLoader
526: .getSystemClassLoader().getClass();
527: }
528: }
529:
530: // get the class loader filter;
531:
532: for (int i = 1;; i++) {
533: StringBuffer b = new StringBuffer("ClassLoaderFilter.");
534: b.append(i);
535: String filter = getProperty(props, b.toString(), null);
536:
537: if (filter == null) {
538: break;
539: }
540:
541: try {
542: ClassLoaderFilter clf = (ClassLoaderFilter) Class
543: .forName(filter).newInstance();
544:
545: if (clf.canFilter()) {
546: _filter = clf;
547: break;
548: }
549: } catch (Exception e) {
550: System.err
551: .print("Could not instantiate ClassLoaderFilter ");
552: System.err.println(filter);
553: }
554: }
555:
556: if (_filter == null) {
557: System.err
558: .println("Using the generic class loader filter.");
559: _filter = new GenericClassLoaderFilter();
560: }
561:
562: Controller._profiler = profiler.replace('.', '/');
563:
564: try {
565: Class c = Class.forName(profiler);
566: Method m = c.getMethod("initProfiler", new Class[0]);
567: m.invoke(null, new Object[0]);
568: } catch (Exception e) {
569: System.err
570: .println("Unable to invoke init on Profiler class.");
571: }
572:
573: System.out.println("------------------");
574: }
575:
576: private static String[] parseList(String list, boolean doReplacement) {
577: if (list == null || list.length() == 0 || list.equals("null")) {
578: return new String[0];
579: }
580:
581: ArrayList<String> al = new ArrayList<String>();
582: StringTokenizer t = new StringTokenizer(list, ", ");
583:
584: while (t.hasMoreTokens()) {
585: String token = t.nextToken();
586:
587: if (doReplacement) {
588: al.add(token.replace('.', '/'));
589: } else {
590: al.add(token);
591: }
592: }
593:
594: String[] sl = new String[al.size()];
595: al.toArray(sl);
596: return sl;
597: }
598:
599: private static String getProperty(Properties props, String key,
600: String defaultValue) {
601:
602: String value = props.getProperty(key, defaultValue);
603: System.out.print(key);
604: System.out.print(": ");
605: System.out.println(value);
606: return value;
607: }
608:
609: public void setFileName(String fileName) {
610: _fileName = fileName;
611: }
612:
613: //
614: // Thread to open a server socket and listen for commands
615: //
616:
617: public void run() {
618: try {
619: _socket = new ServerSocket(_port);
620:
621: top: while (true) {
622: Socket child = _socket.accept();
623:
624: // so that someone cannot tie-up this socket for too long
625: //
626: child.setSoTimeout(5000);
627: InputStream in = child.getInputStream();
628: BufferedInputStream bin = new BufferedInputStream(in);
629: StringBuffer b = new StringBuffer();
630:
631: while (true) {
632: char c = (char) bin.read();
633:
634: if (c == '\r') {
635: break;
636: } else {
637: b.append(c);
638: }
639: }
640:
641: String command = b.toString();
642: System.out.println(command);
643:
644: if (command.startsWith(START)) {
645: start();
646: } else if (command.startsWith(STOP)) {
647: stop();
648: } else if (command.startsWith(DUMP)) {
649: ProfileDump.dump();
650: } else if (command.startsWith(CLEAR)) {
651: Profile.init();
652: } else if (command.startsWith(FILE)) {
653: String name = command.substring(command
654: .indexOf(' ') + 1);
655: _fileName = name;
656: } else if (command.startsWith(FINISH)) {
657: stop(); // stop
658: ProfileDump.dump(); // dump
659: Profile.init(); // clear
660: }
661:
662: child.close();
663: }
664: } catch (SocketException e) {
665: // eat this type of exception ...
666: } catch (IOException e) {
667: e.printStackTrace();
668: throw new RuntimeException(e);
669: }
670:
671: }
672:
673: public void close() throws IOException {
674: System.err.println("Controller -- shuttingdown");
675:
676: if (_remote && _socket != null && !_socket.isClosed()) {
677: _socket.close();
678: }
679: }
680:
681: public void start() {
682:
683: // Nice little hack to explain to the user why they aren't getting
684: // a profile.
685: //
686: if (_instrumentCount == 0) {
687: System.err
688: .println("Warning: a request has been made to start the "
689: + "profiler but no classes have been instrumented. "
690: + "Possible reasons: ");
691: System.err
692: .println("1. All the classes that have been classloaded "
693: + "have been \"excluded\". Check the exclude property in "
694: + "the current profile properties file.");
695: System.err
696: .println("2. No appropriate class loader filter has been "
697: + "provided (see \"ClassLoaderFilter.x\" in the current "
698: + "profile properties file.) If no appropriate "
699: + "filter can be found, the standard filter is used. This"
700: + "filter is really only useful for stand-alone "
701: + "applications. Make sure your environment has a "
702: + "ClassLoaderFilter and that your profile properties "
703: + "file is configured correctly.");
704: System.err
705: .println("3. Sometimes when Tomcat is launched from "
706: + "within "
707: + "Eclipse, Tomcat, for some reason, will start using the "
708: + "appication classloader (the one that stand-alone "
709: + "apps use) to classload the webapp rather than the "
710: + "web app classloader that it should use. One thing that "
711: + "you can try to get around this odd behavior is to not "
712: + "run Tomcat in debug mode (Window > Perferences > Tomcat > "
713: + "JVM settings : Don't run Tomcat in debug mode.) If this "
714: + "doesn't work, you could try removing the webapp "
715: + "classlaoder filter from your profile properties file.");
716: }
717:
718: Profile.clear();
719: _profile = true;
720: }
721:
722: public void stop() {
723:
724: // Explain to the user why they aren't going to be seeing a profile.
725: //
726: if (_instrumentCount == 0) {
727: System.err
728: .println("No classes have been instrumented for "
729: + "profiling. There should be a previous message to this "
730: + "effect which outlines why this is happening.");
731: }
732:
733: Profile.shutdown();
734: }
735: }
|