In the previous article, we discussed how to set up a remote native image debugging session by creating a Docker container with debug information in a Docker container. We also looked into debugging native images in Eclipse IDE. Please read the article first to familiarize yourself with the theory.
Starting with IntelliJ IDEA 2024.3, you can also debug native images from IntelliJ IDEA Ultimate. All you need is to install the plugin and follow the instructions in this article!
Table of Contents
Considerations when debugging native images from IntelliJ IDEA
The GraalVM Native Debugger plugin is implemented over the GDB protocol, meaning that there are certain limitations compared to the standard debugging experience (see below). In addition, there is an issue of mixing several sources for one binary function. This issue stems from the nature of AOT compilation and can be minimized by reducing the number of optimizations.
Other important factors to consider are:
- GraalVM Native Debugger plugin is only available in IntelliJ IDEA Ultimate Edition and the smooth experience will be on Linux. But if you use other operating systems, you can debug native images in containers. So, the tutorial below is applicable to all OSs as we build and run native images in containers.
- Local debugging with this plugin is possible on Linux only currently.
- Remote debugging (provided that target OS is Linux) is available for all operating systems via SSH or Docker Compose.
- If you want to debug a native image remotely,
gdbserver
must be installed and running on the target environment. - With GraalVM Native Debugger, it is possible to step through a program in the Java source code or machine code.
For more information on GraalVM Native Image debugging, refer to the documentation.
How to set up native image debugging in IntelliJ IDEA
Prerequisites
- GraalVM Native Debugger plugin installed;
- Docker up and running.
Local debugging on Linux
Let’s first look at setting up a debugging session if you are running a native image from IntelliJ IDEA and use Linux.
First of all, we need to build two images.
The first Dockerfile (Dockerfile-ext) is for building and debugging the native image:
FROM bellsoft/liberica-native-image-kit-container:jdk-17-nik-23-musl
RUN apk add gdb
This Dockerfile enables you to meet the requirements to debug a native image on a local machine: you have to have GraalVM and gdbserver available in the resulting image specified as the target.
Another Dockerfile is for simply running and debugging the native image:
FROM bellsoft/alpaquita-linux-base:stream-musl
RUN apk add gdb
Build the first image:
docker build . -t debug-native-runtime-ext -f Dockerfile-ext
Build the second image:
docker build . -t debug-native-runtime -f Dockerfile
In the run widget, click Edit.
Click + (Add New Configuration) in the toolbar, then select GraalVM Native Image. In the case of this configuration, IDE can build and run your application.
First of all, let’s specify the name of our configuration: in our case, it is Petclinic-cmd.
Then, choose where you are going to run our configuration. In our example, we run it on Docker. Select Run on Docker, and then click on Manage targets.
Select Pull or use existing, which means that the image will be pulled from the local repository.
Specify the image tag of the image we built previously (debug-native-runtime-ext:latest) and run options. For instance, you can specify the port.
After setting up the target, specify the path to your native executable in a container.
Finally, specify the module path so that the source files can be found.
Now, you can start a debugging session by choosing the Native Image configuration in the run widget and clicking Debug.
After that, the debugging workflow is familiar: you can set breakpoints, work with expressions, etc.
Remote debugging session
If you want to debug a native executable running remotely, you must first start a containerized native image with debug information and gdbserver
built as specified in this article.
docker start native-image-debug
Then, log into the container:
docker exec -it native-image-debug sh
And start the application manually in the container:
$ cd path/to/executable
$ ./your-app
After that, log into the container again and run ps to find out the PIDs of the process. Then, start the gdbserver
with the PID of the running native image:
gdbserver --attach :2345 PID
Now, we have a gdbserver
debugging our native image in a remote container. And we can port forward and have a port on our localhost available for the IDE.
Now, you can set up the GraalVM Native Attach run configuration. In the case of this configuration, we attach to the process through the port, the user is responsible for starting the gdbserver on the target. The IDE is not aware of what is happening on the target.
In the run widget, click Edit.
Click + (Add New Configuration) in the toolbar, then select GraalVM Native Attach.
Give a name to your configuration; in our case, it is petclinic-native-attach.
Specify the host and port for attaching to the running process.
Finally, specify the classpath of the module.
After that, click Debug in the same window.
Conclusion
In this article, we look at several approaches to setting up the Native Image debugging session in IntelliJ IDEA. In reality, there are more scenarios and approaches to implementing them.
The plugin is currently under active development, its functionality is being enhanced. We will update the article with the changes.