JDK-8229012 : When single stepping, the debug agent can cause the thread to remain in interpreter mode after single stepping completes
  • Type: Bug
  • Component: core-svc
  • Sub-Component: debugger
  • Affected Version: 14
  • Priority: P3
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2019-08-02
  • Updated: 2024-12-13
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.
JDK 25
25Unresolved
Related Reports
Duplicate :  
Relates :  
Relates :  
Relates :  
Description
When single stepping is initiated, the following code is executed in initState() in stepControl.c:

    /*
     * Try to get a notification on frame pop. If we're in an opaque frame
     * we won't be able to, but we can use other methods to detect that
     * a native frame has exited.
     *
     * TO DO: explain the need for this notification.
     */
    error = JVMTI_FUNC_PTR(gdata->jvmti,NotifyFramePop)
                (gdata->jvmti, thread, 0);
    if (error == JVMTI_ERROR_OPAQUE_FRAME) {
        step->fromNative = JNI_TRUE;
        error = JVMTI_ERROR_NONE;
        /* continue without error */
    } else if (error == JVMTI_ERROR_DUPLICATE) {
        error = JVMTI_ERROR_NONE;
        /* Already being notified, continue without error */
    } else if (error != JVMTI_ERROR_NONE) {
        return error;
    }

It's unclear (as the comment suggests) why this NotifyFramePop is needed. When a NotifyFramePop is done, it puts the thread in interpreter only mode (_interp_only_mode). This is done mainly so jvmti can detect when popping the frame for which NotifyFramePop has been called. Once this is done, _interp_only_mode is disabled assuming there is nothing else keeping it enabled.

The problem with doing NotifyFramePop on the current frame in initState() is that even after single stepping completes, we won't yet have exited the frame that NotifyFramePop was setup on. This keeps the thread in _interp_only_mode until that frame is exited. Worse case situation is you single step in the main method of the app, and the app (or at least the thread running the main method) is forever stuck in _interp_only_mode.

The above NotifyFramePop should be re-evaluated to see if it is really necessary. Unfortunately there is no way to undo a NotifyFramePop, so the only way to avoid keeping the thread in _interp_only_mode is to not have called it in the first place.

The following code is in handleFramePopEvent and seems to be the code that could potentially get triggered by this NotifyFramePop:

        /*
         * If we are exiting the original stepping frame, record that
         * fact here. Once the next step event comes in, we can safely
         * stop stepping there.
         */
        if (fromDepth > afterPopDepth ) {
            step->frameExited = JNI_TRUE;
        }

However, I'm not sure how this situation could ever come about since we should always complete single stepping before returning from the original stepping frame. If there had been an exception, handleExceptionCatchEvent() would have been called, and it has similar code for dealing with exiting the original stepping frame. So that leaves doing a normal return out of the frame. I think the only way you could return out of the frame without first completing the single step is if you stepped over a return statement, maybe even a line of source with other statements on the same line before the return. If so, another way to handle this would be to install a MethodExit handler to detect exiting the original single stepping frame. It could be removed as soon as you enter a method (there's already a MethodEnter handler) and then re-installed after getting the FramePop event for the called method (NotifyFramePop is done once you enter the called method when you step over a method call).
Comments
Reassigning to Chris as we agreed.
13-12-2024

See Redhat discussion on this issue: https://issues.redhat.com/browse/RHEL-3498
31-05-2024

I've attached stepreq.patch, which is what I was working on to fix this issue quite a while back (almost a year ago). I've attached it simply for archival purposes. It has issues and is no where close to working the way it needs to be.
16-08-2023

JDK-8187289 introduced this issue, but at the same time JDK-8187289 is a necessary fix. At least one OpenJDK user has resorted to reverting JDK-8187289 to work around the problem, but this can lead to incorrect behavior that JDK-8187289 is intended to fix. It would be nice to get this fix so customers aren't forced to choose between the lesser of two evils. I've experimented with some fixes, but they always resort in introducing some new issues. The single stepping code involved is very complex and hard to follow since it is scattered and executes in a very async matter. For example there are many occurrence of code that sets up state for another piece of code that isn't executed until an event comes at a later point, and there are all sorts of conditionals in the code that you need to weed through to figure out what the actual result of certain new events will be. For now I've tabled this issue but would like to get back to it at some point.
16-08-2023

I tried putting a JDI_ASSERT(JNI_FALSE) inside the following "if" block: if (fromDepth > afterPopDepth ) { step->frameExited = JNI_TRUE; } The assert triggered during about 39 tests. Looking at a couple of the tests a bit closer, it seemed to be happening when either doing a STEP_OVER of the last line of code in the method, or doing a STEP_OUT. I also tried removing the NotifyFramePop call so no handler was installed. 15 tests failed, all of which were also in the list of tests that were previously asserting. So it looks like if we are to remove this NotifyFramePop call, we need to replace it with some other mechanism to detect exiting the method we are single stepping in. I think a MethodExit handler would work.
03-02-2022

ILW = MMM = P3
02-02-2022

Also see JDK-6960970, which documents a similar issue caused by a different NotifyFramePop call.
29-06-2021