如何在 SIGSEGV 上使用 _Unwind_Backtrace 获取 fullstacktrace

发布于 2024-11-13 08:44:02 字数 1255 浏览 5 评论 0原文

我通过代码处理 SIGSEGV:

int C()
{
  int *i = NULL;
  *i = 10; // Crash there
}

int B()
{
  return C();
}

int A()
{
   return B();
}

int main(void)
{
  struct sigaction handler;
  memset(&handler,0,sizeof(handler));
  handler.sa_sigaction = handler_func;
  handler.sa_flags = SA_SIGINFO;
  sigaction(SIGSEGV,&handler,NULL);
  return(C());
}

其中处理程序代码是:

static int handler_func(int signal, siginfo_t info, void* rserved)
{
  const void* stack[MAX_DEPTH];
  StackCrowlState state;
  state.addr = stack;
  state.count = MAX_DEPTH;

  _Unwind_Reason_Code code = _Unwind_Backtrace(trace_func,&state);
  printf("Stack trace count: %d, code: %d\n",MAX_DEPTH - state.count, code);
  
  kill(getpid(),SIGKILL);
}

static _Unwind_Reason_Code trace_func(void* context, void* arg)
{
  StackCrowlState *state = (StackCrowlState *)arg;
  if(state->count>0)
  {
     void *ip = (void *)_Unwind_GetIP(context);
     if(ip)
     {
       state->addr[0] = ip;
       state->count--;
       state->addr++;
     }
  }
  return(_URC_NO_REASON);
}

但是 trace_func 仅调用一次,并且仅在 _Unwind_Backtrace 调用时显示。是否可以使用 _Unwind_Backtrace 获取导致 SIGSEGV 信号的代码的堆栈跟踪?

I handle SIGSEGV by code:

int C()
{
  int *i = NULL;
  *i = 10; // Crash there
}

int B()
{
  return C();
}

int A()
{
   return B();
}

int main(void)
{
  struct sigaction handler;
  memset(&handler,0,sizeof(handler));
  handler.sa_sigaction = handler_func;
  handler.sa_flags = SA_SIGINFO;
  sigaction(SIGSEGV,&handler,NULL);
  return(C());
}

Where handler code are:

static int handler_func(int signal, siginfo_t info, void* rserved)
{
  const void* stack[MAX_DEPTH];
  StackCrowlState state;
  state.addr = stack;
  state.count = MAX_DEPTH;

  _Unwind_Reason_Code code = _Unwind_Backtrace(trace_func,&state);
  printf("Stack trace count: %d, code: %d\n",MAX_DEPTH - state.count, code);
  
  kill(getpid(),SIGKILL);
}

static _Unwind_Reason_Code trace_func(void* context, void* arg)
{
  StackCrowlState *state = (StackCrowlState *)arg;
  if(state->count>0)
  {
     void *ip = (void *)_Unwind_GetIP(context);
     if(ip)
     {
       state->addr[0] = ip;
       state->count--;
       state->addr++;
     }
  }
  return(_URC_NO_REASON);
}

But trace_func where called only once, and shows only on _Unwind_Backtrace calls. Is it possible to get stacktrace of code which cause SIGSEGV signal using _Unwind_Backtrace?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

守不住的情 2024-11-20 08:44:02

如果您想特别使用_Unwind_Context(),您可以这样做(该代码是32位ARM特定的):


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;

        // Reset the Thumb bit, if it is set.
        const uintptr_t thumb_bit = 1;
        ip &= ~thumb_bit;

        // Ignore null addresses.
        // They sometimes happen when using _Unwind_Backtrace()
        // with the compiler optimizations,
        // when the Link Register is overwritten by the inner
        // stack frames.
        if (ip == 0)
            return true;

        // Ignore duplicate addresses.
        // They sometimes happen when using _Unwind_Backtrace()
        // with the compiler optimizations,
        // because we both add the second address from the Link Register
        // in ProcessRegisters() and receive the same address
        // in UnwindBacktraceCallback().
        if (address_count > 0 && ip == addresses[address_count - 1])
            return true;

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

void ProcessRegisters(
        _Unwind_Context* unwind_context, BacktraceState* state) {
    assert(state);
    assert(unwind_context);

    const ucontext_t* signal_ucontext = state->signal_ucontext;
    assert(signal_ucontext);

    const sigcontext* signal_mcontext = &(signal_ucontext->uc_mcontext);
    assert(signal_mcontext);

    _Unwind_SetGR(unwind_context, REG_R0,  signal_mcontext->arm_r0);
    _Unwind_SetGR(unwind_context, REG_R1,  signal_mcontext->arm_r1);
    _Unwind_SetGR(unwind_context, REG_R2,  signal_mcontext->arm_r2);
    _Unwind_SetGR(unwind_context, REG_R3,  signal_mcontext->arm_r3);
    _Unwind_SetGR(unwind_context, REG_R4,  signal_mcontext->arm_r4);
    _Unwind_SetGR(unwind_context, REG_R5,  signal_mcontext->arm_r5);
    _Unwind_SetGR(unwind_context, REG_R6,  signal_mcontext->arm_r6);
    _Unwind_SetGR(unwind_context, REG_R7,  signal_mcontext->arm_r7);
    _Unwind_SetGR(unwind_context, REG_R8,  signal_mcontext->arm_r8);
    _Unwind_SetGR(unwind_context, REG_R9,  signal_mcontext->arm_r9);
    _Unwind_SetGR(unwind_context, REG_R10, signal_mcontext->arm_r10);
    _Unwind_SetGR(unwind_context, REG_R11, signal_mcontext->arm_fp);
    _Unwind_SetGR(unwind_context, REG_R12, signal_mcontext->arm_ip);
    _Unwind_SetGR(unwind_context, REG_R13, signal_mcontext->arm_sp);
    _Unwind_SetGR(unwind_context, REG_R14, signal_mcontext->arm_lr);
    _Unwind_SetGR(unwind_context, REG_R15, signal_mcontext->arm_pc);

    // Program Counter register aka Instruction Pointer will contain
    // the address of the instruction where the crash happened.
    // UnwindBacktraceCallback() will not supply us with it.
    state->AddAddress(signal_mcontext->arm_pc);

    // UnwindBacktraceCallback() does not always supply us with
    // the return address of the frame where the crash happened.
    // Sometimes Link Register will contain this address
    // (noticed when compiling with Clang without optimization),
    // but LR may also contain address of some previously visitied frame
    // (noticed when compiling with GCC without optimization),
    // or LR may contain null address
    // (noticed when compiling with Clang with optimization).
    // These heuristics are unreliable.
#if __clang__
    state->AddAddress(signal_mcontext->arm_lr);
#endif
}

_Unwind_Reason_Code UnwindBacktraceCallback(
        struct _Unwind_Context* unwind_context, void* state_voidp) {
    assert(unwind_context);
    assert(state_voidp);

    BacktraceState* state = (BacktraceState*)state_voidp;
    assert(state);

    // On the first UnwindBacktraceCallback() call,
    // set registers to _Unwind_Context and BacktraceState.
    if (state->address_count == 0) {
        ProcessRegisters(unwind_context, state);
        return _URC_NO_REASON;
    }

    uintptr_t ip = _Unwind_GetIP(unwind_context);
    bool ok = state->AddAddress(ip);
    if (!ok)
        return _URC_END_OF_STACK;

    return _URC_NO_REASON;
}

void CaptureBacktrace(BacktraceState* state) {
    assert(state);
    _Unwind_Backtrace(UnwindBacktraceCallback, state);
}

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);
    CaptureBacktrace(&backtrace_state);
    // Do something with the backtrace - print, save to file, etc.
}

但我建议您不要使用_Unwind_Context() code>,而是使用针对 32 位 ARM 的预编译 libunwind,与现代 Android NDK 捆绑在一起(位于sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libunwind.a)以及所有 LLVM。您必须使用 libc++ (LLVM STL)。我的答案演示了如何做到这一点,您必须结合此处的示例。

https://stackoverflow.com/a/50027799/1016580

如果您使用 libstdc++ (GNU STL),则可以使用Dar Hoo 的解决方案:

https://stackoverflow.com/a/48593413/1016580

If you want to use particularly _Unwind_Context(), you can do it like this (the code is 32-bit ARM specific):


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;

        // Reset the Thumb bit, if it is set.
        const uintptr_t thumb_bit = 1;
        ip &= ~thumb_bit;

        // Ignore null addresses.
        // They sometimes happen when using _Unwind_Backtrace()
        // with the compiler optimizations,
        // when the Link Register is overwritten by the inner
        // stack frames.
        if (ip == 0)
            return true;

        // Ignore duplicate addresses.
        // They sometimes happen when using _Unwind_Backtrace()
        // with the compiler optimizations,
        // because we both add the second address from the Link Register
        // in ProcessRegisters() and receive the same address
        // in UnwindBacktraceCallback().
        if (address_count > 0 && ip == addresses[address_count - 1])
            return true;

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

void ProcessRegisters(
        _Unwind_Context* unwind_context, BacktraceState* state) {
    assert(state);
    assert(unwind_context);

    const ucontext_t* signal_ucontext = state->signal_ucontext;
    assert(signal_ucontext);

    const sigcontext* signal_mcontext = &(signal_ucontext->uc_mcontext);
    assert(signal_mcontext);

    _Unwind_SetGR(unwind_context, REG_R0,  signal_mcontext->arm_r0);
    _Unwind_SetGR(unwind_context, REG_R1,  signal_mcontext->arm_r1);
    _Unwind_SetGR(unwind_context, REG_R2,  signal_mcontext->arm_r2);
    _Unwind_SetGR(unwind_context, REG_R3,  signal_mcontext->arm_r3);
    _Unwind_SetGR(unwind_context, REG_R4,  signal_mcontext->arm_r4);
    _Unwind_SetGR(unwind_context, REG_R5,  signal_mcontext->arm_r5);
    _Unwind_SetGR(unwind_context, REG_R6,  signal_mcontext->arm_r6);
    _Unwind_SetGR(unwind_context, REG_R7,  signal_mcontext->arm_r7);
    _Unwind_SetGR(unwind_context, REG_R8,  signal_mcontext->arm_r8);
    _Unwind_SetGR(unwind_context, REG_R9,  signal_mcontext->arm_r9);
    _Unwind_SetGR(unwind_context, REG_R10, signal_mcontext->arm_r10);
    _Unwind_SetGR(unwind_context, REG_R11, signal_mcontext->arm_fp);
    _Unwind_SetGR(unwind_context, REG_R12, signal_mcontext->arm_ip);
    _Unwind_SetGR(unwind_context, REG_R13, signal_mcontext->arm_sp);
    _Unwind_SetGR(unwind_context, REG_R14, signal_mcontext->arm_lr);
    _Unwind_SetGR(unwind_context, REG_R15, signal_mcontext->arm_pc);

    // Program Counter register aka Instruction Pointer will contain
    // the address of the instruction where the crash happened.
    // UnwindBacktraceCallback() will not supply us with it.
    state->AddAddress(signal_mcontext->arm_pc);

    // UnwindBacktraceCallback() does not always supply us with
    // the return address of the frame where the crash happened.
    // Sometimes Link Register will contain this address
    // (noticed when compiling with Clang without optimization),
    // but LR may also contain address of some previously visitied frame
    // (noticed when compiling with GCC without optimization),
    // or LR may contain null address
    // (noticed when compiling with Clang with optimization).
    // These heuristics are unreliable.
#if __clang__
    state->AddAddress(signal_mcontext->arm_lr);
#endif
}

_Unwind_Reason_Code UnwindBacktraceCallback(
        struct _Unwind_Context* unwind_context, void* state_voidp) {
    assert(unwind_context);
    assert(state_voidp);

    BacktraceState* state = (BacktraceState*)state_voidp;
    assert(state);

    // On the first UnwindBacktraceCallback() call,
    // set registers to _Unwind_Context and BacktraceState.
    if (state->address_count == 0) {
        ProcessRegisters(unwind_context, state);
        return _URC_NO_REASON;
    }

    uintptr_t ip = _Unwind_GetIP(unwind_context);
    bool ok = state->AddAddress(ip);
    if (!ok)
        return _URC_END_OF_STACK;

    return _URC_NO_REASON;
}

void CaptureBacktrace(BacktraceState* state) {
    assert(state);
    _Unwind_Backtrace(UnwindBacktraceCallback, state);
}

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);
    CaptureBacktrace(&backtrace_state);
    // Do something with the backtrace - print, save to file, etc.
}

But I am advising you to not use _Unwind_Context(), but instead use precompiled libunwind for 32-bit ARM, bundled with modern Android NDKs (at sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libunwind.a) and with all LLVM. You will have to use use libc++ (LLVM STL). How to do it, is demonstrated in this my answer, you'd have to combine the examples here.

https://stackoverflow.com/a/50027799/1016580

If you use libstdc++ (GNU STL), you could use the Dar Hoo's solution:

https://stackoverflow.com/a/48593413/1016580

作业与我同在 2024-11-20 08:44:02

您想从信号触发函数进行回溯,但您却从信号处理函数进行回溯。这是两个不同的堆栈。 (注意,sigaction中的SA_ONSTACK标志与您的问题无关。)

要查找触发函数的堆栈指针,请使用处理程序的第三个参数,即void *rserved。您可以参考这个问题的答案:获取从信号处理程序保存的指令指针地址

You want to backtrace from the signal triggering function, but you backtrace from the signal handler function. That's two different stacks. (Note, the SA_ONSTACK flag in sigaction is irrelevant to your question.)

To find the stack pointer of the of the triggering function, use the third parameter of the handler, i.e. void *rserved. You can reference to the answer in this question: Getting the saved instruction pointer address from a signal handler

若无相欠,怎会相见 2024-11-20 08:44:02

最好使用 backtrace 和 backtrace_symbols_fd 从信号处理程序获取堆栈跟踪。

better you use backtrace and backtrace_symbols_fd to get a stacktrace from a signal handler.

花开雨落又逢春i 2024-11-20 08:44:02

您可以使用 __gnu_Unwind_Backtrace 来代替。 ARM32 示例:

typedef struct
{
    uintptr_t r[16];
} core_regs;

typedef struct
{
    uintptr_t demand_save_flags;
    core_regs   core;
} phase2_vrs;

extern "C" _Unwind_Reason_Code __gnu_Unwind_Backtrace(_Unwind_Trace_Fn trace, void * trace_argument, phase2_vrs * entry_vrs);

int AndroidGetBackTraceWithContext(VOID **stack, UINT32 size, ucontext_t *ctx)
{
    ANDROID_UNWIND_STATE state;
    state.count = size;
    state.stack = stack;

    // First call stack is current pc
    state.stack[0] = (VOID *)ctx->uc_mcontext.arm_pc;
    state.stack++;
    state.count--;

    phase2_vrs pre_signal_state;
    pre_signal_state.demand_save_flags = 0;
    pre_signal_state.core = *reinterpret_cast<const core_regs*>(&(ctx->uc_mcontext.arm_r0));

    // Return value is of no use and might be wrong on some systems
    __gnu_Unwind_Backtrace(DmpAndroidUnwindCallback, &state, &pre_signal_state);

    return size - state.count;
}

You may use __gnu_Unwind_Backtrace instead. Example for ARM32:

typedef struct
{
    uintptr_t r[16];
} core_regs;

typedef struct
{
    uintptr_t demand_save_flags;
    core_regs   core;
} phase2_vrs;

extern "C" _Unwind_Reason_Code __gnu_Unwind_Backtrace(_Unwind_Trace_Fn trace, void * trace_argument, phase2_vrs * entry_vrs);

int AndroidGetBackTraceWithContext(VOID **stack, UINT32 size, ucontext_t *ctx)
{
    ANDROID_UNWIND_STATE state;
    state.count = size;
    state.stack = stack;

    // First call stack is current pc
    state.stack[0] = (VOID *)ctx->uc_mcontext.arm_pc;
    state.stack++;
    state.count--;

    phase2_vrs pre_signal_state;
    pre_signal_state.demand_save_flags = 0;
    pre_signal_state.core = *reinterpret_cast<const core_regs*>(&(ctx->uc_mcontext.arm_r0));

    // Return value is of no use and might be wrong on some systems
    __gnu_Unwind_Backtrace(DmpAndroidUnwindCallback, &state, &pre_signal_state);

    return size - state.count;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文