【问题标题】:gdb: set a breakpoint for a SIGBUS handlergdb:为 SIGBUS 处理程序设置断点
【发布时间】:2010-01-11 19:04:41
【问题描述】:

我正在尝试使用 GDB 调试一个简单的停止和复制垃圾收集器(用 C 编写)。 GC 通过处理 SIGBUS 工作。我在 SIGBUS 信号处理程序的顶部设置了一个断点。我已经告诉 GDB 将 SIGBUS 传递给我的程序。但是,它似乎不起作用。

以下程序(内联解释)显示了我的问题的本质:

#include <stdio.h>
#include <sys/mman.h>
#include <assert.h>
#include <signal.h>

#define HEAP_SIZE 4096

unsigned long int *heap;

void gc(int n) {
  signal(SIGBUS, SIG_DFL); // just for debugging
  printf("GC TIME\n");
}

int main () {

  // Allocate twice the required heap size (two semi-spaces)
  heap = mmap(NULL, HEAP_SIZE * 2, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED,
              -1, 0);
  assert (heap != MAP_FAILED);
  // 2nd semi-space is unreadable. Using "bump-pointer allocation", a SIGBUS
  // tells us we are out of space and need to GC.
  void *guard = mmap(heap + HEAP_SIZE, HEAP_SIZE, PROT_NONE, MAP_ANON |
                     MAP_SHARED | MAP_FIXED, -1, 0);
  assert (guard != MAP_FAILED);
  signal(SIGBUS, gc);
  heap[HEAP_SIZE] = 90; // pretend we are out of heap space
  return 0;
} 

我在 Mac OS X 10.6 上编译并运行程序并得到我期望的输出:

$ gcc debug.c
$ ./a.out
GC TIME
Bus error

我想使用 GDB 运行和调试这个程序。特别是,我想在 gc 函数(实际上是 gc 信号处理程序)处设置一个断点。当然,我也需要告诉 GDB 不要在 SIGBUS 上停止:

$ gdb ./a.out 
GNU gdb 6.3.50-20050815 (Apple version gdb-1346) (Fri Sep 18 20:40:51 UTC 2009)
... snip ...
(gdb) handle SIGSEGV SIGBUS nostop noprint
Signal        Stop  Print   Pass to program Description
SIGBUS        No    No  Yes     Bus error
SIGSEGV       No    No  Yes     Segmentation fault
(gdb) break gc
Breakpoint 1 at 0x100000d6f

但是,我们永远不会到达断点:

(gdb) run
Starting program: /snip/a.out 
Reading symbols for shared libraries +. done

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x0000000100029000
0x0000000100000e83 in main ()
(gdb) 

显然,未调用信号处理程序(未打印 GC TIME)。此外,我们仍然在 main() 中,在出错的 mov 处:

0x0000000100000e83 <main+247>:  movq   $0x5a,(%rax)

有什么想法吗?

谢谢。

【问题讨论】:

    标签: c gdb garbage-collection


    【解决方案1】:

    在内部,错误的内存访问会导致将 Mach 异常 EXC_BAD_ACCESS 发送到程序。通常,这被转换为 SIGBUS UNIX 信号。但是,gdb 在信号转换之前直接拦截 Mach 异常。解决方案是在运行程序之前给 gdb 命令set dont-handle-bad-access 1。然后使用正常机制,并遵守信号处理程序中的断点。

    【讨论】:

      【解决方案2】:

      相同的代码(也被修改以处理 SIGSEGV)在 Linux 上的 GDB 中按预期工作;这可能是 OS X 或 GDB 移植到该平台的错误。

      谷歌搜索发现 broken OS X behavior 与您在 10.1 上的情况一模一样,但有一种解决方法(在运行程序之前是 set inferior-bind-exception-port off)。

      (有一个similar bug on Windows。)

      【讨论】:

      • 谢谢,这是 OS X 特有的错误。变通方法使程序运行完成,但 gdb 不再在断点处停止。哦,好吧...
      • 查看我的答案,了解让您在处理程序内部中断的解决方案。
      【解决方案3】:

      不如在printf()后面加一个for( ;; );,正常运行程序,打印GC TIME后用gdb连接进程?

      【讨论】:

      • 嗯,这行得通。但是,没有更清洁的解决方案吗?真的不可能在 SIGBUS 处理程序中设置断点吗?
      【解决方案4】:

      您为什么希望获得 SIGBUS?在某些数据类型有对齐要求的架构上,SIGBUS 通常意味着对齐错误。看起来你只是想访问分配区域之外的内存,我希望你得到 SIGSEGV 而不是 SIGBUS。

      编辑:

      我认为 SIGSEGV 的 Mac OS X 名称似乎是 SIGBUS。因此,请忽略此答案。

      如果有任何帮助,当我在 Linux 系统上尝试使用 SIGBUS 替换为 SIGSEGV 的程序时,断点会按预期工作(即,它可以工作)。

      编辑 2:

      您也可以尝试在您的程序中捕获 SIGSEGV 吗?似乎信号的类型可能会根据内存在 Mac OS X 中的映射位置而有所不同(我刚刚阅读了讨论 herehere),并且当您在调试器中运行时可能会抛出不同的信号?

      【讨论】:

      • 我希望有一个 SIGBUS,因为当我在没有 gdb 的情况下运行它时会得到一个 SIGBUS。正如你所建议的,这不是我期望的开始。
      猜你喜欢
      • 2015-09-22
      • 1970-01-01
      • 1970-01-01
      • 2015-10-05
      • 1970-01-01
      • 2016-10-16
      • 2014-09-22
      • 1970-01-01
      相关资源
      最近更新 更多