Friday, May 1, 2009

Remote Debugging Java Applications Using Eclipse

Ok so after completing the BBSpot quiz, I was really in mood to do something interesting. Suddenly I remembered a technical paper which I need to submit to my manager for some news letter (yes one more news letter of dozens of them you get every day from all over the net)

So was just thinking some interesting topic which will catch interest of all in developer community of my team. I did not want to come up with some new emerging technology with lots of technical jargon and theories. After all am not preparing a presentation for any (dumb) client who gets impressed by such not-so-useful articles/papers. My target audience is a bunch of smart and intelligent Java developers. So I was thinking of a topic which can catch the interest of these worldly creatures who then can use it for some better purpose to ease their development/debugging life. So the idea struck to my mind was Remote Debugging. I know this is nothing new to Java world, but still I find this feature very exciting, helpful & powerful. Remote debugging has always been an integral part of JDK or J2SE SDK since long and perhaps most unused or less explored by most of us. So what's remote debugging and why we actually need it?

Many a times, it so happens that some nasty bug shows up on the functional testing or the business testing environment, which are typically on the customer network infrastructure with very limited or no debugging tools? To make matters worse, when you try to reproduce the same bug on your local development environment, it won't show up and system will behave just fine. This is a scenario which each and every software project and its developers face without exception.

Traditionally, in such scenarios, we tend to put in a lot of debug statements (logger.debug or System.out.println) or in the case of web applications some hidden buttons which just the developer is aware of and on click of which a specific business scenario is executed and the logs, thereof, are generated. This approach has many flaws, as the typical algorithm illustrates:
1. Correctly identify [or guess] the piece of code which might be causing the bug to show up on env other then dev.
2. Code change to introduce debug statements. [Prone to introduce more bugs.]
3. Code change means extra round of unit/sanity testing and then new deployment.
4. Once deployed, collect the log and carefully analyze it to find the root cause. If nothing found, introduce more detailed debug statements i.e. Go back to step 1/2 and repeat.
5. If the root cause is found, fix it locally assuming that the bug is fixed [if the bug is not reproducible locally], remove all the debug statements introduced as a part of step 2 and generate a new build after successful unit/sanity testing.
6. Pray hard and hope the bug does not resurface again in your lifetime!

This technique is highly error prone and time consuming. To overcome this, as with most of the other matured programming languages, Java too ships with a capability to debug a remotely deployed application from your local development environment using your favorite IDE such as Eclipse. Java debugging capabilities is a result of JPDA (Java Platform Debugger Architecture). For more details on JPDA please refer JPDA Architecture

In this article, I won't dig much into the details of JPDA, but will focus on how to remotely debug an application using our good old friend – Eclipse. Here, I take up three common application scenarios and describe the remote debugging steps for each of them:
1. A standalone application. (such as a batch application)
2. A web application deployed on Apache Tomcat Servlet container.
3. A web application deployed on IBM WebSphere 5.1 application server.

To debug any application remotely, you need to...
  1. Make sure you are building (compiling) your application with available debug information.
    1. In Eclipse you can control these settings from Windows -> Preferences -> Java -> Compiler.
    2. If you are building your application with an ANT build script, then in the <javac> task, set the “debug” attribute to “true”.
    3. If you are building your application with Maven, set the debug configuration tag for maven-compiler-plugin to “true”.
  2. Deploy the application in the debug mode. This opens up a port for your debugger to get attached to the application's process and do its magic1. This is done by passing some pre-defined VM arguments in the application's launch scripts. The commonly used VM arguments are:

VM ArgumentDescription
-XdebugEnables debugging
-Xrunjdwp:<options>Loads the JPDA implementation residing in targeted JVM.
The commonly used <options>...
1. transport: Transport to use in connecting to debugger application. There can be many transport implementations out of which Sun comes with two mostly used implementation - A socket transport based on TCP/IP and a shared memory transport.
2. address: Transport address for the debugger application to attach.
3. server: If set to 'y'(mostly used), it listens for a debugger application to attach. Otherwise, attach to the debugger application at the specified address.
4. suspend: If set to 'y', JVM starts in suspended mode and stays suspended until a debugger is attached to it.


For a full JVM-version-specific list of 'runjdwp' options, please refer to:
  1. Java 1.4.2: http://java.sun.com/j2se/1.4.2/docs/guide/jpda/conninv.html#Invocation
  2. Java 1.5: http://java.sun.com/j2se/1.5.0/docs/guide/jpda/conninv.html#Invocation
  3. Java 6: http://java.sun.com/javase/6/docs/technotes/guides/jpda/conninv.html#Invocation
Remote debugging a standalone application
To remotely debug a standalone application, the first thing you need to do is compile the application source with available debug information, as mentioned in the above section and package it as a .jar file.

For example your jar file name is sandbox.jar and the class launches the application is foo.bar.Test, then you can run Test as

java -cp ./sandbox.jar foo.bar.Test
However, with just this, the application will not allow remote debugging. In order to debug Test application remotely, start it in the debug mode by providing the additional VM arguments, as shown below:

-Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=10007,suspend=n
These options will allow the Test application to execute in debug mode with a socket open on port 10007 for a debugger to attach to.

To verify if your application is truly running in debug mode and has opened a socket on 10007 listening for debugger to attach, on windows machine open a command prompt and type netstat /a or on Unix/Linux environment netstat -a and you should be able to see a TCP socket on port 10007 with state as LISTEN.

In a real life scenario, you won't be starting your standalone application directly by issuing java command. You will have a script file which will be invoking your application. So to avoid any script change to run application under debug mode or not, you can set an environment variable _JAVA_OPTIONS before your script invokes java command. For instance, if your application is launched by a script file called sandbox.sh then before executing sandbox.sh you can simply set _JAVA_OPTIONS environment variable as:

set _JAVA_OPTIONS="-Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=10007,suspend=n"
Once you are done with your debugging, you can reset _JAVA_OPTIONS to blank and then re-execute sandbox.sh

Eclipse configuration
Once your application is running in debug mode, you can switch back to Eclipse and open the Debug configuration dialogue from Run -> Debug Configuration... menu item. Over there, select “Remote Java Application” from the left navigation tree. Right click and select New.

On the right hand side, under Connect tab, select the project which holds the foo.bar.Test class which you intend to debug remotely. Select connection type as "Standard (Socket Attach)"

Under the 'Connection Properties', enter the host name or IP where you are running your foo.bar.Test class in debug mode and port as 10007. Depending upon your remote JVM settings and your wish you can check/uncheck "Allow termination of remote VM" checkbox.

Click Debug button and your eclipse debugger is now connected to your remote application and ready to debug. Insert debug points in your program and you are ready to debug your application remotely.

Remote debugging a web application on Apache Tomcat
The steps for compiling and building your application remains the same, as described above. What changes is how we start the Tomcat server. To start Tomcat in the debug mode, set two environment variables as...

JPDA_TRANSPORT=dt_socket
JPDA_ADDRESS=10007
After setting the above system environment variables, start Tomcat using following command…

%CATALINA_HOME%\bin\catalina.bat jpda start
OR on Linux...

$CATALINA_HOME/bin/catalina.sh jpda start
Once the server is up, it's in debug mode with a socket on port 10007 open for any debugger to attach for debugging the deployed application. Once Tomcat is running in debug mode you can follow the Eclipse Configuration steps to debug the web application. Once you are done with debugging, you can stop the server by:

%CATALINA_HOME%\bin\catalina.bat stop
OR on Linux...

$CATALINA_HOME/bin/catalina.sh stop

Remote debugging a web application on IBM WebSphere 5.1
Once again, the steps for compiling and building your application remains the same, as described above. What changes is how we inform WebSphere to run in debug mode. To do this:
1. Open WebSphere admin console.
2. Navigate to Servers -> Application Servers -> <YOUR_SERVER_NAME> -> Process Definitions -> Java Virtual Machine.
3. Scroll down and check the Debug Mode check box.
4. You can either accept the default debug arguments in Debug Arguments textbox or can provide as per your requirement.
5. Restart your application server.
6. Done !!!

Once your WebSphere is running debug mode, follow Eclipse Configuration to do debug the web application.

Some times you might get an error message while you try to connect to remote JVM through Eclipse...

Failed to connect to remote VM. Connection refused.
The most likely case if you are on a Windows machine is that, you have the Windows firewall on and it is preventing you from connecting. Stop the firewall and try to reconnect. Don’t forget to turn your firewall on once you are done, else you might end up debugging your Windows for mysterious behavior if some hacker gets hold of your PC ;-)

Here’s to smarter debugging!

Reference
Links to JPDA documentation & debug options across J2SE 1.4.2., 1.5.0 & 6:
http://java.sun.com/javase/technologies/core/toolsapis/jpda/

Detailed article on Remote Debugging in Java:
http://www.ibm.com/developerworks/opensource/library/os-eclipse-javadebug/index.html

Remote Debugging with Eclipse
http://www.jacoozi.com/index.php?option=com_content&task=view&id=119&Itemid=134

Cheers !!!
- Jay

1 You can also configure your remote debugging so that the application you are interested to remotely debug attaches itself to the debugger. For this article I’ll limit discussion in debugging application by attaching debugger to application.

Which OS I'm?

You are Debian Linux. People have difficulty getting to know you.  Once you finally open your shell they're apt to love you.
Its start of the long weekend and start was of course the best you can expect. Sleeping till noon after working late night. Anyway had to wake up after a lot of pinching from my wife. Somehow made it out of my bed, brushed the teeth, holding a mug full of tea wondering what creative thing I can do. Obviously first thought was to check the mails if I won some million $ lottery ;). As always marked all mails as read and trashed switched to google and encountered to this bit weired but interesting quiz on BBSpot. Just thought to give it a try and started answering the questions and found myself as a Debian Linux :). Coincidentally I recently got myself Ubuntu - the debian flavor. Seems BBSpot guys are intelligent. Give it a try...

Cheers !!!
- Jay
 
Disclaimer : This is a personal blog and all content represent what I think and it does not advocate/support/advertise any other person/company. I do not earn money or intended to do so with this blog or any of the contents the blog hosts (except the google ads which you see). If I post something here that you find helpful, that's wonderful. Just in case, if I say something stupid, the stupidity is mine, and mine alone and I can not be held for anything if you fall for such stupidity :-). I cannot be held responsible for any kind of damage that may be caused by downloading or viewing the files or information provided herewith. Anybody and everybody can use/refer the contents of this blog at their own will and of course at own risk. There is no need for any kind of approval of the author. Although it would be great if feedback is left for any such usage to the author.