2014-05-13

JIT debugging compiled Java on Wowza Streaming Engine with NetBeans



How to debug compiled Java code using NetBeans IDE to step through code and how to set-up Wowza Streaming Engine for just-in-time debugging.

In my daily work, I have a Maven project creating Wowza modules. Wowza's software uses reflection to invoke the methods in custom modules. This means that any exceptions which are thrown in these methods are wrapped in Java's java.lang.reflect.InvocationTargetException. Wowza Streaming Engine prints an error message to its logs, but only the message of this exception. The cause — that which is really the problem — is not reported. Anywhere.

To find out what was going on, I needed to use a debugger to step though the code, line by line, to identify the problem code.

On my development machine using NetBeans, I could not identify any problems. The issues only occurred on the test Wowza Streaming Engine, where the code was in its compiled, JAR form.

Here's how I went about using NetBeans on my development machine to debug the compile code "in production" as it was running.


Setting Up Maven to Compile for Debugging

Skip this step if you aren't using Maven.

In your project's pom.xml file, find or create the element project/build/plugins. Find or create the plugin whose artifactId is maven-compiler-plugin. Add configuration options so that it looks something like this:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>2.3.2</version>
  <configuration>
    <source>1.7</source>
    <target>1.7</target>
    <showDeprecation>true</showDeprecation>
    <compilerArgument>-g:source,lines,vars</compilerArgument>
    <debug>true</debug>
    <debuglevel>lines,vars,source</debuglevel>
    <optimize>false</optimize>
  </configuration>
</plugin>

The lines of note are 9, 10 and 11: compilerArgument, debug and debuglevel. These will tell Maven to compile the code and to include the debug symbols, which our debugger will need.


Compiling For Debugging Without Maven

Skip this step if you are using Maven.

You must tell the Java compiler to include the debug symbols when it compiles your code.

Simply supply the switch -g to the compiler.


Setting up Java for Debugging

Java needs to be run with certain switches to tell it to allow for remote debuggers to attach to it. What these switches are depends on the version of Java which is running. You can find this out by running this command:

java -version

The commands to allow remote debugging in the different versions of Java are:

JDK 1.3 and earlier
-Xnoagent -Djava.compiler=NONE -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=6006
JDK 1.4
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=6006
Newer JDKs
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=6006

These are taken from a StackOverflow answer. I have not tried the switches for the earlier versions. If you are needing those, you should consider updating your JDK.

suspend tells Java to pause execution every time the program runs. You probably don't want this and actually want the program to run as normal until you stop it in the debugger. In that case, the argument is n.

address tells Java to listen on this port for attached debuggers. Remember this port as you'll need it later in NetBeans.

To get Wowza to start with these switches, there is a file containing the start-up switches for Java. The XML file is /usr/local/WowzaStreamingEngine/conf/Tune.xml. Add a VMOption to the /Root/Tune/VMOptions element with the switches which your version of Java requires. It should look something like:

<VMOptions>
  <VMOption>-server</VMOption>
  <VMOption>-Djava.net.preferIPv4Stack=true</VMOption>
  <VMOption>-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=6006</VMOption>
  <!-- <VMOption>-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath="${com.wowza.wms.AppHome}/logs"</VMOption> -->
  <!-- <VMOption>-Duser.language=en -Duser.country=US -Dfile.encoding=Cp1252</VMOption> -->
  <!-- <VMOption>-verbose:gc -Xloggc:"${com.wowza.wms.AppHome}/logs/gc_${com.wowza.wms.StartupDateTime}.log" -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime</VMOption> -->
</VMOptions>

The next time that Wowza is started, you should be able to see that Java is listening to port 6006 (or whichever port you chose).


Debugging in NetBeans

This example is using NetBeans IDE 7.4.

In the IDE, open the project which you wish to debug. That this code is on your development machine and the compiled code is on a different server does not matter.

Click on the Debug (shortcut Alt+D) menu item and then Attach Debugger...

Check the values in the Attach pop-up:

FieldValue
Debugger: Java Debugger (JPDA)
Connectior: SocketAttach (Attaches by socket to other VMs)
Transport: dt_socket
Host 127.0.0.1
Port 6006
Timeout [ms]:

Change the Host and Port to point to the server which you want to debug.

It will look something like this:

NetBeans 7.4 Linux Attach Remote Debugger config

After you click on OK, NetBeans will attempt to connect to the Java process on the remote server. Once it is done, the Attaching Debugger progress bar at the bottom will eventually disappear and the standard debugging windows and buttons will be available. NetBeans should look like you are debugging local code.

You can now add breakpoints in real-time. When the executing Java on the remote server reaches these points in its own code, the execution will stop and you can step through the code and it will even evaluate the variables for you.


Conclusion

This guide should have allowed you to debug your compiled Java code remotely and to set-up a Wowza Streaming Engine for debugging.

It may be possible to use this guide as a base for debugging other Java programs.

When I was debugging the cause of my InvocationTargetException errors, I found that try-catch on Exception was not enough to catch the exceptions. I changed this to Throwable, which allowed me to catch everything. Don't leave your code in production catching Throwables; it is not intended for that. Stick with Exception.

Don't forget to switch off debug symbols for production code.

No comments:

Post a Comment