文章目录

  • (一)、core.dump
  • (二)、dmesg
  • (三)、pstack
  • (四)、strace
  • (五)、valgrind

对于c++程序来说,以segment fault为代表的程序异常行为前奇百怪,没有一套比较丰富的工具集去对付他们,在处理实际问题时就会显得捉襟见肘。本文列举几种程序异常的定位方法。

(一)、core.dump

最有效的一种方法,可以在程序挂断之后通过core文件定位程序错误的地方,而且不影响程序运行。需要以下三个步骤。

  1. 在编译可执行的时候需要给g++加参数-g,否则不能够定位到具体的行数。

如何判断一个可执行是否加了参数-g编译出来的?
readelf -S sonodebug |grep debug,若有输出则是加了-g编译的

  1. 允许系统生成core file
    开启方法为在 /etc/profile 文件中加入ulimit -c unlimited,查看系统是否允许生成core文件可以用命令ulimit -c查看,或者用ulimit -a查看其中的core file size 行。有一点需要注意的是,进程到底是否会在down掉时生成core文件不取决于当前系统是否允许生成core文件,还需要在启动程序时就开启core文件。查看要分析的进程是否会产生core文件的方法:
ps -ef|grep a.out#查询到进程号
cat /proc/pid/limits|grep "Max core file size"#为0则不运行

Max core file size 字段的Soft Limit和Hard Limit值,才是标明了该进程是否真的能生成core文件。

  1. gdb带core文件运行可执行文件
gdb ./aout core.file

gdb调试core文件的一些常用命令:

  • 查看案发现场情况,函数调用关系,一层一层顺藤摸瓜。
(gdb)bt          //这个命令会列出程序崩溃时的堆栈信息,一层一层会有标号  #0  #1  #2 ....
  • 想要局部放大,具体查看某一层具体的堆栈变量等信息
(gdb)f N //其中N是项查看的层数,在bt命令里会有打印
  • 来到某一层之后,想查看具体信息用以下命令
(gdb)info args//打印当前函数的参数以及其值
(gdb)info locals//打印所有的局部变量
(gdb)info catch//打印当前函数的异常处理信息
(gdb)p var//打印具体的某一个变量值

gdb+core文件的方法,可以定位到常见的绝大数错误。但也存在一些不好解决的问题

  • 由于某些原因,例如程序退出时破坏了堆栈等,在用gdb的bt命令打印堆栈信息时,会出现不能定位的情况,出现全是 ?? 的情况,让人不知所以然。以下这个例子就是这种情况,当时用gdb+core不管用,gdb前台运行结果一样,最终使用valgrind定位到了问题。
  gdb ./a.out core.out……………… ………………Program terminalted with signal 11, segmentation fault.#0 __memmove_ssse3_back () at ../sysdeps/x86_64/multiarch/memcpy-ssse3-back.S:1658 1658        movdqu  -0x40(%rsi),%xmm4 (gdb) bt#0 __memmove_ssse3_back () at ../sysdeps/x86_64/multiarch/memcpy-ssse3-back.S:1658#1 0x0000000000000000 in ?? () (gdb) quit
  • 由于运行环境和调试环境不同,出现定位不准确的问题。

(二)、dmesg

/var/log/message 中记录了程序的异常退出,dmesg也可以列出程序的段错误。dmesg + grep a.out 可以查出程序是否发生了segment fault。
dmesg中有哪些有用信息呢?它可以告诉我们程序的内存错误类型和错误位置。以下是可执行文件sowithdebug段错误之后,用dmesg |grep sowithdebug获得的错误信息。

sowithdebug[6847]: segfault at 28 ip 00000000004008cc sp 00007fff016d9400 error 4 in sowithdebug[400000+2000]

其中的ip后面的数字是错误的行数(4008cc), in 后面的是错误的文件(sowithdebug)。然后用objdump可以查错误的汇编代码,用addr2line可以定位到具体的行数。

dmesg |grep sowithdebug                    #查询错误文件及行数
objdump -d sowithdebug |grep 4008cc        #查询汇编指令
addr2line -e sowithdebug 4008c             #定位行数

error 4是错误的类型,表示用户态程序读操作访问越界。
error后边数字的含义:

bit2: 值为1表示是用户态程序内存访问越界,值为0表示是内核态程序内存访问越界
bit1: 值为1表示是写操作导致内存访问越界,值为0表示是读操作导致内存访问越界
bit0: 值为1表示没有足够的权限访问非法地址的内容,值为0表示访问的非法地址根本没有对应的页面,也就是无效地址

dmesg的不足之处:

  • 在可执行没有加 -g 编译时依然不能定位行数
  • 如果dmesg显示的出错文件是动态库so文件,问题位置依然比较难以定位
    这篇博文非常非常精彩的介绍了dmesge定位出错行数的一些方法,包括处理静态库和动态库的情况:《在Linux中如何利用backtrace信息解决问题》

(三)、pstack

要想清楚进程此刻正在忙什么,pstack是一个很好的工具。它可以告诉我们各线程的函数调用情况。

pstack PID  #PID既可以是进程号也可以是线程号

当pstack加进程号的时候,它会打印出所有的线程运行情况。如果加线程号,就打印此线程的函数调用情况。

其中的LWP就是线程号。

pstack的弊端:
只是进程的一个静态照片,不能把握程序的整体运行流程。多次执行pstack,或者结合watch -n 0.1,通过“高频率的快照近似看视频”的方式,掌握程序的运行情况。但watch的最高频率是0.1s。

(四)、strace

如果说pstack是给各线程照了一张照片,那么strace就是给各线程录视频。
strace可以实时动态的查看程序的运行情况,使用起来也很方便。

ps -ef|grep a.out      #查出进程的PID
strace -p PID          #更踪此进程的运行(既可以跟踪进程PID,也可以是线程PID)

strace 也可以直接运行程序:

strace -f -F -o out.log a.out   #a.out是可执行文件,out.log是运行结果

其中的 -f , -F 告诉strace同时跟踪fork和vfork出来的进程,否则strace只会跟踪主线程。

strace的弊端:
它打印的都是一些系统调用,需要对这些系统调用有比较熟悉的掌握,才能清楚strace告诉我们的到底是什么。
这篇博文很精彩的介绍了strace的一些使用方法,很巧妙的解决一些问题:《strace命令详解》

(五)、valgrind

valgriind是一个代码动态检测工具,它是一个大的工具集,有几个小组件。其中比较常用的是memcheckhelgrind

  1. memcheck检测内存问题
valgrind --leak-check=full --show-reachable=yes --trace-children=yes --log-file=./valgrind.log  ./a.out

–leak-check=full指的是完全检查内存泄漏,–show-reachable=yes是显示内存泄漏的地点,–trace-children=yes是跟入子进程。
以上介绍的我遇到的gdb+core出现的不能定位问题,gdb只仍给我一行又一行的问号问题,就是用valgrind的找到原因的。用以上参数运行程序,最后的日志会非常庞大而杂乱,可以重点关注其中的内存写(write)错误,程序断掉一般都是这些错误导致的。

  1. helgrind检测线程问题
    用工具helgrind检测线程问题:
valgrind --tool=helgrind --trace-children=yes ./a.out

它可以检测到大部分的共享资源竞争。错误类型也分的比较细致。

  1. 检测内存泄漏
    运行命令同 1. memcheck检测内存问题 ,但是需要注意的是要让valgrind运行结束,他才会给出内存报告。所以,如果我们的程序是一个可以自动推出的程序,就不会存在问题,等程序运行完毕,valgrind也就会推出,内存报告就给出了。但是如果我们的程序是一个不会自己退出的程序,就需要用killall 命令杀死valgrind,以让其生成内存报告。然后在valgrind的输出中grep “definitely lost”,是内存泄漏最严重的地方,是必须修改的地方。如果valgrind报告因为错误太多超过1千,不再显示新的错误,就需要修改参数改为不限制错误数量。
killall memcheck-    #先用top看一下valgrind实际运行的进程名称,killall杀死

killall 默认发送SIGTERM(15),如果用kill -9 直接杀死 memcheck是不能生成内存报告的

用valgrind定位程序问题的弊端:

  1. valgrind是动态工具,用valgring带程序跑,必须要等到问题复现,valgrind才能报告给我们有用的信息。
  2. valgrind带程序跑之后,会大大降低程序的运行速度。如果在生产环境上调试,这有可能是一个必须忍受的问题。

c++程序的异常定位,是一个非常复杂的问题,写出没有bug的c++代码,也是一项几乎不可能完成的任务。掌握排错工具和方法,就是一个非常重要的技能。后续实践中又好的排错工具和方法,会继续更新本文。

c++程序异常定位方法相关推荐

  1. stm32进入HardFault的异常定位方法

    在用Keil对STM32的程序进行仿真时程序有时会跑飞,停止仿真程序会停在HardFault_Handler函数里的死循环while(1)中.这说明STM32出现了硬件错误. STM32出现硬件错误可 ...

  2. ARM 之十二 Cortex-M 内核异常处理、异常定位方法、在线调试、Keil MDK-ARM 的使用

      Cortex-M 内核本身提供了非常强大的异常处理机制.它可以非常有效的捕捉非法的内存访问以及其他一些异常.而我们常用的开发工具的异常处理就是使用了 Cortex-M 核的异常处理机制.   在 ...

  3. vmlinux 反汇编_ARM Linux内核驱动异常定位方法分析--反汇编方式

    通常认为,产生异常的地址是lr寄存器的值,从上面的异常信息可以看到[lr]的值是c01a4e30. 接下来,我们可以通过内核镜像文件反汇编来找到这个地址.内核编译完成后,会在内核代码根目录下生成vml ...

  4. 微服务系统异常检测和根因定位 方法综述

    CSUR22 - Anomaly Detection and Failure Root Cause Analysis in (Micro) Service-Based Cloud Applicatio ...

  5. Android Native程序crash的一些定位方法简介

    Android Native程序crash的一些定位方法简介 经常,避免不了,我们的代码会崩溃.如果crash在native代码上,Android会和其他Linux一样,生成一份core dump,将 ...

  6. windows 程序异常崩溃等错误定位

      MAP/映射文件 1.      MAP 映射文件的作用:MAP文件可以查找崩溃或者程序异常地址,然后就可以精确地定位到源代码中出错的代码行. 2.VS中生成MAP文件的方法,项目属性中选择生成映 ...

  7. 通过gdb core dump方法查看程序异常时的堆栈信息

    在Linux下可通过core文件来获取当程序异常退出(如异常信号SIGSEGV, SIGABRT等)时的堆栈信息.core dump叫做核心转储,当程序运行过程中发生异常的那一刻的一个内存快照,操作系 ...

  8. 解决“安装程序无法定位现有系统分区,也无法创建新的系统分区”的方法

    使用老毛桃PE格式化C盘后安装Win7出现"安装程序无法定位现有系统分区,也无法创建新的系统分区"的错误.本文给出了我遇到该情况的解决办法,亲身经历,绝非抄袭. 在网上看了好多办法 ...

  9. linux下怎么查看程序异常,如何检测、定位linux程序异常

    作为linux开发,在工作中或者面试的时候经常会遇到怎么检测程序异常的问题,下面对此作一个总结. 0. 查看程序日志.项目更新日志,发现可疑的地方 使用linux查看内存cpu指令,top.ps初步看 ...

最新文章

  1. 120000字,你们要的Java 并发编程图文小册整理出来了,免费送给大家!
  2. jquery通过ajax提交form
  3. 关于登陆到域的用户,不需要显示登陆界面的问题(aspx)
  4. 阿里云的很多域名没有办法进行实名认证了吗
  5. .toString(c) 将数字值 渲染成 货币形式
  6. Lab1--关于安装JUnit的简要描述
  7. 【Elasticsearch】如何构建一个好的电商搜索引擎?
  8. 电子科大计算机2014级,电子科大-计算机-操作系统实验报告-2014级.docx
  9. php处理管道文件流
  10. JAVA菜鸟教程(一)
  11. 海康威视Java SDK实战
  12. DNSPod十问党霏霏:充电桩是披着高科技外皮的传统基建?
  13. Mac实现ts文件转为mp4文件
  14. Pytorch基础知识(7)单目标检测
  15. 如何调用Sphinx
  16. 3.3.9nbsp;艾利·高德拉特——TOC制…
  17. 服务器怎么导入皮肤文件,Malody皮肤导入的详细技巧
  18. 用VC++进行MapX二次开发::之三------使用MapX工具
  19. Vue3通过axios来读取本地json文件
  20. 干掉 强硬的 css !important

热门文章

  1. 情人之间礼物推荐 情人节该送什么礼物
  2. 江苏大学计算机考研资料汇总
  3. IEC104模拟终端
  4. wmm(Wi-Fi MultiMedia)
  5. 《小学数学“核心问题”教学模式研究》开题报告
  6. 烤仔喊你写作业 | 这么可爱的烤仔有人领养吗?
  7. NEUQ-ACM预备队-week8-DP2
  8. 安卓高通机型的基带移植 修改 编译的相关 增加信号 支持5G等
  9. CSS动画制作绽放的花朵
  10. 智慧旅游背景下的景区新模式建设方案研讨