debugging 确定导致分段错误的代码行?

mf98qq94  于 2022-11-14  发布在  其他
关注(0)|答案(7)|浏览(116)

如何确定导致segmentation fault的代码中的错误位置?
我的编译器(gcc)能否显示程序中错误的位置?

33qvvth1

33qvvth11#

GCC不能这样做,但GDB(a debugger)肯定可以。使用-g开关编译程序,如下所示:

gcc program.c -g

然后使用gdb:

$ gdb ./a.out
(gdb) run
<segfault happens here>
(gdb) backtrace
<offending code is shown here>

Here是一个很好的教程,让你开始使用GDB。
segfault发生的位置通常只是代码中“导致错误”的位置的一个线索。给定的位置不一定是问题所在的位置。

wgxvkvu9

wgxvkvu92#

此外,您可以给予一下valgrind:如果安装valgrind并运行

valgrind --leak-check=full <program>

然后它会运行你的程序,并显示任何segfault的堆栈跟踪,以及任何无效的内存读写和内存泄漏。

2fjabf4q

2fjabf4q3#

你也可以使用一个核心转储,然后用gdb来检查它。为了得到有用的信息,你还需要用-g标志来编译。
每当您收到以下消息时:

Segmentation fault (core dumped)

一个核心文件被写入当前目录。你可以用下面的命令检查它

gdb your_program core_file

该文件包含程序崩溃时内存的状态。核心转储在软件部署过程中非常有用。
确保系统未将核心转储文件大小设置为零。您可以使用以下命令将其设置为无限制:
ulimit -c unlimited
但是要小心!内核转储可能会变得很大。

368yc8dk

368yc8dk4#

有许多工具可以帮助调试分段错误,我想将我最喜欢的工具添加到列表中:地址消毒剂(通常缩写为ASAN)
现代的¹编译器附带了方便的-fsanitize=address标志,增加了一些编译时间和运行时开销,从而进行更多的错误检查。
根据the documentation,这些检查默认包括捕捉分段错误。这里的优点是你得到一个类似于gdb输出的堆栈跟踪,但不需要在调试器中运行程序。一个例子:
第一个
输出比gdb的输出稍微复杂一些,但也有优点:

  • 不需要再现问题来接收堆栈跟踪。在开发期间简单地启用标记就足够了。
  • ASAN捕获的不仅仅是分段错误,即使进程可以访问内存区域,也会捕获许多越界访问。

¹这就是叮当3.1+和GCC 4.8+

7rtdyuoh

7rtdyuoh5#

以上所有答案都是正确的,建议您填写;如果上述方法都不能使用,则该答案仅作为最后的手段。
如果所有其他方法都失败了,您总是可以使用各种临时调试打印语句重新编译程序(例如fprintf(stderr, "CHECKPOINT REACHED @ %s:%i\n", __FILE__, __LINE__);),然后运行程序,观察崩溃发生前最后一次debug-print打印的内容--你知道程序已经走到这一步了,所以崩溃一定是在那之后发生的。添加或删除调试打印,重新编译,然后再次运行测试,直到你把测试范围缩小到一行代码。这时你就可以修复bug并删除所有临时的调试打印了。
这是相当乏味的,但它的优点是几乎在任何地方都能工作--唯一的情况是,由于某种原因,您无法访问stdout或stderr,或者您试图修复的bug是一个竞争条件,当程序的定时改变时,它的行为也会改变(因为调试打印会减慢程序并改变其定时)

zzlelutf

zzlelutf6#

Lucas关于内核转储的回答很好。在我的.cshrc中,我有:

alias core 'ls -lt core; echo where | gdb -core=core -silent; echo "\n"'

输入“core”来显示回溯。还有日期戳,以确保我看到的是正确的文件:(。

补充:如果存在 stack corruption bug,那么应用于核心转储的回溯跟踪通常是无用的。在这种情况下,在gdb中运行程序可以给予更好的结果,根据可接受的答案(假设错误很容易重现)。还要注意多个进程同时转储核心;某些操作系统会将PID添加到核心文件的名称中。

093gszye

093gszye7#

这是一种粗略的方法,用于查找在其之后存在分段错误的确切行。
1.定义行记录功能

#include \<iostream> 

void log(int line) {  
std::cout << line << std::endl;  
}

1.查找日志函数后的所有分号并将其替换为“;日志(* 行 *);“
1.确保在for(;;)循环被删除

相关问题