Home  >  Article  >  Java  >  Debugging Inactive Applications

Debugging Inactive Applications

WBOY
WBOYOriginal
2024-08-19 12:32:341028browse

Read in other languages: English Español 中文

There are many debugger tutorials out there that teach you how to set line breakpoints, log values, or evaluate expressions. While this knowledge alone provides many tools for debugging your application, real-world scenarios can be a bit more complex and require a more advanced approach.

In this article, we will learn how to find codes that cause a UI freeze without much prior design knowledge and fix faulty codes in real time.

The Problem

If you want to follow, start by cloning this repository: https://github.com/flounder4130/debugger-example

Suppose you have a complex application that crashes when you perform some action. You know how to reproduce the bug, but the difficulty is that you don't know which part of the code is responsible for this functionality.

Depurando Aplicativos Inativos

In our example app, sleep occurs when you click Button N. However, it is not so easy to find the code responsible for this action:

Depurando Aplicativos Inativos

Let's see how we can use the debugger to find it.

Method Breakpoints

The advantage of method breakpoints over line breakpoints is that they can be used across entire class hierarchies. How is this useful in our case?

If you look at the example project, you will see that all action classes are derived from the Action interface with a single method: perform().

Depurando Aplicativos Inativos

Setting a method breakpoint on this interface method will suspend the application whenever one of the derived methods is called. To set a method breakpoint, click the line that declares the method.

Start the debugger session and click Button N. The application is suspended in ActionImpl14. Now we know where the code corresponding to this button is located.

Depurando Aplicativos Inativos

Although in this article we are focused on finding the bug, this technique can also save a lot of time when you want to understand how something works in a large codebase.

Pause the Application

The approach with method lock points works well, but it is based on the assumption that we know something about the parent interface. What if this assumption is wrong, or we can't use this approach for some other reason?

Well, we can do this even without the breakpoints. Click Button N, and while the application is locked, go to IntelliJ IDEA. From the main menu, select Run | Debugging Actions | Pause Program.

Depurando Aplicativos Inativos

The application will suspend, allowing us to examine the current state of the threads in the Threads & Variables tab. This gives us an understanding of what the application is currently doing. Since it is locked, we can identify the locking method and trace it back to the location of the call.

This approach has some advantages over a more traditional thread dump, which we'll cover shortly. For example, it provides information about variables in a convenient way and allows you to control further program execution.

Note: For more tips and tricks with Pause Program see Debug without Breakpoints and Debugger.godMode().

Thread Dumps

Finally, we can use a thread dump, which is not strictly a debugger feature. It is available regardless of whether you are using the debugger or not.

Click Button N. While the application is locked, go to IntelliJ IDEA. From the main menu, select Run | Debugging Actions | Get Thread Dump.

Look at the available threads on the left, and under AWT-EventQueue you will see what is causing the problem.

Depurando Aplicativos Inativos

The disadvantage of thread dumps is that they only provide a snapshot of the state of the program at the time they were taken. You cannot use thread dumps to explore variables or control program execution.

In our example, we don't need to resort to a thread dump. However, I still wanted to mention this technique as it can be useful in other cases, such as when you are trying to debug an application that was launched without the debug agent.

Understanding the Problem

Regardless of the debugging technique, we arrive at ActionImpl14. In this class, someone intended to do the work in a separate thread, but confused Thread.start() with Thread.run(), which runs the code in the same thread as the calling code.

IntelliJ IDEA's static analyzer even warns us about this at design time:

Depurando Aplicativos Inativos

A method that does heavy work (or heavy sleep in this case) is called on the UI thread and blocks it until the method finishes. That's why we can't do anything in the UI for some time after clicking on Button N.

HotSwap

Now that we've discovered the cause of the bug, let's fix the problem.

We could stop the program, recompile the code and then run it again. However, it is not always convenient to refold the entire application just for the sake of a small change.

Let's do this in a smart way. First, fix the code using the suggested quick-fix:

Depurando Aplicativos Inativos

After the code is ready, click Run | Debugging Actions | Reload Changed Classes. A message appears, confirming that the new code has reached the VM.

Depurando Aplicativos Inativos

Let's go back to the app and check. Clicking Button N no longer crashes the app.

Tip: Remember that HotSwap has its limitations. If you are interested in HotSwap's extended capabilities, it might be a good idea to take a look at advanced tools like DCEVM or JRebel.

Summary

Using our reasoning and some debugger features, we were able to locate the code that was causing the UI to freeze in our project. We then proceed to fix the code without wasting time on recompilation and redeployment, which can be time-consuming in real-world projects.

I hope you find the techniques described useful. Let me know what you think!

If you are interested in more articles related to debugging and profiling, check out some of my other articles:

  • Debugger.godMode() – Hack a JVM Application with the Debugger
  • Debugger Slow Troubleshooting
  • Debug without Breakpoints
  • What's Wrong with createDirectories()? - CPU Profile Guide

The above is the detailed content of Debugging Inactive Applications. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn