【CSDN 编者按】我们在开发或使用一个程序时,最怕的就是程序莫名其妙的当掉,虽然系统没事,但我们下次仍可能遇到相同的问题。这到底该如何解决呢?

责编 | 欧阳姝黎

缘来缘起

core的最原始含义是磁芯,是一种存储设备,dump的意思是倒出,那么core dump的含义就是:当进程发生异常时,会把当时的内存信息倾倒出来,形成core文件。

每个做linux C++开发的人,必然会遇到过core dump问题。在C++相关的面试中,core dump的调试,几乎是一个必考的考点,旨在检验应聘者的实战调试经验。

我知道的一个真实案例是:面试官让应聘者现场写出一个core dump程序,结果应聘者很懵圈,不知道怎么写。这说明,应聘者没有相关的调试经历,何谈通过面试?

接下来,我们以一个简单的core dump程序为例,来说说调试core dump的六种经验和方法,希望能对大家的开发实战有所帮助,顺便地,横扫那些简单的面试题。

本文示例的core dump程序如下:

#include <stdio.h>void swap(int *px, int *py)
{int tmp = *px;*px = *py;*py = tmp;
}int main()
{int a = 1;int b = 2;int c = a + b;printf("%d, %d, %d\n", a, b, c);swap(&a,& b);printf("%d, %d, %d\n", a, b, c);int *p = NULL;*p = 0;return 0;
}

方法一: 代码review

代码review,是一种比较原始的笨方法。对于简单的代码而言,还可以进行review, 但是,一旦代码达到数万行,出现core dump后,便无从看起。所以,这种方法很鸡肋,几乎没什么用。

方法二: 打印log夹逼

打印log来夹逼,也是一种很简单的方法,在很多场景下,非常奏效。许多大学生和职场新手,容易出现core dump问题,那么, 我建议直接用log夹逼。有点类似二分查找,且看具体的姿势:

#include <stdio.h>void swap(int *px, int *py)
{int tmp = *px;*px = *py;*py = tmp;
}int main()
{                        printf("xxx1\n");int a = 1;       printf("xxx2\n");int b = 2;       printf("xxx3\n");int c = a + b;   printf("xxx4\n");printf("%d, %d, %d\n", a, b, c);  printf("xxx5\n");swap(&a,& b);    printf("xxx6\n");printf("%d, %d, %d\n", a, b, c);  printf("xxx7\n");int *p = NULL;  printf("xxx8\n");*p = 0;         printf("xxx9\n");printf("xxx10\n");return 0;
}

编译运行一下:

ubuntu@VM-0-15-ubuntu:~$ g++ -g test.cpp
ubuntu@VM-0-15-ubuntu:~$
ubuntu@VM-0-15-ubuntu:~$ ./a.out
xxx1
xxx2
xxx3
xxx4
1, 2, 3
xxx5
xxx6
2, 1, 3
xxx7
xxx8
Segmentation fault (core dumped)
ubuntu@VM-0-15-ubuntu:~$

很显然,有xxx8,但没有xxx9, 所以,必然是第21行出了问题。

方法三: dmesg + addr2line

有时候,如果core dump的开关没有打开,无法生成core文件,那怎么办呢?也是有办法的!用dmesg和addr2line吧。关于这两个命令的介绍,直接man一下即可。且看具体调试:

ubuntu@VM-0-15-ubuntu:~$ g++ -g test.cpp
ubuntu@VM-0-15-ubuntu:~$
ubuntu@VM-0-15-ubuntu:~$ ./a.out
Segmentation fault (core dumped)
ubuntu@VM-0-15-ubuntu:~$
ubuntu@VM-0-15-ubuntu:~$ dmesg
a.out[3709]: segfault at 0 ip 080483c9 sp bff75a60 error 6 in a.out[8048000+1000]
ubuntu@VM-0-15-ubuntu:~$ addr2line -e a.out 080483c9
/home/ubuntu/test.cpp:21

很显然,代码的第21行出了问题。

方法四: strace + addr2line

接下来,我们介绍一个重要的linux命令,即strace, 直接man一下就知道,它是用查看系统调用的,我们不过多赘述。来看具体的调试过程:

ubuntu@VM-0-15-ubuntu:~$ g++ -g test.cpp
ubuntu@VM-0-15-ubuntu:~$
ubuntu@VM-0-15-ubuntu:~$ strace -i ./a.out
[00ff4424] execve("./a.out", ["./a.out"], [/* 22 vars */]) = 0
[0086e2fd] brk(0)                       = 0x818e000
[0086f6d3] mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb771c000
[0086f5d1] access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
[0086f494] open("/etc/ld.so.cache", O_RDONLY) = 3
[0086f45e] fstat64(3, {st_mode=S_IFREG|0644, st_size=49072, ...}) = 0
[0086f6d3] mmap2(NULL, 49072, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7710000
[0086f4cd] close(3)                     = 0
[0086f494] open("/lib/libc.so.6", O_RDONLY) = 3
[0086f514] read(3, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0 N\211\0004\0\0\0"..., 512) = 512
[0086f45e] fstat64(3, {st_mode=S_IFREG|0755, st_size=1855584, ...}) = 0
[0086f6d3] mmap2(0x87e000, 1620360, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x87e000
[0086f754] mprotect(0xa03000, 4096, PROT_NONE) = 0
[0086f6d3] mmap2(0xa04000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x185) = 0xa04000
[0086f6d3] mmap2(0xa07000, 10632, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xa07000
[0086f4cd] close(3)                     = 0
[0086f6d3] mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb770f000
[0085a552] set_thread_area({entry_number:-1 -> 6, base_addr:0xb770f6c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
[0086f754] mprotect(0xa04000, 8192, PROT_READ) = 0
[0086f754] mprotect(0x876000, 4096, PROT_READ) = 0
[0086f711] munmap(0xb7710000, 49072)    = 0
[00ba1424] fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
[00ba1424] mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb771b000
[00ba1424] write(1, "1, 2, 3\n", 81, 2, 3
)     = 8
[00ba1424] write(1, "2, 1, 3\n", 82, 1, 3
)     = 8
[08048479] --- SIGSEGV (Segmentation fault) @ 0 (0) ---
[????????] +++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)
ubuntu@VM-0-15-ubuntu:~$
ubuntu@VM-0-15-ubuntu:~$ addr2line -e a.out 08048479
/home/ubuntu/test.cpp:21

很显然,代码的第21行出了问题。

方法五: valgrind

之前,在调试内存泄漏时,介绍过valgrind,其实valgrind能查其他更多内存问题,非常强大。下面,我们来看看valgrind查core dump问题,如下:

ubuntu@VM-0-15-ubuntu:~$ g++ -g test.cpp
ubuntu@VM-0-15-ubuntu:~$
ubuntu@VM-0-15-ubuntu:~$ valgrind -v ./a.out
==23889== Memcheck, a memory error detector
==23889== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==23889== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==23889== Command: ./a.out
......(部分非关键信息,我省略了哈)
==23889== Invalid write of size 4
==23889==    at 0x4006D6: main (test.cpp:21)
==23889==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==23889==
==23889==
==23889== Process terminating with default action of signal 11 (SIGSEGV)
==23889==  Access not within mapped region at address 0x0
==23889==    at 0x4006D6: main (test.cpp:21)
==23889==  If you believe this happened as a result of a stack
==23889==  overflow in your program's main thread (unlikely but
==23889==  possible), you can try to increase the size of the
==23889==  main thread stack using the --main-stacksize= flag.
==23889==  The main thread stack size used in this run was 8388608.
--23889-- REDIR: 0x4ebe4f0 (libc.so.6:free) redirected to 0x4c2ed80 (free)
==23889==
==23889== HEAP SUMMARY:
==23889==     in use at exit: 0 bytes in 0 blocks
==23889==   total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==23889==
==23889== All heap blocks were freed -- no leaks are possible
==23889==
==23889== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
==23889==
==23889== 1 errors in context 1 of 1:
==23889== Invalid write of size 4
==23889==    at 0x4006D6: main (test.cpp:21)
==23889==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==23889==
==23889== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)
ubuntu@VM-0-15-ubuntu:~$

很显然,我们可以看到,第21行有问题,进程在21行core dump了。

方法六: gdb

gdb调试,是本文的重头戏,也几乎是笔试面试的必考内容。话不多说,直接来看姿势。使用gdb a.out core(不会重新拉取a.out进程)或者gdb a.out(会重新拉起a.out进程)都可以,如下:

ubuntu@VM-0-15-ubuntu:~$ g++ -g test.cpp
ubuntu@VM-0-15-ubuntu:~$
ubuntu@VM-0-15-ubuntu:~$ gdb a.out
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...done.
(gdb) r
Starting program: /home/ubuntu/a.out
1, 2, 3
2, 1, 3Program received signal SIGSEGV, Segmentation fault.
0x0000000000400646 in main () at test.cpp:21
21                                                                                                  *p = 0;
(gdb) bt
#0  0x0000000000400646 in main () at test.cpp:21

显然,程序在第21行core dump了。gdb的调试,尤为重要,必须掌握。

最后的话

方法千万条,搞定问题第一条。在后续文章中,我们会更多地介绍各种调试方法和技巧,快速查杀bug, 这样大家就可以少加班啦。祝顺利。

明晚(周二)晚19点准时开播!点击阅读原文,预约直播,中奖的几率更高哦~

☞苹果因不带充电器被罚款200万美元;杨笠代言英特尔被抵制,品牌方连夜下架;Linux考虑加入对Rust的支持 | 极客头条☞Flutter 即将占领整个 Web 开发☞“Mac 不靠谱”,被苹果放弃的英特尔开启“嘲讽技能”!
☞为什么不能完全相信自动驾驶?

六招制敌,搞定 core dump 问题相关推荐

  1. u盘电视测试软件,智能电视安装软件无法识别U盘怎么办?简单几招教你搞定!...

    原标题:智能电视安装软件无法识别U盘怎么办?简单几招教你搞定! U盘是智能电视及网络机顶盒安装软件必备的工具,但是也经常会出现U盘插在智能设备上不识别的情况,那么针出现这类情况有哪些原因呢?又该如何解 ...

  2. flac格式如何转mp3,3招帮你搞定

    flac格式如何转mp3,3招帮你搞定的方法来啦.当你的音频是flac格式是不是很头疼,又不知道怎么转mp3 .然后网上搜索出很多方法又不知道从哪个下手,是不是很疑惑?那今天就来看看小编推荐的方法吧, ...

  3. 计算机怎样设置开机音乐,电脑开机声音听腻了怎么办?一招教你搞定

    Hello,大家好,我是余生.如果今天带来的内容足够精彩,希望各位动手来给小编评论点赞喔!您的每一次评论点赞都会带来好运气喔! 如何修改电脑开机声音?一招教你搞定 对于经常使用电脑的朋友,每次电脑开机 ...

  4. MIUI V5八门神器不能用?一招教你搞定

    为什么80%的码农都做不了架构师?>>>    在本月9号刚刚结束的"米粉节"上,小米公司不仅推出了一系列新的产品,更是放出了全新MIUI V5的正式版.一时间, ...

  5. 计算机上面的东西爆满怎么清理,电脑C盘满了怎么清理?一招帮你搞定

    在日常电脑的使用中,我们总是会发现C盘的东西越来越多,然而对于C盘里面的东西我们又不知道如何清理,很多系统文件又不懂识别,无用和缓存文件堆积的特别多,最后导致C盘爆满,电脑卡顿.运作效率慢.那么,当C ...

  6. xp无法访问查找工作组计算机,一招教你搞定XP“网上邻居”、“查看工作组计算机”打不开的情况...

    作 者:杜超-2号   ID:16058  城市:江阴 摘 要:一招教你搞定XP"网上邻居"."查看工作组计算机"打不开的情况 正 文: 在一些被优化过的XP系 ...

  7. linux修改mysql配置文件_忘记MySQL密码怎么办?一招教你搞定!

    在安装完 MySQL 或者是在使用 MySQL 时,最尴尬的就是忘记密码了,墨菲定律也告诉我们,如果一件事有可能出错,那么它一定会出错.那如果我们不小心忘记了 MySQL 的密码,该如何处理呢?别着急 ...

  8. 忘记MySQL密码怎么办?一招教你搞定!

    作者 | 王磊 来源 | Java中文社群(ID:javacn666) 转载请联系授权(微信ID:GG_Stone) 在安装完 MySQL 或者是在使用 MySQL 时,最尴尬的就是忘记密码了,墨菲定 ...

  9. 不花一分钱,七个小招式简单搞定新车异味

    新车提回来味道较大,一些刺鼻的气味让人觉得头晕恶心,如果在车里呆久了,这些异味会使人头晕目眩.精神恍惚最重要的是还让人无法防备.那么有哪些办法可以快速,方便,经济的去除味道呢?这次有车大师来跟大家说说 ...

最新文章

  1. Git 分支 - 分支的新建与合并
  2. 30天敏捷结果(19):你在为谁做事?
  3. python实现Linux命令wget
  4. (3)[wp7数据存储] WP7 IsolatedStorage系列篇——通过XmlSerializer读写XML文件 [复制链接]...
  5. Java实现点击导出excel页面遮罩屏蔽,下载完成后解除遮罩
  6. git 32位_完整的GIT笔记 快速上手小白教程
  7. VC++2012编程演练数据结构《35》多路平衡归并
  8. android gdb gdbserver
  9. python生成密码字典
  10. 如何将3DMAX参数重置为默认值?
  11. Python编程题(二)
  12. 盈利“晴空”下,唯品会拨不开的“乌云”
  13. RoNIN: Robust Neural Inertial Navigation预训练模型测试
  14. 【附源码】计算机毕业设计SSM校园论坛
  15. Java校验框架使用@Valid、@Validated、OVAL+Groovy
  16. 实现图片加载先模糊后清晰的效果
  17. 微软 CTO 韦青:“程序员 35 岁就被淘汰”是个伪概念
  18. C#操作Excel表格,不积硅步无以至千里
  19. 计算机会计综合实训心得体会,会计电算化实训心得总结
  20. markdown 换行

热门文章

  1. JS实现图片翻书效果
  2. 关于layui.laypage.render 刷新首页没有分页问题
  3. android service 样例(电话录音和获取系统当前时间)
  4. node(基础)_node中的javascript
  5. 27_线程安全操作及其案例
  6. 20165309 实验三 敏捷开发与XP实践
  7. Intellij_idea-15 常用快捷键
  8. iOS页面间跳转的方式
  9. 查看MySQL以及SQL Server 实际存储类型
  10. [论文阅读] Stereoscopically Attentive Multi-scale Network for Lightweight Salient Object Detection