JNI 代码无法在 Linux 上调用 C 函数,但不能在 Windows 上调用

JNI code fails to call C function on Linux but not Windows

我有以下 Java 使用 SWIG 生成的代码。虽然用了SWIG来生成SWIG的代码知识应该不需要看懂这道题

面向 Java 代码的用户:

public class UuidUtil implements OCUuidUtilConstants {
  public static Uuid generateUuid() {
    long cPtr = UuidUtilJNI.generateUuid();
    return (cPtr == 0) ? null : new Uuid(cPtr, true);
  }
}

UuidUtilJNI.generateUuid()定义如下

Java代码调用JNI代码

public class UuidUtilJNI {
  public final static native long generateUuid();
}

SWIG 生成了 JNI C 包装器代码uuid_wrap.c

uuid_t * jni_gen_uuid()
{
  printf("Inside jni_gen_uuid\n");
  uuid_t *value = (uuid_t *)malloc(sizeof(uuid_t));
  printf("Calling gen_uuid\n");
  gen_uuid(value);
  return value;
}

SWIGEXPORT jlong JNICALL Java_UuidUtilJNI_generateUuid(JNIEnv *jenv, jclass jcls) {
  jlong jresult = 0 ;
  uuid_t *result = 0 ;

  (void)jenv;
  (void)jcls;
  result = (uuid_t *)jni_gen_uuid();
  *(uuid_t **)&jresult = result; 
  return jresult;
}

gen_uuid(uuid_t *uuid) 函数 uuid.c

的部分 C 代码
void gen_uuid(uuid_t *uuid)
{
  printf("Inside gen_uuid\n");
  /* code to set the uuid to type 4 UUID according to RFC4122 */
}

我正在测试如下:

@Test
public void generateAndConvert() {
    Uuid testUuid = UuidUtil.generateUuid();
    assertNotNull(testUuid);
    // other test code left out for readability
}

当 Windows 上的测试为 运行 时,代码按预期工作。

当我尝试 运行 Linux 上的相同代码时,测试挂起。

它打印代码中找到的 3 个打印语句中的 2 个。

Inside jni_gen_uuid
Calling gen_uuid

在 Linux 机器 (Fedora 30) 上进行 运行 测试时,永远不会调用行 Inside gen_uuid

我的第一个想法是目标文件的输出存在某种不匹配,导致包装代码在调用 uuid 代码时失败。我比较了用于构建 uuid.cuuid_wrap.c 代码的构建标志,它们使用相同的标志,除了包装代码的一些构建警告被关闭,因为它是由工具生成的并不打算 由我修改。

我真的不知道还能去哪里找。

我已经使用 nm 和 objdump 尽我所能检查了 uuid.o 文件,我可以告诉它有 gen_uuid 符号。

我无法将 gdb 正确附加到 运行ning 示例,因此不确定这是否会提供任何有用的信息。

有什么可以帮助找到问题的建议吗?

不幸的是,这个解决方案非常适合我的代码。

当我写那个打印语句时

printf("Inside gen_uuid\n");
/* code to set the uuid to type 4 UUID according to RFC4122 */ 

从未被调用我错了。文本只是没有刷新到屏幕上。在 print 语句后添加 fflush(stdout); 会显示确实调用了 gen_uuid 函数。

如问题中所述,此代码生成类型 4 UUID,这是一个随机 UUID。

生成随机数的代码是 OS 特定的。在读取随机数之前,必须初始化随机数生成器。在 Linux 上,这是通过抓取一个文件描述符到 /dev/urandom 在 Windows 上,它是通过播种 srand() 函数来完成的。

如果未调用 random_init

Linux 会在生成随机数时出现段错误。另一方面,Windows 即使没有播种也会产生一个数字。

解决方案是在单元测试中调用 generateUuid() 之前调用 randomInit()

如果您是一名探索类似故障的开发人员,并且正在使用 printf 调试来检查是否到达了一行代码,那么在断定未到达一行代码之前,请确保将 print 语句刷新到屏幕上.