[External] : Re: Grouped method entry events

Egor Ushakov egor.ushakov at jetbrains.com
Mon Aug 8 11:13:43 UTC 2022


Thanks, Chris!
We'll try to implement a workaround in IDEA to ignore such merged events 
somehow.
Egor

On 08.08.2022 09:50, Chris Plummer wrote:
> I think the issue is that when you issue a "next", jvmti single 
> stepping is enabled. That means when the first MethodEntry event is 
> triggered, it is automatically deferred. See the following in 
> deferEventReport:
>
>         case EI_METHOD_ENTRY:
>                 error = methodLocation(method, &start, &end);
>                 if (error == JVMTI_ERROR_NONE) {
>                     deferring = isBreakpointSet(clazz, method, start) ||
> threadControl_getInstructionStepMode(thread)
>                                     == JVMTI_ENABLE;
>
>
> When using (JDWP) STEP_OVER, JVMTI single stepping is only enabled 
> during the call to the method, which allows the debug agent to stop on 
> the first instruction of the called method. After that it disables 
> JVMTI single stepping (until there is a FramePop). When the above code 
> is executed for METHOD_ENTRY, JVMTI single stepping is enabled. When 
> JVMTI is returned to, it then sends a SINGLE_STEP event, but this is a 
> special one just used by the debug agent, and not sent on the the 
> debugger. So we never hit the code right below the above code that 
> handles colocating the SINGLE_STEP event.
>
>         case EI_SINGLE_STEP:
>             deferring = isBreakpointSet(clazz, method, location);
>             if (deferring) {
>                 threadControl_saveCLEInfo(env, thread, ei,
>                                           clazz, method, location);
>             }
>
> Meanwhile the generated MethodEntry event sits in the thread's event 
> bag, and I guess gets added to when the next MethodEntry happens. This 
> goes on on a MethodExit event is generated, witch seems to push out 
> all pending events (there is actually no logic in the debug agent to 
> colocate MethodExit events, which seems odd).
>
> So bottom line is that the CLE code is wrong when JVMTI single 
> stepping is enabled for the sake of supporting a JDWP STEP_OVER. I 
> think instead of checking if JVMTI single stepping is enabled, it 
> should be checking if the current StepDepth is INTO, although I 
> suspect there might be other factors to consider that will make the 
> check more complicated.
>
> I'll look at this some more tomorrow.
>
> Chris
>
> On 8/7/22 11:50 PM, Chris Plummer wrote:
>> Hi Egor,
>>
>> The reason only the last event prints location information is because 
>> there is special logic in jdb that assumes if the suspend type is not 
>> SUSPEND_NONE, then we will be stopping, and that will result in the 
>> location info being printed.
>>
>>         if (me.request().suspendPolicy() != EventRequest.SUSPEND_NONE) {
>>             // We are stopping; the name will be shown by the normal 
>> mechanism
>>             MessageOutput.lnprint("Method entered:");
>>         } else {
>>             // We aren't stopping, show the name
>>             MessageOutput.print("Method entered:");
>>             printLocationOfEvent(me);
>>         }
>>
>> The assumption here is wrong with co-located events, since there 
>> could be more than one. However, normally it shouldn't matter since 
>> all colocated events are suppose to be at the same location, and if 
>> one is stopping then they all are. Yet clearly we are seeing that 
>> they are not all at the same location. When I enable location tracing 
>> for the first part of the "if" block, I get:
>>
>> > Method entered: "thread=main", java.lang.ClassLoader.loadClass(), 
>> line=521 bci=0
>> Method entered: "thread=main", 
>> jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(), line=180 
>> bci=0
>> Method entered: "thread=main", java.lang.System.getSecurityManager(), 
>> line=497 bci=0
>> Method entered: "thread=main", 
>> java.lang.System.allowSecurityManager(), line=205 bci=0
>> Method exited: return value = false, "thread=main", 
>> java.lang.System.allowSecurityManager(), line=205 bci=12
>>
>> I also printed out the EventSet, which confirms the same:
>>
>> event set, policy:2, count:5 = 
>> {MethodEntryEvent at java.lang.ClassLoader:521 in thread main, 
>> MethodEntryEvent at jdk.internal.loader.ClassLoaders$AppClassLoader:180 
>> in thread main, MethodEntryEvent at java.lang.System:497 in thread main, 
>> MethodEntryEvent at java.lang.System:205 in thread main, 
>> MethodExitEvent at java.lang.System:205 in thread main}
>>
>> The JDWP spec says:
>>
>> Several events may occur at a given time in the target VM. For 
>> example, there may be more than one breakpoint request for a given 
>> location or you might single step to the same location as a 
>> breakpoint request. These events are delivered together as a 
>> composite event.
>> ...
>> The events that are grouped in a composite event are restricted in 
>> the following ways:
>> ...
>>   � Only with other members of this group, at the same location and 
>> in the same thread:
>>         Breakpoint Event
>>         Step Event
>>         Method Entry Event
>>         Method Exit Event
>>
>> So there seems to be a debug agent bug that is grouping all the 
>> MethodEntry events until a MethodExit is hit. But I don't think this 
>> is new. I see the same thing in JDK11, but as Alan pointed out, there 
>> are fewer method calls.
>>
>> Chris
>>
>> On 8/6/22 2:18 AM, Egor Ushakov wrote:
>>> Hi Chris,
>>>
>>> I was able to somewhat reproduce it with jdb:
>>> compile any simple samle app like
>>>
>>> public class A { public static void main(String[] args) { 
>>> System.out.println("A"); // line 3 } }
>>> now debug with jdb:
>>>
>>>  % jdb A
>>> Initializing jdb ...
>>> > stop at A:3
>>> Deferring breakpoint A:3.
>>> It will be set after the class is loaded.
>>> > run
>>> run A
>>> Set uncaught java.lang.Throwable
>>> Set deferred uncaught java.lang.Throwable
>>> >
>>> VM Started: Set deferred breakpoint A:3
>>>
>>> Breakpoint hit: "thread=main", A.main(), line=3 bci=0
>>>
>>> main[1] exclude none
>>> main[1] trace methods
>>> main[1] next
>>> >
>>> *Method entered: **
>>> **Method entered: **
>>> **Method entered: **
>>> **Method entered: *
>>> Method exited: return value = false, "thread=main", 
>>> java.lang.System.allowSecurityManager(), line=198 bci=12
>>>
>>> main[1] next
>>> >
>>> Method exited: return value = null, "thread=main", 
>>> java.lang.System.getSecurityManager(), line=493 bci=11
>>>
>>> main[1] next
>>> >
>>> *Method entered: **
>>> **Method entered: **
>>> **Method entered: **
>>> **Method entered:*
>>> Method exited: return value = <void value>, "thread=main", 
>>> java.lang.Object.<init>(), line=44 bci=0
>>>
>>> main[1] next
>>> >
>>> *Method entered: **
>>> **Method entered: **
>>> **Method entered: **
>>> **Method entered: *
>>> Method exited: return value = true, "thread=main", 
>>> java.lang.String.isLatin1(), line=4,546 bci=18
>>>
>>> Unfortunately jdb does not show method description for the events 
>>> with suspend policy thread (except for the last one), but you can 
>>> see that there are 5 events in one set here.
>>>
>>> Hope it helps,
>>> Egor
>>>
>>> On 05.08.2022 22:02, Chris Plummer wrote:
>>>>
>>>> Hi Egor,
>>>>
>>>>
>>>> That is odd, and clearly wrong. It means that MethodEntryEvents are 
>>>> being delivered when no longer even executing in the method. And 
>>>> you are using the EVENT_THREAD suspend policy, so there should be a 
>>>> suspend for each event. Offhand I can't think of any changes that 
>>>> would have caused this. I also don't see how our testing would 
>>>> passed with this behavior. I'm suspecting a faulty interaction with 
>>>> the debug agent and IDEA (possibly the IDEA version of JDI).
>>>>
>>>> Can you see where the thread is actually suspended at when you get 
>>>> the event set?
>>>>
>>>> Does this happen with 17 also? It would be nice to know what 
>>>> version introduced this problem.
>>>>
>>>> Can you create a JDI test case for this?
>>>>
>>>>
>>>> > Without the active step request it works in jdk 18 as in jdk 11:
>>>>
>>>>
>>>> I'm not sure what you mean by "active step". Do you mean single 
>>>> stepping is enabled? The JDWP spec does allow BreakPoint, 
>>>> SingleStep, MethodEntry, and MethodExit events to be combined into 
>>>> the same composite event (which should translate to the same JDI 
>>>> event set) if they occur at the same location and in the same 
>>>> thread. So I can see enabling single stepping having an impact on 
>>>> the behavior you are seeing.
>>>>
>>>>
>>>> Chris
>>>>
>>>> On 8/5/22 9:59 AM, Egor Ushakov wrote:
>>>>> Hi,
>>>>>
>>>>> we're experiencing strange grouping of method entry/exit events, 
>>>>> is this a bug or a feature?
>>>>> We have one method entry request active with suspend policy 1 
>>>>> (suspend thread).
>>>>> important: we also have one active step request (step over)
>>>>>
>>>>> In jdk 11 we have one composite event for each event, which is 
>>>>> what's expected:
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at java.lang.ClassLoader:522 in thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at jdk.internal.loader.ClassLoaders$AppClassLoader:170 
>>>>> in thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at java.lang.System:375 in thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at jdk.internal.loader.BuiltinClassLoader:579 in 
>>>>> thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at jdk.internal.loader.BuiltinClassLoader:591 in 
>>>>> thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at java.lang.ClassLoader:665 in thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at java.lang.Object:50 in thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at java.util.concurrent.ConcurrentHashMap:1541 in 
>>>>> thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at java.util.concurrent.ConcurrentHashMap:1011 in 
>>>>> thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at java.lang.String:1502 in thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at java.lang.String:3266 in thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at java.lang.StringLatin1:194 in thread main}
>>>>> ...
>>>>>
>>>>> In jdk 18 they are grouped now!
>>>>> event set, policy:1, count:17 = 
>>>>> {MethodEntryEvent at java.lang.ClassLoader:521 in thread main, 
>>>>> MethodEntryEvent at jdk.internal.loader.ClassLoaders$AppClassLoader:180 
>>>>> in thread main, MethodEntryEvent at java.lang.System:490 in thread 
>>>>> main, MethodEntryEvent at java.lang.System:198 in thread main, 
>>>>> MethodEntryEvent at jdk.internal.loader.BuiltinClassLoader:639 in 
>>>>> thread main, 
>>>>> MethodEntryEvent at jdk.internal.loader.BuiltinClassLoader:651 in 
>>>>> thread main, MethodEntryEvent at java.lang.ClassLoader:664 in thread 
>>>>> main, MethodEntryEvent at java.lang.Object:44 in thread main, 
>>>>> MethodEntryEvent at java.util.concurrent.ConcurrentHashMap:1541 in 
>>>>> thread main, 
>>>>> MethodEntryEvent at java.util.concurrent.ConcurrentHashMap:1011 in 
>>>>> thread main, MethodEntryEvent at java.lang.String:2336 in thread 
>>>>> main, MethodEntryEvent at java.lang.String:4546 in thread main, 
>>>>> MethodEntryEvent at java.lang.StringLatin1:192 in thread main, 
>>>>> MethodEntryEvent at java.util.concurrent.ConcurrentHashMap:697 in 
>>>>> thread main, 
>>>>> MethodEntryEvent at java.util.concurrent.ConcurrentHashMap:760 in 
>>>>> thread main, MethodEntryEvent at jdk.internal.misc.Unsafe:2153 in 
>>>>> thread main, 
>>>>> MethodEntryEvent at jdk.internal.misc.Unsafe.getReferenceVolatile(java.lang.Object, 
>>>>> long)+-1 in thread main}
>>>>> event set, policy:1, count:4 = 
>>>>> {MethodEntryEvent at java.util.concurrent.ConcurrentHashMap$Node:631 
>>>>> in thread main, MethodEntryEvent at java.lang.Object:44 in thread 
>>>>> main, MethodEntryEvent at java.util.concurrent.ConcurrentHashMap:765 
>>>>> in thread main, 
>>>>> MethodEntryEvent at jdk.internal.misc.Unsafe.compareAndSetReference(java.lang.Object, 
>>>>> long, java.lang.Object, java.lang.Object)+-1 in thread main}
>>>>> event set, policy:1, count:2 = 
>>>>> {MethodEntryEvent at java.util.concurrent.ConcurrentHashMap:2326 in 
>>>>> thread main, 
>>>>> MethodEntryEvent at jdk.internal.misc.Unsafe.compareAndSetLong(java.lang.Object, 
>>>>> long, long, long)+-1 in thread main}
>>>>>
>>>>> Which is really strange and breaks some login in IDEA debugger.
>>>>>
>>>>> Without the active step request it works in jdk 18 as in jdk 11:
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at java.lang.ClassLoader:521 in thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at jdk.internal.loader.ClassLoaders$AppClassLoader:180 
>>>>> in thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at java.lang.System:490 in thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at java.lang.System:198 in thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at jdk.internal.loader.BuiltinClassLoader:639 in 
>>>>> thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at jdk.internal.loader.BuiltinClassLoader:651 in 
>>>>> thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at java.lang.ClassLoader:664 in thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at java.lang.Object:44 in thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at java.util.concurrent.ConcurrentHashMap:1541 in 
>>>>> thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at java.util.concurrent.ConcurrentHashMap:1011 in 
>>>>> thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at java.lang.String:2336 in thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at java.lang.String:4546 in thread main}
>>>>> event set, policy:1, count:1 = 
>>>>> {MethodEntryEvent at java.lang.StringLatin1:192 in thread main}
>>>>>
>>>>> Could anyone have a look please.
>>>>> Thanks,
>>>>> Egor
>>>



More information about the serviceability-dev mailing list