Draft JEP: Efficient Stack Walking API
Jeremy Manson
jeremymanson at google.com
Wed Jul 9 05:25:37 UTC 2014
Thanks for the response, Mandy. I'm looking forward to seeing the final
version.
For CallerFinder, we use reflective goop to get at
sun.misc.JavaLangAccess.getStackTraceElement. It requires us to build a
Throwable (with associated stacktrace), but not to generate all of those
StackTraceElement[] strings. This saves a lot of CPU cycles over something
like Thread.getStackTrace(), but in this case, cheaper is better, and we'd
definitely prefer a better solution. Functions like
Java_java_lang_Throwable_fillInStackTrace are still big cycle consumers.
Jeremy
On Mon, Jul 7, 2014 at 8:06 PM, Mandy Chung <mandy.chung at oracle.com> wrote:
> Hi Jeremy,
>
> Thanks for the feedback and the CallerFinder API you have.
>
>
> On 7/7/2014 9:55 AM, Jeremy Manson wrote:
>
> Hey folks,
>
> I don't know if Mandy's draft JEP has gotten any love,
>
>
> The JEP process is in transition to 2.0 version. Hope this JEP will come
> out soon.
>
>
> but this is something that has (in the past) been a major CPU cycle
> consumer for us, and we've had to invent / reinvent many wheels to fix it
> internally, so we'd love to see a principled solution.
>
> A couple of notes:
>
> - A large percentage of the time, you just want to find one of:
> 1) The direct caller of the method,
> 2) The first caller outside a given package.
>
>
> The current thinking is to allow you to find the direct caller as well as
> express the predicate for filtering that will cover these cases.
>
>
> We added a CallerFinder API that basically looks like this:
>
> // Finds the caller of the invoking method in the current stack that
> isn't in one of the excluded classes
> public static StackTraceElement findCaller(Class<?>... excludedClasses);
>
> // Finds the first caller of a given class
> public static StackTraceElement findCallerOf(Class<?>... classesToFind);
>
> This isn't the ideal API (it is more the one that happened to be
> convenient when we threw together the class), but it gets the vast majority
> of use cases.
>
>
> Does it use Thread.getStackTrace() to implement this CallerFinder API?
> Thread.getStackTrace or Throwable.getStackTrace both eagerly capture the
> entire stack trace that is expensive. We want to have the VM to be able to
> only capture the stack frames that the client needs and the implementation
> as efficient as possible.
>
>
> 2) Even with a super-efficient stack walker, anyone who uses the
> java.util.logging framework pervasively is going to see a lot of CPU cycles
> consumed by determining the caller.
>
>
> The current LogRecord implementation calls new Throwable that has to pay
> the cost of capturing the entire stack.
>
>
> We've had a lot of luck minimizing this by using a bytecode rewriter to
> change callers of log(msg) to log(sourceClass, sourceMethod, msg). This is
> almost certainly something that could be done (even in a principled way!)
> by the VM; improvements to CPU usage in such apps have been dramatic.
>
>
> Thanks. I'll make sure to measure and compare the performance with
> java.util.logging using the new stack walk API and also may ask your help
> to determine if you observe the performance difference comparing the
> rewritten bytecode vs the java.util.logging using the new API.
>
> Mandy
>
>
> Jeremy
>
>
>
> On Sun, Mar 30, 2014 at 4:02 PM, Mandy Chung <mandy.chung at oracle.com>
> wrote:
>
>> Below is a draft JEP we are considering submitting for JDK 9.
>>
>> Mandy
>>
>> ----------------------------
>> Title: Efficient API for Stack Walking
>>
>> Goal
>> ----
>>
>> Define a standard API for stack walking that will be efficient and
>> performant.
>>
>> Non-goal
>> --------
>>
>> It is not a goal for this API be easy to use via Reflection for example
>> use in code that is compiled for an older JDK.
>>
>> Motivation
>> ----------
>>
>> There is no standard API to obtain information about the caller's class
>> and traverse the execution stack in a performant way. Existing libraries
>> and frameworks such as Log4j and Groovy have to resort to using the
>> JDK internal API `sun.reflect.Reflection.getCallerClass(int depth)`.
>>
>> This JEP proposes to define a standard API for stack walking that will
>> be efficient and performant and also enable the implementation up
>> level the stack walk machinery from the VM to Java and replaces
>> the current mechanism of `Throwable.fillInStackTrace.
>>
>> Description
>> -----------
>>
>> There is no standard API to traverse certain frames on the execution
>> stack efficiently and access the Class instance of each frame.
>>
>> There are APIs that allow to access the stack trace information:
>> - `Throwable.getStackTrace()` and `Thread.getStackTrace()` that returns
>> an array of `StackTraceElement` which contains the classname
>> and method name of a stack trace.
>> - `SecurityManager.getClassContext()` which is a protected method
>> such that only `SecurityManager` subclass can access the class
>> context.
>>
>> These APIs require the VM to eagerly capture a snapshot of the entire
>> stack trace and returns the information representing the entire stack.
>> There is no other way to avoid the cost to examine all frames if
>> the caller is only interested in the top few frames on the stack.
>> Both `Throwable.getStackTrace()` and `Thread.getStackTrace()` methods
>> return an array of `StackTraceElement` that contains the classname and
>> method name of a stack frame but the `Class` instance.
>>
>> In fact, for applications interested in the entire stack, the
>> specification
>> allows VM implementation to omit some frames in the stack for performance.
>> In other words, `Thread.getStackTrace()` may return a partial stack trace.
>>
>> These APIs do not satisfy the use cases that currently depend on
>> the `getCallerClass(int depth)` method or its performance overhead
>> is intolerable. The use cases include:
>>
>> - JDK caller-sensitive APIs look up its immediate caller's class
>> which will be used to determine the behavior of the API. For example
>> `Class.forName(String classname)` and
>> `ResourceBundle.getBundle(String rbname)` methods use the immediate
>> caller's class loader to load a class and a resource bundle
>> respectively.
>> `Class.getMethod` etc will use the immediate caller's class loader
>> to determine the security checks to be performed.
>>
>> - `java.util.logging`, Log4j and Groovy runtime filter the intermediary
>> stack frames (typically implementation-specific and reflection frames)
>> and find the caller's class to be used by the runtime of such library
>> or framework.
>>
>> - Traverse the entire stack trace or the stack trace of a `Throwbale`
>> and obtain additional information about classes for enhanced
>> diagnosibility in addition to the class and method name.
>>
>> This JEP will define a stack walk API that allows laziness, frame
>> filtering,
>> supports short reaches to stop at a frame matching some criteria
>> as well as long reaches to traverse the entire stack trace. This would
>> need the JVM to provide a flexible mechanism to traverse and materialize
>> the specific stack frame information to be used and allow efficient
>> lazy access to additional stack frames when required.
>> Native JVM transitions should be minimzed.
>>
>> The API will define how it works when running with a security manager
>> that allows access to a `Class` instance
>> of any frame ensuring that the security is not compromised.
>>
>> An example API to walk the stack can be like:
>> Thread.walkStack(Consumer<StackFrameInfo> action, int depthLimit)
>>
>> that takes a callback to be invoked for each frame traversed. A variant
>> of the walkStack method will take a predicate for stack frame filtering.
>>
>> Thread.getCaller(Function<StackFrameInfo, R> function)
>> Thread.findCaller(Predicate<StackFrameInfo> predicate,
>> Function<StackFrameInfo, R> function)
>>
>> finds the caller frame with or without filtering.
>>
>> Testing
>> -------
>>
>> Unit tests and JCK tests for the new SE API will need to be developed.
>> In addition, the performance of the new API for different use cases
>> will be assessed.
>>
>>
>> Impact
>> ------
>>
>> - Performance/scalability: performance measurement shall be performed
>> using micro-benchmarks as well as real world usage of `getCallerClass`
>> replaced with the new API.
>>
>> - TCK: New JCK test cases shall be developed.
>>
>>
>
>
More information about the core-libs-dev
mailing list