如何在gdb内部附加gdb
和run
时调试不会崩溃的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万的循环。
Ps,对C不太熟悉 - 尤其是任何类型的调试。在答案中要善良,富有表现力和冗长。 :)
答案 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);