Wednesday, April 16, 2008

Debugging JNI code using Eclipse and Visual Studio

Debugging Java Native Interface code (JNI) can be quite an adventure, but fortunately it works quite nice when you have Eclipse and Visual Studio (and preferrably at least 2 monitors). I have Eclipse on the primary monitor, and Visual Studio on the secondary. This is how it goes:

  1. Start your java program, with a breakpoint before entering the native method you want to debug. You probably want to break after the native code has been loaded, but you don't have to.
  2. Switch to Visual Studio, and bring up the "Attach to process" dialog (Ctrl-Alt-P). Select the java process (javaw.exe or java.exe) by typing "j" (possible several times, epending on how many processes you have starting with the letter "j"). Press return. Visual Studio should now switch to debugging and the title bar should include the test "(Running)", indicating that you are debugging a running process.
  3. Set a breakpoint to cause Visual Studio to stop in your native code.
  4. Switch back to Eclipse, and start running. When eclipse enters your native code, Visual Studio will break the debugged java program. (Eclipse may note that the debugged program is not responding, since it is suspended by Visual Studio).
  5. When you're done debugging, you can "release" the java-program using the "Stop Debugging" command in Visual Studio. This will cause the java program to continue running normally (and terminate, unless Eclipse has a breakpoint on it).

Some things you may want to pay special attention to:

  • Eclipse and Visual Studio have different shortcuts for run/step/step-over, etc. This is a major pain, and I think my muscle memory is permanently damaged when it comes to learning them.
  • You need to keep track of who is debugging. This can be confusing.

People are working on making JDT and CDT cooperate in debugging JNI code, allowing seamless stepping in and out of Java/JNI code and integrated debugger views (callstack with both Java and native frames displayed, for example), but it is still in the early prototyping phase.

6 comments:

fulanito said...

Jesper,

I am trying your technique to debug a JNI method, but when I set a breakpoint in the .cpp file, the VS informs me that the breakpoint will not be hit because no symbols have been loaded.

When I look at the modules list, I see the remark "Binary was not built with debug information." next to my library.

I use the cl.exe to compile, with -MD -LD arguments to generate the dll. I have tried switching these arguments with -MDd and -LDd, together and each one seperately, to generate the debug info, but not only it doesn´t help, Eclipse fails to load the dll.

What am I doing wrong?
How should I compile my source in order to use your technique?

Thanks,
Fulanito.

fulanito said...

got it.

just add /Odi /Zi as arguments to cl.exe, and -FdFileName (replace FileName with the actual name of the file, as you choose to call it) if you want to specify the name and location of the .pdb file.

JesperE said...

-MDd and -LDd only affects what type of runtime library to link with. In order to be able to set breakpoints on source code lines you have to build with debug info as well (-Zi), as you discovered.

Yuriy said...

This is pretty common -- you can debug this way any dll launched through somehing you did not built -- in this case Java.exe or javaw.exe. A more chellenging, at least for me, is the situation when you create jvm and start some Java code from there and want to debug it, say, in eclipse. Do you know, or know who knows, how to do it?

mario said...

@Yuriy This is even more common. See http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Ftasks%2Ftask-remotejava_launch_config.htm

Benjamin Carlyle said...

Thanks,

This technique also works under linux with gdb or a gdb-based debugger. If using gdb directly:
1. Then, start the java program in eclipse and allow it to stop at a breakpoint.
2. Start gdb from the command-line with no arguments, then attach to the java process (be sure to attach to the program being debugged, not to eclipse or another java instance). eg attach 10616 from the gdb prompt.
3. Set any breakpoints you want in gdb
4. Continue in gdb: cont
5. Continue in the eclipse debugger
You should now find the program stopped on the gdb breakpoint.

Benjamin.