web-dev-qa-db-ja.com

Android NDK:バックトレースの取得

NDK経由でAndroid=で動作するネイティブアプリケーションを開発しています。クラッシュが発生したときにbacktrace()関数を呼び出す必要があります。問題はないことです。 <execinfo.h> NDKの場合。

バックトレースを取得する他の方法はありますか?

43
givi

backtrace()は非標準のGlibc拡張機能であり、それでもARMで多少不安定です(すべてを-funwind-tablesで構築する必要があります。新しいGlibc?)

私の知る限り、この関数はAndroidが使用するBionic Cライブラリには含まれていません。

Glibcバックトレースのソースをプロジェクトにプルしてから、アンワインドテーブルを使用して興味深いものを再構築することもできますが、私には大変な作業のように思えます。

デバッグ情報がある場合は、プロセスにアタッチするスクリプトを使用してGDBを起動し、そのようにバックトレースを出力できますが、GDBがAndroid(Androidは基本的にLinuxであるため、IDは問題ありません。インストールの詳細に問題がある可能性がありますか?)何らかの方法でコアをダンプし(Bionicはそれをサポートしていますか?).

19
ams

C/C++(ネイティブ)とJavaの両方を含む、非常に詳細なスタックトレースを取得するためのクレイジーな1行の方法を次に示します。JNIを悪用する

env->FindClass(NULL);

アプリがデバッグをコンパイルするか、AndroidのCheckJNIを使​​用する限り、この誤った呼び出しはAndroidの組み込みJNIチェッカーをトリガーし、コンソールに(「アート」ログソースから)豪華なスタックトレースを生成します。このスタックトレースは、Androidのlibart.so私たちのような低レベルのNDKユーザーには簡単に入手できない最新のテクノロジーと機能をすべて使用します。

デバッグがコンパイルされていないアプリでも、CheckJNIを有効にできます。詳細については、 このGoogle FAQ をご覧ください。

このトリックがSIGSEGVハンドラーから動作するかどうかはわかりません(SIGSEGVから間違ったスタックのスタックトレースを取得するか、アートがまったくトリガーされないかもしれません)が、試してみる価値はあります。

コードでスタックトレースを使用できるようにするソリューションが必要な場合(たとえば、ネット経由で送信したりログに記録したりできるようにするため)、この同じ質問の他の回答を参照してください。

6
Louis Semprini

CallStackを使用できます。

#include <utils/CallStack.h>

void log_backtrace()
{
    CallStack cs;
    cs.update(2);
    cs.dump();
}

結果は c++filt または同様のもの:

D/CallStack( 2277): #08  0x0x40b09ac8: <_ZN7Android15TimedEventQueue11threadEntryEv>+0x0x40b09961
D/CallStack( 2277): #09  0x0x40b09b0c: <_ZN7Android15TimedEventQueue13ThreadWrapperEPv>+0x0x40b09af9

you @ work> $ c ++ filt _ZN7Android15TimedEventQueue11threadEntryEv _ZN7Android15TimedEventQueue13ThreadWrapperEPv

    Android::TimedEventQueue::threadEntry()
    Android::TimedEventQueue::ThreadWrapper(void*)
5

いくつかの(たとえば2-5)最上位の呼び出しフレームが必要で、GCCが十分に新しい場合は、いくつかの 戻りアドレスまたはフレームアドレス組み込み関数を使用することを検討できます。

(しかし、私はAndroidについてあまり知らないので、間違っている可能性があります)

Libunwindを使用して32ビットARMでバックトレースをキャプチャする方法は次のとおりです。libunwindは、modern Android NDK(NDK r16bなど)にバンドルされています。


// Android NDK r16b contains "libunwind.a" for armeabi-v7a ABI.
// This library is even silently linked in by the ndk-build,
// so we don't have to add it manually in "Android.mk".
// We can use this library, but we need matching headers,
// namely "libunwind.h" and "__libunwind_config.h".
// For NDK r16b, the headers can be fetched here:
// https://Android.googlesource.com/platform/external/libunwind_llvm/+/ndk-r16/include/
#include "libunwind.h"

struct BacktraceState {
    const ucontext_t*   signal_ucontext;
    size_t              address_count = 0;
    static const size_t address_count_max = 30;
    uintptr_t           addresses[address_count_max] = {};

    BacktraceState(const ucontext_t* ucontext) : signal_ucontext(ucontext) {}

    bool AddAddress(uintptr_t ip) {
        // No more space in the storage. Fail.
        if (address_count >= address_count_max)
            return false;

        // Add the address to the storage.
        addresses[address_count++] = ip;
        return true;
    }
};

void CaptureBacktraceUsingLibUnwind(BacktraceState* state) {
    assert(state);

    // Initialize unw_context and unw_cursor.
    unw_context_t unw_context = {};
    unw_getcontext(&unw_context);
    unw_cursor_t  unw_cursor = {};
    unw_init_local(&unw_cursor, &unw_context);

    // Get more contexts.
    const ucontext_t* signal_ucontext = state->signal_ucontext;
    assert(signal_ucontext);
    const sigcontext* signal_mcontext = &(signal_ucontext->uc_mcontext);
    assert(signal_mcontext);

    // Set registers.
    unw_set_reg(&unw_cursor, UNW_ARM_R0,  signal_mcontext->arm_r0);
    unw_set_reg(&unw_cursor, UNW_ARM_R1,  signal_mcontext->arm_r1);
    unw_set_reg(&unw_cursor, UNW_ARM_R2,  signal_mcontext->arm_r2);
    unw_set_reg(&unw_cursor, UNW_ARM_R3,  signal_mcontext->arm_r3);
    unw_set_reg(&unw_cursor, UNW_ARM_R4,  signal_mcontext->arm_r4);
    unw_set_reg(&unw_cursor, UNW_ARM_R5,  signal_mcontext->arm_r5);
    unw_set_reg(&unw_cursor, UNW_ARM_R6,  signal_mcontext->arm_r6);
    unw_set_reg(&unw_cursor, UNW_ARM_R7,  signal_mcontext->arm_r7);
    unw_set_reg(&unw_cursor, UNW_ARM_R8,  signal_mcontext->arm_r8);
    unw_set_reg(&unw_cursor, UNW_ARM_R9,  signal_mcontext->arm_r9);
    unw_set_reg(&unw_cursor, UNW_ARM_R10, signal_mcontext->arm_r10);
    unw_set_reg(&unw_cursor, UNW_ARM_R11, signal_mcontext->arm_fp);
    unw_set_reg(&unw_cursor, UNW_ARM_R12, signal_mcontext->arm_ip);
    unw_set_reg(&unw_cursor, UNW_ARM_R13, signal_mcontext->arm_sp);
    unw_set_reg(&unw_cursor, UNW_ARM_R14, signal_mcontext->arm_lr);
    unw_set_reg(&unw_cursor, UNW_ARM_R15, signal_mcontext->arm_pc);

    unw_set_reg(&unw_cursor, UNW_REG_IP, signal_mcontext->arm_pc);
    unw_set_reg(&unw_cursor, UNW_REG_SP, signal_mcontext->arm_sp);

    // unw_step() does not return the first IP,
    // the address of the instruction which caused the crash.
    // Thus let's add this address manually.
    state->AddAddress(signal_mcontext->arm_pc);

    // Unwind frames one by one, going up the frame stack.
    while (unw_step(&unw_cursor) > 0) {
        unw_Word_t ip = 0;
        unw_get_reg(&unw_cursor, UNW_REG_IP, &ip);

        bool ok = state->AddAddress(ip);
        if (!ok)
            break;
    }
}

void SigActionHandler(int sig, siginfo_t* info, void* ucontext) {
    const ucontext_t* signal_ucontext = (const ucontext_t*)ucontext;
    assert(signal_ucontext);

    BacktraceState backtrace_state(signal_ucontext);
    CaptureBacktraceUsingLibUnwind(&backtrace_state);

    exit(0);
}

上記のメソッドを含む3つのバックトレースメソッドを実装したサンプルバックトレーステストアプリを次に示します。

https://github.com/alexeikh/Android-ndk-backtrace-test

0