调试Objective C JNI代码

时间:2012-10-04 18:36:30

标签: objective-c xcode eclipse debugging java-native-interface

情况如下:

我在eclipse中打开了一个客户端的java项目。它使用由Xcode Objective C项目创建的JNI库。在执行Java代码时,有没有什么好方法可以从eclipse调试C代码?显然,eclipse的默认调试器无法进入jni库文件而我们丢失了线程(线程意味着调查线程在这里,而不是编程线程)。

任何建议或输入都会受到赞赏,因为代码库足够大,以至于客户端的代码将比其他选项快得多。

感谢。

编辑:

应该注意的是,jni库是用Objective-C编写的原因是因为它与Mac OSX集成。它使用Cocoa框架与Apple语音api集成。

4 个答案:

答案 0 :(得分:5)

我不确定我是否已完全理解您的设置,以及是否需要通过eclipse完成此操作。无论如何,我有兴趣使用JNI和Cocoa库做一个小测试程序,只是为了尝试调试obj-c / c代码。

我成功完成了此设置并调试了代码。我使用IntelliJ for Java和Xcode作为objc / c部分,但在eclipse中执行java部分是不费脑子的。

因此,您应该能够准确设置我的项目结构并开始调试。从那里你应该能够将这些知识应用到你自己更复杂的代码中。

这就是我开始的方式:

  • 通过选择Cocoa Library在Xcode中创建一个新项目。

Cocoa Library

  • 将项目命名为libnative,并将其命名为Type Dynamic。

Choose Options

  • 为新项目选择一个位置。我使用~/Development/并跳过Create local git...部分。

  • 这将在您选择的文件夹中创建一个名为lib native.xcodeproj的新项目。已自动创建两个文件:libnative.hlibnative.m

  • 首先,您必须更改项目设置。

      必须将Executable Extension部分中的
    • Packagingdynlib更改为jnilib
    • 必须更新Framework Search Paths部分中的
    • Search Paths以指向JNI框架:/System/Library/Frameworks/JavaVM.framework/Frameworks/JavaNativeFoundation.framework/

enter image description here

  • 现在是时候添加一些代码了。请注意,使用此设置时,您必须使用<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,发现它正在断点处等待!

At Breakpoint


正如我之前所说,这是一个非常简单的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} ...)。

  1. 创建名为MyDebug.gdbinit的文件
  2. 将这4行添加到其中:

    设置args -classpath .; xxxx.jar; yyy.jar path.to.your.Main

    show args

    运行

    BT

  3. 启动gdb和Java:gdb“%JAVA_HOME%\ bin \ java”

  4. 使用Java图层的GUI重现错误
  5. 如果您想逐步执行JNI代码,gdb允许您放置一些断点