JetBrains Fleet 1.42 Help

Debugging quickstart

You have created and run your Java application. Let's imagine you have discovered that it functions not the way you expected. For example, it returns a wrong value or crashes with an exception. Seems like you have errors in your code, and it’s time to debug it.

What Is Debugging?

Broadly, debugging is the process of detecting and correcting errors in a program.

There are different kinds of errors, which you are going to deal with. Some of them are easy to catch, like syntax errors, because they are taken care of by the compiler. Another easy case is when the error can be quickly identified by looking at the stack trace, which helps you figure out the cause.

However, there are errors that can be very tricky and take really long to find and fix. For example, a subtle logic error, which happened early in the program may not manifest itself until very late, making it a real challenge to sort things out.

This is where the debugger is useful. It is a tool that lets you find bugs in an efficient manner by providing an insight into the internal operations of a program. This is possible by pausing the execution at a specified point, analyzing the program state, and, if necessary, advancing the execution step-by-step. While debugging, you are in full control of the things. In this manual we are covering a basic debugging scenario to get you started.

Examine the code

Let's try a simple debugging case. Imagine we have the following application:

public class AverageFinder { public static void main(String[] args) { System.out.println("Average finder v0.1"); double avg = findAverage(args); System.out.println("The average is " + avg); } private static double findAverage(String[] input) { double result = 0; for (String s : input) { result += Integer.parseInt(s); } return result; } }

The program is supposed to calculate the average of all values passed as command-line arguments.

It compiles and runs without issues; however, the result is not what one would expect. For instance, when we pass 1 2 3 as the input, the result is 6.0.

First of all, you need to think about where the error might be coming from. We can assume the problem is not in the print statements. Most likely, unexpected results are coming from our findAverage method. In order to find the cause, let's examine its behavior in the runtime.

Set breakpoints

To investigate the bug, we need to pause the program when it reaches the piece of code that is producing a wrong result. This is done by setting breakpoints. Breakpoints indicate the lines of code where the program will be suspended for you to examine its state.

  • Click the gutter at the line where the findAverage method is called.

    A breakpoint is set at the line that calls the findAverage method

Run the program in debug mode

Now let's start the program in debug mode.

Since we are going to pass arguments for running and debugging the program, make sure the run/debug configuration has these arguments in place.

{ "configurations": [ { "type": "gradle", "name": "average-finder", "tasks": ["run"], "args": ["--args=1 2 3"] } ] }

Press ⌘ R, select the run configuration, and then press ⌥ ⏎.

Starting the app in debug mode from the Run and Debug popup

Analyze the program state

After the debugger session has started, the program runs normally until a breakpoint is hit. When this happens, JetBrains Fleet pauses the program, highlights the line, at which the program is suspended, and shows the Debug tool.

Debug tool window appears. The line with the breakpoint is highlighted

The highlighted line has not been executed yet. The program now waits for further instructions from you. The suspended state lets you examine variables, which hold the state of the program.

As the findAverage method has not been called yet, all its local variables like result are not yet in scope, however, we can examine the contents of the args array (args is in scope of the main method). The contents of args are displayed in the Variables panel:

Fleet java variables panel

Step through the program

Now that we are comfortable with the Debug tool, it's time to step into the findAverage method and find out what is happening inside it.

  1. To step into a method, click the Step Into button on the Debug tool window's toolbar or press ⌘ ;.

    Step into button located in the top part of the Debug tool

    The highlighting in the editor moves to another line because we advanced the execution point one step further.

  2. Let's keep stepping and see how the local variable result is declared and how it is changed with each iteration of the loop.

    New values in the Variables panel

    Right now the variable s contains the value "3". It is going to be converted to int and be added to result, which currently has the value of 3.0. No errors so far. The sum is calculated correctly.

  3. Two more steps take us to the return statement, and we see where the omission was. We are returning result, which has the value of 6.0, without dividing it by the number of inputs. This was the cause of incorrect program output.

  4. Let's correct the error:

    return result / input.length;

Stop the debugger session and rerun the program

To check that the program works fine, let's stop the debugger session and rerun the program.

  1. On the Debug tool window's toolbar, click the Stop button or press ⌘ ⇧ \.

    Debugger session is stopped using the Stop button located in the Debug tool
  2. Click the Run button near the main method. From the menu, select Run.

  3. Verify that the program works correctly now.

    > Task :app:run Average finder v0.1 The average is 2.0
Last modified: 23 October 2024