gdb:为SIGBUS处理程序设置断点

时间:2010-01-11 19:04:41

标签: c gdb garbage-collection

我正在尝试使用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)

有什么想法吗?

感谢。

4 个答案:

答案 0 :(得分:5)

相同的代码(修改为处理SIGSEGV)在Linux上的GDB中按预期工作;它可能是OS X或GDB到该平台的端口的错误。

谷歌搜索发现broken OS X behavior就像你的回到10.1一样,有一种解决方法(运行程序前set inferior-bind-exception-port off)。

(有一个similar bug on Windows。)

答案 1 :(得分:5)

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

答案 2 :(得分:4)

如何在for( ;; );之后放置printf(),正常运行程序,然后在GC TIME打印出来之后用gdb连接到流程?

答案 3 :(得分:2)

为什么期望获得SIGBUS? SIGBUS通常表示对齐错误,在某些数据类型具有对齐要求的体系结构上。看起来你只是试图访问你分配区域之外的内存,我希望你得到SIGSEGV而不是SIGBUS。

编辑:

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

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

编辑2:

您是否也可以尝试在程序中捕获SIGSEGV?看起来信号的类型可能会有所不同,具体取决于Mac OS X中内存的映射位置(我只是阅读了讨论herehere),当你发现时可能会抛出不同的信号在调试器中运行?