You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@velocity.apache.org by jo...@locus.apache.org on 2000/12/04 05:22:35 UTC
cvs commit: jakarta-velocity/src/java/org/apache/velocity/util Timer.java Trace.java
jon 00/12/03 20:22:35
Added: src/java/org/apache/velocity/util Timer.java Trace.java
Log:
the start of using tracing code.
Revision Changes Path
1.1 jakarta-velocity/src/java/org/apache/velocity/util/Timer.java
Index: Timer.java
===================================================================
package org.apache.velocity.util;
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000 The Apache Software Foundation. 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 acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Velocity", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 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 APACHE SOFTWARE FOUNDATION 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* This class was originally written by
* Carl Ludwig <ca...@destinymusic.com>. We appreciate his contributions.
*/
import java.util.*;
/**
A stopwatch class. Measures real time in milliseconds.
*/
public class Timer
{
Date startTime;
double seconds;
double totalSeconds;
/**
Creates a new timer. The timer does not start running until start is called.
*/
public Timer()
{
reset();
}
/**
Returns the time between start and stop. If the timer is running, it returns
the time since start was called.
*/
public double getSeconds()
{
return seconds + calcTime();
}
/**
Returns the total time spent running since the timer was created. Useful for
aggregating several invocations of start & stop.
*/
public double getTotalSeconds()
{
return totalSeconds + calcTime();
}
/**
Resets the timer. Both seconds and total seconds are cleared.
*/
public void reset()
{
startTime = null;
seconds = 0;
totalSeconds = 0;
}
/**
Starts the timer.
*/
public void start()
{
if (timerRunning())
return;
startTime = new Date();
}
/**
Stops the timer.
*/
public void stop()
{
if (!timerRunning())
return;
seconds = calcTime();
totalSeconds += seconds;
startTime = null;
}
/**
Returns true if the timer is running.
*/
public boolean timerRunning()
{
return (startTime != null);
}
double calcTime()
{
if (!timerRunning())
return 0.0;
Date now = new Date();
return (now.getTime() - startTime.getTime()) / 1000.0;
}
}
1.1 jakarta-velocity/src/java/org/apache/velocity/util/Trace.java
Index: Trace.java
===================================================================
package org.apache.velocity.util;
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000 The Apache Software Foundation. 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 acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Velocity", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 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 APACHE SOFTWARE FOUNDATION 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* This class was originally written by
* Carl Ludwig <ca...@destinymusic.com>. We appreciate his contributions.
*/
import java.io.*;
import java.text.*;
import java.util.*;
/**
* Java trace facility. Useful for generating stack traces as a program executes.
* Also times each function that is traced and can print out a summary performance
* report. All timings are real (wall-clock) time. Below is an instrumented program
* with some sample output. The try and finally blocks guarantee that the trace is
* completed even if the method throws an exception:
* <blockquote>
* <pre>
* import org.apache.velocity.util.*;
*
* class TraceTest
* {
* public static void main(String argv[])
* throws InterruptedException
* {
* if (Trace.ON) Trace.start("main"); try {
*
* Thread.currentThread().sleep(500);
* f();
* int i = g();
* if (Debug.ON) Debug.log("i: " + i);
*
* } finally { if (Trace.ON) Trace.stop("main"); }
* if (Trace.ON) Trace.printTotals();
* }
*
* public static void f()
* throws InterruptedException
* {
* if (Trace.ON) Trace.start("f"); try {
*
* Thread.currentThread().sleep(200);
* //throw new InterruptedException();
*
* } finally { if (Trace.ON) Trace.stop("f"); }
* }
*
* public static int g()
* throws InterruptedException
* {
* if (Trace.ON) Trace.start("g"); try {
*
* Thread.currentThread().sleep(100);
* f();
* return 3;
*
* } finally { if (Trace.ON) Trace.stop("g"); }
* }
* }
*
* % java -Dtrace=true TraceTest
* enter main-main
* enter f-main
* leave f-main 0.2s
* enter g-main
* enter f-main
* leave f-main 0.2s
* leave g-main 0.3s
* leave main-main 1.011s
*
* Trace Totals
* ------------
* main 1 calls 1.011s total 1.011s avg.
* g 1 calls 0.3s total 0.3s avg.
* f 2 calls 0.4s total 0.2s avg.
* </pre>
* </blockquote>
* Other properties which can be set are <b>trace.detail</b>, which may be set to
* false to disable the printing of enter/leave statements, and <b>trace.pattern</b>,
* which will cause only the methods containing the string specified to be traced. You
* may also set <b>trace.log</log> to the name of a file to redirect output to a file.
*/
public class Trace
{
/** Set via the cj.trace property. */
public static final boolean ON = active();
private static final PrintWriter LOG = getLog();
private static final boolean SHOW_DETAIL = showDetail();
private static final String PATTERN = getPattern();
private static final String ENTER = "enter ";
private static final String LEAVE = "leave ";
private static final String PAD = " ";
private static NumberFormat format = getFormat();
private static Hashtable levels = new Hashtable(); // entry per thread
private static Hashtable timers = new Hashtable(); // entry per method+thread
private static Hashtable totals = new Hashtable(); // entry per method
/** Sorts and formats totals in a manner suitable for printing. */
public static final synchronized String getFormattedTotals()
{
// Sort the data from high to low time used.
int i = 0;
TraceData data[] = new TraceData[totals.size()];
Enumeration e = totals.keys();
while (e.hasMoreElements())
{
String method = (String)e.nextElement();
TraceTotal t = (TraceTotal)totals.get(method);
data[i++] = new TraceData(method, t);
}
Arrays.sort(data);
StringBuffer buf = new StringBuffer(512);
buf.append("Trace Totals\n");
buf.append("------------\n");
for (i = 0; i < data.length; i++)
{
buf.append(
pad(data[i].method, 32, false) + " " +
pad(Integer.toString(data[i].total.getCount()), 4, true) + " calls " +
pad(format.format(data[i].total.getTotal()), 8, true) + "s total " +
pad(format.format(data[i].total.getAverage()), 8, true) + "s avg.\n");
}
return buf.toString();
}
/** Pads/truncates a string. */
private static final String pad(String s, int len, boolean rightJustify)
{
if (s.length() > len)
s = s.substring(0, len-1) + '#';
int padding = len - s.length();
String pad = "";
for (int i = 0; i < padding; i++)
pad += " ";
if (rightJustify)
s = pad + s;
else
s = s + pad;
return s;
}
/** Resets the trace totals data. */
public static void reset()
{
totals.clear();
}
/** Begins trace of method. */
public static final void start(String method)
{
if (!doTrace(method))
return;
TraceKey key = new TraceKey(method);
Timer t = (Timer)timers.get(key);
if (t == null)
{
t = new Timer();
timers.put(key, t);
}
TraceLevel l = (TraceLevel)levels.get(key.threadName);
if (l == null)
{
l = new TraceLevel();
levels.put(key.threadName, l);
}
if (SHOW_DETAIL)
{
String pad = new String();
for (int i = 0; i < l.level; i++)
pad += PAD;
LOG.println(pad + ENTER + key);
}
l.level++;
t.start();
}
/** Ends trace of method. */
public static final void stop(String method)
{
if (!doTrace(method))
return;
TraceKey key = new TraceKey(method);
Timer t = (Timer)timers.get(key);
if (t == null)
{
System.err.println(
"Trace.stop: No trace started for " + key);
return;
}
t.stop();
TraceLevel l = (TraceLevel)levels.get(key.threadName);
if (l == null)
{
System.err.println(
"Trace.stop: Internal error; no level for thread "
+ key.threadName);
return;
}
l.level--;
if (SHOW_DETAIL)
{
String pad = new String();
for (int i = 0; i < l.level; i++)
pad += PAD;
LOG.println(pad + LEAVE + key + " " + t.getSeconds() + "s");
}
updateTotals(key.method, t.getSeconds());
}
/** Prints performance summary. */
public static final synchronized void printTotals()
{
printTotals(LOG);
}
/** Prints performance summary. */
public static final synchronized void printTotals(PrintStream ps)
{
printTotals(new PrintWriter(ps, true));
}
/** Prints performance summary. */
public static final synchronized void printTotals(PrintWriter writer)
{
writer.println("\n" + getFormattedTotals());
}
static final boolean active()
{
String property = System.getProperty("trace");
if (property != null)
return Boolean.valueOf(property).booleanValue();
else
return false;
}
static final boolean doTrace(String method)
{
return (PATTERN == null || method.indexOf(PATTERN) != -1);
}
static final NumberFormat getFormat()
{
NumberFormat format = NumberFormat.getInstance();
format.setMinimumFractionDigits(3);
format.setMaximumFractionDigits(3);
return format;
}
static final PrintWriter getLog()
{
String fileName = "";
try
{
fileName = System.getProperty("trace.log");
if (fileName != null && fileName.trim().length() > 0)
{
return new PrintWriter(new FileWriter(fileName, true), true);
}
}
catch (Exception e)
{
System.err.println("Can't open trace log: " +
new File(fileName).getAbsolutePath() + " : " + e);
}
return new PrintWriter(System.out, true);
}
static final String getPattern()
{
return System.getProperty("trace.pattern");
}
static final boolean showDetail()
{
String property = System.getProperty("trace.detail");
if (property != null)
return Boolean.valueOf(property).booleanValue();
else
return true;
}
static final synchronized void updateTotals(String method, double seconds)
{
TraceTotal t = (TraceTotal)totals.get(method);
if (t == null)
{
t = new TraceTotal();
totals.put(method, t);
}
t.add(seconds);
}
}
class TraceKey
{
String method;
String threadName;
TraceKey(String method)
{
this.method = method;
this.threadName = Thread.currentThread().getName();
}
public boolean equals(Object o)
{
return (this.getClass() == o.getClass()
&& this.method.equals(((TraceKey)o).method)
&& this.threadName.equals(((TraceKey)o).threadName));
}
public int hashCode()
{
return toString().hashCode();
}
public String toString()
{
return new String(method + "-" + threadName);
}
}
class TraceLevel
{
int level = 0;
}
class TraceData implements Comparable
{
String method;
TraceTotal total;
TraceData(String method, TraceTotal total)
{
this.method = method;
this.total = total;
}
public int compareTo(Object comp)
{
// Reverse order sort.
if (this.total.seconds < ((TraceData)comp).total.seconds)
return 1;
else if (this.total.seconds > ((TraceData)comp).total.seconds)
return -1;
else
return 0;
}
}
class TraceTotal
{
double seconds;
int count;
TraceTotal()
{
this.seconds = 0.0;
count = 0;
}
void add(double seconds)
{
this.seconds += seconds;
count++;
}
double getAverage()
{
return seconds / count;
}
int getCount()
{
return count;
}
double getTotal()
{
return seconds;
}
}