Linux 如何在进程信号处理程序(armv7 uclibc)中获得正确的回溯跟踪?
我已经在google上搜索了很多次,为信号处理程序中的backtrace()找到了正确的解决方案,并尝试了几乎所有的方法,但我无法在信号处理程序中成功地获得backtrace-这不是SIGUSR1处理程序Linux 如何在进程信号处理程序(armv7 uclibc)中获得正确的回溯跟踪?,linux,arm,uclibc,Linux,Arm,Uclibc,我已经在google上搜索了很多次,为信号处理程序中的backtrace()找到了正确的解决方案,并尝试了几乎所有的方法,但我无法在信号处理程序中成功地获得backtrace-这不是SIGUSR1处理程序 enable UCLIBC_在UCLIBC config中有_BACKTRACE=y并编译了它 已验证是否已创建libubacktrace.so 使用以下选项编译我的应用程序二进制文件 -g -动力学 -fexception或-funwind表 二进制文件本身似乎被“剥离” 然而,我无法从
- enable UCLIBC_在UCLIBC config中有_BACKTRACE=y并编译了它
- 已验证是否已创建libubacktrace.so
- 使用以下选项编译我的应用程序二进制文件 -g -动力学 -fexception或-funwind表
- 二进制文件本身似乎被“剥离”
1) Makefile中的编译器选项 CFLAGS+=-g-feexceptions-funwind表-Werror$(警告) 2) 代码 代码非常简单
#define CALLSTACK_SIZE 10
static void print_stack(void) {
int i, nptrs;
void *buf[CALLSTACK_SIZE + 1];
char **strings;
nptrs = backtrace(buf, CALLSTACK_SIZE);
printf("%s: backtrace() returned %d addresses\n", __func__, nptrs);
strings = backtrace_symbols(buf, nptrs);
if(strings == NULL) {
printf("%s: no backtrace captured\n", __func__);
return;
}
for(i = 0; i < nptrs; i++) {
printf("%s\n", strings[i]);
}
free(strings);
}
...
static void sigHandler(int signum)
{
printf("%s: signal %d\n", __FUNCTION__, signum);
switch(signum ) {
case SIGUSR2:
// told to quit
print_stack();
break;
default:
break;
}
}
#定义调用堆栈大小10
静态void打印堆栈(void){
int i,NPTR;
void*buf[CALLSTACK_SIZE+1];
字符**字符串;
nptrs=回溯(buf、调用堆栈大小);
printf(“%s:backtrace()返回了%d个地址\n”,\uuuu func\uuuu,nptrs);
字符串=回溯符号(buf、NPTR);
if(strings==NULL){
printf(“%s:未捕获回溯\n”,\uuuu func\uuuu);
返回;
}
对于(i=0;i
请仔细阅读并确认
信号处理程序仅限于调用(直接或间接)异步信号安全函数(实际上,仅限于大多数函数),或者甚至free
都不是异步信号安全函数。因此,您的代码是不正确的:信号处理程序signandler
正在调用printf
,并且间接地(通过print\u stack
)free
,它们不是异步信号安全的
因此,您唯一的选择是使用gdb
调试器
阅读有关POSIX&的更多信息。实际上,信号处理程序可以做的几乎唯一明智的事情是设置一些全局、线程本地或静态的易失性sig_原子\u t
,这必须在其他地方进行测试。它还可以直接将几个字节放入一个文件中,应用程序将在其他地方读取该文件(例如,如果它是GUI应用程序,则在其事件循环中)
您也可以使用Ian Taylor(假设您的程序是使用调试信息编译的,例如使用-g
)。它不能保证在信号处理程序中工作(因为它不仅使用异步信号安全函数),但实际上非常有用
请注意,内核正在为处理信号时设置调用帧(在中)
您还可以使用(特别是在应用程序是单线程的情况下)来拥有备用信号堆栈。我不确定这是否有帮助
如果你有一个事件循环,你可能会考虑使用Linux特定的,并要求你的事件循环到<代码>投票/代码>。对于
SIGTERM
或SIGQUIT
或SIGALRM
来说,这是一个非常有用的技巧。我想在@Basile Starynkevitch的答案中添加一些东西,这太迂腐了。
虽然您的信号处理程序确实不是,但它很有可能在Linux上正常工作,因此,如果您看到结果被打印出来,这并不是导致您无法看到相关堆栈信息的原因
一些更可能出现的问题包括:
-fno省略帧指针
和-fasynchronous unwind tables
调用的代码的堆栈跟踪。因此,未使用正确的编译器标志编译的通常会导致重复或截断的回溯
SIGSEGV
,或者另一个线程使用类似于pthread\u kill
的东西发送特定线程。有关更多信息,请参阅
malloc()
,free()
,等等,但不能使用健全版本的glibc/libgcc调用backtrace
。从中可以看出,backtrace\u symbols\u fd
当前是异步信号安全的。您还可以看到,backtrace
不是。看起来很不安全。但是,请告诉我们为什么适用这些限制:
回溯\u符号\u fd()不调用malloc(3),并且
因此,可以在后一种功能可能发生故障的情况下使用
失败,但请参见注释
后来:
backtrace()和backtrace_符号_fd()不调用malloc()
显式地,但它们是libgcc的一部分,libgcc将被加载
第一次使用时动态地。动态加载通常会触发
打电话给malloc(3)。如果你需要打电话给这两个
不分配内存的函数(例如,在信号处理程序中)
例如),您需要确保事先加载libgcc
快速浏览一下下面的内容
// On linux, especially on ARM, you want to use the sigaction version of this call.
// See my comments below.
static void
handle_signal(int sig)
{
// Check signal type or whatever you want to do.
// ...
void* symbols[100];
int n = backtrace(symbols, 100);
// You could also either call a string formatting routine that you know
// is async-signal-safe or save your backtrace and let another thread know
// that this thread has crashed and the backtrace needs to be printed.
//
write(STDERR_FILENO, "Crash:\n", 7);
backtrace_symbols_fd(symbols, n, STDERR_FILENO);
// In the case of notifying another thread, which is what I do, you would
// do something like this:
//
// threadLocalSymbolCount = backtrace(threadLocalSymbols, 100);
// sem_post() or write() to an eventfd or whatever.
}
int main(int argc, char** argv)
{
void* dummy = NULL;
backtrace(&dummy, 1);
// Setup custom signal handling
// ...
function_that_crashes();
return 0;
}