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

时间:2009-06-17 04:00:21

标签: c debugging gdb segmentation-fault

我已经做了很多编程,但在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}}显然很困惑。有谁知道为什么?或者有人在没有使用观察点的情况下有任何调试此错误的建议吗?

6 个答案:

答案 0 :(得分:3)

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

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

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

答案 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)。

相关问题