KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > protomatter > util > StackTraceUtil


1 package com.protomatter.util;
2
3 /**
4  * {{{ The Protomatter Software License, Version 1.0
5  * derived from The Apache Software License, Version 1.1
6  *
7  * Copyright (c) 1998-2002 Nate Sammons. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution,
22  * if any, must include the following acknowledgment:
23  * "This product includes software developed for the
24  * Protomatter Software Project
25  * (http://protomatter.sourceforge.net/)."
26  * Alternately, this acknowledgment may appear in the software itself,
27  * if and wherever such third-party acknowledgments normally appear.
28  *
29  * 4. The names "Protomatter" and "Protomatter Software Project" must
30  * not be used to endorse or promote products derived from this
31  * software without prior written permission. For written
32  * permission, please contact support@protomatter.com.
33  *
34  * 5. Products derived from this software may not be called "Protomatter",
35  * nor may "Protomatter" appear in their name, without prior written
36  * permission of the Protomatter Software Project
37  * (support@protomatter.com).
38  *
39  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
40  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
41  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
42  * DISCLAIMED. IN NO EVENT SHALL THE PROTOMATTER SOFTWARE PROJECT OR
43  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
45  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
46  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
47  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
48  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
49  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50  * SUCH DAMAGE. }}}
51  */

52
53 import java.io.*;
54 import java.text.DecimalFormat JavaDoc;
55
56 /**
57  * A utility class for parsing stack traces.<P>
58  *
59  * Determining the stack information at runtime
60  * is a relatively expensive operation. I've tested this
61  * on a 650MHz PIII Coppermine Sony Vaio
62  * laptop running RedHat Linux 7.2, kernel 2.4.9,
63  * I saw these results with single-threaded tests:<P>
64  *
65  * <table border=1 cellpadding=4 cellspacing=0>
66  *
67  * <tr>
68  * <td>Classic VM (build JDK-1.2.2_012, green threads, nojit)</td>
69  * <td>Average 0.73543ms</td>
70  * </tr>
71  *
72  * <tr>
73  * <td>Classic VM (build 1.3.1, J2RE 1.3.1 IBM build cxia32131-20020410 (JIT enabled: jitc))</td>
74  * <td>Average 0.31817ms</td>
75  * </tr>
76  *
77  * <tr>
78  * <td>Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1_03-b03)</td>
79  * <td>Average 0.18201ms</td>
80  * </tr>
81  *
82  * <tr>
83  * <td>Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)</td>
84  * <td>Average 0.1024ms</td>
85  * </tr>
86  *
87  * <tr>
88  * <td>JRockit Virtual Machine (build 3.1.4-dax.appeal.se-20020319-1000)<BR>
89  * Native Threads, Generational Concurrent Garbage Collector</td>
90  * <td valign=top>Average 0.0486ms</td>
91  * </tr>
92  *
93  * </table><P>
94  *
95  * If possible, this class loads an implementation
96  * of itself that uses new APIs available in JDK 1.4
97  * to improve performance. Under JDK 1.2 and 1.3,
98  * this class parses a stack trace to determine
99  * call stack information.<P>
100  *
101  * Also, under the IBM and JRockit JVMs, line numbers
102  * are usually not available. They may or may
103  * not be availble under other JVMs because the JIT
104  * may or may not strip that information from
105  * stack traces.<P>
106  *
107  * Sustained, rapid creation of <tt>java.lang.Throwable</tt>
108  * objects under the IBM JDK repeatedly caused JVM deadlocks.
109  * This should not be an issue in actual systems, but
110  * it is concerning.
111  */

112 public class StackTraceUtil
113 {
114     private static StackTraceUtil instance = null;
115
116     static
117     {
118         // try and load the JDK 1.4 version of ourselves
119
// and fail back on the "normal" version.
120
// The JDK 1.4 version is much faster.
121
try
122         {
123             instance = (StackTraceUtil)Class.forName("com.protomatter.util.JDK14StackTraceUtil").newInstance();
124             instance.getInfo(0);
125         }
126         catch (Throwable JavaDoc t)
127         {
128             instance = new StackTraceUtil();
129         }
130     }
131
132     /**
133      * Protected constructor so nobody instantiates this class.
134      */

135     protected StackTraceUtil()
136     {
137     }
138
139     private static String JavaDoc LINE_SEP = System.getProperty("line.separator");
140
141     private static char SPACE = ' ';
142     private static char DOT = '.';
143     private static char COLON = ':';
144     private static char OPEN_P = '(';
145     private static char CLOSE_P = ')';
146
147     /**
148      * Determine what class and method you are in.
149      */

150     public static StackTraceInfo whereAmI()
151     {
152         return instance.getInfo(1);
153     }
154
155     /**
156      * Determine what class and method you are in.
157      * The offset is how many levels above where
158      * this method is called.
159      */

160     public static StackTraceInfo whereAmI(int stackOffset)
161     {
162         return instance.getInfo(++stackOffset);
163     }
164
165     protected StackTraceInfo getInfo(int stackOffset)
166     {
167         Throwable JavaDoc t = new Throwable JavaDoc();
168         StringWriter sw = new StringWriter(256);
169         PrintWriter pw = new PrintWriter(sw);
170         t.printStackTrace(pw);
171         String JavaDoc stack = sw.toString();
172
173         stackOffset++;
174
175         int start;
176         int end;
177         int dot;
178
179         String JavaDoc className = null;
180         String JavaDoc methodName = null;
181         int lineNumber = StackTraceInfo.LINE_NUMBER_UNKNOWN;
182
183         try
184         {
185             start = stack.indexOf(LINE_SEP) +1;
186             for (dot=0; dot<stackOffset; dot++)
187             {
188                 start = stack.indexOf(LINE_SEP, ++start);
189             }
190             start = stack.indexOf(SPACE, start);
191             end = stack.indexOf(OPEN_P, start);
192             dot = stack.lastIndexOf(DOT, end);
193
194             className = stack.substring(++start, dot);
195             methodName = stack.substring(++dot, end);
196
197             // see if we can get the line number
198
dot = stack.indexOf(COLON, end);
199             if (dot > 0)
200             {
201               end = stack.indexOf(CLOSE_P, dot);
202               lineNumber = Integer.parseInt(stack.substring(++dot, end));
203             }
204         }
205         catch (Exception JavaDoc x)
206         {
207             ; // just return as much as is ready
208
}
209
210         return new StackTraceInfo(className, methodName, lineNumber);
211     }
212
213     private static class TestThread
214     extends Thread JavaDoc
215     {
216         private int numRuns = 0;
217         private long time = 0;
218         private StackTraceInfo info = null;
219
220         public TestThread(int runs)
221         {
222             super();
223             this.numRuns = runs;
224         }
225
226         public void run()
227         {
228             time = System.currentTimeMillis();
229             for (int i=0; i<numRuns; i++)
230             {
231                 info = StackTraceUtil.whereAmI();
232             }
233             time = System.currentTimeMillis() - time;
234         }
235
236         public void info()
237         {
238             DecimalFormat JavaDoc format = new DecimalFormat JavaDoc("####.######");
239             DecimalFormat JavaDoc tf = new DecimalFormat JavaDoc("###,###,##0");
240             System.out.println(" " + tf.format(numRuns) + " runs in " + tf.format(time) + "ms");
241             double average = ((double)time / (double)numRuns);
242             System.out.println(" Average is " + format.format(average) + "ms");
243             System.out.println(" Stack trace info = " + info);
244             System.out.println("");
245         }
246     }
247
248     /**
249      * Performance test rig. Optional command-line
250      * arguments are the number of threads (default is 5),
251      * and number of calls per thread (default is 10,000).
252      */

253     public static void main(String JavaDoc args[])
254     {
255         try
256         {
257             int numThreads = 5;
258             int numTries = 10000;
259
260             if (args.length == 2)
261             {
262                 try
263                 {
264                     numThreads = Integer.parseInt(args[0]);
265                     numTries = Integer.parseInt(args[1]);
266                 }
267                 catch (NumberFormatException JavaDoc x)
268                 {
269                     System.out.println("Usage: java com.protomatter.util.StackTraceUtil num-threads num-calls");
270                     System.exit(0);
271                 }
272             }
273             else if ((args.length == 1) || (args.length > 2))
274             {
275                 System.out.println("Usage: java com.protomatter.util.StackTraceUtil num-threads num-calls");
276                 System.exit(0);
277             }
278
279             DecimalFormat JavaDoc tf = new DecimalFormat JavaDoc("###,###,##0");
280             System.out.println("StackTraceUtil.whereAmI() test:");
281             System.out.println("");
282             System.out.println("JVM Information:");
283             System.out.println(" VM Name: " + System.getProperty("java.vm.name"));
284             System.out.println(" VM Version: " + System.getProperty("java.vm.version"));
285             System.out.println(" Runtime name: " + System.getProperty("java.runtime.name"));
286             System.out.println(" Runtime version: " + System.getProperty("java.runtime.version"));
287             System.out.println("");
288             System.out.println("OS Information:");
289             System.out.println(" " + System.getProperty("os.name") + " " + System.getProperty("os.version"));
290             System.out.println("");
291             System.out.println("Creating " + numThreads + " test threads (" + tf.format(numTries) + " calls each)...");
292             TestThread threads[] = new TestThread[numThreads];
293             for (int i=0; i<numThreads; i++)
294             {
295                 threads[i] = new TestThread(numTries);
296             }
297
298             System.out.println("Running test threads...");
299             long time = System.currentTimeMillis();
300             for (int i=0; i<numThreads; i++)
301             {
302                 threads[i].start();
303             }
304             for (int i=0; i<numThreads; i++)
305             {
306                 threads[i].join();
307             }
308             time = System.currentTimeMillis() - time;
309
310             System.out.println("Per-thread results:");
311             for (int i=0; i<numThreads; i++)
312             {
313                 threads[i].info();
314             }
315
316             System.out.println("");
317             System.out.println("Overall:");
318             DecimalFormat JavaDoc format = new DecimalFormat JavaDoc("####.######");
319             System.out.println(" " + tf.format((numThreads * numTries)) + " runs in " + tf.format(time) + "ms");
320             double average = ((double)time / (double)(numThreads * numTries));
321             System.out.println(" Average is " + format.format(average) + "ms");
322             System.out.println("");
323         }
324         catch (Exception JavaDoc x)
325         {
326             x.printStackTrace();
327         }
328     }
329 }
330
Popular Tags