在C中调试破坏的静态变量(gdb损坏?)

我已经完成了很多编程,但在C中并没有太多,我需要有关调试的建议。 我有一个静态变量(文件范围)在执行multithreading程序大约10-100秒后被破坏(在OS X 10.4上使用pthreads)。 我的代码看起来像这样:

static float some_values[SIZE]; static int * addr; 

addr指向有效的内存地址一段时间,然后被一些值(有时为0,有时非零)破坏,从而在解除引用时导致段错误。 使用gdb我已经validation了addr正在人们期望的some_values之后立即在内存中进行some_values ,所以我的第一个猜测就是我使用了一个越界索引来写入some_values 。 但是,这是一个很小的文件,所以很容易检查这不是问题。

显而易见的调试技术是在变量addr上设置观察点。 但这样做似乎在gdb造成了不稳定和无法解释的行为。 观察点在第一次分配到addr时触发; 然后在我继续执行之后,我立即在另一个线程中得到一个无意义的段错误…据说是在程序的不同部分访问静态变量的地址时的段错误! 但是gdb允许我以交互方式读取和写入该内存地址。

程序收到信号EXC_BAD_ACCESS,无法访问内存。
原因:KERN_PROTECTION_FAILURE,地址:0x001d5bd0
在mainloop.c:39中接收(arg = 0x0)的0x0000678d
 39 sample_buf_cleared ++;
 (gdb)p&sample_buf_cleared
 $ 17 =(int *)0x1d5bd0
 (gdb)p sample_buf_cleared
 18美元= 1
 (gdb)设置sample_buf_cleared = 2
 (GDB) 

gdb显然很困惑。 有谁知道为什么? 或者有没有人有任何建议调试此错误而不使用观察点?

  1. 您可以在some_values和addr之间放置一个uint数组,并确定是否超出some_values,或者如果损坏影响更多地址,那么您首先想到的是。 我会将填充初始化为DEADBEEF或其他一些易于区分且不太可能在程序中出现的明显模式。 如果填充中的值发生更改,则将其转换为浮动状态,并查看该数字是否有意义。

static float some_values [SIZE]; static unsigned int padding [1024]; static int * addr;

  1. 多次运行程序。 在每次运行中禁用一个不同的线程,看看问题何时消失。

  2. 将程序进程关联性设置为单个核心,然后尝试观察点。 如果没有两个线程同时修改该值,您可能会有更好的运气。 注意:此解决方案不排除发生这种情况。 它可以更容易捕获调试器。

static变量和multithreading一般不混用。

没有看到你的代码(你应该包括你的线程代码),我的猜测是你有两个线程同时写入addr变量。 它不起作用。

你要么需要:

  • 为每个线程创建单独的addr实例; 要么
  • addr周围提供某种同步,以阻止两个线程同时更改值。

尝试使用valgrind; 我没有尝试过OS X上的valgrind,我不明白你的问题,但是当你说“破坏”时,“尝试valgrind”是我想到的第一件事。

你可以尝试的一件事是创建一个单独的线程,其唯一目的是观察addr的值,并在它发生变化时中断。 例如:

 static int * volatile addr; // volatile here is important, and must be after the * void *addr_thread_proc(void *arg) { while(1) { int *old_value = addr; while(addr == old_value) /* spin */; __asm__("int3"); // break the debugger, or raise SIGTRAP if no debugger } } ... pthread_t spin_thread; pthread_create(&spin_thread, NULL, &addr_thread_proc, NULL); 

然后,只要addr的值发生更改, int3指令就会运行,这会破坏调试器,停止所有线程。

gdb经常对multithreading程序很奇怪。 另一种解决方案(如果你能负担得起的话)就是将printf()放在所有地方,试着抓住你的价值被破坏的那一刻。 不是很优雅,但有时​​候很有效。

我没有在OSX上做任何调试,但我在Linux上的GDB中看到了相同的行为:程序崩溃,但GDB可以读取和写入程序刚试图读/写失败的内存。

这并不一定意味着GDB混淆了; 相反,内核允许GDB通过ptrace()读/​​写内存,不允许下级进程读或写。 IOW,这是(最近修复的)内核错误。

尽管如此,听起来GDB观察点无论出于何种原因都不适合你。

您可以使用的一种技术是为some_values空间,而不是为它们静态分配空间,安排数组在页面边界上结束 ,并安排下一页不可访问(通过mprotect )。

如果任何代码试图访问some_values的末尾,它将得到一个exception(实际上你正在设置一个不可写的“观察点”刚刚超过some_values )。