6511515: poor performance of LogRecord.inferCaller depending on java.lang.Throwable.getStackTraceElement

Allow random access to stack trace elements; retrieve only needed ones

Reviewed-by: swamyv
This commit is contained in:
Jeremy Manson 2009-06-14 14:33:30 -07:00 committed by Martin Buchholz
parent a5e977cf14
commit 5bf951e2a6
4 changed files with 48 additions and 23 deletions

View File

@ -1174,6 +1174,12 @@ public final class System {
public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) {
Shutdown.add(slot, registerShutdownInProgress, hook);
}
public int getStackTraceDepth(Throwable t) {
return t.getStackTraceDepth();
}
public StackTraceElement getStackTraceElement(Throwable t, int i) {
return t.getStackTraceElement(i);
}
});
}

View File

@ -645,17 +645,21 @@ public class Throwable implements Serializable {
/**
* Returns the number of elements in the stack trace (or 0 if the stack
* trace is unavailable).
*
* package-protection for use by SharedSecrets.
*/
private native int getStackTraceDepth();
native int getStackTraceDepth();
/**
* Returns the specified element of the stack trace.
*
* package-protection for use by SharedSecrets.
*
* @param index index of the element to return.
* @throws IndexOutOfBoundsException if <tt>index &lt; 0 ||
* index &gt;= getStackTraceDepth() </tt>
*/
private native StackTraceElement getStackTraceElement(int index);
native StackTraceElement getStackTraceElement(int index);
private synchronized void writeObject(java.io.ObjectOutputStream s)
throws IOException

View File

@ -29,6 +29,9 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.io.*;
import sun.misc.JavaLangAccess;
import sun.misc.SharedSecrets;
/**
* LogRecord objects are used to pass logging requests between
* the logging framework and individual log Handlers.
@ -522,29 +525,31 @@ public class LogRecord implements java.io.Serializable {
// Private method to infer the caller's class and method names
private void inferCaller() {
needToInferCaller = false;
// Get the stack trace.
StackTraceElement stack[] = (new Throwable()).getStackTrace();
// First, search back to a method in the Logger class.
int ix = 0;
while (ix < stack.length) {
StackTraceElement frame = stack[ix];
JavaLangAccess access = SharedSecrets.getJavaLangAccess();
Throwable throwable = new Throwable();
int depth = access.getStackTraceDepth(throwable);
String logClassName = "java.util.logging.Logger";
boolean lookingForLogger = true;
for (int ix = 0; ix < depth; ix++) {
// Calling getStackTraceElement directly prevents the VM
// from paying the cost of building the entire stack frame.
StackTraceElement frame =
access.getStackTraceElement(throwable, ix);
String cname = frame.getClassName();
if (cname.equals("java.util.logging.Logger")) {
break;
if (lookingForLogger) {
// Skip all frames until we have found the first logger frame.
if (cname.equals(logClassName)) {
lookingForLogger = false;
}
} else {
if (!cname.equals(logClassName)) {
// We've found the relevant frame.
setSourceClassName(cname);
setSourceMethodName(frame.getMethodName());
return;
}
}
ix++;
}
// Now search for the first frame before the "Logger" class.
while (ix < stack.length) {
StackTraceElement frame = stack[ix];
String cname = frame.getClassName();
if (!cname.equals("java.util.logging.Logger")) {
// We've found the relevant frame.
setSourceClassName(cname);
setSourceMethodName(frame.getMethodName());
return;
}
ix++;
}
// We haven't found a suitable frame, so just punt. This is
// OK as we are only committed to making a "best effort" here.

View File

@ -73,4 +73,14 @@ public interface JavaLangAccess {
* the slot is not valid to register.
*/
void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook);
/**
* Returns the number of stack frames represented by the given throwable.
*/
int getStackTraceDepth(Throwable t);
/**
* Returns the ith StackTraceElement for the given throwable.
*/
StackTraceElement getStackTraceElement(Throwable t, int i);
}