Details

    • Author:
      Mandy Chung
    • JEP Type:
      Feature
    • Exposure:
      Open
    • Scope:
      SE
    • Discussion:
      core dash libs dash dev at openjdk dot java dot net
    • Effort:
      M
    • Duration:
      M
    • Alert Status:
       Green
    • JEP Number:
      259

      Description

      Summary

      Define an efficient standard API for stack walking that allows easy filtering of, and lazy access to, the information in stack traces.

      Non-Goal

      • It is not a goal to convert all existing stack walking code in the JDK to use this new API.

      Motivation

      There is no standard API to traverse selected frames on the execution stack efficiently and access the Class instance of each frame.

      There are existing APIs that provide access to a thread's stack:

      • Throwable::getStackTrace and Thread::getStackTrace return an array of StackTraceElement objects, which contain the class name and method name of each stack-trace element.

      • SecurityManager::getClassContext is a protected method, which allows a SecurityManager subclass to access the class context.

      These APIs require the VM to eagerly capture a snapshot of the entire stack, and they return information representing the entire stack. There is no way to avoid the cost of examining all the frames if the caller is only interested in the top few frames on the stack. Both the Throwable::getStackTrace and Thread::getStackTrace methods return an array of StackTraceElement objects, which contain class names and method names but not the actual Class instances. For applications interested in the entire stack, the specification allows the 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 upon the JDK-internal sun.reflect.Reflection::getCallerClass method, or else their performance overhead is intolerable. These use cases include:

      • Walk the stack until the immediate caller's class is found. Every JDK caller-sensitive API looks up its immediate caller's class in order to determine the behavior of the API. For example, the Class::forName and ResourceBundle::getBundle methods use the immediate caller's class loader to load a class and a resource bundle respectively. Reflective APIs such as Class::getMethod use the immediate caller's class loader to determine the security checks to be performed.

      • Walk the stack, filtering out the stack frames of specific implementation classes to find the first non-filtered frame. The java.util.logging API, Log4j, and the Groovy runtime filter intermediate stack frames (typically implementation-specific and reflection frames) to find the caller's class.

      • Walk the stack to find all protection domains, until the first privileged frame is reached. This is required in order to do permission checks.

      • Walk the entire stack, possibly with a depth limit. This is required to generate the stack trace of any Throwable object, and to implement the Thread::dumpStack method.

      Description

      This JEP will define a stack-walking API that allows laziness and frame filtering, supports short walks that stop at a frame matching given criteria, and also supports long walks that traverse the entire stack.

      The JVM will be enhanced to provide a flexible mechanism to traverse and materialize the required stack-frame information and allow efficient lazy access to additional stack frames when required. Native JVM transitions will be minimized. The implementation will need to have a stable view of a thread's stack: Returning a stream holding a stack pointer for further manipulation in an uncontrolled manner will not work since, as soon as the stream factory returns, the JVM will be free to reorganize the control stack (via deoptimization, for example). This will influence the API's definition.

      The API will specify its behavior when running with a security manager, so that access to the Class objects in stack frames do not compromise security.

      The proposal is to define a capability-based StackWalker API to traverse the stack. The security permission check will be performed on each StackWalker object when it is constructed rather than each time it is used. It will define the following methods:

      public <T> T walk(Function<Stream<StackFrame>, T> function);
      public Class<?> getCallerClass();

      The walk method opens a sequential stream of StackFrame for the current thread and then applies the function with the StackFrame stream. The spliterator of the stream performs the stack frame traversal in an ordered manner. The Stream<StackFrame> object can only be traversed once and will be closed when the walk method returns. The stream becomes invalid to use once it is closed. For example, to find the first caller filtering a known list of implementation class:

      Optional<Class<?>> frame = new StackWalker().walk((s) ->
      {
          s.filter(f -> interestingClasses.contains(f.getDeclaringClass()))
           .map(StackFrame::getDeclaringClass)
           .findFirst();
      }); 

      To snapshot the stack trace of the current thread,

      List<StackFrame> stack =
           new StackWalker().walk((s) -> s.collect(Collectors.toList())); 

      The getCallerClass() method is for convenience to find the caller's frame and is the replacement for sun.reflect.Reflection.getCallerClass. An equivalent way to get the caller class using the walk method is:

      walk((s) -> s.map(StackFrame::declaringClass).skip(2).findFirst());

      Alternatives

      An alternative API choice would be for the walk method to return Stream<StackFrame>. Such an alternative will not work as the returned stream object may be used in an uncontrolled manner for further manipulation. When a stream of stack frames is created, as soon as the stream factory returns, the JVM is free to reorganize the control stack (via deoptimization, for example) and there is no robust way to detect if the stack has been mutated.

      Instead, similar to AccessController::doPrivileged, at least one native method must be created which will establish its own stack frame and then provide controlled access to the JVM's stack walking logic, for older frames. When this native method returns, that capability must be deactivated, or else made inaccessible in some other way. In this way, we can do efficient lazy access to stack frames, on a stable view of the thread's own control stack.

        Issue Links

        There are no Sub-Tasks for this issue.

          Activity

          Hide
          mchung Mandy Chung added a comment - - edited
          Javadoc of the proposed StackWalker API is:
             http://cr.openjdk.java.net/~mchung/jdk9/jep259/api/java/lang/StackWalker.html

          JEP 259 defines the StackWalker API and provides the replacement of sun.reflect.Reflection.getCallerClass(int) and getCallerClass() methods.

          JEP 259 replaces java.util.logging, Thread::dumpThread and Thread::getStackTrace to use StackWalker API.

          JDK-8141239 is separate from this JEP to enable Throwable's backtrace to use the StackWalker API as Throwable is performance sensitive and requires performance work and may also require works from the hotspot team.
          Show
          mchung Mandy Chung added a comment - - edited Javadoc of the proposed StackWalker API is:     http://cr.openjdk.java.net/~mchung/jdk9/jep259/api/java/lang/StackWalker.html JEP 259 defines the StackWalker API and provides the replacement of sun.reflect.Reflection.getCallerClass(int) and getCallerClass() methods. JEP 259 replaces java.util.logging, Thread::dumpThread and Thread::getStackTrace to use StackWalker API. JDK-8141239 is separate from this JEP to enable Throwable's backtrace to use the StackWalker API as Throwable is performance sensitive and requires performance work and may also require works from the hotspot team.
          Hide
          mchung Mandy Chung added a comment -
          Show
          mchung Mandy Chung added a comment - The implementation for this JEP has been pushed to jdk9/hs-rt.    http://hg.openjdk.java.net/jdk9/hs-rt/jdk/rev/94838afd5e5b    http://hg.openjdk.java.net/jdk9/hs-rt/hotspot/rev/f671d5510375
          Hide
          mchung Mandy Chung added a comment -
          All subtasks are completed. This JEP is now closed.
          Show
          mchung Mandy Chung added a comment - All subtasks are completed. This JEP is now closed.
          Hide
          iris Iris Clark added a comment -
          Updating Scope to "SE" to accurately reflect impact to Java SE APIs (new java.lang.*Stack*, etc.).
          Show
          iris Iris Clark added a comment - Updating Scope to "SE" to accurately reflect impact to Java SE APIs (new java.lang.*Stack*, etc.).

            People

            • Assignee:
              mchung Mandy Chung
              Reporter:
              mchung Mandy Chung
              Owner:
              Mandy Chung
              Reviewed By:
              Brian Goetz, Mark Reinhold
              Endorsed By:
              Brian Goetz
            • Votes:
              0 Vote for this issue
              Watchers:
              17 Start watching this issue

              Dates

              • Due:
                Created:
                Updated:
                Resolved:
                Integration Due: