zoukankan      html  css  js  c++  java
  • Linux程序调试-常用调试技巧

    程序调试阶段:

    测试:找出程序的错误或缺陷

    固化:让程序错误可重现

    定位:确定相关代码行

    纠正:修改代码 修正错误

    验证:确定修改解决了问题

    1 gcc -Wall -pedantic -ansi   //gcc 编译  产生编译的警告信息

    1取样法:在程序中添加printf等输出程序执行过程中的信息,程序错误修复后需要删除

    1 #ifdef DEBUG
    2     printf("….
    ");
    3 #endif

    定义调试级别,输出不同类型的内容

     
    1 #define BASIC_DEBUG 1
    2 #define EXTRA_DEBUG 2
    3 #define SUPER_DEBUG 4
    4 #if (DEBUG & EXTRA_DEBUG)
    5     printf...
    6 #endif

    C语言预处理器定义的一些宏可以帮助我们调试(符号前后各有两个下划线)

       

    无需编译的调试技巧 定义全局变量debug 用户在调用程序执行时使用 -d 调试选项,决定是否打开调试模式

    将调试信息存储于文件,可以方便自身或用户自行调试代码,查找问题

    1 if (debug) {
    2     sprintf(msg, ...)
    3     write_debug(msg)
    4 }

    程序的受控执行

    商业版本常见的调试器有adb、sdb、idebug、dbx等 能用那些调试器取决于UNIX系统

    GNU使用调试器gdb,一些gdb的前端程序提供非常友好的界面,xxfdb,KDbg,ddd等

    -g 选项是对程序进行调试性编译的常用选项,需要在编译每个文件时都加上这个选项,对链接器也要加上-g选项。(编译器会把这个标志自动传递给链接器)

    调试信息会使可执行程序的长度成倍增加(最多10倍),虽然可执行程序的大小增加,但使用内存的数量和原来是一样的。

    调试完后才能后,可以不经过编译将可执行文件中的调试信息删除

    1 strip <file>

    使用gdb进行调试

    2019123

    10:30

    开始调试

     
    1 //进入调试器
    2 gdb ./xxx    //xxx为可执行文件  此时进入gdb软件  help可查看gdb提供的命令选项
    3 //运行
    4 run [option]  //  [option]将作为参数传递给程序  在程序执行错误,gdb将在出错位置退出  若编译时使用了 -g选项
    5 //则在程序停止后输出程序终止的位置

    栈跟踪

    在到达错误位置时,输入backtrance 简写 bt 或 where 输出调用出错函数的函数和出错函数的位置

    检查变量

    print 可以给出变量和其他表达式的内容,并将表达式的值赋给伪变量 $<number>

    最后一次操作的结果总是以$开头,而倒数第二次的结果为$$

    列出程序源代码

    list

    设置断点

    1 break 21 //在21行处打断点 打断点之后可以用print 输出当前关注的变量值
    2 cont //程序继续执行到下一个断点处
    3 display //程序每次停在断点位置时,自动打印关心的变量值
    4 command //指定程序在到达断点时执行的命令,以end结束 此时设置 >cont >end

    程序每次运行到断点处,自动打印关心的内容,并自动调用程序继续执行指令 设定完成后程序将一直执行到最后 并在过程中输出值

       

    使用调试器打补丁 gcc 可以在程序进行调试时 直接更改变量的值来进行调试

    程序下一次调试,使用info display 和 info break 来查看当前显示与断点的内容

    1 set variable n = n+1 //设置在调试时,将变量n的值 +1

    深入学习gdb 强大的功能

    1.在支持硬件断点的cpu上,gdb支持可以在符合某个条件时暂停程序运行

    2.gdb可监控表达式的,即当某个表达式取一个特定的值时,gdb可以暂停程序的运行(这样会对性能造成影响)

    3.断点、计数、条件可以结合在一起设置

    4.gdb还可以将自己附在正在运行的程序上,对异常的程序可以在调试过程中直接进行修改,而不必停下编译并重启

    可以在编译时用 gcc -O -g来同时获得程序优化和调试信息 但优化可能会改变程序执行顺序

    5.调试崩溃的程序时,Linx通常会产生一个 核心转存储(core dump).这个文件是程序的内存映像文件

       

    一些工具

    lint splint LClint等 清理程序中的垃圾,严格编译程序,产生警告

    函数调用工具

    ctags 为程序中所有的函数创建索引,每个函数对应一个列表,列表列出函数调用位置

    cxref 程序分析C语言源代码并生成一个交叉引用的表格

    cflow 程序打印出一个函数调用树(function call tree),显示函数之间的调用关系,可以理清程序调用架构,理解操作流程,了解函数的改动将会产生什么样的影响

    prof/gprof 产生执行存档

    想要查找程序的性能问题时,一种常用的技巧是执行存档(execution profiling),需要特殊的编译选项,执行存档可以显示执行它所花费的时间具体用在哪些步骤上。

    给编译器加上 -p 标志(针对prof程序) -pg 标志(针对gprof)

    之后执行程序时,将生成mon.out(gmon.out)文件

    断言 assert

    测试某个假设是否成立,如果不成立就停止程序的运行

     
    #include <assert.h>
    void assert(int expression)
    1 assert //宏对表达式进行求值,如果结果为0,就向标准错误输出一些诊断信息,然后调用abort函数结束程序运行
    2 
    3 #define NDEBUG //关闭断言宏

    在产品中保留assert并不可取,因为不希望客户在看到一条assert之后程序强制退出,好的方法是编写自己的错误中断陷阱例程

       

       

    内存调试

    2019123

    13:50

       

    内存调试

    1.内存泄露,malloc申请内存后赋给指针,指针的值被改变,此时没有任何指针指向申请的内存,程序运行时间长了之后将会越来越慢,导致内存耗尽

    2.在一个已分配的内存块尾部的后面(或在它的前面)写数据,就很可能会损坏malloc库用于记录内存分配的数据结构。之后,一个malloc或free调用都会导致段错误(Segmentation fault)。此时检查错误发生的地点是很困难的。

    ElectricFence可以使用Linux的虚拟内存机制来保护malloc 和 free使用的内存。

    使用虚拟内存,在出现非法的内存访问时,引发段冲突信号并停止程序的运行

    valgrind 可以检测出前边所说的很多问题,特别是可以检测出数组访问错误和内存泄露

    在程序运行结束时进行内存泄露的检查 使用 valgrind --leak-check=yes 选项

  • 相关阅读:
    select option 下拉多选单选bootstrap插件使用总结
    bootstrap-dialog的使用
    display的table和cell外加table-layout:fixed等分布局,外加换行,word-wrap:break-word
    css样式实现字体删除线效果
    递归实现遍历二叉树
    童晶老师的游戏开发课程作业--实时时钟的实现
    张宵 20201120-1 每周例行报告
    张宵 20201112-1 每周例行汇报
    20201105-1 每周例行报告
    张宵 20201029-1 每周例行报告
  • 原文地址:https://www.cnblogs.com/Mrwang-tju/p/12468628.html
Copyright © 2011-2022 走看看