我已经做了很多编程,但在C语言上做得不多,我需要有关调试的建议。我有一个静态变量(文件范围)在执行多线程程序大约10-100秒后被破坏(在OS X 10.4上使用pthreads)。我的代码看起来像这样:
static float some_values[SIZE];
static int * addr;
addr
指向有效的内存地址一段时间,然后被一些值(有时为0,有时非零)破坏,从而在解除引用时导致段错误。使用gdb
进行了调查我已经确认addr
正在some_values
之后立即在some_values
中进行了记忆,所以我的第一个猜测就是我使用了一个以外的要写入addr
的边界索引。但是,这是一个很小的文件,所以很容易检查这不是问题。
明显的调试技术是在变量gdb
上设置观察点。但这样做似乎在addr
中造成了不稳定和无法解释的行为。观察点在第一次分配到gdb
时被触发;然后在我继续执行之后,我立即在另一个线程中得到一个无意义的段错误...据说是在程序的不同部分访问静态变量的地址时的段错误!但是gdb
允许我以交互方式读取和写入该内存地址。
Program received signal EXC_BAD_ACCESS, Could not access memory. Reason: KERN_PROTECTION_FAILURE at address: 0x001d5bd0 0x0000678d in receive (arg=0x0) at mainloop.c:39 39 sample_buf_cleared ++; (gdb) p &sample_buf_cleared $17 = (int *) 0x1d5bd0 (gdb) p sample_buf_cleared $18 = 1 (gdb) set sample_buf_cleared = 2 (gdb)
{{1}}显然很困惑。有谁知道为什么?或者有人在没有使用观察点的情况下有任何调试此错误的建议吗?
答案 0 :(得分:3)
static float some_values [SIZE]; static unsigned int padding [1024]; static int * addr;
多次运行程序。在每次运行中禁用一个不同的线程,看看问题什么时候消失。
将程序进程关联设置为单个核心,然后尝试观察点。如果您没有两个线程同时修改该值,您可能会有更好的运气。注意:此解决方案不排除发生这种情况。它可以更容易地捕获调试器。
答案 1 :(得分:2)
static
变量和多线程通常不会混合。
没有看到你的代码(你应该包含你的线程代码),我的猜测是你有两个线程同时写入addr
变量。它不起作用。
您需要:
addr
的单独实例;或addr
周围提供某种同步,以阻止两个线程同时更改该值。答案 2 :(得分:1)
尝试使用valgrind;我没有尝试过OS X上的valgrind,我不明白你的问题,但是当你说“破坏”时,“尝试valgrind”是我想到的第一件事。
答案 3 :(得分:1)
您可以尝试的一件事是创建一个单独的线程,其唯一目的是观察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
指令就会运行,这将破坏调试器,停止所有线程。
答案 4 :(得分:0)
gdb通常对多线程程序很奇怪。另一种解决方案(如果你能负担得起的话)就是将printf()
放在所有地方试图抓住你的价值被破坏的那一刻。不是很优雅,但有时候很有效。
答案 5 :(得分:0)
我没有对OSX进行任何调试,但我在Linux上的GDB中看到过相同的行为:程序崩溃,但是GDB可以读取和写入程序只是尝试读/写失败的内存。
这并不一定意味着GDB感到困惑;相反,内核允许GDB通过ptrace()读/写内存,不允许下级进程读或写。 IOW,这是(最近修复的)内核错误。
但是,听起来无论出于何种原因,GDB观察点都不适合你。
您可以使用的一种技术是mmap
空间some_values
,而不是为它们静态分配空间,在页面边界上安排数组 end ,并安排使下一页无法访问(通过mprotect
)。
如果有任何代码尝试访问some_values
的末尾,则会出现异常(实际上您正在设置一个不可写的“观察点”,只是过了some_values
)。