Wednesday, October 17, 2012

Remote debugging with Eclipse

Sometimes you can run out of memory on your local workstation when you run a JBoss server. In this case it can be useful to run the application remote on a server that has more memory available and debug it remotely with Eclipse. This is a how-to to get you started.

In this example I will use an EAR application.

Do your code changes locally and publish to your local JBoss server. Do not start the server, just publish to it.
Once you have done this copy the EAR file to the server you want to debug it on.
Publish the application as normal to the JBoss server by copying it to
${JBOSS_HOME}/server/${JBOSS_CONFIG}/deploy/.
In this case I place it here:
/apps/jboss/server/mpo/deploy/test.ear

To start JBoss in remote debug mode you can take two different approaches. The first one includes editing a file that you must remember to change back once you’re done. The second one is cleaner, but might not be supported by your JBoss setup, and includes setting an environment variable.
If you start JBoss from “/etc/init.d/[jboss-script]” you must choose the Alter file approach.
If you wish to run JBoss directly from “/apps/jboss/bin/run.sh” you can choose the Environment Variable approach.

Alter file example
If you choose or need to take this approach edit this file or the file you usually use to start JBoss:
/apps/jboss/bin/run-jboss.sh
If you don’t have a file like this you need to change the global config file. Be warned that this file is used by all JBoss instances when started so the need to change it back is even greater.
/apps/jboss/bin/run.conf

If you have “/apps/jboss/bin/run-my-jboss.sh” add a line so the file matches this look:

JAVA_OPTS="$JAVA_OPTS -Xms128m -Xmx1024m -XX:MaxPermSize=256m -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -Duser.country=SE -Duser.language=sv"
JAVA_OPTS="$JAVA_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n"
export JAVA_OPTS

If you don’t have that file do your changes to the following file.
In the end of the file “/apps/jboss/bin/run.conf” uncomment this line
#JAVA_OPTS="$JAVA_OPTS -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n"
and alter it so it looks like this
JAVA_OPTS="$JAVA_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n"

-Xdebug - Enables debugging.

-Xrunjdwp:<sub-options> - Loads the JPDA reference implementation of JDWP. This library resides in the target VM and uses JVMDI and JNI to interact with it. It uses a transport and the JDWP protocol to communicate with a separate debugger application. Specific sub-options are described below.

transport=dt_socket - Use a network socket to communicate with debugger application.

address=8787 - Use local port 8787 to communicate with debugger application

server=y - If "y", listen for a debugger application to attach; otherwise, attach to the debugger application at the specified address.

suspend=n - Do not suspend the VM before main class. This will start the server and you must attach the debugger application and set a breakpoint somewhere in the code that you will hit. If you want to debug everything from main class, set this to “y”.


Environment variable example
This approach is just like the first, but you don’t have to edit any files and thus, don’t need to remember to change the file back to it’s original. Just export the JAVA_OPTS environment variable with the above line.
JAVA_OPTS="$JAVA_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n"
export JAVA_OPTS


Start the server
Start the server in your preferred way.

If you used Alter file approach, use
sudo /etc/init.d/run-jboss.sh start

If you used Environment Variable approach, as user jboss run
/apps/jboss/bin/run.sh


Getting through firewalls
Now it’s time to connect Eclipse debugger to the server. To do this we need to connect to port 8787 on the server and this is usually not allowed through the firewall that the server is behind. To solve this we use a tcp tunnel through ssh. To set this up in Putty.exe do the following.
Connect to the server via ssh with Putty.exe.
Once connected and logged in with your user, click the putty icon in your putty window and select “Change Settings...”. On the left side of the “Putty Reconfiguration” window expand the folders “Connection” and the subfolder “SSH” and go to “Tunnels”.
Set it up like this:


Click Add and then Apply.


Create a debug configuration in Eclipse
Now it’s time to create a new debug configuration in Eclipse to connect to your VM.
In Eclipse, select “Run” from the main menu and then select “Debug Configurations...”.
Right click “Remote Java Application” and select “New”.
Give your new configuration a name to recognize it. I’ll use “Remote Java debugger”.

In Project I choose the project that contains the code you want to put a breakpoint in. If you try to set a breakpoint in another project then what you choose here, the server will stop but it Eclipse won’t be able to map the remote class to your local code.

Connection Type should be “Standard (Socket Attach)”.

Host should be “localhost” or “127.0.0.1”.
Port should be 8787.
We use this because we will attach to our ssh tunnel that is listening on localhost for connections on port 8787. Whatever connects to 127.0.0.1:8787 will be sent to the server that ssh is connected and the port 8787 on that server.

I choose NOT to “Allow termination of remote VM”. If you do choose this you can kill the VM on the remote server by hitting the “Terminate” button in Eclipse. It’s up to you, but I prefer to stop the VM manually on my own through ssh.

Click “Apply” and “Debug”.
You’re done now.
Set a breakpoint somewhere in you code and surf to the webserver as usual. Once your VM on the remote server hits your breakpoint it stops and Eclipse.