我正在为Android(仅限ARM)编写这个,但我相信通用Linux的原理也是如此.
我正在尝试从信号处理程序中捕获堆栈跟踪,以便我可以在应用程序崩溃时记录它.这就是我想出来的用法<unwind.h>.
初始化:
struct sigaction signalhandlerDescriptor;
memset(&signalhandlerDescriptor, 0, sizeof(signalhandlerDescriptor));
signalhandlerDescriptor.sa_flags = SA_SIGINFO;
signalhandlerDescriptor._u._sa_sigaction = signalHandler;
sigaction(SIGSEGV, &signalhandlerDescriptor, 0);
Run Code Online (Sandbox Code Playgroud)
代码本身:
struct BacktraceState
{
void** current;
void** end;
void* pc;
};
inline _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg)
{
BacktraceState* state = static_cast<BacktraceState*>(arg);
state->pc = (void*)_Unwind_GetIP(context);
if (state->pc)
{
if (state->current == state->end)
return _URC_END_OF_STACK;
else
*state->current++ = reinterpret_cast<void*>(state->pc);
}
return _URC_NO_REASON;
}
inline size_t captureBacktrace(void** addrs, size_t max, unsigned long pc)
{
BacktraceState state = {addrs, addrs + max, …Run Code Online (Sandbox Code Playgroud) 我通过代码处理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());
}
Run Code Online (Sandbox Code Playgroud)
处理程序代码在哪里:
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 …Run Code Online (Sandbox Code Playgroud) 我刚刚发现有人正在调用 - 从信号处理程序 - 一个绝对不是我编写的异步信号安全函数.
所以,现在我很好奇:如何避免这种情况再次发生?我希望能够轻松确定我的代码是否在信号处理程序上下文中运行(语言是C,但解决方案不适用于任何语言吗?):
int myfunc( void ) {
if( in_signal_handler_context() ) { return(-1) }
// rest of function goes here
return( 0 );
}
Run Code Online (Sandbox Code Playgroud)
这是在Linux下.希望这不是一个简单的答案,否则我会觉得自己像个白痴.
我一直在研究glibc/nptl的取消点的实现,并将它与POSIX进行比较,除非我弄错了,否则完全错了.使用的基本模型是:
int oldtype = LIBC_ASYNC_CANCEL(); /* switch to asynchronous cancellation mode */
int result = INLINE_SYSCALL(...);
LIBC_CANCEL_RESET(oldtype);
Run Code Online (Sandbox Code Playgroud)
根据POSIX:
在函数调用期间暂停时对取消请求起作用的副作用与单个线程程序中可能在信号中断对函数的调用时看到的副作用相同函数返回[EINTR].任何此类副作用都会在调用任何取消清除处理程序之前发生.
我对这段经文的解读是,如果我打电话open,我可以预期它要么在它无法打开文件之前被取消(连同我的整个线程),要么返回有效的文件描述符或-1和errno值,但从不创建一个新的文件描述符,然后将其丢失到void中.另一方面,取消点的glibc/nptl实现似乎允许竞争条件,其中取消请求恰好在系统调用返回之后但在LIBC_CANCEL_RESET发生之前发生.
我疯了,还是他们的实施真的破了吗?如果是这样,POSIX是否允许这种破坏的行为(除非你手动推迟,这似乎会导致取消完全无法使用),或者他们只是公然忽略了POSIX?
如果这种行为实际上已被破坏,那么在没有这种竞争条件的情况下实施它的正确方法是什么?