Wednesday 10 April 2013

Notes on Remote Debugging in Java

Hello Readers,

Been a while!  This post will cover how to enable remote debugging in a java application and hook Eclipse's debugger onto it.  It'll also cover the explanations of all those little details that can really bite you if you don't understand them.  If you're looking to debug issues in a testing or production environment and are feeling blind without being able to snoop through your code then you've hopefully come to the right place!

Enable Remote Debugging


Remote debugging of a Java application isn't enabled by default.  You need to run the application (or Target Java VM) with certain Java properties that enable and expose the debugging functionality.  There are many properties we can use to configure Java debugging on the VM's end but for this post, we'll stick to one standard set of properties that suffice for allowing the Eclipse debugger to connect.  When running the Target VM, these properties look like:

java myApplication -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=9000,server=y,suspend=n

What these properties mean (found from around the web) are as follows:
  • -Xdebug : Enables debugging support in the Java VM
  • -Xnoagent: Disables oldjdb, a legacy Java debugger that has now been deprecated
  • -Xrunjdwp: loads libraries required for Java debugging
    • transport: specifies the manner in which the VM and debuggers will communicate.  For instance, setting transport=dt_socket means that the debugger and the VM will communicate socket to socket via a TCP/IP connection.
    • server: specifies whether or not the Java VM will act as a 'server'.  If server=y, the VM will expose a port for debuggers to attach onto.  If server=n, the VM will instead attach onto a specific debugger application.
    • address: If server=y, this is the address that the VM exposes to debuggers.  If server=n, this is the address of the specific debugger that the VM attaches to.
    • suspend: specifies whether or not the VM should wait for a debugger to attach before starting
Thus, in our example:

java myApplication -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=9000,server=y,suspend=n

What we have done is:
  • enabled debugging for the Target Java VM
  • exposed it as a server at port 9000, so that any debugger can attach to the VM at this port
  • ensured that the VM will not suspend while waiting for a debugger to attach

Ensure that the Classes of Interest in the Target VM have Source Attached


Java debugging seems to work on a file line or method signature basis.  If the environment you are running happens to be in a production or testing environment with Obfuscated source, then it will be near impossible for your debugging application to break when you want it to break.  The approach I've seen used at work has been to:
  • Stop the target VM
  • Replace any obfuscated classes (in the form of a .jar library), with non-obfuscated classes
  • Start the target VM
This way the source for your class files in Eclipse will match those in your Target VM.

Hook the Eclipse Debugger onto Target VM


To use Eclipse as our debugger:
  • In Eclipse, open 'Debug Configurations' (found in the drop down menu next to the bug icon)
  • Create a new 'Remote Java Application' configuration
  • Set the 'Project' to the Eclipse project that contains the classes you want to debug.  I believe this project determines which classpath the debugger will be looking through.  Thus, classes from projects referenced by this project should also be able to be found.
  • Set Connection Type to 'Standard (Socket Attach)'.  This matches our dt_socket option set on the Target VM.
  • Set the Connection Properties:
    • Host: the host of the Target VM (e.g. localhost).
    • Port: the port that the Target VM has exposed for debugging.  This should match the address option we set for the Target VM earlier.
  • Hit 'Debug'

Set Breakpoints on Lines... not on Method Signatures!


Setting breakpoints on Method Signatures then attaching the debugger seems to cause the Target VM to slow to a crippled pace, pretty much killing any chance you have of investigating your issue.  Apparently this happens because Method Breakpoints are "expensive to evaluate" (http://devnet.jetbrains.com/docs/DOC-23).  Avoiding Method Signature breakpoints can save you a lot of pain :).

References


Check out the following sites if you want to learn more details: