Impossible to control which thread is stepped in debugger. Debugger rapidly oscillates between threads.

Hi folks,

When I have multiple threads hitting breakpoints, it's sometimes impossible to control which thread runs when you step over the code. When you step, it switches to another thread that is on a breakpoint and steps that instead. It seems to sometimes be possible to select the thread to step by the Frames or Threads tab, but it is unreliable. And sometimes the debugger rapidly oscillates (flickers) between threads on breakpoints so fast that it's unusable. This is IDEA 14.0.1 with suspend policy = Thread, not All. Let me explain how I expect it to behave: when a thread hits a breakpoint the debugger should switch context to that thread and show its stack and variables, even if you were looking at another thread on a breakpoint. It should be possible to switch context back to the original thread and resume stepping that. Either by selecting the thread in the Frames or Threads tab. The debugger should only switch to another stopped thread when you resume the thread (F9), or select another thread, or a new thread hits a breakpoint.

I had this problem with an earlier verison of IDEA (10?) and had a conversation with you guys at Jetbrains but I can't find a forum thread or Youtrack issue for it, so I think it must have been on email from a work email account I no longer have access to. IIRC it was fixed in a newer version of IDEA (11?) but seems to have reappeared in 14. There are these issues which describe it well:

https://youtrack.jetbrains.com/issue/IDEA-73165

https://youtrack.jetbrains.com/issue/IDEA-78016

Can anyone comment on whether this was fixed and has now regressed and whether you are aware of this issue?

Thanks a lot.

Alex

11 comments
Comment actions Permalink

Hi Alex,

Thanks for the detailed description. I agree with this statement:

"It should be possible to switch context back to the original thread and resume stepping that"

This is possible right now by selecting the thread in the FramesView's combobox.

But I cannot agree with this:

"when a thread hits a breakpoint the debugger should switch context to that thread and show its stack and variables, even if you were looking at another thread on a breakpoint".

Previously it was working like this, and this is what many users called "context flickering". Right now, if breakpoint is hit and there is already another suspend context, which you might be looking at and examining, it would be extremely inconvenient to switch the context to this new thread and force the user to switch back manually. What if there were 3, 4 or more threads that hit breakpoints within a short period of time? That is why IDEA keeps already existing context unchanged and only marks additional threads that hit breakpoints in the thread tree. At the same time threre is always a possibility to manually switch the context to any different thread currently suspended at a breakpoint.

Another question is what debugger should do, if you resume current context and thare are another suspend contexts available.
For example, let threads A and B hit breakpoints with suspend_policy==Thread.
A was the first one to hit a breakpoint, that's why A is the current active suspent context.

If you resume A with F9 (Resume command), then debugger will automatically switch to B and this is expected result.
But what happens if you step in A? Stepping is actually a kind of Resume that most likely will soon stop, but may won't stop for a long time. From debugger's point of view, when you step, the current suspend context is destroyed and thread A becomes "running" again. Because of that debugger now switches current context to point to the next available context B.
But step operation usually terminates quickly and A becomes suspended again.
Current logic is to switch back to A, because its suspended state was caused by a stepping and not by a breakpoint. This is done deliberately in order not to forciby switch current stepping context. Note that even if A stops because of another breakpoint which had happened before the the step command finished, the context will be switched to A anyway, because A is still considered as a context where you do stepping.
This is actually what is

https://youtrack.jetbrains.com/issue/IDEA-73165

about. With the sample from this issue debugger works as intended.

What behavior can be corrected if needed? There are two questions one should answer:
1. Should debugger show the next available suspended thread, if user resumes current thread with the "Step" command?  Current behavior: debugger switches to the next context
2. If A was resumed by a "Step" command, should we switch to A back if the user managed to perform step in B, while A was running? Current behaviour: debugger switches back to A, because A is still in the "stepping" state.

I admit debugging multithreaded programs is indeed not an easy process, but debugger offers all necessary tools to deal with this. One should always mind the current context though.

How you would answer these questions?

0
Comment actions Permalink

Hi Eugene,


When I said "when a thread hits a breakpoint the debugger should switch  context to that thread and show its stack and variables, even if you  were looking at another thread on a breakpoint", what I really meant was that I could understand it if that is how it behaved, but I personally would prefer it if it did not switch thread.

There are two questions one should answer:

1. Should debugger show the next available suspended thread, if user resumes current thread with the "Step" command?  Current behavior: debugger switches to the next context
2. If A was resumed by a "Step" command, should we switch to A back if the user managed to perform step in B, while A was running? Current behaviour: debugger switches back to A, because A is still in the "stepping" state.

If user resumes current thread with Step, debugger should switch back to A if possible, i.e. I guess it should temporarily mute all breakpoints for threads other than A, until it returns from the step.

So to summarise what I am reporting,

  • selecting the thread in the FramesView's combobox does not always work and it insists on keeping another thread in context.
  • I still sometimes experience thread context flicker.


Are you aware that these 2 issues are still happening in this version of IDEA?

I suspect it's going to be difficult to provide a test case to reproduce it. But I'll see if I can identify the conditions that lead to it.

Regards,
Alex

0
Comment actions Permalink

> I guess it should temporarily mute all breakpoints for threads other than A,

This is exactly how it works. The difference is that no _new_ breakpoints are hit. Here we _already_, at the moment when you press "step",  have several threads suspended. If A and B were suspended and you were staying in A and press step in A, the A thread is temporarily resumend to make stepping happen.
One never knows for how long thread A will be resumed: this depends only on the code being executed during the step.
The question #1 deals with the situation when it takes relatively long to perform the step. As we had more than one suspended threads (B), debugger shows you the next available suspended thread B for the time being. But when step in A finishes and A is suspended again, it switches to A, because you were stepping in it. So it looks it works like you would expect it to work.

0
Comment actions Permalink

Ok, that's fine. But either you are not understanding what I am reporting, namely in IDEA 14.0.1 the ability to change thread using the combobox does not always work, and additionally sometimes it flickers between threads; Or I'm not understanding how what you've said addresses these issues.

0
Comment actions Permalink

> thread using the combobox does not always work

I'm not aware of the case where it does not work. If you could provide an example, we could analyse the particular problem.

> and additionally sometimes it flickers between threads;

An example here would be great too. My reply was based on the issues you mentioned above and I described what happens in those situations. You may mean something different, but so far the description is too general for me to be able to comment.

0
Comment actions Permalink

Hi Eugene,

This is now a real problem for me as it is very difficult to debug my application. What is particularly inconvenient is when it switches to another thread, in the Frames combobox the first thread becomes selected and I have to scroll through hundreds of threads to get back to the one I am interested in. Sometimes I have to do this after every line I step over. My colleagues who are using Eclipse are laughing at me because Eclipse doesn't have this problem. I really don't want to go back to Eclipse so I hope we can find a solution. I have created a simple test class to reproduce the bug where, when stepping in one thread, sometimes the debugger switches to another thread and does not switch back:

public class MultithreadedDebuggerTest {
    private static final Logger logger = Logger.getLogger(MultithreadedDebuggerTest.class);
    final ReadWriteLock lock = new ReentrantReadWriteLock();

    @Test
    public void test() throws Exception {
        ExecutorService svc = Executors.newFixedThreadPool(100);

        for (int i=0; i<500; i++) {
            svc.submit(new Runnable() {
                @Override
                public void run() {
                    if (logger.isDebugEnabled()) logger.debug("Running");
                    int i = 0;
                    i++;
                    //long-running method
                    long prime = findPrime(1000);
                    logger.info(prime);
                    lock.readLock().lock();
                    int j;
                    try {
                        j = i + 1;
                    } finally {
                        lock.readLock().unlock();
                    }
                    if (logger.isDebugEnabled()) logger.debug("Finished " + j);
                }

            });
        }

        Thread.sleep(10000);
        svc.shutdown();
        svc.awaitTermination(5, TimeUnit.SECONDS);
    }



I have a breakpoint on the first log statement and the j = i + 1 line. I select a few threads to start stepping through the block. Sometimes when I step, it switches to another thread and does not return. One of the places it frequently switches is when stepping from the logger.info(prime) line to the lock.readLock line. It might take you a few threads but with patience you should be able to reproduce it. I also sometimes get thread flicker but it eventually stabilises after a couple of seconds.

Thanks a lot.

Alex

0
Comment actions Permalink

Hi Alex,

Thanks for the example, I've tried it. Here we indeed have a similar situation to what I describved above. The main reason for "flickering" is that when you do step in some thread, you resume it and debugger shows you the next suspended thread that is also paused at a breakpoint. If step is performet relatively fast, the context is switched back to the "stepping" thread and you don't notice anything. But if step takes some time, you'll see another thread in UI. If you press Step action again in this new context, you'll initiate stepping in another thread, and now these two threads will start "competing" for your attention when their step requests finish. In your example, if you wait until the step action compeles, you'll notice that debugger selects the "stepping" thread back, so you can continue stepping. This can be considered as a workaround for now: always check in what context you are stepping.

We are going to stop selecting the next suspended thread if you have several threads waiting and you do "step" action in one of them. This will eliminate chances to perform stepping in a "wrong" thread just because debugger temporarily switches contexts.
You may watch this issue to track the progress:

https://youtrack.jetbrains.com/issue/IDEA-63378

0
Comment actions Permalink

Hi Eugene,

I understand the reason for flickering. Flickering is less of an issue for me. Being unable to control which thread is stepped, or the debugger switching thread after every line of code I step, is by far the greater issue. The only workaround is to switch back to my thread using the combobox after every step, which with hundreds of threads takes several seconds, which I'm sure you can imagine really slows down debugging. It is exactly the issue described better than me by Dave in that issue. I have voted for that issue.

Regards,
Alex

0
Comment actions Permalink

> is to switch back to my thread using the combobox after every step

You don't have to, actually. Just wait until the step completes, and debugger will bring you back to the thread where you started stepping. Just pay attention what thread is selected in the combobox before pressing step. This is inconvenient, I understand, but this may be considered as a workaround until we fix the issue.

0
Comment actions Permalink

Eugene,

Clearly you do not understand the bug I am reporting: when I step on a thread, it switches to another thread and does NOT bring me back to the thread where I started stepping, after any amount of time; it remains on another, random thread. So I have to continually reselect my thread from the Frames combobox, sometimes after every single line I step over. This is incredibly inconvenient, to the point where it is barely worth using the debugger at all.

0
Comment actions Permalink

> after any amount of time; it remains on another, random thread.

But is the "original" thread stopped at all? After you invoke step, it does not automatically mean that the thread will be suspended soon with 100% guarantee. For example, for application servers when you do step on the last statement of the handler's code, the thread is resumed and may be returned in the thread pool before the step request conditions are met. That way the thread won't be suspended until it is chosen to handle another client request.
This is just an example, that time period between pressing "Step" and actual "step event" may last quite long.

if
(after invoking step the context is switched to another thread) _and_
(the original thread is suspended again)  _and_
(you did not invoke any step actions in any of suspended threads while waiting for the original step in the original thread to complete) _and_
(the context is not switched to the original thread as soon as it is suspended after the step action),

then it's definitely a bug.

But I was not able to reproduce exactly this with your example.

0

Please sign in to leave a comment.