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.c
和 uuid_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 语句刷新到屏幕上.
我有以下 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
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.c
和 uuid_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 语句刷新到屏幕上.