I've been tearing my hair out trying to figure out something that's braindead in Windows / Visual Studio. I have a JNI library under OS X 10.6 that I would like to invoke with the Java executable and debug under Xcode: stepping through code, examining variables in my JNI library, setting breakpoints, etc.
Here is a working JNI example which compiles and runs fine from the command line. If someone could tell me how to set this up in Xcode, I'd be eternally grateful. Ideally, I'd like step-by-step instructions starting from scratch in Xcode and cutting and pasting from the code below as appropriate, resulting in something that is debuggable under Xcode 4 and allowing me to set breakpoints in the JNI code, examine variables, see stacktraces, etc.
As a token of gratitude, I'll send $20 to the first person that gives me such instructions I can reproduce on my end if you give me a Paypal address.
Thanks in advance!
HelloWorld.java
class HelloWorld {
public native String displayHelloWorld();
static {
System.loadLibrary("HelloWorldImp");
}
public static void main(String[] args) {
System.out.println("--> "+new HelloWorld().displayHelloWorld());
}
}
HelloWorldImp.mm
#include <stdio.h>
#include <jni.h>
#include <string>
#import <Foundation/Foundation.h>
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jstring JNICALL
Java_HelloWorld_displayHelloWorld(JNIEnv *env, jobject obj)
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString* name = @"Yo Cocoa";
std::string s = [name UTF8String];
jstring ret = env->NewStringUTF(s.c_str());
[pool drain];
return ret;
}
#ifdef __cplusplus
}
#endif
buildjni.sh
gcc -bundle -I/System/Library/Frameworks/JavaVM.framework/Headers -lstdc++ -o libHelloWorldImp.jnilib -framework Foundation HelloWorldImp.mm
Here you go.
Building the lib
You probably know that jni libs on the mac must be named in the form libFoo.jnilib. You then load the lib from Java by calling System.loadLibrary("Foo");
1. Create a new dynamic library project. Name it libSomething. That's half the naming battle.
2. Search the target build settings for Executable Extension. By default this is set to dylib. Modify it to be jnilib. That's the second half of the naming battle.
3. Add the JavaVM framework to the linked frameworks. This has the JNI stuff in it.
4. Generate your native header file as usual.
5. Include it in your project and create a corresponding .m file.
6. You have to modify the include path for jni.h. On the mac this is <JavaVM/jni.h> or use the JavaNativeFoundation framework. I recommend the latter.
7. (optional) The JavaNativeFoundation provides a number of macros and methods that will make converting data/methods to/from Java/Objective-C a lot easier. It's not well documented, but it's worth investigating. If you decide to use JNF, use the open triangle on the JavaVM framework. You will see that JNF is a subframework. Drag it up to the top level equal to JavaVM. Then you can start using it. import <JavaNativeFoundation/JavaNativeFoundation.h> Do a Google search on JNF_COCOA_ENTER to see examples of how to use JNF
You should now be able to build a valid jni library. Assuming you have no bugs in your code, you're done. ;-)
Debugging
I tried everything I found on various web pages to get debugging JNI under Xcode to work. It sort of worked, but would hang when stepping over cocoa calls. However, debugging with gdb does work. It's really not all that bad if you run gdb within emacs. The drawback, of course, is you'll need to come up to speed on emacs and gdb, if you aren't already. Here's the gist:
1. Compile your lib with debugging symbols
2. Set a breakpoint in your java calling code after the lib has been loaded, but before calling the code you want to debug. Alternatively, you could put a showMessageDialog there. Anything the will block the java program at the right point.
3. Open Activity Monitor and note the pid of your java program.
4. Start emacs. If you are on Lion, the terminal emacs that comes with should work. On SL the terminal emacs doesn't have some of the gdb magic. In both cases, however, I recommend you download and use a mac gui emacs. There's more than one available, I use the one I got here. If you use emacs in Terminal, you'll want to go to Termina/Preferences/Settings/Keyboard and look down toward the bottom. There's a checkbox "use option as meta key". You'll want that checked.
5. With emacs running press M-x (option x). Type gdb and press enter. You'll see something like "gdb --annotate=2 xxxxxxxxxx". Press backspace until you get to the annotate setting. Enter "pid xxx" where xxx is the pid of your java calling app. You should end up with the line looking like "gdb --annotate=2 pid xxxx". Press enter.
6. You should see gdb loading the symbols for your jni lib.
7. Now press M-x. Type gdb-many-windows. Press enter.
8. You should now see multiple windows in emacs. A gdb command window, local variables, source code, and others.
9. Enter break someFunctionName in the command window. Press Enter.
10. Enter cont in the command window. (This causes execution to continue). Press enter.
11. Now continue in the java debugger, or press enter in the java dialog.
12. You should see gdb hit the breakpoint in your jni code.
13. Study up on emacs and gdb. In particular check out the po command in gdb.
14. And Bob's your uncle.
Enjoy!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With