前言
记录批改 linphone-sdk-android 过程,打算分为上、中、下三篇
本文是上篇,本篇仅记录下书问题 2 的初步排查过程,尽量形容排查问题过程中的思路与方向
余下两篇记录问题 1、2 的批改过程
背景
接上文编译 linphone-sdk-android
我的项目中应用的 linphone-sdk-android 版本为 4.5.x,应用过程中发现以下两个问题:
- 关上音频编解码 G722、G729 时,发动呼叫的 INVITE SDP 中,没有 G722、G729 的 RTP MAP,过后认为是 linphone 的 bug,前面看源码及查资料发现可能不是 bug,这里先按下不表
- 应用 sdk 提供的 JavaLogger 输入日志时,伪代码:
mFactory.getLoggingService().addListener(mAndroidLoggingService);
,偶现 JNI 解体问题
--------- beginning of crash
2022-04-11 14:16:22.350 1142-1430/? A/libc: Fatal signal 6 (SIGABRT), code -6 in tid 1430 (RealLinphonePro)
2022-04-11 14:16:22.441 3756-3756/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
2022-04-11 14:16:22.442 3756-3756/? A/DEBUG: Build fingerprint: 'Android/rk3288/rk3288:7.1.2/NHG47K/builde03162201:userdebug/test-keys'
2022-04-11 14:16:22.444 3756-3756/? A/DEBUG: Revision: '0'
2022-04-11 14:16:22.446 3756-3756/? A/DEBUG: ABI: 'arm'
2022-04-11 14:16:22.447 3756-3756/? A/DEBUG: pid: 1142, tid: 1430, name: RealLinphonePro >>> com.guodong.android.linphone <<<
2022-04-11 14:16:22.448 3756-3756/? A/DEBUG: signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
2022-04-11 14:16:22.454 3756-3756/? A/DEBUG: Abort message: 'art/runtime/indirect_reference_table.cc:80] JNI ERROR (app bug): accessed deleted WeakGlobal 0x21ab'
2022-04-11 14:16:22.456 3756-3756/? A/DEBUG: r0 00000000 r1 00000596 r2 00000006 r3 00000008
2022-04-11 14:16:22.457 3756-3756/? A/DEBUG: r4 89aff978 r5 00000006 r6 89aff920 r7 0000010c
2022-04-11 14:16:22.459 3756-3756/? A/DEBUG: r8 00000043 r9 aaa7eef0 sl 0000000a fp 89d04400
2022-04-11 14:16:22.461 3756-3756/? A/DEBUG: ip 0000000b sp 89afef88 lr ab175857 pc ab1780c0 cpsr 600b0010
2022-04-11 14:16:22.509 3756-3756/? A/DEBUG: backtrace:
2022-04-11 14:16:22.511 3756-3756/? A/DEBUG: #00 pc 0004a0c0 /system/lib/libc.so (tgkill+12)
2022-04-11 14:16:22.512 3756-3756/? A/DEBUG: #01 pc 00047853 /system/lib/libc.so (pthread_kill+34)
2022-04-11 14:16:22.514 3756-3756/? A/DEBUG: #02 pc 0001d8b5 /system/lib/libc.so (raise+10)
2022-04-11 14:16:22.515 3756-3756/? A/DEBUG: #03 pc 00019401 /system/lib/libc.so (__libc_android_abort+34)
2022-04-11 14:16:22.517 3756-3756/? A/DEBUG: #04 pc 00017048 /system/lib/libc.so (abort+4)
2022-04-11 14:16:22.518 3756-3756/? A/DEBUG: #05 pc 0031d8cd /system/lib/libart.so (_ZN3art7Runtime5AbortEPKc+328)
2022-04-11 14:16:22.520 3756-3756/? A/DEBUG: #06 pc 000b5503 /system/lib/libart.so (_ZN3art10LogMessageD2Ev+1134)
2022-04-11 14:16:22.521 3756-3756/? A/DEBUG: #07 pc 001bd0ff /system/lib/libart.so (_ZN3art22IndirectReferenceTable17AbortIfNoCheckJNIERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE+134)
2022-04-11 14:16:22.523 3756-3756/? A/DEBUG: #08 pc 0023ecaf /system/lib/libart.so (_ZNK3art22IndirectReferenceTable10GetCheckedEPv+250)
2022-04-11 14:16:22.525 3756-3756/? A/DEBUG: #09 pc 0023c05b /system/lib/libart.so (_ZN3art9JavaVMExt16DecodeWeakGlobalEPNS_6ThreadEPv+30)
2022-04-11 14:16:22.526 3756-3756/? A/DEBUG: #10 pc 00337679 /system/lib/libart.so (_ZNK3art6Thread13DecodeJObjectEP8_jobject+164)
2022-04-11 14:16:22.528 3756-3756/? A/DEBUG: #11 pc 00265843 /system/lib/libart.so (_ZN3art3JNI11NewLocalRefEP7_JNIEnvP8_jobject+406)
2022-04-11 14:16:22.530 3756-3756/? A/DEBUG: #12 pc 0060eff9 /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so (getLoggingService+88)
2022-04-11 14:16:22.531 3756-3756/? A/DEBUG: #13 pc 0061a977 /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so
2022-04-11 14:16:22.533 3756-3756/? A/DEBUG: #14 pc 005ef911 /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so
2022-04-11 14:16:22.535 3756-3756/? A/DEBUG: #15 pc 00025437 /data/app/com.guodong.android.linphone-1/lib/arm/libbctoolbox.so (bctbx_logv+182)
2022-04-11 14:16:22.536 3756-3756/? A/DEBUG: #16 pc 006f5061 /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so
2022-04-11 14:16:22.538 3756-3756/? A/DEBUG: #17 pc 006f51d9 /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so (wake_lock_acquire+152)
2022-04-11 14:16:22.539 3756-3756/? A/DEBUG: #18 pc 006601c5 /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so
2022-04-11 14:16:22.541 3756-3756/? A/DEBUG: #19 pc 00660b27 /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so (belle_sip_client_transaction_init+138)
2022-04-11 14:16:22.542 3756-3756/? A/DEBUG: #20 pc 006fb1d3 /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so (belle_sip_nict_new+30)
2022-04-11 14:16:22.544 3756-3756/? A/DEBUG: #21 pc 0065c3a7 /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so (belle_sip_provider_create_client_transaction+46)
2022-04-11 14:16:22.546 3756-3756/? A/DEBUG: #22 pc 0065dec3 /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so
2022-04-11 14:16:22.547 3756-3756/? A/DEBUG: #23 pc 0065daf7 /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so (belle_sip_refresher_refresh+28)
2022-04-11 14:16:22.549 3756-3756/? A/DEBUG: #24 pc 005fac23 /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so (linphone_proxy_config_refresh_register+34)
2022-04-11 14:16:22.550 3756-3756/? A/DEBUG: #25 pc 005e9fc5 /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so (linphone_core_refresh_registers+40)
2022-04-11 14:16:22.552 3756-3756/? A/DEBUG: #26 pc 019fc3bf /data/app/com.guodong.android.linphone-1/oat/arm/base.odex (offset 0x18de000)
2022-04-11 14:16:23.096 227-255/? I/AudioFlinger: BUFFER TIMEOUT: remove(4099) from active list on thread 0xabd03e00
剖析
问题 2
通过解体日志第 27 行 2022-04-11 14:16:22.530 3756-3756/? A/DEBUG: #12 pc 0060eff9 /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so (getLoggingService+88)
能够判断解体是在 getLoggingService()
这个办法里
如此须要先找到 getLoggingService()
办法的源码,先在 Source Insight 中搜寻getLoggingService()
,搜寻一圈发现没有,猜测此办法应该是编译后主动生成的
在 Ubuntu 编译环境里找找,通过 find
命令查找蕴含 getLoggingService
文本的文件
$ cd linphone-sdk/build/
$ find . -type f | xargs grep "getLoggingService"
./WORK/android-armv7/Build/linphone/wrappers/java/src/linphone_jni.cc:JNIEXPORT jobject JNICALL getLoggingService(JNIEnv *env, LinphoneLoggingService *cptr, bool_t takeref) {./WORK/android-armv7/Build/linphone/wrappers/java/src/linphone_jni.cc: jobject j_logService = getLoggingService(env, (LinphoneLoggingService *)logService, TRUE);
./WORK/android-armv7/Build/linphone/wrappers/java/src/linphone_jni.cc: jobject jni_result = (jobject)getLoggingService(env, (LinphoneLoggingService *)linphone_logging_service_get(), TRUE);
找到了 bingo~
关上 linphone_jni.cc
,getLoggingService()
办法体如下:
JNIEXPORT jobject JNICALL getLoggingService(JNIEnv *env, LinphoneLoggingService *cptr, bool_t takeref) {
jobject jobj = nullptr;
if (cptr != nullptr) {void *up = belle_sip_object_data_get((belle_sip_object_t *)cptr, belle_sip_java_user_data_key);
LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_factory_get_user_data(linphone_factory_get());
if (!ljb) {ljb = new LinphoneJavaBindings(env);
linphone_factory_set_user_data(linphone_factory_get(), ljb);
}
jclass linphone_logging_service_class = ljb->linphone_logging_service_class;
jmethodID linphone_logging_service_constructor = ljb->linphone_logging_service_class_constructor;
if (up == nullptr) {jobj = env->NewObject(linphone_logging_service_class, linphone_logging_service_constructor, (jlong)cptr);
belle_sip_object_data_set((belle_sip_object_t *)cptr, belle_sip_java_user_data_key, (void*)env->NewWeakGlobalRef(jobj), nullptr);
if (takeref)
linphone_logging_service_ref(cptr);
} else {jobj = env->NewLocalRef((jobject)up);
if (jobj == nullptr) {
// Delete weak ref ?
env->DeleteWeakGlobalRef((jobject)up);
// takes implicit local ref
jobj = env->NewObject(linphone_logging_service_class, linphone_logging_service_constructor, (jlong)cptr);
belle_sip_object_data_set((belle_sip_object_t *)cptr, belle_sip_java_user_data_key, (void*)env->NewWeakGlobalRef(jobj), nullptr);
if (takeref)
linphone_logging_service_ref(cptr);
}
}
}
return jobj;
}
嗯,看起来没啥问题,也有判空解决,一时间摸不着脉络,先在 getLoggingService()
办法中加点日志输入看看吧
后面说 linphone_jni.cc
是主动生成的,当初须要找到主动生成 linphone_jni.cc
的代码,依据解体日志能够发现 getLoggingService()
在linphone.so
中,而源码中有 liblinphone
这个目录,先在这个目录下找找吧,通过 find
命令查找文件名中蕴含 jni
的文件
$ cd linphone-sdk/liblinphone/
$ find . -name *jni*
./coreapi/linphonecore_jni.cc
./tools/lpc2xml_jni.cc
./tools/xml2lpc_jni.cc
./tools/my_jni.h
./wrappers/java/jni.mustache
查找进去多个文件,既然 linphone_jni.cc
是主动生成的,所以能够确定 .cc
、.h
后缀的几个文件必定不是,排除后只剩下这个 jni.mustache
文件,关上文件 vim ./wrappers/java/jni.mustache
正文第一行就是linphone_jni.cc
,太棒了,找到主动生成的代码了
通过一些查看,并与 linphone_jni.cc
中的实现比照,锁定了以下代码即为主动生成的模板代码:
290 {{#objects}}
291 JNIEXPORT jobject JNICALL get{{className}}(JNIEnv *env, {{classCName}} *cptr, bool_t takeref) {
292 jobject jobj = nullptr;
293
294 if (cptr != nullptr) {295 void *up = belle_sip_object_data_get((belle_sip_object_t *)cptr, belle_sip_java_user_data_key);
296 LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_factory_get_user_data(linphone_factory_get());
297 if (!ljb) {298 ljb = new LinphoneJavaBindings(env);
299 linphone_factory_set_user_data(linphone_factory_get(), ljb);
300 }
301
302 jclass {{cPrefix}}_class = ljb->{{cPrefix}}_class;
303 jmethodID {{cPrefix}}_constructor = ljb->{{cPrefix}}_class_constructor;
304
305 if (up == nullptr) {306 jobj = env->NewObject({{cPrefix}}_class, {{cPrefix}}_constructor, (jlong)cptr);
307 belle_sip_object_data_set((belle_sip_object_t *)cptr, belle_sip_java_user_data_key, (void*)env->NewWeakGlobalRef(jobj), nullptr);
308 if (takeref)
309 {{#refCountable}}{{cPrefix}}_ref(cptr);{{/refCountable}}
310 } else {311 jobj = env->NewLocalRef((jobject)up);
312 if (jobj == nullptr) {
313 // Delete weak ref ?
314 env->DeleteWeakGlobalRef((jobject)up);
315 // takes implicit local ref
316 jobj = env->NewObject({{cPrefix}}_class, {{cPrefix}}_constructor, (jlong)cptr);
317 belle_sip_object_data_set((belle_sip_object_t *)cptr, belle_sip_java_user_data_key, (void*)env->NewWeakGlobalRef(jobj), nullptr);
318 if (takeref)
319 {{#refCountable}}{{cPrefix}}_ref(cptr);{{/refCountable}}
320 }
321 }
322 }
323 return jobj;
324 }
这里 linphone-sdk 应用了 mustache,还记得在上篇编译 Linphone-SDK-Android 中有装置 pip3 install pystache
,pystache
是mustache
的 Python 实现,mustache
的语法比较简单,在 github
上看一眼文档,再跟着源码比着葫芦画瓢,应该就差不多了
好的,学习完 mustache
的语法,理解到高级用法能够传一个配置文件进行渲染,正好与 jni.mustache
同级目录中有个 genwrapper.py
文件,关上此文件,外面定义了很多类,其中有个 Jni
的类,外面有些字段与 jni.mustache
中的标签正好对应,就是它了
class Jni:
def add_object(self, javaClass):
if javaClass.className == 'Factory':
return
obj = {
'jniPrefix': self.jni_package,
'jniPath': self.jni_path,
'cPrefix': javaClass.cPrefix,
'className': javaClass.className,
'classCName': javaClass.cName,
'classImplName': javaClass.classImplName,
'refCountable': javaClass.refCountable,
'notRefCountable': not javaClass.refCountable,
}
self.objects.append(obj)
happy~ 万事具备,当初能够批改源码,增加一些日志输入了
因为在 jni.mustache
中模板办法 get{{className}}
是能够生成多个以 get
结尾的办法,当初须要判断 {{className}}
是不是等于 LoggingService
,而在 mustache 中没有发现比拟字符串是否雷同的语法,所以在genwrapper.py
的Jni
类中新增一个 isLoggingService
字段示意是否是 getLoggingService()
办法
class Jni:
def add_object(self, javaClass):
if javaClass.className == 'Factory':
return
obj = {
'jniPrefix': self.jni_package,
'jniPath': self.jni_path,
'cPrefix': javaClass.cPrefix,
'className': javaClass.className,
'classCName': javaClass.cName,
'classImplName': javaClass.classImplName,
'refCountable': javaClass.refCountable,
'notRefCountable': not javaClass.refCountable,
'isLoggingService': javaClass.className == 'LoggingService',
}
self.objects.append(obj)
而后批改 jni.mustache
文件,减少日志,输入 up
指针指向的地址,up
指针转成 jobject
后与 NULL
、nullptr
比拟的后果,判断 up
是否曾经被回收了
这里说一下代码的大略意思,up
指针从 belle_sip_object_data_get
办法中通过 belle_sip_java_user_data_key
这个 Key 获取,能够简略了解为 Java 中的 Map;如果 up
为空,则调用 Java 办法创立一个对象,将创立的 Java 对象通过 NewWeakGlobalRef
办法转换为全局弱援用,再通过 belle_sip_object_data_set
办法保存起来;如果 up
不为空,则强转为 jobject
,判断jobject
是否为空,为空则删除全局弱援用,再创立保留
{{#objects}}
JNIEXPORT jobject JNICALL get{{className}}(JNIEnv *env, {{classCName}} *cptr, bool_t takeref) {
jobject jobj = nullptr;
if (cptr != nullptr) {void *up = belle_sip_object_data_get((belle_sip_object_t *)cptr, belle_sip_java_user_data_key);
LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_factory_get_user_data(linphone_factory_get());
if (!ljb) {ljb = new LinphoneJavaBindings(env);
linphone_factory_set_user_data(linphone_factory_get(), ljb);
}
jclass {{cPrefix}}_class = ljb->{{cPrefix}}_class;
jmethodID {{cPrefix}}_constructor = ljb->{{cPrefix}}_class_constructor;
{{#isLoggingService}}
#ifdef __ANDROID__
__android_log_print(ANDROID_LOG_DEBUG, "guodongAndroid", "up = %p", up);
jobject temp_jobj1 = (jobject)up;
jboolean up_available1 = env->IsSameObject(temp_jobj1, NULL);
__android_log_print(ANDROID_LOG_DEBUG, "guodongAndroid", "up_available1 = %d", up_available1);
jobject temp_jobj2 = (jobject)up;
jboolean up_available2 = env->IsSameObject(temp_jobj2, nullptr);
__android_log_print(ANDROID_LOG_DEBUG, "guodongAndroid", "up_available2 = %d", up_available2);
#endif /* __ANDROID__ */
{{/isLoggingService}}
if (up == nullptr) {jobj = env->NewObject({{cPrefix}}_class, {{cPrefix}}_constructor, (jlong)cptr);
belle_sip_object_data_set((belle_sip_object_t *)cptr, belle_sip_java_user_data_key, (void*)env->NewWeakGlobalRef(jobj), nullptr);
if (takeref)
{{#refCountable}}{{cPrefix}}_ref(cptr);{{/refCountable}}
} else {jobj = env->NewLocalRef((jobject)up);
if (jobj == nullptr) {
// Delete weak ref ?
env->DeleteWeakGlobalRef((jobject)up);
// takes implicit local ref
jobj = env->NewObject({{cPrefix}}_class, {{cPrefix}}_constructor, (jlong)cptr);
belle_sip_object_data_set((belle_sip_object_t *)cptr, belle_sip_java_user_data_key, (void*)env->NewWeakGlobalRef(jobj), nullptr);
if (takeref)
{{#refCountable}}{{cPrefix}}_ref(cptr);{{/refCountable}}
}
}
}
return jobj;
}
好的,批改完源码保留后,就能够编译了
$ cd linphone-sdk/build/
$ cmake --build . --parallel 8
期待编译实现,拷贝进去编译好的aar
,放到 Android Studio 中运行,查看 Logcat 输入