6511515: poor performance of LogRecord.inferCaller depending on java.lang.Throwable.getStackTraceElement
Summary: Allow random access to stack trace elements; retrieve only needed ones
Reviewed-by: swamyv
Contributed-by: jeremymanson@google.com
--- a/jdk/src/share/classes/java/lang/System.java Sun Jun 14 14:23:22 2009 -0700
+++ b/jdk/src/share/classes/java/lang/System.java Sun Jun 14 14:33:30 2009 -0700
@@ -1174,6 +1174,12 @@
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);
+ }
});
}
--- a/jdk/src/share/classes/java/lang/Throwable.java Sun Jun 14 14:23:22 2009 -0700
+++ b/jdk/src/share/classes/java/lang/Throwable.java Sun Jun 14 14:33:30 2009 -0700
@@ -645,17 +645,21 @@
/**
* 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 < 0 ||
* index >= getStackTraceDepth() </tt>
*/
- private native StackTraceElement getStackTraceElement(int index);
+ native StackTraceElement getStackTraceElement(int index);
private synchronized void writeObject(java.io.ObjectOutputStream s)
throws IOException
--- a/jdk/src/share/classes/java/util/logging/LogRecord.java Sun Jun 14 14:23:22 2009 -0700
+++ b/jdk/src/share/classes/java/util/logging/LogRecord.java Sun Jun 14 14:33:30 2009 -0700
@@ -29,6 +29,9 @@
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 @@
// 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.
--- a/jdk/src/share/classes/sun/misc/JavaLangAccess.java Sun Jun 14 14:23:22 2009 -0700
+++ b/jdk/src/share/classes/sun/misc/JavaLangAccess.java Sun Jun 14 14:33:30 2009 -0700
@@ -73,4 +73,14 @@
* 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);
}