I assume you are vaguely familiar with JNI, the useful if tricky technology that allows Java code to call native C code. If you are not, fair warning--you will find this post boring or even slightly headache-inducing. Go read The Oatmeal instead.

Working with JNI may not be the most exciting proposition, but it doesn't need to be painful. Tools like JNIWrapper take care of the mundane parts such as loading the native library from the classpath, mapping the different types and so on. It even comes with a tool that will create wrapper code for you, so the only work that is left for you is implementing Real Stuff (TM).

That's all fine and well, until you introduce a bug; at that point chances are you'll get an error message that is just about as useful as a necktie in the Sahara. To wit:

Turning to your trusty debugger won't help you much; even if you set a breakpoint in Eclipse and single-step over the call to native code, it will just skip over--and crash you again. Unless your problem is very obvious (say, passing a null in the first place), not useful.

A solution

The good news is, depending on the operating system two debuggers may be allowed to attach to the same process. In my case, working on Mac OS X, that means I can use both Eclipse and Xcode; the process will happily jump between them depending on the function I'm currently in. If you have two monitor, it's actually mildly entertaining (at least for me--but maybe I'm just easily entertained).

How to work that magic:

  1. set a breakpoint in Eclipse, and start the application;
  2. when execution stops, go over to Xcode and select Product > Attach to Process from the menu; find your process in the list and click it (it will probably just show up as "java"; the pid is also provided);
  3. if you want to, you can also set breakpoints in your C code;
  4. now go over to Eclipse and continue execution or single step until you hit the native code. You will see Eclipse pause execution, and Xcode will bring up the function you just entered.

Bingo!

One word of notice: you may want to wait a few moments between steps 2 and 4, otherwise Eclipse may lose its wits and error out with an obscure message. That's especially true if you have a lot of breakpoints.

If you are that kind of person, you can do the same in plain old gdb. Not sure why you'd want to do that, but who I am to argue. However if you do figure it out, please leave a comment with instructions for other weirdos like you.

Trivia

In case you are wondering, the code I'm calling is actually Objective-C (CoreAudio), with a relatively thin wrapper in C. Also, the Java code is a faceless applet that interacts with JavaScript.

So in a typical scenario, the user clicks on something in the browser, that triggers JavaScript, which calls Java, that will quickly go through C only to have it call into Objective-C. Enough to make your head spin!