我通过代码处理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 信号的代码堆栈跟踪?
如果你想特别使用
_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()
,而是使用针对 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 的解决方案:
你想从信号触发函数回溯,但你却从信号处理函数回溯。这是两个不同的堆栈。 (请注意,sigaction 中的 SA_ONSTACK 标志与您的问题无关。)
要查找触发函数的堆栈指针,请使用处理程序的第三个参数,即 void *rserved。你可以参考这个问题的答案:Getting the Saved instructions Pointer Address from a signal handler
最好使用 backtrace 和 backtrace_symbols_fd 从信号处理程序获取堆栈跟踪。
您可以使用
__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;
}