Master Debugging Without Breakpoints

cover
5 May 2024

In a typical debugging scenario, you would set breakpoints to tell the debugger when to suspend your program. A breakpoint usually corresponds to the moment that marks the starting point of further investigation.

It can happen that in some situations you aren't sure where to set a breakpoint. Other times, you might prefer to suspend the program at a particular time rather than aiming at a specific line.

In this article, we'll look at IntelliJ IDEA's Pause – a lesser-known debugging technique, which can be extremely powerful in some scenarios, including the ones described above. We'll discuss its use cases and limitations as well as discover the secret stepping trick.

What Is Pause?

Pause is a feature in IntelliJ IDEA's debugger that enables you to arbitrarily suspend your application at any given point in time. To use it, you don't have to be familiar with the application code. In fact, you can completely ignore it!

Pause button on the debugger's toolbar

To pause a program, click Pause on the debugger's toolbar. As a result, the program gets suspended right in the middle of what it is currently doing.

Limitations

At first sight, a paused program may look exactly like the one that has been suspended at a breakpoint. However, this is only true to a certain extent.

Debugger looks the same regardless of whether Pause or a breakpoint was used to suspend the app

It would be correct to consider Pause a sort of thread dump plus. You can still inspect variables and threads just as you typically would. However, some of the more advanced features, such as Evaluate expression, won't work.

Use-cases

There are countless ways you can use Pause. Often, it can be used interchangeably with traditional breakpoints. But there are also scenarios where Pause fits better. Let's consider a couple of them.

Unresponsive apps

If you encounter a UI freeze, that's usually because the UI thread is blocked or doing something heavy. Pause might be useful in both those cases.

The stack of the blocked UI thread shows what method it is currently executing

Pause the application while it is being unresponsive and examine the call stack of the UI thread. Often this is sufficient to diagnose the problem.

Tip: To learn more about this scenario, check out Debug Unresponsive Apps

Missing sources

As I said before, Pause allows you to ignore the source code. Actually, the source code might as well be completely missing for you! This scenario is not typical, but when you encounter it, breakpoints wouldn't be of any help.

... which is not a problem for Pause.

hehe cat meme

Tip: To learn more about this scenario, check out Debugger.godMode()

Locks

If you suspect a synchronization problem, such as a deadlock or a livelock, Pause might help you find the exact threads and monitors that are causing the issue.

The list of threads shows which threads have ended up in a deadlock

Pause the program and inspect the thread list. It will show which threads are blocked. By navigating to the execution point, you will also see the critical sections they are locked in. This information might guide you toward a solution.

Secret Stepping Trick

As I pointed out earlier, Pause indeed limits your access to some of the advanced debugger features. Nevertheless, there is a workaround for this restriction.

After you have paused an application, proceed by performing any stepping action. Step Into or Step Over will do. After that, you'll find yourself in a regular debugging session, which is similar to suspending an application using a breakpoint.

All advanced features are now unlocked!

Conclusion

That's it for today! Hope you find these tips and tricks useful.

If there's something specific you want me to cover about Java debugging, don't hesitate to reach out! Your feedback will help me in prioritizing and publishing the content that is the most interesting to you.

To receive updates on the new posts, follow me on X or subscribe to the mailing list on my blog.

See you soon!