Files
quarkus-workshop/docs/debugging.adoc
jamesfalkner c7b3693bfc more fixes
2019-07-24 18:18:22 -04:00

136 lines
6.5 KiB
Plaintext

= Debugging in Dev Mode
:experimental:
Debugging is an art of finding errors (bugs) and eradicating (debug) them from a piece of code written in any programming language. Quarkus apps are no different in this regard and you can use the raw Java command line debugger (`jdb`) or, if your IDE supports it, graphical debugging. Most IDEs have integration with Java debuggers, including Eclipse Che, so let's exercise it.
== Write some bad code
Let's introduce a subtle off-by-one bug in our application and use the debugger to debug it.
Open up the `GreetingResource.java` class again, and add another RESTful endpoint that returns the last letter in a name:
[source, java, role="copypaste"]
----
@GET
@Path("/lastletter/{name}")
@Produces(MediaType.TEXT_PLAIN)
public String lastLetter(@PathParam("name") String name) {
int len = name.length();
String lastLetter = name.substring(len);
return lastLetter;
}
----
A a bug has been reported where the last letter is not working. You can probably spot the bug right away but let's see how you could find this with the debugger, and more importantly fix it and re-test very quickly.
== Run the app in debug mode
Before you can debug the app, you need to run it in _debug mode_. Typically this means adding a lot of JVM command line switches, and Quarkus has simplified this for you.
In Che, open up the command palette once again to run our app in Debug mode. Double-click on **Debug Quarkus App** from the menu
to run the Quarkus app with a debugger. This will invoke `mvn compile quarkus:dev -Ddebug` under the the covers:
image::debugcmd.png[test, 600]
You will see the tell-tale Java log statment showing that debugging is enabled: `Listening for transport dt_socket at address: 5005`. Quarkus will wait until you attach a debugger before continuing.
== Attach debugger
To connect to it, use the _Run > Edit Debug Configurations_ menu command. In the dialog, Click the `+` button next to `JAVA` to create a new Java debug configuration. You can name it whatever you want, but keep the `localhost` field as-is and select port `5005` in the port selection field:
image::debugconfig.png[debugconfig,600]
Click **Save** to save this configuration, and then click **Debug** to attach the debugger to our running Quarkus app. The debugger view will appear at the bottom of the screen with several small buttons used to start/stop execution, step into/over/out of code, and other operations, and the app will begin executing.
You can switch between the debug pane and the Terminal panes using the small icons to the left of the debugger window:
image::buttons.png[buttons, 600]
== Test app
Click the Terminal button to go back to your terminal(s), and open a Terminal and run:
[source,sh,role="copypaste"]
----
curl http://localhost:8080/hello/lastletter/foo
----
Although we were expecting `o`, we got nothing. There is something wrong with our code.
== Set a Breakpoint
Click Debugger button to go back to the debugger.
To debug the app, let's step through our function that has the bug. In the left gutter of the code, where the line numbers are shown, click once on the line number next to `int len = name.length();` to set a breakpoint. The line number will be highlighted and the breakpoint will be registered in the debug pane:
image::break.png[breakpoint,800]
== Trigger the bug
Now that we have a breakpoint, go back to Terminals with the Terminal button. In the Terminal issue the same `curl` command as before:
[source, sh, role="copypaste"]
----
curl http://localhost:8080/hello/lastletter/foo
----
This time, the command will appear to hang as the breakpoint has been reached. The line where you set the breakpoint will be highlighted. Click the Debugger button to go back to the debugger, which has paused the execution at the breakpoint:
image::breakreached.png[breakpointreached]
You will see three main sections of the debug view:
* **Breakpoints** - This lists the breakpoints you've set. Each Breakpoint can be further configured, or selectively disabled, by right-clicking on the breakpoint in the breakpoint list.
* **Frames** - This is an ordered list of _stack frames_ showing the path through the code from the beginning of the thread to the current location in our code.
* **Variables** - Here you can see the value of local variables in the selected stack frame. In our code we have no local variables defined yet, but once we start stepping through the code, newly defined variables (like `len`) will appear here.
You can use the various buttons to step across code:
image::debugbuttons.png[debugbuttons, 800]
Step over the current line by clicking **Step Over**. This will fully execute the current line, and advance to the next line in the code and stop again. (You could also step _into_ methods for deeper debugging).
At this point, `len` is defined (and listed on the right side):
image::len.png[length, 800]
Click **Step Over** again, which executes the line to grab the last letter using `len` an offset to the `substring` method. See the bug? Look at the value of `lastLetter` in the variables list on the right - it's empty!
We need to pass an offset that is one _before_ the end, to get the last letter.
Click the **Resume** button to let the method continue and return the value (your `curl` command has probably timed out by now).
== Fix the bug
Fix the code by changing the line that calls `substring()` to read:
[source, java, role="copypaste"]
----
String lastLetter = name.substring(len - 1);
----
With the bug fixed, re-trigger the method by running the `curl` command again in a Terminal:
[source, sh, role="copypaste"]
----
curl http://localhost:8080/hello/lastletter/foo
----
The breakpoint will be hit once again. step over the lines to verify the value of `lastLetter` is correct before the method returns. You've fixed the bug!
Remove the breakpoint by clicking on the line number again to de-highlight it. Run the `curl` command once more to see the full bugfix which should return the last letter of the generated name now: You should see `o`.
Click **End Debug Session** button to quit the debugging session.
Quarkus apps are just like any other Java app, so debugging is straightforward and supported by many IDEs and CLIs out there.
== Cleanup
Go back to your Terminals and stop the app for now by pressing kbd:[CTRL+C] in the terminal or closing the Terminal window in which the app runs.
== Congratulations!
Debugging Quarkus apps is the same as any other Java app. Combined with Live Reload, it makes development quick and (relatively) painless!