[External] : RE: Question about the Method Exit Test

Chris Plummer chris.plummer at oracle.com
Wed Sep 13 21:42:58 UTC 2023


Hi Babneet,


Generally speaking I think your analysis is correct, although 
realistically I don't think the test as written can ever get the 
unexpected MethodExit on the carrier thread. The test is run with 
-Djdk.defaultScheduler.parallelism=2, so that means there will be 
exactly two carrier threads, one for the PRODUCER and one for the 
CONSUMER. Under these circumstances it is hard, and probably impossible, 
to agitate the thread scheduling in such a way that a virtual thread 
ends up being rescheduled on a different carrier thread.


Perhaps adding a 3rd virtual thread (that is not related to the first 
two) and having it perform a potentially blocking (and carrier thread 
rescheduling) action (such as a short Thread.sleep()) would cause the 
unexpected MethodExit on the carrier thread. Or maybe allowing for 3 
carrier threads would accomplish this. But even then, the carrier thread 
switch would have to happen during the short period between the two 
breakpoints, so there is only one brief opportunity for the switch to 
happen in a way that would cause the test to fail. In any case, I don't 
think the goal of this test is to test that scenario, so at best would 
be done to prove our understanding the of situation, and maybe used as a 
basis for writing a different test.


thanks


Chris


On 9/13/23 9:39 AM, Babneet B Singh wrote:
> Hi Chris,
>
> Thanks for the explanation. Do you agree with the below analysis? If 
> so, will it be possible to have the test fixed?
>
> |vthread| - virtual thread.
>
>
> Between |breakpoint_hit1| and |breakpoint_hit2|, the test doesn't 
> expect the |PRODUCER vthread's carrier thread| to *RUN*. So, the test 
> explicitly checks that no |JVMTI MethodExit event| is triggered by the 
> |PRODUCER vthread's carrier thread|.
>
> I feel that this check is incorrect because the |PRODUCER vthread| 
> |unmounts| and |mounts| between |breakpoint_hit1| and 
> |breakpoint_hit2|. Whenever a |vthread| unmounts, the corresponding 
> carrier thread is mounted and allowed to *RUN*.
>
> |PRODUCER vthread| |unmounts| while inserting an element to the 
> SynchronousQueue 
> <https://urldefense.com/v3/__https://download.java.net/java/early_access/jdk21/docs/api/java.base/java/util/concurrent/SynchronousQueue.html__;!!ACWV5N9M2RV99hQ!LlH2H_2hk1cRKDCE2wGHwsrWg0wj3G27-OujErjxOEwj1jq6_O6jcPk8L6FfRhBoZJzEtqEPgh1gl80Z1Fp3$>, 
> which is a blocking queue. Insert and remove operations are 
> synchronized. |vthread| seems to yield and unmount if it has to wait 
> on such an operation. Below is the PRODUCER vthread's stack trace 
> during the |VirtualThreadUnmount event, which happens between 
> breakpoint_hit1| and |breakpoint_hit2.|
>
> |Hit #1: VirtualThreadUnmount #340: enabling FramePop for method: 
> java/lang/VirtualThread::notifyJvmtiUnmount on virtual thread: 
> 0x27e640 VirtualThreadUnmount #340: method: 
> java/lang/VirtualThread::notifyJvmtiUnmount, thread: VT-PRODUCER#0 
> JVMTI Stack Trace for thread VT-PRODUCER#0: frame count: 12  0: 
> java/lang/VirtualThread: yieldContinuation()Z  1: 
> java/lang/VirtualThread: tryYield()V  2: java/lang/Thread: yield()V 
>  3: java/util/concurrent/SynchronousQueue$TransferStack: 
> transfer(Ljava/lang/Object;ZJ)Ljava/lang/Object;  4: 
> java/util/concurrent/SynchronousQueue: put(Ljava/lang/Object;)V  5: 
> MethodExitTest: qPut(Ljava/lang/String;)V  6: MethodExitTest: 
> lambda$static$0()V  7: MethodExitTest$$Lambda.0x00000000d7030b98: 
> run()V  8: java/lang/VirtualThread: 
> runWith(Ljava/lang/Object;Ljava/lang/Runnable;)V  9: 
> java/lang/VirtualThread: run(Ljava/lang/Runnable;)V 10: 
> java/lang/VirtualThread$VThreadContinuation$1: run()V 11: 
> jdk/internal/vm/Continuation: enter(Ljdk/internal/vm/Continuation;)V|
> Regards,
> Babneet
>
>
> ------------------------------------------------------------------------
> *From:* loom-dev <loom-dev-retn at openjdk.org> on behalf of Chris 
> Plummer <chris.plummer at oracle.com>
> *Sent:* August 8, 2023 6:21 PM
> *To:* Gengchen Tuo <Gengchen.Tuo at ibm.com>; loom-dev at openjdk.org 
> <loom-dev at openjdk.org>
> *Subject:* [EXTERNAL] Re: Question about the Method Exit Test
> When the first breakpoint is hit, METHOD_EXIT is enabled, but it is only
> enabled on the carrier thread. Since we are only executing on the
> virtual thread when the breakpoint is hit, no METHOD_EXIT event should
> be generated.
>
>      // Enable METHOD_EXIT events on the cthread. We should not get one.
>      LOG("Hit #1: Breakpoint: %s: enabling MethodExit events on carrier
> thread: %p\n",
>             mname, (void*)cthread);
>      set_event_notification_mode(jvmti, jni, JVMTI_ENABLE,
> JVMTI_EVENT_METHOD_EXIT, cthread);
>
> When the 2nd breakpoint is hit, that is when we enable METHOD_EXIT on
> the virtual thread:
>
>    // Enable METHOD_EXIT events on the vthread. We should get one.
>    LOG("Hit #2: Breakpoint: %s: enabling MethodExit events on %s thread:
> %p\n",
>            mname, is_virtual ? "virtual" : "carrier", (void*)thread);
>    set_event_notification_mode(jvmti, jni, JVMTI_ENABLE,
> JVMTI_EVENT_METHOD_EXIT, thread);
>
> So now we will see METHOD_EXIT events. I don't think this has anything
> to do with lines 59-60, which have to do with deferring the enabling of
> the initial breakpoint until after a warmup period:
>
> if (i == MSG_COUNT - 10) {
>                      // Once we have warmed up, enable the first
> breakpoint which eventually will
>                      // lead to enabling single stepping.
>                      enableEvents(Thread.currentThread(),
> MethodExitTest.class);
>                  }
>
> My guess is this because we have 3 threads in play, and want to make
> sure they are all executing in the virtual thread before enabling
> METHOD_EXIT events.
>
> And I think it's safe to say you can ignore the "single stepping"
> comment. This test was cloned from one of our very early jvmti virtual
> threads tests, and that comment appears to be a relic. There is also a
> reference in the C file that can go away:
>
>    if (strcmp(event_name, "SingleStep") != 0) {
>      print_stack_trace(jvmti, jni, thread);
>    }
>
> Chris
>
> On 8/8/23 12:35 PM, Gengchen Tuo wrote:
> >
> > Hi all. In the Method Exit test
> > 
> https://github.com/openjdk/jdk/blob/master/test/hotspot/jtreg/serviceability/jvmti/vthread/MethodExitTest/MethodExitTest.java 
> <https://urldefense.com/v3/__https://github.com/openjdk/jdk/blob/master/test/hotspot/jtreg/serviceability/jvmti/vthread/MethodExitTest/MethodExitTest.java__;!!ACWV5N9M2RV99hQ!LlH2H_2hk1cRKDCE2wGHwsrWg0wj3G27-OujErjxOEwj1jq6_O6jcPk8L6FfRhBoZJzEtqEPgh1gl5iy97tY$> 
> ,
> > no MethodExit event is expected between the first and the second
> > breakpoint hit. Why are we making this assumption? Maybe that’s
> > related to line 59 and 60 that I don’t really understand? To my
> > knowledge, the producer thread may yield between the two breakpoint
> > hits and MethodExit events will be reported.
> >
> >
> >
> > I tried to enable the MethodEntry event in the agent code and the test
> > started to fail but this shouldn’t affect the test result I believe?
> >
> >
> >
> > Thanks in advance
> >
> > Gengchen
> >
> >


More information about the loom-dev mailing list