情况如下:
我在eclipse中打开了一个客户端的java项目。它使用由Xcode Objective C项目创建的JNI库。在执行Java代码时,有没有什么好方法可以从eclipse调试C代码?显然,eclipse的默认调试器无法进入jni库文件而我们丢失了线程(线程意味着调查线程在这里,而不是编程线程)。
任何建议或输入都会受到赞赏,因为代码库足够大,以至于客户端的代码将比其他选项快得多。
感谢。
编辑:
应该注意的是,jni库是用Objective-C编写的原因是因为它与Mac OSX集成。它使用Cocoa框架与Apple语音api集成。
答案 0 :(得分:5)
我不确定我是否已完全理解您的设置,以及是否需要通过eclipse完成此操作。无论如何,我有兴趣使用JNI和Cocoa库做一个小测试程序,只是为了尝试调试obj-c / c代码。
我成功完成了此设置并调试了代码。我使用IntelliJ for Java和Xcode作为objc / c部分,但在eclipse中执行java部分是不费脑子的。
因此,您应该能够准确设置我的项目结构并开始调试。从那里你应该能够将这些知识应用到你自己更复杂的代码中。
这就是我开始的方式:
libnative
,并将其命名为Type Dynamic。
为新项目选择一个位置。我使用~/Development/
并跳过Create local git...
部分。
这将在您选择的文件夹中创建一个名为lib native.xcodeproj
的新项目。已自动创建两个文件:libnative.h
和libnative.m
。
首先,您必须更改项目设置。
Executable Extension
部分中的Packaging
从dynlib
更改为jnilib
。Framework Search Paths
部分中的Search Paths
以指向JNI框架:/System/Library/Frameworks/JavaVM.framework/Frameworks/JavaNativeFoundation.framework/
<JavaVM/jni.h>
。将libnative.m
更新为以下代码://
// libnative.m
// libnative
//
// Created by maba on 2012-10-09.
// Copyright (c) 2012 maba. All rights reserved.
//
#import "libnative.h"
#include <JavaVM/jni.h>
@implementation libnative
@end
#ifdef __cplusplus
extern "C" {
#endif
#ifndef VEC_LEN
#define VEC_LEN(v) (sizeof(v)/sizeof(v[0]))
#endif/*VEC_LEN*/
static JavaVM *javaVM;
static void print();
static JNINativeMethod Main_methods[] =
{
{ "print", "()V", (void*)print },
};
static struct {
const char *class_name;
JNINativeMethod *methods;
int num_methods;
} native_methods[] = {
{ "com/stackoverflow/Main", Main_methods, VEC_LEN(Main_methods) },
};
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
JNIEnv *env = 0;
jclass cls = 0;
jint rs = 0;
if ((*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_4)) {
return JNI_ERR;
}
javaVM = jvm;
for (unsigned int i = 0; i < VEC_LEN(native_methods); i++) {
cls = (*env)->FindClass(env, native_methods[i].class_name);
if (cls == NULL) {
return JNI_ERR;
}
rs = (*env)->RegisterNatives(env, cls, native_methods[i].methods, native_methods[i].num_methods);
assert(rs == JNI_OK);
}
return JNI_VERSION_1_4;
}
static void print(JNIEnv *env, jclass cls) {
printf("Hello from C");
}
#ifdef __cplusplus
}
#endif
按⌘ + B 构建代码。
现在是时候创建Java代码了。我只是在包com.stackoverflow
中创建了一个名为Main的类。
com.stackoverflow.Main.java
package com.stackoverflow;
/**
* @author maba, 2012-10-09
*/
public class Main {
static native void print();
static {
System.out.println(System.getProperty("java.library.path"));
System.loadLibrary("native");
}
public static void main(String[] args) {
System.out.println("Loading native");
Main.print();
}
}
Main.print();
之前的行上设置断点。使用以下JVM选项启动调试器:-Djava.library.path="/Users/maba/Library/Developer/Xcode/DerivedData/libnative-cquleqohzyhghnercyqdwpnznjdf/Build/Products/Debug/"
这一行有点长,也是用户特定的。您必须自己查找目录名称,但除了生成的libnative-cquleqohzyhghnercyqdwpnznjdf
路径之外,它们或多或少与我的相同。
程序应该在断点处运行并等待。是时候将Xcode调试器附加到正在运行的应用程序。
选择菜单Product
- &gt; Attach to Process >
并指向下拉菜单java
部分中正在运行的System
进程。如果有多个java
进程,则很可能是具有最高PID的进程,但并非总是如此。你必须尝试。
在printf("Hello from C");
行的c代码中创建一个断点。
返回Java IDE并从停止的位置继续执行。
回到Xcode,发现它正在断点处等待!
正如我之前所说,这是一个非常简单的obj-c / JNI方法,你的项目可能很大,但是通过这个小型测试项目,你至少可以看到它是如何工作的,然后继续你自己的项目设置。
答案 1 :(得分:1)
您可以从终端附加gdb
(或lldb
)。如果使用本地代码启动流程是fork()/exec()
的结果 - 即如果您无法键入gdb /some/command/line
- 那么您可以使用--waitfor
选项(看到手册页)等待下级的发射。
加载符号会很棘手。
这是一个使用cocoa框架的Mac OS X项目。这会影响吗? 此?
不应该。如果有的话,它会更容易,希望符号文件是可用的格式。关键是通常找到在java和本机代码之间的边界处打破的正确位置。
dylib中的本机代码是否已加载到JVM中,或者您是否具有在内部激活JVM的自定义可执行文件?
在任何情况下,您都需要将本机调试器附加到运行该本机代码的任何进程。可能是在你适当地设置了基于java的调试会话之后。
答案 2 :(得分:1)
在过去做JNI时,我已经构建了一个测试工具,以便于开发应用程序的本机部分 - 以及JNI代码 - 这很容易搞砸,避免了从双方同时调试的需要。
这是作为本机应用程序编写的,它以编程方式调用JVM,而不是从Java应用程序开始,然后尝试连接到JVM。
当然,您可以在Xcode中启动它并进行调试 - 这对于使用CDT的Eclipse来说是一种无限优选的体验。
这种安排的Java方面通常非常简单且非争议 - 基本上是从应用程序的本机部分调用的方法,然后通过JNI将一个或多个调用返回到本机部分。
答案 3 :(得分:0)
以下是我在Windows下调试JNI(C / C ++)的步骤,我认为ObjectiveC需要相同的步骤。对于Linux,它非常相似(替换;通过:,%XXX%by $ {XXX} ...)。
将这4行添加到其中:
设置args -classpath .; xxxx.jar; yyy.jar path.to.your.Main
show args
运行
BT
启动gdb和Java:gdb“%JAVA_HOME%\ bin \ java”