• 总结下c/c++的一些调试经验


    工作2年,干了一年ARM平台嵌入式,一年后台,总结下这两年开发中调试的经验。我把调试手段分成2种:打印日志和用工具分析。因为平时主要开发在Linux平台,就以GDB为例

    一、打印日志

    1. 合理设置日志级别

      一般的日志库都会有日志级别,合理使用能避免因线上环境的日志打印过大而导致的磁盘占用过高,搜索日志耗时太长,因为日志大小超过设置值,导致一天打印日志数过多等问题。线上环境的日志一般需要以下级别:

    Info,表示业务走到关键点时候的变量信息,队列消费数目,等待处理数目这些用于定位业务是否正常,分析程序性能所需要的指标信息

    Warn,正常情况下程序不应该出现这种状态,但不是致命错误

    Error,程序异常了,或者已经挂掉了

    而测试环境,功能测试时可以把日志设置成Debug级别,最后代码提交时,可以带有Debug级别的日志,但务必保证留下的日志精简,明确。一个精简的调试日志,可以替代大部分函数内注释

    2. 善于运用内置的调试宏

      c++提供的几个宏:__FILE__ , __FUNCTION__ , __LINE__ 帮助定位代码位置

    void Foo(){
        //error here
        Log.Error<<__FUNCTION__<<"  is errors in Line["<<__LINE__<<"] varable is "<<var<<endl;
    }

    当然,有的库自带了这些信息

    3. 日志划分

      多线程下,一个模块可能有多个业务流程。日志划分时最好按照业务,或者模块划分。比如数据库操作的一个日志,和消息中间件通信的一个日志。便于日志归档管理

    4. 避免滥用日志

    日志级别设成info时,debug是不会打的。但这并不代表这条debug级别的日志不会消耗cpu的资源。看下面这个例子

    log.debug("fun[%s] return [%d]", __func__, fun());

    这条日志不会被打印,但仍然会执行,并且消耗cpu。理由有2点。

    1. 日志的接口函数被调用了,只是判断级别不够,不输出到日志文件。
    2. fun()作为函数的实参传递,fun先被掉用,再将结果传给日志接口

    所以一定不能觉得测试的日志反正线上不会显示,就可以随意打印。

    总结起来,日志需要注意以下3点

    •  一条日志描述清楚when,what,where信息
    • 在可能出现问题的地方打日志,通过其他日志能推断出信息的地方,无需再打日志
    • 调用日志打印接口时,不要调用函数

    二、GDB调试以及coredump分析

    1. ELF文件

     ELF(Executable Linkable Format)是COFF(Common File Format)的格式变种。系统中采用ELF的有以下几种

    • 可重定位文件
    • 可执行文件
    • 共享目标文件
    • 核心转储文件(Core Dump File)

    这4类文件在Linux中可以通过`file [file name]`看出属于哪一种。核心转储文件就是我们常说的core文件,当程序意外终止时,系统将进程的所有地址空间以及终止信息存在该文件中。所以我们需要对ELF有所了解,才能正确分析core文件。有的系统限制了core文件的大小,需要ulimit -a看一下,然后设置成需要的值,例如ulimit -c unlimit

     windows下的设置可参考文末的参考资料。

    2.处理目标文件的工具

    • AR:创建静态库,插入删除列出和提取成员
    • STRIP:列出一个目标文件中所有可打印的字符串
    • NM:列出一个目标文件的符号表中定义的符号
    • SIZE:列出目标文件中节的名字和大小
    • READELF:显示一个目标文件的完整结构,包括ELF头中编码的所有信息。包含SIZE和NM的功能
    • OBJDUMP:所有二进制工具之母,能够显示一个目标文件中所有的信息。最大的作用是反汇编.text节中的二进制指令
    • LDD:列出一个可执行文件在运行时需要的共享库

    常用的参数举例:

    readelf -h [file] 查看elf头部信息

    readelf -S

    objdump -d -j [section] [file]

    3. gdb的使用

      (图摘自CSAPP ch3.11)

     产生coredump文件时分析步骤:

    1. bt  查看程序crash位置,where也可以
    2. `frame number` `up/down n`  到对应标号的栈帧
    3. list +  查看代码
    4. info locals  简化命令i locals。看栈变量、函数行
    5. `print name_of_variable` or `p name_of_variable`  打印变量

    需要注意的是,由于现在gcc编译器普遍开了优化,使源代码和生成的代码之间的映射更难看出。这些优化对程序性能有提升,却增加了调试的难度。当看不出时,需要结合日志和代码推断问题。

    参考资料:

    CSAPP

    gdb调试coredump(使用篇) CSDN

  • 相关阅读:
    python 操作数据库
    python学习
    Java学习(十)
    Java学习(九)
    Java学习(八)
    Java学习(七)
    Java学习(六)
    Java学习(五)
    Java学习(四)
    Java学习(三)
  • 原文地址:https://www.cnblogs.com/pusidun/p/11332259.html
Copyright © 2020-2023  润新知