4

情况如下:

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

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

谢谢。

编辑:

需要注意的是,jni 库之所以用 Objective-C 编写,是因为它正在与 Mac OSX 集成。它使用 Cocoa 框架与 Apple 语音 api 集成。

4

4 回答 4

5

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

我成功地完成了这个设置并调试了代码。我将 IntelliJ 用于 Java,将 Xcode 用于 objc/c 部分,但在 eclipse 中执行 java 部分是轻而易举的事。

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

这就是我开始的方式:

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

可可库

  • 将项目命名为libnative动态类型。

选择选项

  • 为您的新项目选择一个地方。我使用~/Development/并跳过该Create local git...部分。

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

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

    • Executable Extension在该Packaging部分必须从 更改dynlibjnilib
    • Framework Search PathsSearch 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
  • 按构建代码⌘</kbd>+B.

  • 现在是时候创建 Java 代码了。我只是在 package 中创建了一个名为 Main 的类 com.stackoverflow

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->Attach to Process >并指向下拉部分中正在运行的java进程。System如果有多个java进程,那么它很可能是具有最高 PID 的进程,但并非总是如此。你得试试。

  • 在c代码中创建断点就行了printf("Hello from C");

  • 返回 Java IDE 并从停止的地方继续执行。

  • 回到 Xcode,看看它在断点处等待!

在断点


正如我之前所说,这是 obj-c/JNI 的一种非常简单的方法,您的项目可能非常大,但是通过这个小型测试项目,您至少可以看到它是如何工作的,然后继续您自己的项目设置。

于 2012-10-09T21:34:13.003 回答
1

您也许可以从终端附加gdb(或)。lldb如果带有本机代码的进程的启动是fork()/exec()- 即如果您无法键入gdb /some/command/line- 那么您可能会使用该--waitfor选项(参见手册页)来等待下级的启动。

加载符号会很棘手。


这是一个使用 cocoa 框架的 Mac OS X 项目。这会影响这个吗?

它不应该。如果有的话,它会更容易,希望符号文件是可用的格式。关键通常是在 Java 和本机代码之间的边界处找到正确的突破点。

dylib 中的本机代码是加载到 JVM 中,还是您有自定义可执行文件在内部启动 JVM?

在任何情况下,您都需要将本机调试器附加到运行该本机代码的任何进程。可能在您适当地设置了基于 java 的调试会话之后。

于 2012-10-04T19:41:31.797 回答
1

过去在做 JNI 时,我构建了一个测试工具来促进应用程序的本机部分的开发 - 以及 JNI 代码 - 这是臭名昭著的容易搞砸的,避免了从双方同时调试的需要。

这是作为一个本地应用程序编写的,它以编程方式调用 JVM,而不是从 Java 应用程序开始,然后尝试附加到 JVM。

当然,您可以在 Xcode 中启动它并对其进行调试——这对于使用 CDT 的 Eclipse 来说是一种无限可取的体验。

这种安排的 Java 方面通常非常简单且非设计性 - 基本上是一种从应用程序的本机部分调用的方法,然后通过 JNI 对本机部分进行一次或多次调用。

于 2012-10-08T14:24:21.823 回答
0

这是我在 Windows 下调试 JNI (C/C++) 所遵循的步骤,我认为 ObjectiveC 也需要相同的步骤。对于 Linux,它非常相似(将 ; 替换为 :,%XXX% 替换为 ${XXX}...)。

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

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

    显示参数

    BT

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

  4. 使用Java层的GUI重现错误
  5. 如果您想逐步执行您的 JNI 代码,gdb 允许您放置一些断点
于 2012-10-13T21:34:01.107 回答