【问题标题】:Backtrace info different on macOS v.s. LinuxmacOS 与 macOS 上的回溯信息不同。 Linux
【发布时间】:2018-10-07 22:22:52
【问题描述】:

这是 C++ 中的测试代码(改编自 StackOverflow 帖子,但我找不到):

#include <signal.h>    
#include <stdio.h>
#include <stdlib.h>

#include <execinfo.h>
#include <unistd.h>

void handler(int sig) {
  void *array[10];
  size_t size;

  // get void*'s for all entries on the stack
  size = backtrace(array, 10);

  // print out all the frames to stderr
  fprintf(stderr, "Error: signal %d:\n", sig);
  backtrace_symbols_fd(array, size, STDERR_FILENO);
  exit(1);
}

void baz() {
  int *foo = (int*)-1; // make a bad pointer
  printf("%d\n", *foo);       // causes segfault
}

void bar() { baz(); }
void foo() { bar(); }


int main(int argc, char **argv) {
  signal(SIGSEGV, handler);   // install our handler
  foo(); // this will call foo, bar, and baz.  baz segfaults.
}

这是编译和链接步骤(在 macOS 上,g++ 实际上是 clang++):

g++ backtrace_example.cc -c -O0
g++ -rdynamic backtrace_example.o -o bt_example

这是印在 macOS 上的回溯信息:

$ ./bt_example
Error: signal 11:
0   bt_example                          0x000000010dd39dbf handler + 31
1   libsystem_platform.dylib            0x00007fff5e0b7b3d _sigtramp + 29
2   ???                                 0x0000000117f6a7c7 0x0 + 4697008071
3   bt_example                          0x000000010dd39eb9 _Z3barv + 9
4   bt_example                          0x000000010dd39ec9 _Z3foov + 9
5   bt_example                          0x000000010dd39efe main + 46
6   libdyld.dylib                       0x00007fff5dece085 start + 1
7   ???                                 0x0000000000000001 0x0 + 1

这是在 Linux 上打印的回溯信息:

$ ./bt_example
Error: signal 11:
./bt_example(handler+0x2b)[0x400982]
/lib/x86_64-linux-gnu/libc.so.6(+0x354b0)[0x7f2aefc534b0]
./bt_example(_Z3bazv+0x14)[0x4009db]
./bt_example(_Z3barv+0x9)[0x4009fa]
./bt_example(_Z3foov+0x9)[0x400a06]
./bt_example(main+0x23)[0x400a2c]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f2aefc3e830]
./bt_example(_start+0x29)[0x4008a9]

除了格式上的不同之外,还有一个关键的区别:macOS版本根本没有提到函数baz(),但很明显segfault是由baz()引起的。

为什么?这是 macOS 实现 backtrace_symbol_fd() 的缺陷,还是有意(如果是,如何)?

参考:glibc's backtraces 上的 GNU 文档。 macOS 是一个不同的操作系统,拥有自己的 C 库,但自 10.5 起在这方面具有相同的回溯 API。

【问题讨论】:

  • 作为预防性说明,我知道它在 C++ 代码中使用 C API,但决定语言的不是我...

标签: linux macos glibc backtrace


【解决方案1】:

首先,您在handler 中调用的任何函数都不能有效地从信号处理程序中调用。因此,您完全处于未定义的行为领域。

其次,这并不是backtrace 中的缺陷,因为这只是两个操作系统中信号处理机制的不同。 macOS 在调用信号处理程序时会临时更改堆栈。如果信号处理程序返回(并且处理程序甚至有机会修改它),它已经保存了足够的上下文信息来恢复它。你可以看到实现here。

【讨论】:

  • 为什么无效以及如何解决?
  • 为什么在信号处理程序中调用这些函数无效?因为 POSIX 是这么说的。因为信号可以在线程持有库中的锁时中断线程;或者在对内部状态进行复杂修改的过程中,使其暂时不一致;等等。请参阅sigaction 的手册页。
猜你喜欢
  • 2018-06-11
  • 2019-01-30
  • 1970-01-01
  • 2019-12-31
  • 1970-01-01
  • 2012-12-03
  • 2017-10-28
  • 2018-10-23
  • 2012-04-28
相关资源
最近更新 更多