如何确定导致segmentation fault的代码中的错误位置?我的编译器(gcc)能否显示程序中错误的位置?
gcc
33qvvth11#
GCC不能这样做,但GDB(a debugger)肯定可以。使用-g开关编译程序,如下所示:
-g
gcc program.c -g
然后使用gdb:
$ gdb ./a.out (gdb) run <segfault happens here> (gdb) backtrace <offending code is shown here>
Here是一个很好的教程,让你开始使用GDB。segfault发生的位置通常只是代码中“导致错误”的位置的一个线索。给定的位置不一定是问题所在的位置。
wgxvkvu92#
此外,您可以给予一下valgrind:如果安装valgrind并运行
valgrind
valgrind --leak-check=full <program>
然后它会运行你的程序,并显示任何segfault的堆栈跟踪,以及任何无效的内存读写和内存泄漏。
2fjabf4q3#
你也可以使用一个核心转储,然后用gdb来检查它。为了得到有用的信息,你还需要用-g标志来编译。每当您收到以下消息时:
Segmentation fault (core dumped)
一个核心文件被写入当前目录。你可以用下面的命令检查它
gdb your_program core_file
该文件包含程序崩溃时内存的状态。核心转储在软件部署过程中非常有用。确保系统未将核心转储文件大小设置为零。您可以使用以下命令将其设置为无限制:ulimit -c unlimited但是要小心!内核转储可能会变得很大。
ulimit -c unlimited
368yc8dk4#
有许多工具可以帮助调试分段错误,我想将我最喜欢的工具添加到列表中:地址消毒剂(通常缩写为ASAN)。现代的¹编译器附带了方便的-fsanitize=address标志,增加了一些编译时间和运行时开销,从而进行更多的错误检查。根据the documentation,这些检查默认包括捕捉分段错误。这里的优点是你得到一个类似于gdb输出的堆栈跟踪,但不需要在调试器中运行程序。一个例子:第一个输出比gdb的输出稍微复杂一些,但也有优点:
-fsanitize=address
¹这就是叮当3.1+和GCC 4.8+。
7rtdyuoh5#
以上所有答案都是正确的,建议您填写;如果上述方法都不能使用,则该答案仅作为最后的手段。如果所有其他方法都失败了,您总是可以使用各种临时调试打印语句重新编译程序(例如fprintf(stderr, "CHECKPOINT REACHED @ %s:%i\n", __FILE__, __LINE__);),然后运行程序,观察崩溃发生前最后一次debug-print打印的内容--你知道程序已经走到这一步了,所以崩溃一定是在那之后发生的。添加或删除调试打印,重新编译,然后再次运行测试,直到你把测试范围缩小到一行代码。这时你就可以修复bug并删除所有临时的调试打印了。这是相当乏味的,但它的优点是几乎在任何地方都能工作--唯一的情况是,由于某种原因,您无法访问stdout或stderr,或者您试图修复的bug是一个竞争条件,当程序的定时改变时,它的行为也会改变(因为调试打印会减慢程序并改变其定时)
fprintf(stderr, "CHECKPOINT REACHED @ %s:%i\n", __FILE__, __LINE__);
zzlelutf6#
Lucas关于内核转储的回答很好。在我的.cshrc中,我有:
alias core 'ls -lt core; echo where | gdb -core=core -silent; echo "\n"'
输入“core”来显示回溯。还有日期戳,以确保我看到的是正确的文件:(。
补充:如果存在 stack corruption bug,那么应用于核心转储的回溯跟踪通常是无用的。在这种情况下,在gdb中运行程序可以给予更好的结果,根据可接受的答案(假设错误很容易重现)。还要注意多个进程同时转储核心;某些操作系统会将PID添加到核心文件的名称中。
093gszye7#
这是一种粗略的方法,用于查找在其之后存在分段错误的确切行。1.定义行记录功能
#include \<iostream> void log(int line) { std::cout << line << std::endl; }
1.查找日志函数后的所有分号并将其替换为“;日志(* 行 *);“1.确保在for(;;)循环被删除
7条答案
按热度按时间33qvvth11#
GCC不能这样做,但GDB(a debugger)肯定可以。使用
-g
开关编译程序,如下所示:然后使用gdb:
Here是一个很好的教程,让你开始使用GDB。
segfault发生的位置通常只是代码中“导致错误”的位置的一个线索。给定的位置不一定是问题所在的位置。
wgxvkvu92#
此外,您可以给予一下
valgrind
:如果安装valgrind
并运行然后它会运行你的程序,并显示任何segfault的堆栈跟踪,以及任何无效的内存读写和内存泄漏。
2fjabf4q3#
你也可以使用一个核心转储,然后用gdb来检查它。为了得到有用的信息,你还需要用
-g
标志来编译。每当您收到以下消息时:
一个核心文件被写入当前目录。你可以用下面的命令检查它
该文件包含程序崩溃时内存的状态。核心转储在软件部署过程中非常有用。
确保系统未将核心转储文件大小设置为零。您可以使用以下命令将其设置为无限制:
ulimit -c unlimited
但是要小心!内核转储可能会变得很大。
368yc8dk4#
有许多工具可以帮助调试分段错误,我想将我最喜欢的工具添加到列表中:地址消毒剂(通常缩写为ASAN)。
现代的¹编译器附带了方便的
-fsanitize=address
标志,增加了一些编译时间和运行时开销,从而进行更多的错误检查。根据the documentation,这些检查默认包括捕捉分段错误。这里的优点是你得到一个类似于gdb输出的堆栈跟踪,但不需要在调试器中运行程序。一个例子:
第一个
输出比gdb的输出稍微复杂一些,但也有优点:
¹这就是叮当3.1+和GCC 4.8+。
7rtdyuoh5#
以上所有答案都是正确的,建议您填写;如果上述方法都不能使用,则该答案仅作为最后的手段。
如果所有其他方法都失败了,您总是可以使用各种临时调试打印语句重新编译程序(例如
fprintf(stderr, "CHECKPOINT REACHED @ %s:%i\n", __FILE__, __LINE__);
),然后运行程序,观察崩溃发生前最后一次debug-print打印的内容--你知道程序已经走到这一步了,所以崩溃一定是在那之后发生的。添加或删除调试打印,重新编译,然后再次运行测试,直到你把测试范围缩小到一行代码。这时你就可以修复bug并删除所有临时的调试打印了。这是相当乏味的,但它的优点是几乎在任何地方都能工作--唯一的情况是,由于某种原因,您无法访问stdout或stderr,或者您试图修复的bug是一个竞争条件,当程序的定时改变时,它的行为也会改变(因为调试打印会减慢程序并改变其定时)
zzlelutf6#
Lucas关于内核转储的回答很好。在我的.cshrc中,我有:
输入“core”来显示回溯。还有日期戳,以确保我看到的是正确的文件:(。
补充:如果存在 stack corruption bug,那么应用于核心转储的回溯跟踪通常是无用的。在这种情况下,在gdb中运行程序可以给予更好的结果,根据可接受的答案(假设错误很容易重现)。还要注意多个进程同时转储核心;某些操作系统会将PID添加到核心文件的名称中。
093gszye7#
这是一种粗略的方法,用于查找在其之后存在分段错误的确切行。
1.定义行记录功能
1.查找日志函数后的所有分号并将其替换为“;日志(* 行 *);“
1.确保在for(;;)循环被删除