C/C++教程

【C++】Debugging Segmentation Faults

本文主要是介绍【C++】Debugging Segmentation Faults,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

 

背景

linux下的程序,在遇到空指针解引用、栈错误等原因崩溃时,bash会输出一条:

Segmentation fault(core dump)

如果你看到core dumped字样,并且在目录下也找到了一个叫core的文件,那你可以直接用gdb定位到程序崩溃的位置。但是,我在实践中发现,在我的ubuntu 20.04环境下,程序段错误后找不到core文件。

  • 检查是否开启core dump?

  先用ulimit -c,如果看到0,说明没有开core dump。
  所以我们输入ulimit -c unlimited,打开core dump。
  再次用ulimit -c,看到unlimited了,说明core dump打开了。 

  • ulimit -c已经是unlimited了,怎么还是看不到core文件?

  输入命令:

man 5 core

  查看手册中的core内容,然后往下翻:

 

它这里面列举了几条不产生core文件的原因,你可以逐条对照。最有可能的原因是/proc/sys/kernel/core_pattern文件里面把你的core文件发送到别处去了。我的ubuntu系统默认就是这样干的。

然后打开/proc/sys/kernel/core_pattern文件,里面如果不是core的内容,表明它可能把你的core文件发到别的地方去了。那么则可以编辑这个文件,把内容改成core,然后保存。

另外有一种更简洁的方式,执行以下命令:

sudo service apport stop

然后你会发现,core_pattern的内容变为core了,这样core文件就可以正常生成了。

GDB

GDB is a powerful debugger that allows a programmer to step through their code line by line and probe any variable for its value at that step of execution. It has a lot of capability beyond what can be addressed in a simple primer. A very useful cheat sheet that I always have a printed copy of on my desk can be found here https://darkdust.net/files/GDB Cheat Sheet.pdf.

 

First and foremost, GDB will need some specific information injected into the executable that needs to be debugged. This requires compiling all of our code with the -g flag. The best way to do this is by adding it to your CXXFLAGS variable in your Makefile. That ensures that all automatically created *.o files are also built using the -g flag. You should also delete the old *.o files before rebuilding. This is a great time to run make clean, assuming you have a well-defined clean rule in your Makefile.

Now that you have recompiled with -g flags, you can fire up the debugger. The -tui flag below opens the source code in the top half of the screen which is great for adding some context to where the program is in the execution. gdb -tui a.out

If files were built with -g, the symbol table should load and GDB is ready to use. The first thing that is typically needed is to add one or more breakpoints. These can be added to method names, or to lines of code (if you specify a line that cannot break, the next breakable line is used).

Breaking on a specific line of source code

To halt execution at line 12 of test.cpp, you would simply type break test.cpp:12. Now when you type run, GDB starts executing and will halt at line 12 (or the next breakable line) so that you can inspect the status of your program.

Breaking on a method

If you suspect a specific method or function in your code, you can halt execution and inspect whenever that method is called. In order to inspect the method Search within the BinarySearch class, I would type break BinarySearch::Search(int*, int, int, int). As you might have guessed, this is a great time for tab-completion which GDB is great at. Just start typing the class or method name and hit tab in order to fill in the rest. Now when you type run, GDB starts executing and will halt the first (and every) time that method is called.

Stepping through

The commands needed to restart execution after a breakpoint has been reached are:

  • next - This goes to the next line of code, but will not dive into functions.
  • step - This goes to the next line of instruction. This might be inside of a function call or elsewhere in the code tree.
  • continue - This runs the program until the end of execution, or until a breakpoint is reached.

Looking at values

The simple act of stepping through code often helps figure out the problem. “This should not go inside that if statement”…well, it does, so figure out why.

Sometimes, it is not enough to just know the current location of your code execution. Luckily, we can print out values of our variables. print myVar would print the value of myVarprint &myVar would print the address of myVar. Pointers will print the address by default. To see their value, you would need to dereference, e.g. print *myPointer. As you might have noticed, the print statement is identical to sticking in std::cout statements, except that you don’t need to guess in advance, you can poke around until you find the values you need. Print will also let you call methods, so print myObject.GetSomeData() would print the results of the method call.

When breaking on a method call, the arguments passed to the call are printed automatically. For the BinarySearch breakpoint we mentioned earlier, when the breakpoint is triggered, the output might read:

Breakpoint 1, BinarySearch::Search (listOfNumbers=0x7fffffffc720, left=7, right=10, searchKey=10) at binary-search.cpp:5

 

参考:

https://stackoverflow.com/questions/2065912/core-dumped-but-core-file-is-not-in-the-current-directory

 

这篇关于【C++】Debugging Segmentation Faults的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!