StackTraceUtil.java :  » Development » protomatter » com » protomatter » util » Java Open Source

Java Open Source » Development » protomatter 
protomatter » com » protomatter » util » StackTraceUtil.java
package com.protomatter.util;

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

import java.io.*;
import java.text.DecimalFormat;

/**
 *  A utility class for parsing stack traces.<P>
 *
 *  Determining the stack information at runtime
 *  is a relatively expensive operation.  I've tested this
 *  on a 650MHz PIII Coppermine Sony Vaio
 *  laptop running RedHat Linux 7.2, kernel 2.4.9,
 *  I saw these results with single-threaded tests:<P>
 *
 *  <table border=1 cellpadding=4 cellspacing=0>
 *
 *  <tr>
 *  <td>Classic VM (build JDK-1.2.2_012, green threads, nojit)</td>
 *  <td>Average 0.73543ms</td>
 *  </tr>
 *
 *  <tr>
 *  <td>Classic VM (build 1.3.1, J2RE 1.3.1 IBM build cxia32131-20020410 (JIT enabled: jitc))</td>
 *  <td>Average 0.31817ms</td>
 *  </tr>
 *
 *  <tr>
 *  <td>Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1_03-b03)</td>
 *  <td>Average 0.18201ms</td>
 *  </tr>
 *
 *  <tr>
 *  <td>Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)</td>
 *  <td>Average 0.1024ms</td>
 *  </tr>
 *
 *  <tr>
 *  <td>JRockit Virtual Machine (build 3.1.4-dax.appeal.se-20020319-1000)<BR>
 *      Native Threads, Generational Concurrent Garbage Collector</td>
 *  <td valign=top>Average 0.0486ms</td>
 *  </tr>
 *
 *  </table><P>
 *
 *  If possible, this class loads an implementation
 *  of itself that uses new APIs available in JDK 1.4
 *  to improve performance.  Under JDK 1.2 and 1.3,
 *  this class parses a stack trace to determine
 *  call stack information.<P>
 *
 *  Also, under the IBM and JRockit JVMs, line numbers
 *  are usually not available.  They may or may
 *  not be availble under other JVMs because the JIT
 *  may or may not strip that information from
 *  stack traces.<P>
 *
 *  Sustained, rapid creation of <tt>java.lang.Throwable</tt>
 *  objects under the IBM JDK repeatedly caused JVM deadlocks.
 *  This should not be an issue in actual systems, but
 *  it is concerning.
 */
public class StackTraceUtil
{
    private static StackTraceUtil instance = null;

    static
    {
        // try and load the JDK 1.4 version of ourselves
        // and fail back on the "normal" version.
        // The JDK 1.4 version is much faster.
        try
        {
            instance = (StackTraceUtil)Class.forName("com.protomatter.util.JDK14StackTraceUtil").newInstance();
            instance.getInfo(0);
        }
        catch (Throwable t)
        {
            instance = new StackTraceUtil();
        }
    }

    /**
     *  Protected constructor so nobody instantiates this class.
     */
    protected StackTraceUtil()
    {
    }

    private static String LINE_SEP = System.getProperty("line.separator");

    private static char SPACE = ' ';
    private static char DOT = '.';
    private static char COLON = ':';
    private static char OPEN_P = '(';
    private static char CLOSE_P = ')';

    /**
     * Determine what class and method you are in.
     */
    public static StackTraceInfo whereAmI()
    {
        return instance.getInfo(1);
    }

    /**
     * Determine what class and method you are in.
     * The offset is how many levels above where
     * this method is called.
     */
    public static StackTraceInfo whereAmI(int stackOffset)
    {
        return instance.getInfo(++stackOffset);
    }

    protected StackTraceInfo getInfo(int stackOffset)
    {
        Throwable t = new Throwable();
        StringWriter sw = new StringWriter(256);
        PrintWriter pw = new PrintWriter(sw);
        t.printStackTrace(pw);
        String stack = sw.toString();

        stackOffset++;

        int start;
        int end;
        int dot;

        String className = null;
        String methodName = null;
        int lineNumber = StackTraceInfo.LINE_NUMBER_UNKNOWN;

        try
        {
            start = stack.indexOf(LINE_SEP) +1;
            for (dot=0; dot<stackOffset; dot++)
            {
                start = stack.indexOf(LINE_SEP, ++start);
            }
            start = stack.indexOf(SPACE, start);
            end = stack.indexOf(OPEN_P, start);
            dot = stack.lastIndexOf(DOT, end);

            className = stack.substring(++start, dot);
            methodName = stack.substring(++dot, end);

            // see if we can get the line number
            dot = stack.indexOf(COLON, end);
            if (dot > 0)
            {
              end = stack.indexOf(CLOSE_P, dot);
              lineNumber = Integer.parseInt(stack.substring(++dot, end));
            }
        }
        catch (Exception x)
        {
            ; // just return as much as is ready
        }

        return new StackTraceInfo(className, methodName, lineNumber);
    }

    private static class TestThread
    extends Thread
    {
        private int numRuns = 0;
        private long time = 0;
        private StackTraceInfo info = null;

        public TestThread(int runs)
        {
            super();
            this.numRuns = runs;
        }

        public void run()
        {
            time = System.currentTimeMillis();
            for (int i=0; i<numRuns; i++)
            {
                info = StackTraceUtil.whereAmI();
            }
            time = System.currentTimeMillis() - time;
        }

        public void info()
        {
            DecimalFormat format = new DecimalFormat("####.######");
            DecimalFormat tf = new DecimalFormat("###,###,##0");
            System.out.println("   " + tf.format(numRuns) + " runs in " + tf.format(time) + "ms");
            double average = ((double)time / (double)numRuns);
            System.out.println("   Average is " + format.format(average) + "ms");
            System.out.println("   Stack trace info = " + info);
            System.out.println("");
        }
    }

    /**
     *  Performance test rig.  Optional command-line
     *  arguments are the number of threads (default is 5),
     *  and number of calls per thread (default is 10,000).
     */
    public static void main(String args[])
    {
        try
        {
            int numThreads = 5;
            int numTries = 10000;

            if (args.length == 2)
            {
                try
                {
                    numThreads = Integer.parseInt(args[0]);
                    numTries = Integer.parseInt(args[1]);
                }
                catch (NumberFormatException x)
                {
                    System.out.println("Usage: java com.protomatter.util.StackTraceUtil num-threads num-calls");
                    System.exit(0);
                }
            }
            else if ((args.length == 1) || (args.length > 2))
            {
                System.out.println("Usage: java com.protomatter.util.StackTraceUtil num-threads num-calls");
                System.exit(0);
            }

            DecimalFormat tf = new DecimalFormat("###,###,##0");
            System.out.println("StackTraceUtil.whereAmI() test:");
            System.out.println("");
            System.out.println("JVM Information:");
            System.out.println("  VM Name:          " + System.getProperty("java.vm.name"));
            System.out.println("  VM Version:       " + System.getProperty("java.vm.version"));
            System.out.println("  Runtime name:     " + System.getProperty("java.runtime.name"));
            System.out.println("  Runtime version:  " + System.getProperty("java.runtime.version"));
            System.out.println("");
            System.out.println("OS Information:");
            System.out.println("  " + System.getProperty("os.name") + " " + System.getProperty("os.version"));
            System.out.println("");
            System.out.println("Creating " + numThreads + " test threads (" + tf.format(numTries) + " calls each)...");
            TestThread threads[] = new TestThread[numThreads];
            for (int i=0; i<numThreads; i++)
            {
                threads[i] = new TestThread(numTries);
            }

            System.out.println("Running test threads...");
            long time = System.currentTimeMillis();
            for (int i=0; i<numThreads; i++)
            {
                threads[i].start();
            }
            for (int i=0; i<numThreads; i++)
            {
                threads[i].join();
            }
            time = System.currentTimeMillis() - time;

            System.out.println("Per-thread results:");
            for (int i=0; i<numThreads; i++)
            {
                threads[i].info();
            }

            System.out.println("");
            System.out.println("Overall:");
            DecimalFormat format = new DecimalFormat("####.######");
            System.out.println("   " + tf.format((numThreads * numTries)) + " runs in " + tf.format(time) + "ms");
            double average = ((double)time / (double)(numThreads * numTries));
            System.out.println("   Average is " + format.format(average) + "ms");
            System.out.println("");
        }
        catch (Exception x)
        {
            x.printStackTrace();
        }
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.