C malloc“无法分配区域”错误,但无法用GDB重新编译?

时间:2017-04-05 07:10:41

标签: c multithreading

如何在gdb内部附加gdbrun时调试不会崩溃的C应用程序?

独立运行时会一直崩溃 - 即使是相同的调试版本!

我们中的一些人在使用为BSD / Linux编写的C程序时遇到此错误,我们正在使用OpenSSL编译macOS。

app(37457,0x7000017c7000) malloc: *** mach_vm_map(size=13835058055282167808) failed (error code=3)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
ERROR: malloc(buf->length + 1) failed!

我知道,没有帮助。

使用-g -rdynamic重新编译应用程序会产生相同的错误。好的,现在我们知道它不是因为它继续失败而发布版本。

虽然在gdb调试会话中运行,但它可以正常工作

$ sudo gdb app
(gdb) b malloc_error_break
Function "malloc_error_break" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (malloc_error_break) pending.
(gdb) run -threads 8
Starting program: ~/code/app/app -threads 8
[New Thread 0x1903 of process 45436]
warning: unhandled dyld version (15)

它会持续数小时。 CTRL-C,并运行./app -threads 8并在一两秒钟后(几百万次迭代)崩溃。

显然,其中一个主题中存在问题。但是线程的那些工作者相当大(几百行代码)。没什么比这更突出的。

请注意,线程遍历大约每秒2000万的循环。

  • macOS 10.12.3
  • Homebrew w / GNU gcc和openssl(链接到加密)

Ps,对C不太熟悉 - 尤其是任何类型的调试。在答案中要善良,富有表现力和冗长。 :)

2 个答案:

答案 0 :(得分:3)

有时被忽视的一种调试技术是在代码中包含调试打印,当然它有它的缺点,但它也有优点。面对异常终止,您必须记住的一件事是确保打印输出实际打印出来。通常它足以打印到stderr(但如果这不能使得技巧可能需要明确地fflush流。)

另一个技巧是在错误发生之前停止程序。这需要您知道程序何时即将崩溃,最好尽可能接近。你可以使用raise:

来做到这一点
raise(SIGSTOP);

这不会终止程序,只是暂停执行。现在,您可以使用gdb命令附加gdb <program-name> <pid>(使用ps查找进程的pid)。现在在gdb,您必须告诉它忽略SIGSTOP

> handle SIGSTOP ignore

然后你可以设置断点。您也可以使用raise命令退出finish函数(可能需要多次发出才能返回代码)。

这项技术使程序在你决定停止它的时候有正常的行为,希望在gdb下运行的最终部分不会改变行为enuogh。

第三种选择是使用valgrind。通常,当您看到这些错误时,valgrind会收到错误。这些访问超出了范围和未初始化的变量。

答案 1 :(得分:1)

许多内存管理器将内存初始化为已知错误值以暴露此类问题(例如,Microsoft的CRT将使用一系列值(0xCD表示未初始化,0xDD表示已经免费等)。

每次使用malloc后,请尝试将内存设置为0xCD(或其他一些常量值)。这将允许您使用调试器更轻松地识别未初始化的内存。不要使用0x00,因为这是一个正常的&#39;价值并且如果它错了将更难发现(它也可能会解决你的问题)。

类似的东西:

void *memory = malloc(sizeof(my_object));
memset(memory, 0xCD, sizeof(my_object));

如果您知道块的大小,您可以在空闲之前执行类似的操作(除非您知道对象的大小或以某种方式跟踪它,否则有时会更难):

memset(memory, 0xDD, sizeof(my_object));
free(memory);