3

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

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

addr指向有效的内存地址一段时间,然后被某个值(有时为 0,有时为非零)破坏,从而在取消引用时导致段错误。gdb我已经证实,正如人们所期望的那样,它addr立即被布置在内存中some_values,所以我的第一个猜测是我使用了一个越界索引来写入some_values. 但是,这是一个很小的文件,因此很容易检查这不是问题所在。

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

程序收到信号 EXC_BAD_ACCESS,无法访问内存。
原因:KERN_PROTECTION_FAILURE 地址:0x001d5bd0
0x0000678d 在 mainloop.c:39 处接收(arg=0x0)
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显然很困惑。有谁知道为什么?或者有人对不使用观察点调试此错误有任何建议吗?

4

6 回答 6

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

静态浮动 some_values[SIZE]; 静态无符号整数填充[1024];静态整数 * 地址;

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

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

于 2009-06-17T05:10:39.737 回答
2

static变量和多线程一般不要混用。

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

您要么需要:

  • addr为每个线程创建单独的实例;或者
  • 提供某种同步addr以阻止两个线程同时更改值。
于 2009-06-17T04:15:38.943 回答
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指令就会运行,这会破坏调试器,停止所有线程。

于 2009-06-17T04:46:35.487 回答
1

尝试使用 valgrind;我没有在 OS X 上尝试过 valgrind,我不明白你的问题,但是当你说“clobbered”时,我首先想到的是“try valgrind”。

于 2009-06-17T04:11:41.470 回答
0

gdb 在多线程程序中经常表现得很奇怪。另一个解决方案(如果你负担得起的话)是把printf()s 放在所有地方,试图抓住你的价值被破坏的那一刻。不是很优雅,但有时​​很有效。

于 2009-06-17T04:14:37.523 回答
0

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

这并不一定意味着 GDB 很困惑;而是内核允许 GDB 通过 ptrace() 读取/写入内存,不允许下级进程读取或写入。IOW,这是一个(最近修复的)内核错误。

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

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

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

于 2009-06-18T02:22:13.450 回答