天天看點

NDK 開發中快速定位 Crash 問題

作者:位元組流動

來源:

https://blog.csdn.net/Kennethdroid/article/details/86418725

寫兩行代碼,先制造一個簡單的 crash 場景。

SDK 頭檔案:

#ifndef NDKSAMPLE_ALGORITHM_H
#define NDKSAMPLE_ALGORITHM_H
#include<android/log.h>
#define  LOGCATE(...)  __android_log_print(ANDROID_LOG_ERROR,"HaoHao",__VA_ARGS__)

class Algorithm
{
public:
    Algorithm();
    ~Algorithm();

    int Init();
    int UnInit();

    int Process(const char* input, char* output);

};
#endif //NDKSAMPLE_ALGORITHM_H
      

SDK 實作:

#include <cstring>
#include "Algorithm.h"

Algorithm::Algorithm()
{
    LOGCATE("Algorithm::Algorithm()");
}

Algorithm::~Algorithm()
{
    LOGCATE("Algorithm::~Algorithm()");
}

int Algorithm::Init()
{
    LOGCATE("Algorithm::Init()");
    return 0;
}

int Algorithm::UnInit()
{
    LOGCATE("Algorithm::Init()");
    return 0;
}

int Algorithm::Process(const char *input, char *output)
{
    LOGCATE("Algorithm::Process()");
    // 簡單拼接,沒有檢查指針
    strcpy(output, input);
    strcat(output, "Process Done.");
    return 0;
}      

JNI 實作:

#include <jni.h>
#include <string>
#include "Algorithm.h"

extern "C" JNIEXPORT jstring JNICALL
Java_com_haohao_ndk_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_haohao_ndk_MainActivity_doProcess(JNIEnv *env, jobject instance, jstring input_)
{
    const char *input = env->GetStringUTFChars(input_, 0);
    LOGCATE("MainActivity_doProcess input = %s", input);
    char output[1024];

    //調用 SDK
    Algorithm* pAlgorithm = new Algorithm();
    pAlgorithm->Init();
    //pAlgorithm->Process(input, output);
    //傳入空指針,制造 crash
    pAlgorithm->Process(input, NULL);
    pAlgorithm->UnInit();
    delete pAlgorithm;

    LOGCATE("MainActivity_doProcess output = %s", output);

    env->ReleaseStringUTFChars(input_, input);

    return env->NewStringUTF(output);
}      

Java 層:

// Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(doProcess("Hello "));
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    public native String doProcess(String input);      

查找線程和程序 ID

NDK 開發中快速定位 Crash 問題

圖中 pid 表示程序 ID ,tid 表示線程 ID 。在多線程場景中,這種方式非常有用,通過搜尋 logcat 抓到的日志,能幫你快速定位在某個線程中代碼執行到哪個位置出現了問題。

用 addr2line 工具定位

addr2line 顧名思義,是記憶體位址轉換成代碼行号的工具。 NDK 中自帶 addr2line ,一般位于以下目錄中:

//32bit
D:\NDK\android-ndk-r16\toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86_64\bin\arm-linux-androideabi-addr2line.exe
//64bit
D:\NDK\android-ndk-r16\toolchains\aarch64-linux-android-4.9\prebuilt\windows-x86_64\bin\aarch64-linux-android-addr2line.exe
      

執行 crash 代碼得到的 crash log 。

01-03 16:17:14.023 17255 17255 E HaoHao  : MainActivity_doProcess input = Hello 
01-03 16:17:14.023 17255 17255 E HaoHao  : Algorithm::Algorithm()
01-03 16:17:14.023 17255 17255 E HaoHao  : Algorithm::Init()
01-03 16:17:14.023 17255 17255 E HaoHao  : Algorithm::Process()
--------- beginning of crash
01-03 16:17:14.024 17255 17255 F libc    : Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 17255 (com.haohao.ndk)
01-03 16:17:14.025  3315  3315 W         : debuggerd: handling request: pid=17255 uid=10217 gid=10217 tid=17255
01-03 16:17:14.152 17273 17273 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
01-03 16:17:14.153 17273 17273 F DEBUG   : Build fingerprint: 'samsung/greatltexx/greatlte:7.1.1/NMF26X/N950FXXE1AQH9:eng/test-keys'
01-03 16:17:14.153 17273 17273 F DEBUG   : Revision: '7'
01-03 16:17:14.153 17273 17273 F DEBUG   : ABI: 'arm64'
01-03 16:17:14.154 17273 17273 F DEBUG   : pid: 17255, tid: 17255, name: com.haohao.ndk  >>> com.haohao.ndk <<<
01-03 16:17:14.154 17273 17273 F DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
01-03 16:17:14.154 17273 17273 F DEBUG   :     x0   0000000000000000  x1   0000007ae8573160  x2   0000000000000000  x3   0000206f6c6c6548
01-03 16:17:14.155 17273 17273 F DEBUG   :     x4   6976697463416e69  x5   0000000000008080  x6   0000007aff30b000  x7   0000000000000038
01-03 16:17:14.155 17273 17273 F DEBUG   :     x8   7f7f7f7f7f7f7f7f  x9   000000000000002b  x10  0000007ff909a7a0  x11  0101010101010101
01-03 16:17:14.156 17273 17273 F DEBUG   :     x12  0000000000000030  x13  0000000000000000  x14  0000000000000000  x15  000162586fb83e86
01-03 16:17:14.156 17273 17273 F DEBUG   :     x16  0000007aee180ee8  x17  0000007afdc144c0  x18  0000000012cffb18  x19  0000007afa8cba00
01-03 16:17:14.156 17273 17273 F DEBUG   :     x20  0000007afa296f30  x21  0000007afa8cba00  x22  0000007ff909b4dc  x23  0000007af9491f1c
01-03 16:17:14.156 17273 17273 F DEBUG   :     x24  0000000000000008  x25  00eda7ded23b7e10  x26  0000007afa8cba98  x27  0000000000000002
01-03 16:17:14.156 17273 17273 F DEBUG   :     x28  00eda7ded23b7e10  x29  0000007ff909ad50  x30  0000007aee139f54
01-03 16:17:14.156 17273 17273 F DEBUG   :     sp   0000007ff909ad10  pc   0000007afdc14518  pstate 0000000080000000
01-03 16:17:14.158  3798  4376 D AllAroundSensingService: packageName : com.haohao.ndk    className : com.haohao.ndk.MainActivity
01-03 16:17:14.158  3798  4376 V AllAroundSensingService: setPlatformBrightnessValue 120
01-03 16:17:14.165 17273 17273 F DEBUG   : 
01-03 16:17:14.165 17273 17273 F DEBUG   : backtrace:
01-03 16:17:14.165 17273 17273 F DEBUG   :     #00 pc 000000000001b518  /system/lib64/libc.so (strcpy+88)
01-03 16:17:14.165 17273 17273 F DEBUG   :     #01 pc 0000000000009f50  /data/app/com.haohao.ndk-1/lib/arm64/libnative-lib.so (_ZN9Algorithm7ProcessEPKcPc+80)
01-03 16:17:14.165 17273 17273 F DEBUG   :     #02 pc 00000000000097d4  /data/app/com.haohao.ndk-1/lib/arm64/libnative-lib.so (Java_com_haohao_ndk_MainActivity_doProcess+180)
01-03 16:17:14.165 17273 17273 F DEBUG   :     #03 pc 0000000000286fec  /data/app/com.haohao.ndk-1/oat/arm64/base.odex (offset 0x24a000)      

crash log 中首先列出來了

tid 17255

,

fault addr 0x0

告訴我們是空指針引起的 crash ,然後寄存器 x0 存儲的指針為空再次确認了是空指針引起的 crash 。 以上 backtrace 中,從 #00 到 #03 共 4 行資訊表示 crash 時函數調用關系,調用關系為從下往上,即 #03 調用了 #02 的方法,以此類推, #00 行告訴我們是拷貝字元串時遇到了問題。 通過 “_ZN9Algorithm7ProcessEPKcPc+80” 大緻可以看出哪個函數出了問題,後面的 “80” 并不是指原始代碼中的第 80 出現問題,實際上編譯工具預設在編譯過程中會進行優化和對齊。

addr2line 是通過 pc (程式計數器)值來定位代碼,’-e’ 後加 .so 檔案名, “-f” 表示輸出函數名。實際上從 log 中可以看到 AndroidStudio 自動幫我們做了這件事。

根據 .so 是 32 位還是 64 位選擇對應的 addr2line 工具,執行

aarch64-linux-android-addr2line.exe -e -f

D:\NDK>D:\NDK\android-ndk-r16\toolchains\aarch64-linux-android-4.9\prebuilt\windows-x86_64\bin\aarch64-linux-android-addr2line.exe -e libnative-lib.so -f 0000000000009f50
_ZN9Algorithm7ProcessEPKcPc
E:\NDKWorkspace\NDKSample\app\src\main\cpp/Algorithm.cpp:34      

通過 addr2line 我們定位出了 Algorithm.cpp 檔案第 34 行出錯,對應的函數名是

_ZN9Algorithm7ProcessEPKcPc

,雖然

_ZN9Algorithm7ProcessEPKcPc

大緻可以看出哪個函數出了問題,但是為什麼變成了一串亂碼?原來編譯器在編譯時對函數名按照一定的規則進行了優化,既然規則是一定的,那麼當然也有人做出了還原方法,如

https://demangler.com/
NDK 開發中快速定位 Crash 問題

點選 DEMANGLE 便可以将

_ZN9Algorithm7ProcessEPKcPc

還原成原始方法名

Algorithm::Process(char const*, char*)

另外在使用 addr2line 過程中經常會遇到 “??:?” 或 “??:0” 這種情況,原因就是一般 C/C++ SDK 都會進行添加 map 混淆以及在編譯配置選項中不生成符号表 symbolic 資訊,不過 AndroidStudio 會預設為 so 檔案添加符号表。

NDK 開發系列文章:

「視訊雲技術」你最值得關注的音視訊技術公衆号,每周推送來自阿裡雲一線的實踐技術文章,在這裡與音視訊領域一流工程師交流切磋。  
NDK 開發中快速定位 Crash 問題