面试官问程序崩溃了怎么调试?

我这里简单演示一下。

The interviewer asks: how do you debug a program crash?

Here I’ll give a quick demonstration.

前期设置

我的环境是 Ubuntu 20.04 ,默认情况下程序崩溃是不产生 core 文件的。

首先通过 ulimit -a 查看 core 文件的大小。

image-20220524143841432

可以看到,默认情况下 core 文件的大小为 0 ,就是不生成 core 文件。

所以这里首先要通过 ulimit -c 文件大小 命令来设置文件大小。

我这里执行 ulimit -c unlimited 设置为无限大小。

可以再使用 ulimit -a 来查看设置结果。

image-20220524144433781

可以看到 core 文件大小已经设置为无限大。

需要注意的是,这种设置方法只对当前用户生效,并且重新登录也会失效。

接下来要设置默认生成的 core 文件名字的格式。

首先问一下 “男人” 关于 core 文件的信息。

man core

我们可以看到关于文件名字的如下内容。(还好没有特别复杂的单词😏)

image-20220524145111591

大概意思就是说修改 /proc/sys/kernel/core_pattern 文件中的内容可以改变 core 文件的默认命名。

这里我简单示范一下。

echo 'core-%s-%p-%t' > /proc/sys/kernel/core_pattern

Preliminary Setup

My environment is Ubuntu 20.04; by default, a program crash does not produce a core file.

First, check the core file size limit with ulimit -a.

image-20220524143841432

As you can see, the core file size is 0 by default, which means no core file is generated.

So first, set the file size with the ulimit -c 文件大小 command.

Here I run ulimit -c unlimited to set it to unlimited.

You can use ulimit -a again to check the result.

image-20220524144433781

As you can see, the core file size is now set to unlimited.

Note that this setting only takes effect for the current user and will be lost after you log in again.

Next, set the naming format of the generated core files.

First, ask “man” for information about core files.

man core

We can see the following content about the file name. (Luckily there are no particularly complicated words 😏)

image-20220524145111591

Roughly speaking, modifying the content of the /proc/sys/kernel/core_pattern file can change the default naming of core files.

Here’s a quick demonstration.

echo 'core-%s-%p-%t' > /proc/sys/kernel/core_pattern

core dumped 代码测试

首先写一个会产生段错误的代码,这里用空指针赋值为例。

Core Dump Code Test

First, write a piece of code that causes a segmentation fault; here we use assigning a value to a null pointer as an example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
* filename:test.cpp
*/
#include <iostream>
using namespace std;

void coredump2(int x){
int* p = nullptr;
*p = x; // 这一行出现错误
return;
}

void coredump1(int x){
coredump2(x); //
return;
}

int main(){
int a = 2;
coredump1(a); //
cout << ++a+a++ << endl;
cout << a+++a++ << endl;
cout << ++a+(++a) << endl;
return 0;
}

使用 g++ 编译,这里加上 -g 调试模式选项,-O0 不开启编译器优化, -Wall 开启所有警告信息。

g++ -o app test.cpp -O0 -Wall -g

编译成功后,执行./app会导致程序崩溃,并产生相应的 core 文件。

image-20220524151640621

接下来可以使用 gdb 调试 core 文件来定位崩溃原因。

需要执行 gdb app core-11-259677-1653376476

image-20220524152647793

此时可以定位到程序崩溃位置为 test.cpp 文件的第 9 行。

使用 bt 命令可以查看程序崩溃时的函数调用栈的情况,方便我们进一步定位问题。

image-20220524152834389

OK !

距离拿到 offer 又进了一步。😂

那么,另一个问题来了,如果主函数中删除掉 coredump1 函数的调用,程序的输出是多少呢?😏

感兴趣的同学试验一下,期待路过的大佬留言给我解释一下,我是解释不通了。😥

Compile with g++, adding the -g debug mode option; -O0 disables compiler optimization, and -Wall enables all warnings.

g++ -o app test.cpp -O0 -Wall -g

After a successful compilation, running ./app crashes the program and produces the corresponding core file.

image-20220524151640621

Next, we can use gdb to debug the core file and locate the cause of the crash.

Run gdb app core-11-259677-1653376476

image-20220524152647793

At this point, we can locate the crash position at line 9 of test.cpp.

Using the bt command, we can inspect the function call stack at the moment of the crash, which helps us further locate the problem.

image-20220524152834389

OK !

One step closer to getting the offer. 😂

Now here’s another question: if we remove the call to coredump1 in the main function, what would the program output? 😏

If you’re interested, try it out. I look forward to an expert passing by leaving a comment to explain it to me, since I can’t figure it out myself. 😥