C++ 确定导致分段错误的代码行?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2876357/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Determine the line of code that causes a segmentation fault?
提问by nc3b
How does one determine where the mistake is in the code that causes a segmentation fault?
如何确定导致分段错误的代码中的错误在哪里?
Can my compiler (gcc
) show the location of the fault in the program?
我的编译器 ( gcc
)能否显示程序中的错误位置?
回答by nc3b
GCC can't do that but GDB (a debugger) sure can. Compile you program using the -g
switch, like this:
GCC 不能这样做,但 GDB(调试器)肯定可以。使用-g
开关编译您的程序,如下所示:
gcc program.c -g
Then use gdb:
然后使用gdb:
$ gdb ./a.out
(gdb) run
<segfault happens here>
(gdb) backtrace
<offending code is shown here>
Hereis a nice tutorial to get you started with GDB.
这是一个很好的教程,可以帮助您开始使用 GDB。
Where the segfault occurs is generally only a clue as to where "the mistake which causes" it is in the code. The given location is not necessarily where the problem resides.
段错误发生的位置通常只是关于“导致错误”在代码中的位置的线索。给定的位置不一定是问题所在。
回答by jwkpiano1
Also, you can give Valgrind a try: if you install Valgrind and run valgrind --leak-check=full , then it will run your program and display stack traces for any segfaults, as well as any invalid memory reads or writes and memory leaks. It's really quite useful.
此外,您可以尝试一下 Valgrind:如果您安装 Valgrind 并运行 valgrind --leak-check=full ,那么它将运行您的程序并显示任何段错误的堆栈跟踪,以及任何无效的内存读取或写入以及内存泄漏. 它真的很有用。
回答by Lucas
You could also use a core dump and then examine it with gdb. To get useful information you also need to compile with the -g
flag.
您也可以使用核心转储,然后使用 gdb 检查它。要获得有用的信息,您还需要使用-g
标志进行编译。
Whenever you get the message:
每当您收到消息时:
Segmentation fault (core dumped)
a core file is written into your current directory. And you can examine it with the command
一个核心文件被写入您的当前目录。你可以用命令检查它
gdb your_program core_file
The file contains the state of the memory when the program crashed. A core dump can be useful during the deployment of your software.
该文件包含程序崩溃时的内存状态。核心转储在软件部署期间非常有用。
Make sure your system doesn't set the core dump file size to zero. You can set it to unlimited with:
确保您的系统没有将核心转储文件大小设置为零。您可以将其设置为无限制:
ulimit -c unlimited
ulimit -c unlimited
Careful though! that core dumps can become huge.
不过要小心!核心转储可能会变得巨大。
回答by asynts
There are a number of tools available which help debugging segmentation faults and I would like to add my favorite tool to the list: Address Sanitizers (often abbreviated ASAN).
有许多工具可以帮助调试分段错误,我想将我最喜欢的工具添加到列表中:Address Sanitizers(通常缩写为 ASAN)。
Modern1 compilers come with the handy -fsanitize=address
flag, adding some compile time and run time overhead which does more error checking.
Modern1 编译器带有方便的-fsanitize=address
标志,增加了一些编译时间和运行时间开销,可以进行更多的错误检查。
According to the documentationthese checks include catching segmentation faults by default. The advantage here is that you get a stack trace similar to gdb's output, but without running the program inside a debugger. An example:
根据文档,这些检查包括默认捕获分段错误。这里的优点是您可以获得类似于 gdb 输出的堆栈跟踪,但无需在调试器中运行程序。一个例子:
int main() {
volatile int *ptr = (int*)0;
*ptr = 0;
}
$ gcc -g -fsanitize=address main.c
$ ./a.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==4848==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5654348db1a0 bp 0x7ffc05e39240 sp 0x7ffc05e39230 T0)
==4848==The signal is caused by a WRITE memory access.
==4848==Hint: address points to the zero page.
#0 0x5654348db19f in main /tmp/tmp.s3gwjqb8zT/main.c:3
#1 0x7f0e5a052b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)
#2 0x5654348db099 in _start (/tmp/tmp.s3gwjqb8zT/a.out+0x1099)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /tmp/tmp.s3gwjqb8zT/main.c:3 in main
==4848==ABORTING
The output is slightly more complicated than what gdb would output but there are upsides:
输出比 gdb 的输出稍微复杂一些,但也有好处:
There is no need to reproduce the problem to receive a stack trace. Simply enabling the flag during development is enough.
ASANs catch a lot more than just segmentation faults. Many out of bounds accesses will be caught even if that memory area was accessible to the process.
无需重现问题即可接收堆栈跟踪。只需在开发过程中启用标志就足够了。
ASAN 捕获的不仅仅是分段错误。即使进程可以访问该内存区域,也会捕获许多越界访问。
1 That is Clang 3.1+and GCC 4.8+.
1 即Clang 3.1+和GCC 4.8+。
回答by Joseph Quinsey
Lucas's answer about core dumps is good. In my .cshrc I have:
卢卡斯关于核心转储的回答很好。在我的 .cshrc 中,我有:
alias core 'ls -lt core; echo where | gdb -core=core -silent; echo "\n"'
to display the backtrace by entering 'core'. And the date stamp, to ensure I am looking at the right file :(.
通过输入“核心”来显示回溯。还有日期戳,以确保我正在查看正确的文件:(。
Added: If there is a stackcorruption bug, then the backtrace applied to the core dump is often garbage. In this case, running the program within gdbcan give better results, as per the accepted answer (assuming the fault is easily reproducible). And also beware of multiple processes dumping core simultaneously; some OS's add the PID to the name of the core file.
补充:如果存在堆栈损坏错误,那么应用于核心转储的回溯通常是垃圾。在这种情况下,根据公认的答案(假设故障很容易重现),在gdb 中运行程序可以获得更好的结果。还要注意多个进程同时转储内核;一些操作系统将 PID 添加到核心文件的名称中。
回答by Jeremy Friesner
All of the above answers are correct and recommended; this answer is intended only as a last-resort if none of the aforementioned approaches can be used.
以上所有答案都是正确的,值得推荐;如果上述方法均无法使用,则此答案仅用作最后的手段。
If all else fails, you can always recompile your program with various temporary debug-print statements (e.g. fprintf(stderr, "CHECKPOINT REACHED @ %s:%i\n", __FILE__, __LINE__);
) sprinkled throughout what you believe to be the relevant parts of your code. Then run the program, and observe what the was last debug-print printed just before the crash occurred -- you know your program got that far, so the crash must have happened after that point. Add or remove debug-prints, recompile, and run the test again, until you have narrowed it down to a single line of code. At that point you can fix the bug and remove all of the temporary debug-prints.
如果一切都失败了,您总是可以使用各种临时调试打印语句(例如fprintf(stderr, "CHECKPOINT REACHED @ %s:%i\n", __FILE__, __LINE__);
)重新编译您的程序,这些语句散布在您认为是代码的相关部分中。然后运行程序,并观察在崩溃发生前最后打印的调试打印的内容——您知道您的程序已经到了那么远,所以崩溃一定是在那之后发生的。添加或删除调试打印、重新编译并再次运行测试,直到您将其缩小到一行代码。那时您可以修复错误并删除所有临时调试打印。
It's quite tedious, but it has the advantage of working just about anywhere -- the only times it might not is if you don't have access to stdout or stderr for some reason, or if the bug you are trying to fix is a race-condition whose behavior changes when the timing of the program changes (since the debug-prints will slow down the program and change its timing)
这很乏味,但它的优点是几乎可以在任何地方工作——唯一可能不是因为某些原因你无法访问 stdout 或 stderr,或者如果你试图修复的错误是一场比赛- 当程序的时间改变时行为改变的条件(因为调试打印会减慢程序并改变它的时间)