ログイン後にアプリケーション問題分析(jni libdvm)を終了
あるアプリケーションが再ログインしたときに突然終了し、アプリケーションは他のプラットフォームでも良いと言っているので、システムromの問題です.まずロゴを見てからにしよう
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>::FireEvent(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/rt 95/e 6700 a/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行を见て、dvdはもうすぐ退出します.
では、abortMaybe()はどこで呼び出されたのでしょうか.0003 dd 36に対応するコードがどこにあるか見てみましょう.
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
チェックを呼び出しているのが見えましたCallStaticBooleanMethodV関数
もう一度見てみろcpp 1671行目:
CALL(jboolean, Boolean, jboolean result, result=, NON_VOID_RETURN("Z", jboolean), "Z");
ここはちょっと困惑していますが、ここのコードはCheckではありません.CallStaticBooleanMethodV関数ですね.ただのCALLマクロです.展開された関数が呼び出し場所だったはずです.その時は思わなかったのですが、ちょっと迷っていました.どこでabortMaybe()が呼び出されたのか分かりませんでした.
後で迂回する方法を採用して、スタックを出す前の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 196650)utm=360 stm=94 core=1ここにwarningがあり、CheckJni.cppは「expected return type」を検索し、1つの場所しかないことを発見しました.
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 }
ここに標識がありますgDvmJniwarnOnly,trueであればdvmAbort()は呼び出されない.
いろいろ探して、やっと修正gDvmJniを見つけた.warnOnlyの方法:
プロジェクトを再コンパイルするときに、
PRODUCT_PROPERTY_OVERRIDES += dalvik.vm.jniopts=warnonly
OKです.
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
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/rt 95/e 6700 a/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行を见て、dvdはもうすぐ退出します.
では、abortMaybe()はどこで呼び出されたのでしょうか.0003 dd 36に対応するコードがどこにあるか見てみましょう.
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
チェックを呼び出しているのが見えましたCallStaticBooleanMethodV関数
もう一度見てみろcpp 1671行目:
CALL(jboolean, Boolean, jboolean result, result=, NON_VOID_RETURN("Z", jboolean), "Z");
ここはちょっと困惑していますが、ここのコードはCheckではありません.CallStaticBooleanMethodV関数ですね.ただのCALLマクロです.展開された関数が呼び出し場所だったはずです.その時は思わなかったのですが、ちょっと迷っていました.どこでabortMaybe()が呼び出されたのか分かりませんでした.
後で迂回する方法を採用して、スタックを出す前の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 196650)utm=360 stm=94 core=1ここにwarningがあり、CheckJni.cppは「expected return type」を検索し、1つの場所しかないことを発見しました.
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 }
ここに標識がありますgDvmJniwarnOnly,trueであればdvmAbort()は呼び出されない.
いろいろ探して、やっと修正gDvmJniを見つけた.warnOnlyの方法:
プロジェクトを再コンパイルするときに、
PRODUCT_PROPERTY_OVERRIDES += dalvik.vm.jniopts=warnonly
OKです.