AndroidNativeCrashのキャプチャと分析

Android開発では、NEは常に無視できない問題でしたが、解決するのは非常に困難です。その理由は、クロスターミナルの開発と分析が含まれるためです。Java、C&C ++、NDKの開発に精通している必要があります。ソリューションはJavaの例外とは異なります。したがって、いくつかの疑問を解決するために、この記事ではNEのキャプチャ、分析、および復元の3つの側面について説明します。

1.NEの紹介

NEのフルネームはNativeCrashで、CまたはC ++の実行中に生成されるエラーです。NEは通常のJavaエラーとは異なります。通常のlogcatは読み取り可能なスタックに直接復元できず、通常はソースコードなしでデバッグできません。

したがって、日常のアプリケーション層のエンジニアは、内部のクラウド診断ログがある場合でも、通常NEエラーを無視します。これらの問題が発生した場合、アプリケーション層であり、C ++を理解していないエンジニアは復元スタックを迅速に解決できますか? NEの問題を特定または解決するには?

以下に焦点を当てます。

1.1だから構成

最初にsoの構成を理解しましょう。完全なsoは、Cコードといくつかのデバッグ情報で構成されます。これらのデバッグ情報は、so内のすべてのメソッドの比較テーブルを記録します。これは、メソッド名とその安価なアドレスの間の対応テーブルです。シンボルテーブルとも呼ばれます。この種の方法はアンストリップとも呼ばれ、通常はサイズが大きくなります。

通常、リリースされたsoはストリップ操作を実行する必要があるため、ストリップ後のsoのデバッグ情報がストリップされ、so全体のサイズが縮小されます。

以下に示すように:

AndroidNativeCrashのキャプチャと分析

ストリップ前後のサイズ比較は以下のようになります。

AndroidNativeCrashのキャプチャと分析

NEなどがわからない場合は、このデバッグ情報をJavaコード難読化のマッピングファイルとして簡単に理解できます。スタック分析は、このマッピングファイルがある場合にのみ実行できます。

スタック情報が失われると、基本的にスタックを復元できず、問題を解決できません。

したがって、これらのデバッグ情報は特に重要であり、NEの問題を分析するための重要な情報です。したがって、コンパイルするときは、後で問題を分析するためにシンボルテーブル情報が削除されないようにコピーを保持する必要があります。コンパイルするたびに、すべてを保存する必要があります。コードを変更して再コンパイルすると、変更前後のシンボルテーブル情報に対応して分析することができなくなります。

1.2ステータスを表示する

実際、Macでfileコマンドを使用するだけで、コマンドラインからそのステータスを表示することもできます。その基本情報の一部は、コマンドの戻り値で表示できます。

次の図に示すように、stripedはデバッグ情報のないsoを表し、debug_infoがある場合、strippedはデバッグ情報のあるsoを表します。

file libbreakpad-core-s.so
libbreakpad-core-s.so: *******, BuildID[sha1]=54ad86d708f4dc0926ad220b098d2a9e71da235a, stripped
file libbreakpad-core.so
libbreakpad-core.so: ******, BuildID[sha1]=54ad86d708f4dc0926ad220b098d2a9e71da235a, with debug_info, not stripped

Windowsシステムの場合は、Linuxサブシステムをインストールしてから、Linuxで同じコマンドを実行することをお勧めします。情報を取得することもできます。

次に、2つの状態でどのようにそうなるかを見てみましょう。

1.3ストリップを取得し、ストリップを解除します。

現在、Android Studioは、mkまたはCmakeのどちらでコンパイルされているかに関係なく、ストリップとアンストライプを同時に出力します。次の図は、そのようにコンパイルされたCmakeによって生成された対応する2つを示しています。

したがって、ストリップ前のパス:build / intermediates / transforms / mergeJniLibs

したがって、ストリップ後のパス:build / intermediates / transforms / stripDebugSymbol

AndroidNativeCrashのキャプチャと分析

さらに、AndroidSDKが提供するツールaarch64-linux-android-stripを手動で削除することもできます。aarch64-linux-android-stripツールは/ Users / njvivo / Library / Android / sdk / ndk /にあります。 21.3.6528147 / toolchainsディレクトリ。

このツールには、主にさまざまな携帯電話のCPUアーキテクチャ用に複数のバージョンがあります。電話のCPUアーキテクチャがわからない場合は、電話に接続し、次のコマンドを使用して表示できます。

adb shell cat /proc/cpuinfo
Processor   : AArch64 Processor rev 12 (aarch64)

上の写真でわかるように、私の電話はCPUにaarch64を使用しているため、aarch64に対応するツールであるaarch64-linux-android-stripを使用します。NDKには多くのツールが用意されているため、今後使用するには次の原則に従ってください。

aarch64架构
/Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-strip
arm架构
/Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-android-strip

次のコマンドを使用して、デバッグを直接削除します。

aarch64-linux-android-strip --strip-all libbreakpad-core.so

Cmakeを使用してコンパイルする場合、次のコマンドを追加して、ストリップを直接コンパイルできます。

#set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s")
#set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")

mkファイルを使用してコンパイルする場合は、次のコマンドを追加するか、ストリップを直接コンパイルしてください。

-fvisibility=hidden

次に、NEのキャプチャと分析

NEの解析は、その名前が示すように、スタックの解析です。もちろん、すべての前提条件は、署名されたテーブル、つまりストライプ化されていないテーブルを保存することです。ストリップの後にそうするだけの場合、できることは何もありません。スタックは基本的に復元できません。

スタックをキャプチャして復元するには、一般に3つの方法があります。

2.1Logcatキャプチャ

名前が示すように、logcatを介してキャプチャされます。AndroidStudioを介してlogcatを開き、NEを作成します。#00pc 00000000000161a0のような多くのシンボルしか表示されず、直接読み取ることができるログはありません。直接出力したいlogcatを介したコピー。ログを直接読み取ります。

Android / SDK / NDKで提供されているツールndk-stackを使用できます。このツールは、NEによって出力されたログを読み取り可能なログに直接解析できます。

ndk-stackは通常ndkのツールの下にあり、Macの下のアドレスは

/Users/XXXX/Library/Android/sdk/ndk/21.3.6528147/ndk-stack

次に、このディレクトリでコンソールコマンドを実行するか、AndroidStudioのターミナルで実行します。

adb shell logcat | androidsdk绝对路径/ndk-stack -sym so所在目录

このように、アプリケーションでNEが発生すると、コンソールは次のログを出力します。ログから、クラッシュの対応するsoと対応するメソッド名を確認できます。cのソースコードが利用可能な場合は、問題を簡単に見つけることができます。

promote:~ njvivo$ adb shell logcat | ndk-stack -sym libbreakpad-core.so
********** Crash dump: **********
Build fingerprint: 'vivo/PD1809/PD1809:8.1.0/OPM1.171019.026/compil04252203:user/release-keys'
#00 0x00000000000161a0 /data/app/com.android.necase-lEp0warh8FqicyY1YqGXXA==/lib/arm64/libbreakpad-core.so (Java_com_online_breakpad_BreakpadInit_nUpdateLaunchInfo+16)
#01 0x00000000000090cc /data/app/com.android.necase-lEp0warh8FqicyY1YqGXXA==/oat/arm64/base.odex (offset 0x9000)
Crash dump is completed

実際、ndk-stackツールの原則は、内部統合がaddr2lineを使用してスタックをリアルタイムで分析し、コンソールに表示することです。

ここで友達を見ると、これは簡単ではないように感じますが、実際のクラッシュシーンを再現するのは簡単ではなく、ユーザーシーンをシミュレートするのが難しい場合があります。次に、オンラインNEクラッシュを監視および特定する方法は2つあります。

2.2DropBoxログの解析-システムアプリケーションに適しています

これは非常に簡単です。DropBoxはJE、NE、ANRのさまざまなログを記録します。分析と解決のためにDropBoxの下にログをアップロードするだけで済みます。サンプルログは以下に掲載されています。

AndroidNativeCrashのキャプチャと分析

解決スキーム1:

上記のndk-stackツールを使用すると、DropBoxの下のログを直接スタックに解析でき、breakpad.cppの111行目のCrash()メソッドでクラッシュが発生していることがわかります。

ndk-stack -sym /Users/njvivo/Desktop/NE -dump [email protected]
********** Crash dump: **********
Build fingerprint: 'vivo/PD1809/PD1809:8.1.0/OPM1.171019.026/compil04252203:user/release-keys'
#00 0x00000000000161a0 /data/app/com.android.necase-lEp0warh8FqicyY1YqGXXA==/lib/arm64/libbreakpad-core.so (Java_com_online_breakpad_BreakpadInit_nUpdateLaunchInfo+16)
Crash()
/Users/njvivo/Documents/project/Breakpad/breakpad-build/src/main/cpp/breakpad.cpp:111:8
Java_com_online_breakpad_BreakpadInit_nUpdateLaunchInfo
/Users/njvivo/Documents/project/Breakpad/breakpad-build/src/main/cpp/breakpad.cpp:122:0
#01 0x00000000000090cc /data/app/com.android.necase-lEp0warh8FqicyY1YqGXXA==/oat/arm64/base.odex (offset 0x9000)
Crash dump is completed

分析計画2:

Android / SDK / NDKが提供するツールlinux-android-addr2lineを引き続き使用します。このツールは/ Users / njvivo / Library / Android / sdk / ndkディレクトリにあり、2つのバージョンがあります。

aarch64架构
/Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-addr2line
arm架构
/Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-addr2line

コマンドの使用方法は次のとおりです。ストライプ化されていないsoと、ログに表示されるスタックシンボル00000000000161a0と組み合わせて、クラッシュアドレスとメソッドを解析することもできます。

aarch64-linux-android-addr2line -f -C -e libbreakpad-core.so 00000000000161a0

Crash()
/Users/njvivo/Documents/project/Breakpad/breakpad-build/src/main/cpp/breakpad.cpp:111

以上のことから、非常にシンプルに見えますが、DropBoxはシステムアプリケーションからしかアクセスできず、非システムアプリケーションはログをまったく取得できないという致命的な問題があります。

2.3BreakPadによるキャプチャ分析-すべてのアプリケーションに適用可能

非システムアプリケーションは、Googleが提供するオープンソースツールBreakPadを介して監視および分析できます。CrashSDKもこの方法を使用します。NEの発生をリアルタイムで監視し、関連ファイルを記録できるため、クラッシュとそれに対応するアプリケーションのクラッシュが発生します。開始され、シーンが結合され、報告されます。

BreakPadの使用方法を簡単に紹介します。

2.3.1BreakPadの実現機能

BreakPadは、主にNEモニタリングとコールバックの2つの機能を提供し、dmpで終わるファイルであるミニダンプファイルを生成します。また、シンボルテーブルツールとスタック復元ツールの2つのツールも提供します。

AndroidNativeCrashのキャプチャと分析

  • シンボルテーブルツール:そこからデバッグ情報を抽出し、スタックに対応するシンボルテーブルを取得するために使用されます。

  • スタック復元ツール: BreakPadによって生成されたダンプファイルを、スタックオフセット値であるシンボルに復元するために使用されます。

これらの2つのツールは、BreakPadソースコードのコンパイル時に生成されます。

コンパイル後、minidump_stackwalkツールが生成されます。コンパイルしたくない生徒がいる場合は、AndroidStudio自体もこのツールを提供します。

このminidump_stackwalkプログラムはAndroidStudioディレクトリにも存在するため、取り出して直接使用できます。コンパイルしたくない場合は、ディレクトリに移動して直接取り出してください。Macのパスは次のとおりです。

/Applications/Android Studio.app/Contents/bin/lldb/bin/minidump_stackwalk

2.3.2BreakPadキャプチャの原則

上記のことから、アプリケーションがNEでクラッシュすると、BreakPadはNEに対応するミニダンプファイルをローカルに書き込み、同時にアプリケーション層にコールバックすることがわかります。アプリケーション層はこのための処理を行うことができます。クラッシュして統計をキャプチャする効果を実現します。minidumpファイルをアップロードした後、minidump_stackwalkツールとaddr2lineツールを使用して実際のスタックを復元できます。概略図は次のとおりです。

AndroidNativeCrashのキャプチャと分析

NEがアプリケーションで発生すると、BreakPadは、次の図に示すように、電話機でローカルにダンプファイルを生成します。

AndroidNativeCrashのキャプチャと分析

上記のファイルでは、アプリケーションでNEが発生したことしかわかりませんが、これらのファイルは実際には読み取り不能であり、解析する必要があります。

以下は、上記で生成されたNEを分析する方法に焦点を当てています。

2.3.3ダンプファイルの解析

1. NEクラッシュのダンプファイルを取得するか、取得したminidump_stackwalkとダンプファイルを同じディレクトリに置くか、そのままにして、パスを入力するときに絶対パスを入力します。

次に、このディレクトリの下のターミナルウィンドウで次のコマンドを実行します。つまり、minidump_stackwalkを使用してダンプファイルを解析し、解析した情報を現在のディレクトリのcrashLog.txtファイルに出力します。

./minidump_stackwalk xxxxxxxx.dmp >crashLog.txt

2.実行後、minidump_stackwalkはNE関連の情報をcrashLog.txtに書き込みます。詳細情報を図に示します。

AndroidNativeCrashのキャプチャと分析

3.解析されたNE情報によると、図の赤いボックスに注意してください。クラッシュが発生したlibbreakpad-core.soで、0x161a0は、クラッシュが  161a0のルート位置からオフセットされた位置で発生したことを示しています。

2.3.4クラッシュスタックを取得する

1.前述のaddr2lineツールを使用すると、クラッシュが発生したsoファイルとオフセットアドレス(0x161a0)に従って、クラッシュのメソッド、行数、およびコールスタックの関係を取得できます。

2.ルートディレクトリのターミナルウィンドウで次のコマンドを実行します。

arm-linux-androideabi-addr2line -C -f -e ${SOPATH} ${Address}
-C -f           //打印错误行数所在的函数名称
-e                //打印错误地址的对应路径及行数
${SOPATH}         //so库路径
${Address}        //需要转换的堆栈错误信息地址,可以添加多个,但是中间要用空格隔开,例如:0x161a0

3.次の図は実際の操作の例です

aarch64-linux-android-addr2line -f -C -e libbreakpad-core.so 0x161a0
Crash()
/Users/njvivo/Documents/project/Breakpad/breakpad-build/src/main/cpp/breakpad.cpp:111

上の図からわかるように、breakpad.cppファイルの111行目でクラッシュが発生しました。関数名はCrash()で、実際のファイルと一致しています。クラッシュコードは次のとおりです。

void Crash() {
    volatile int *a = (int *) (NULL);
    *a = 1; //此处在代码里是111行
}

extern "C"
JNIEXPORT void JNICALL
Java_com_online_breakpad_BreakpadInit_nUpdateLaunchInfo(JNIEnv *env, jobject instance,
                                                        jstring mLaunchInfoStr_) {

    DO_TRY
    {
        Crash();
        const char *mLaunchInfoStr = env->GetStringUTFChars(mLaunchInfoStr_, 0);
        launch_info = (char *) mLaunchInfoStr;
//        env->ReleaseStringUTFChars(mLaunchInfoStr_, mLaunchInfoStr);
    }
    DO_CATCH("updateLaunchInfo");

}

上記に基づいて、NEの詳細なスタック情報は、アプリケーションによって収集されたダンプファイルを介して分析できます。

 3.soシンボルテーブルの抽出

3.1soのシンボルテーブルを抽出する

上記のコンテンツから、シンボルテーブルとも呼ばれるデバッグ情報が含まれていることがわかります。これらのデバッグ情報を個別に分離するにはどうすればよいですか?ndkは関連ツールも提供します。

aarch64架构
/Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-objdump
arm架构
/Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-android-objdump

コマンドの実行方法は次のとおりです。このコマンドを使用すると、その中のデバッグ情報をファイルに抽出できます。

promote:~ njvivo$ aarch64-linux-android-objdump -S libbreakpad-core.so > breakpad.asm

3.2シンボルテーブル分析

3.2.1直接分析

次の図は、出力シンボルテーブルファイルを示しています。上のログと下のシンボルテーブルファイルを組み合わせて、スタックを分析することもできます。

ログに示されているように、クラッシュアドレスは161a0であり、161a0に対応するコードは* a = 1であることが示されています。上記の分析から、クラッシュがbreakpad.cppの111行目にあることがすでにわかっています。は* a = 1です。場所は正確に予想どおりです。

backtrace:
#00 pc 00000000000161a0 /data/app/com.android.necase-lEp0warh8FqicyY1YqGXXA==/lib/arm64/libbreakpad-core.so (Java_com_online_breakpad_BreakpadInit_nUpdateLaunchInfo+16)
#01 pc 00000000000090cc /data/app/com.android.necase-lEp0warh8FqicyY1YqGXXA==/oat/arm64/base.odex (offset 0x9000)

AndroidNativeCrashのキャプチャと分析

3.2.2ツール分析

Googleは、シンボルテーブルとログを組み合わせてスタックを直接分析できるPythonツールを提供しています。Pythonツールへのアクセスhttps://code.google.com/archive/p/android-ndk-stacktrace-analyzer/ をダウンロードできます。

コマンドを実行することにより、関連するスタックを解析できます。このツールはサーバー側でのバッチ分析に使用できるため、ここでは詳しく説明しません。

python parse_stack.py <asm-file> <logcat-file>

3.2.3オフセット位置の簡単な分析

上記の記事では、オフセット位置の概念について説明しましたが、それについてはよくわかりませんが、大まかに概念があります。Cコードにはルート位置コードがあり、コードの各行にはルートコードに対するオフセット位置があります。

上記の例に示されているように、ログにステートメントの行があり(Java_com_online_breakpad_BreakpadInit_nUpdateLaunchInfo + 16)、+ 16は、nUpdateLaunchInfoメソッドに対する相対的な位置が16オフセットされていることを意味します。

上の図からわかるように、nUpdateLaunchInfoメソッドの位置は16190、オフセット16、つまり16190 + 10(10進数の16が16進数に変換された後の10)= 161a0であり、これはログ出力と同じです。

 4、まとめ

上記はこの記事のすべての内容です。主に、AndroidでのNEのクラッシュ、キャプチャおよび分析ソリューションなど、その基本的な知識について簡単に説明します。このドキュメントがNEに関連する小規模なパートナーに役立つことを願っています。また、フォローアップのCrashSDKは、関連するNEの分析機能もサポートします。

著者:vivo-マリ

おすすめ

転載: blog.51cto.com/14291117/2635609