JDK-8268526 : Lambdas are hidden inconsistently in stack traces.
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 8u291,11,17
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: generic
  • CPU: generic
  • Submitted: 2021-06-09
  • Updated: 2021-06-13
  • Resolved: 2021-06-10
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdResolved
Related Reports
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
java.vm.vendor=Oracle Corporation
java.vm.name=Java HotSpot(TM) 64-Bit Server VM
java.version=1.8.0_221
java.specification.version=1.8
java.runtime.version=1.8.0_221-b11
user.language=en
user.country=GB
user.variant=
Default locale=en_GB
os.name=Mac OS X
os.arch=x86_64
os.version=10.16

A DESCRIPTION OF THE PROBLEM :
As a result of https://bugs.openjdk.java.net/browse/JDK-8025636 lambdas are hidden in stack traces.
Sadly, that is true only when stack trace is taken from the same thread.
When Thread.getStackTrace() is taken from a different thread than the invoked object, the stack trace still contains lambdas.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the attached application.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
----------From different thread----------
java.lang.Thread.sleep(Native Method)
HiddenLambda.bar(HiddenLambda.java:36)
HiddenLambda.lambda$null$0(HiddenLambda.java:10)
HiddenLambda.foo(HiddenLambda.java:31)
HiddenLambda.lambda$main$1(HiddenLambda.java:10)
java.lang.Thread.run(Thread.java:748)
----------From the same thread----------
HiddenLambda.bar(HiddenLambda.java:38)
HiddenLambda.lambda$null$0(HiddenLambda.java:10)
HiddenLambda.foo(HiddenLambda.java:31)
HiddenLambda.lambda$main$1(HiddenLambda.java:10)
java.lang.Thread.run(Thread.java:748)
ACTUAL -
----------From different thread----------
java.lang.Thread.sleep(Native Method)
HiddenLambda.bar(HiddenLambda.java:36)
HiddenLambda.lambda$null$0(HiddenLambda.java:10)
HiddenLambda$$Lambda$2/277640063.run(Unknown Source)
HiddenLambda.foo(HiddenLambda.java:31)
HiddenLambda.lambda$main$1(HiddenLambda.java:10)
HiddenLambda$$Lambda$1/531885035.run(Unknown Source)
java.lang.Thread.run(Thread.java:748)
----------From the same thread----------
HiddenLambda.bar(HiddenLambda.java:38)
HiddenLambda.lambda$null$0(HiddenLambda.java:10)
HiddenLambda.foo(HiddenLambda.java:31)
HiddenLambda.lambda$main$1(HiddenLambda.java:10)
java.lang.Thread.run(Thread.java:748)

---------- BEGIN SOURCE ----------
import java.util.Arrays;
import java.util.List;
import java.util.Set;

public class HiddenLambda {

    public static void main(String[] args) {
        Thread myThread = new Thread(()->
        {
            foo(() -> bar());
        });
        myThread.start();
        Set<Thread> threads = Thread.getAllStackTraces().keySet();
        for (Thread thread : threads) {
            if (myThread == thread){
                System.out.println("----------From a different thread----------");
                List<StackTraceElement> stackTraceElements = Arrays.asList(thread.getStackTrace());
                for (StackTraceElement stackTraceElement : stackTraceElements) {
                    System.out.println(stackTraceElement);
                }
            }
        }
        try {
            myThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    static void foo(Runnable lambda) {
        lambda.run();
    }

    static void bar() {
        try {
            Thread.sleep(1000);
            System.out.println("----------From the same thread----------");
            List<StackTraceElement> stackTraceElements = Arrays.asList((new Exception()).getStackTrace());
            for (StackTraceElement stackTraceElement : stackTraceElements) {
                System.out.println(stackTraceElement);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
I found no workaround to hide the lambdas, but for consistency between snapshots you can disable hiding lambdas by adding VM options:
-XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames

FREQUENCY : always



Comments
JDK-8212620 is the RFE related to stack trace filtering that the submitter may be interested in.
13-06-2021

Thanks David for the detailed analysis. Closing as Not an issue
10-06-2021

As far as I can see, contrary to the claim in JEP 371, the notion of "hidden frames" was only applied to exception stacktraces, not raw thread stacktraces. So I would say that this is not in fact a bug.
10-06-2021

This is a consequence of the different API's being used. According to "JEP 371: Hidden Classes": Hidden classes in stack traces Methods of hidden classes are not shown in stack traces by default. They represent implementation details of language runtimes, and are never expected to be useful to developers diagnosing application issues. However, they can be included in stack traces via the options -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames. There are three APIs which reify stack traces: Throwable::getStackTrace, Thread::getStackTrace and the newer StackWalker API introduced in Java 9. For the Throwable::getStackTrace and Thread::getStackTrace API, stack frames for hidden classes are omitted by default; they can be included with the same options as for stack traces above. --- But ShowHiddenFrames predates Hidden classes, and was originally intended to hide frames related to MethodHandles. The simple fact is that Throwable.getStackTrace() does check ShowHiddenFrames when constructing a stack trace for the Throwable, and hence what is seen in an exception backtrace. But Thread.getStackTrace uses the same underlying mechanism as JVM TI and JMX and that mechanism does not consult ShowHiddenFrames, and so will show the lambda class in this case. This is certainly an inconsistency, but whether it is a bug is hard to say without more extensive archaeology on the codebase. Changing the behaviour now could have unintended consequences.
10-06-2021

Result on jdk17 b20 == jdk-17-ea+20/fastdebug/bin/java HiddenLambda.java ----------From a different thread---------- java.base@17-ea/java.lang.Thread.sleep(Native Method) HiddenLambda.bar(HiddenLambda.java:36) HiddenLambda.lambda$main$0(HiddenLambda.java:10) HiddenLambda$$Lambda$223/0x0000000801154448.run(Unknown Source) HiddenLambda.foo(HiddenLambda.java:31) HiddenLambda.lambda$main$1(HiddenLambda.java:10) HiddenLambda$$Lambda$222/0x0000000801154218.run(Unknown Source) java.base@17-ea/java.lang.Thread.run(Thread.java:831) ----------From the same thread---------- HiddenLambda.bar(HiddenLambda.java:38) HiddenLambda.lambda$main$0(HiddenLambda.java:10) HiddenLambda.foo(HiddenLambda.java:31) HiddenLambda.lambda$main$1(HiddenLambda.java:10) java.base/java.lang.Thread.run(Thread.java:831) ==
10-06-2021