登录后退出应用问题分析(jni libdvm )

有个应用再登录的时候突然退出,而应用说再其他平台上都是好的,所以是系统rom的问题.先看看log再说

native_eup( 4929): waitpid:return n=5143 status=00000b7f
10391 I/native_eup( 4929): child is stopped
10392 I/native_eup( 4929): cause by fatal signal SIGSEGV
10393 I/native_eup( 4929):  collect crashInfo
10394 I/native_eup( 4929): start to collect crash info of child pid:5143 tid:5143
10395 I/native_eup( 4929): create_tombstone filePath :/data/data/com.tencent.qqgame.qqlord.tv/app_tomb/tomb_1444874383556.txt
10396 I/native_eup( 4929): file open success! /data/data/com.tencent.qqgame.qqlord.tv/app_tomb/tomb_1444874383556.txt:
10397 I/native_eup( 4929): dump crash banner start
10398 I/native_eup( 4929): dump dump_crash_banner start
10399 I/native_eup( 4929): read /proc/4929/cmdline
10400 I/native_eup( 4929): read success!
10401 I/native_eup( 4929): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
10402 I/native_eup( 4929): dump dump_build_info start
10403 I/native_eup( 4929): Build fingerprint: 'realtek/rtd299x_tv030/rtd299x_tv030:4.2.1/JOP40D/eng.ffeng.20150928.163008:eng/test-keys'
10404 I/native_eup( 4929): dump dump_build_info end
10405 I/native_eup( 4929): pid: 5143, tid: 5143  >>> com.tencent.qqgame.qqlord.tv <<<
10406 I/native_eup( 4929): NativeRQDVersion:NRQD_1.7.4.1
10407 I/native_eup( 4929): dump dump_fault_addr start
10408 I/native_eup( 4929): signal 11 (SIGSEGV), fault addr deadd00d

原来是 SIGSEGV, 一般是内存地址非法访问的问题.看看堆栈

 7639 D/WeGame BeaconHelper.reportMSDKEvent( 4929):  >>>event:MSDK_getNotice,wattingTime:259,flag:true
 7640 D/beacon_step_api( 4929):  onUA: MSDK_getNotice,true,259,-1,true
 7641 I/dalvikvm( 4929):   #00  pc 000012a0  /system/lib/libcorkscrew.so (unwind_backtrace_thread+27)
 7642 I/dalvikvm( 4929):   #01  pc 0005f270  /system/lib/libdvm.so (dvmDumpNativeStack(DebugOutputTarget const*, int)+35)
 7643 I/dalvikvm( 4929):   #02  pc 00053b68  /system/lib/libdvm.so (dvmDumpThreadEx(DebugOutputTarget const*, Thread*, bool)+303)
 7644 I/dalvikvm( 4929):   #03  pc 00053c02  /system/lib/libdvm.so (dvmDumpThread(Thread*, bool)+25)
 7645 I/dalvikvm( 4929):   #04  pc 00038b7a  /system/lib/libdvm.so
 7646 I/dalvikvm( 4929):   #05  pc 0003dd36  /system/lib/libdvm.so
 7647 I/dalvikvm( 4929):   #06  pc 002bf7b8  /data/app-lib/com.tencent.qqgame.qqlord.tv-1/libgame.so (_JNIEnv::CallStaticBooleanMethod(_jclass*,             _jmethodID*, ...)+15)
 7648 I/dalvikvm( 4929):   #07  pc 002cdad4  /data/app-lib/com.tencent.qqgame.qqlord.tv-1/libgame.so (GameManager::OnWakeupNotify(WakeupRet&)+71)
 7649 I/dalvikvm( 4929):   #08  pc 0041b7b4  /data/app-lib/com.tencent.qqgame.qqlord.tv-1/libgame.so (X8Observer::OnTimer(unsigned int)+243)
 7650 I/dalvikvm( 4929):   #09  pc 0041c4aa  /data/app-lib/com.tencent.qqgame.qqlord.tv-1/libgame.so (void MTGame::TEventProducerImpl<MTGame::               TModuleImpl<MTGame::ITimerManagerProxy, MTGame::CTimerManagerProxy, 1l> >::FireEvent<MTGame::ITimerManagerProxyEvent, unsigned int>(void 

看logcat里crush前的堆栈,腾讯的so调用了libdvm.so,看看对应的代码吧,

运行命令:

arm-none-linux-gnueabi-addr2line -aCfe libdvm.so 00038b7a
0x00038b7a
abortMaybe
/home/lw/rt95/e6700a/project/kernel/android/JB/dalvik/vm/CheckJni.cpp:37

看看/home/lw/rt95/e6700a/project/kernel/android/JB/dalvik/vm/CheckJni.cpp的37行吧

  35 static void abortMaybe() {                                                                                          ||     checkCallResultCommon
  36     if (!gDvmJni.warnOnly) {                                                                                        ||     checkClass [ScopedCheck
  37     ┊   dvmDumpThread(dvmThreadSelf(), false);                                                                      ||     checkClassName [ScopedC
  38     ┊   dvmAbort();                                                                                                 ||     checkFieldTypeForGet [S
  39     }                                                                                                               ||     checkFieldTypeForSet [S
  40 }

这里已经再dump信息,看到38行了么,dvm就要退出了.

那么是哪里调用了abortMaybe()呢, 我们再看看0003dd36 对应的代码是那里,

lw@pub:~/rt95/e6700a/project/kernel/android/JB/out/target/product/rtd299x_tv030/symbols/system/lib$ arm-none-linux-gnueabi-addr2line -aCfe libdvm.so 0003dd36


0x0003dd36
Check_CallStaticBooleanMethodV
/home/lw/rt95/e6700a/project/kernel/android/JB/dalvik/vm/CheckJni.cpp:1671

看到了是调用Check_CallStaticBooleanMethodV函数


再看看CheckJni.cpp第1671行:

CALL(jboolean, Boolean, jboolean result, result=, NON_VOID_RETURN("Z", jboolean), "Z"); 

这里有点困惑,这里的代码不是Check_CallStaticBooleanMethodV函数啊,只是一个CALL宏,展开的函数应该就是调用地方.当时没想到这点,有点迷茫了,看不出是哪里调用了abortMaybe()了.

后来采取了一种迂回的方法, 打出堆栈前的log会不会有调用函数的线索呢? 突然发现log中

7629 W/dalvikvm( 4929): JNI WARNING: expected return type 'Z'
 7630 W/dalvikvm( 4929):              calling Lcom/qqgame/MTLoginAdapter/JNIInterface;.SetMStartedFromHallJNI (Z)V
 7631 W/dalvikvm( 4929):              in Lcom/qqgame/MTSDK/MTTimer;.nativeOnTimer:(I)V (CallStaticBooleanMethodV)
 7632 I/dalvikvm( 4929): "GLThread 193" prio=5 tid=15 NATIVE
 7633 I/dalvikvm( 4929):   | group="main" sCount=0 dsCount=0 obj=0x428363b0 self=0x72220df8
 7634 I/dalvikvm( 4929):   | sysTid=4946 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=1914544320
 7635 I/dalvikvm( 4929):   | state=R schedstat=( 4550392204 4687113569 19650 ) utm=360 stm=94 core=1
这里有个warning, 在CheckJni.cpp搜索"expected return type", 发现只有一个地方,

 384     void checkSig(jmethodID methodID, const char* expectedType, bool isStatic) {                                    ||     checkArray [ScopedCheck
 385     ┊   const Method* method = (const Method*) methodID;                                                            ||     checkCallResultCommon
 386     ┊   bool printWarn = false;                                                                                     ||     checkClass [ScopedCheck
 387                                                                                                                     ||     checkClassName [ScopedC
 388     ┊   if (*expectedType != method->shorty[0]) {                                                                   ||     checkFieldTypeForGet [S
 389     ┊   ┊   ALOGW("JNI WARNING: <span style="background-color: rgb(0, 0, 0);"><span style="color:#33CC00;">expected return type '%s</span></span>'", expectedType);                                          ||     checkFieldTypeForSet [S
 390     ┊   ┊   <span style="background-color: rgb(0, 0, 0);"><span style="color:#009900;">printWarn = true</span></span>;                                                                                       ||     checkInstance [ScopedCh
 391     ┊   } else if (isStatic && !dvmIsStaticMethod(method)) {                                                        ||     checkInstanceFieldID [S
 392     ┊   ┊   if (isStatic) {                                                                                         ||     checkLengthPositive [Sc
 393     ┊   ┊   ┊   ALOGW("JNI WARNING: calling non-static method with static call");                                   ||     checkNonNull [ScopedChe
 394     ┊   ┊   } else {                                                                                                ||     checkObject [ScopedChec
 395     ┊   ┊   ┊   ALOGW("JNI WARNING: calling static method with non-static call");                                   ||     checkReleaseMode [Scope
 396     ┊   ┊   }                                                                                                       ||     checkSig [ScopedCheck] 
 397     ┊   ┊   printWarn = true;                                                                                       ||     checkStaticFieldID [Sco
 398     ┊   }                                                                                                           ||     checkStaticMethod [Scop
 399                                                                                                                     ||     checkString [ScopedChec
 400     ┊   if (<span style="background-color: rgb(0, 0, 0);"><span style="color:#33CC00;">printWarn</span></span>) {                                                                                            ||     checkThread [ScopedChec
 401     ┊   ┊   char* desc = dexProtoCopyMethodDescriptor(&method->prototype);                                          ||     checkUtfBytes [ScopedCh
 402     ┊   ┊   ALOGW("             calling %s.%s %s", method->clazz->descriptor, method->name, desc);                  ||     checkUtfString [ScopedC
 403     ┊   ┊   free(desc);                                                                                             ||     checkVirtualMethod [Sco
 404     ┊   ┊   showLocation();                                                                                         ||     create [GuardedCopy]
 405     ┊   ┊   <span style="background-color: rgb(0, 0, 0);"><span style="color:#33CC00;">abortMaybe()</span></span>;                                                                                           ||     createGuardedPACopy
 406     ┊   }                                                                                                           ||     debugAlloc [GuardedCopy
 407     } 

看到了吧,就是这个原因导致的. jni在调用的时候会检查,在我们这里应该需要一个boolean返回值(Z代表boolean),但是底层jni应该是返回了void, 由于jni是应用自己的so,所以我也看不到源码了.
知道了原因,怎么去修改呢,有两种方案.

1. 让应用去修改他们的jni

2. 在系统里修改,这种不是致命的warning检查中,不去调用dvmAbort().

看到

  35 static void abortMaybe() {                                                                                          ||     checkCallResultCommon
  36     if (!gDvmJni.warnOnly) {                                                                                        ||     checkClass [ScopedCheck
  37     ┊   dvmDumpThread(dvmThreadSelf(), false);                                                                      ||     checkClassName [ScopedC
  38     ┊   dvmAbort();                                                                                                 ||     checkFieldTypeForGet [S
  39     }                                                                                                               ||     checkFieldTypeForSet [S
  40 }

这里有一个标识,gDvmJni.warnOnly,只要为true的话, 那么就不会调用dvmAbort()了.

经过多方寻找,终于找到了修改gDvmJni.warnOnly的方法:

再编译项目的时候,加上

PRODUCT_PROPERTY_OVERRIDES += dalvik.vm.jniopts=warnonly

就ok了.











你可能感兴趣的:(jni,libdvm.so,dvmAbo)