Diving Into Java Debugging

Ari
7 min readMay 25, 2020

--

Software bugs are an inevitable part of the development process. For this reason, debugging is a core skill every programmer should have. For this reason, Java debugging is a core skill every programmer should have.

It not only helps you understand the flow of your programs but also improve the quality of your code. In the end, it increases your ability to solve problems as a developer, and ultimately, deliver better-quality software.

This article dives into some of the most important tips and techniques for Java debugging. We will also go through the best practices when debugging Java in production.

Java Debugging for Your Project

This section puts together some tips and tricks to use when debugging your Java project, including breakpoints, step filters, drop-to-frame, and more. These can be applied even in the most complex scenarios

Conditional Breakpoints

A breakpoint is used to specify the point where program execution should stop when debugging. By temporarily suspending execution, you can investigate the program’s fields and variables, change their values, and more. In this article, examples are run on Eclipse but the same concept can be applied across other Java IDEs.

Conditional breakpoints are set to trigger when a certain criteria is met so you can examine the state of your program, the call stack or even values of valuables. In a program designed to calculate the geometric mean of ratings or scores for music albums, we can set up a conditional breakpoint

The program flows and works well at a glance, but in an event when it returns 0, chances are that one of the albums has a 0 rating. Placing a simple conditional breakpoint inside the loop means the program will stop too many times (imagine when we have hundreds of albums).

The solution, in this case is to specify album.getRating().score == 0 as the base condition for our breakpoint. This will suspend execution right at the bogus or zero-rated album.

More Types of Breakpoints

In addition to the types of breakpoints explained above, there are other types of breakpoints, but it depends on the Java IDE you’re using. They include:

  • Event-based breakpoints — These are tied to events and are usually triggered whenever an event recognized by the debugger is encountered.
  • Field watchpoints — This type of breakpoint will stop an executing program whenever the value of a given field or expression changes. When debugging, you can specify a field watchpoint to stop execution when an expression is read, modified, or both.
  • Method breakpoints — Used to suspend a program upon reaching or exiting a specified method or its implementation. This allows you to check the entry or exit conditions of a particular method.
  • Line breakpoints — Halts the execution of a program when it reaches a particular line of code as set in the breakpoint.

Make Working With Breakpoints Easier Using Rookout

Rookout, a next-gen debugging platform, is designed to help developers add non-breaking breakpoints to their program code so that they can collect any type of data they need. It allows you to specify rules that refine conditions of when or how a breakpoint is triggered.

One cool thing about debugging with breakpoints on Rookout is its breakpoint status feature — a simple visual indicator that provides quick feedback on how your breakpoint is behaving. Breakpoint status appear as a warning sign or a light bulb right next to your breakpoint (on the right-side pane).

There are five types of breakpoint status in Rookout namely:

  • Active (solid purple)
  • Pending (hollow purple
  • Warning (solid purple with a triangle)
  • Error (hollow purple with a triangle)
  • Disabled (hollow grey)

The breakpoint status capability gives you some insight into your program even before you receive the logline or debug messages. This information makes your debugging a lot easier.

Step Filters

Step filtering allows you to define packages that can be skipped during debugging. This is especially important when you need to shift from one class to another, access external libraries, or use a framework without testing the framework’s classes.

By restricting transitions use the step filters feature, you can skip specific packages. In Eclipse, step filters can be configured through the path Window Preferences >>Java Debug >>Step Filtering.

Drop to frame

This technique allows you to select and re-run part of your program during debugging. You can select any frame in the call stack and set the debugger to restart from that point.

To use this functionality, select a frame or level in your stack and hit the Drop-to-frame button while in Debug view.

Although this feature performs a kind of step-back or reset, it does not affect your fields or the existing data. For instance, if you write some entries into the database, and then drop to a previous level or frame, the entries will still remain in the database.

Remote Debugging

Most Java IDEs such as Eclipse allow developers to debug applications running on another device or Java Virtual Machine (JVM). However, you will need to start your Java program with certain flags.

The code snippet below demonstrates how this is done:

Eclipse users can enter the hostname and port number to connect for remote debugging as depicted in the snapshot below:

Debugging Java in Production

Today’s software development environment is so fast-paced, so R&D teams are always striving to meet these demands by pushing code faster to production. However, no matter how strict your testing process is, things will go wrong once in a while.

Additionally, when code is in production and real data is flowing through the system, things might go south pretty quickly. To stay on top of things as your system scales, it is important to address potential vulnerabilities in your application.

Here’s a strategy every Java developer can implement:

  • Identify when an error happens
  • Assess how severe the error is for prioritization
  • Single out the state(s) that made the program to run into an error
  • Next, trace and solve the root cause
  • Finally, deploy an effective fix

Other than using the five-step strategy above, follow some best production debugging practices you should follow:

Ramp up logging levels

It is easier to debug an application if the error logs contain sufficiently detailed messages. In most cases, error messages do not offer enough context, so programmers should increase the log levels.

This enables you to capture the full context and understand what exactly led to the error. Every log line should help you trace the root cause of an error.

A practical yet often underutilized way of tracing where errors originate is generating UUIDs at the application entry point of every thread.

Ideally, you can format the thread name as follows:

So, instead of producing an anonymous name like “pool-7-thread-22” your application will create smart stack traces starting with “threadName: pool-7-thread-22, UUID: EB85GTA, MsgType: AnalyzeGraph, MsgID: 415669, 29/03/2020 04:44”.

Centralize logs

An efficient logging mechanism should be a priority when handling bugs during production or any other stage of the application lifecycle.

Recording all important events during a session and storing the logs in a centralized server for analysis can make debugging easier.

It also helps you keep an eye on conditions where something goes wrong in your application while tracking key product metrics.

Examine the stack trace and other logs

Stack traces come in handy when debugging exceptions. They help you identify what functions in the program had been called when it crashed.

With stack traces, it is easier to figure out what went wrong because you can view the order of calls that led to an exception.

The code snippet below can print the exception stack using the printStack() method in the exception class:

It produces the following results:

Replicate Instances

After retaining the logs, it is important to replicate the situation/instance on your end. This can be done by creating a similar environment so you can see the issue in the IDE.

Reproducing situations helps in resolving errors as you can understand your code and the environment much better.

Wrap Up

The Java debugging process doesn’t have to be a nightmare. You only need to have a creative mindset and use the right tools. Rookout is one such tool that helps developers expedite the debugging process. It is designed to help you identify, diagnose, and resolve errors within your code with greater confidence, speed, and accuracy.

--

--