c/c++ 程序调试方法
2015-11-24 17:11
316 查看
总结一下自己常用的c/c++程序调试方法,首先介绍一下我的开发环境
系统
编辑器
编译器
调试器
总的来说, 按照使用率来排序,本人最常用的方法如下:
1. printf 大法(日志)
2. core-dump/map 调试
3. gdb
4. 注释
当然,对于多线程的情况,可以考虑增加打印tid、加锁、解锁等操作。
MAP 文件是程序的全局符号、源文件和代码行号信息的唯一的文本表示方法,是整个程序工程信息的静态文本,通常由linker生成。
core-dump
注意,在编译可执行文件前,要使用-g选项,否则找不到符号,无法精确定位。一般使用"ulimit -c unlimited"打开这个机制,其实这个命令是限制core-dump文件大小。"ulimit -c 0"将禁用这个机制。可通过修改/proc/sys/kernel/core_pattern修改保存路径、文件名等等参数。默认情况下, 这个文件保存在崩溃的可执行程序目录下。得到这个文件之后,执行"gdb
execute-file core-file"即可看到程序崩溃原因以及奔溃前执行的代码。
例如: main.c
map文件
编译可执行程序时增加一个gcc参数"gcc -Wl,-Map,map-file" (-Wl表示将后面的参数传递给链接器ld)。是程序链接的内存映像,表示了某个符号(函数和全局变量等)的地址。值得注意的是,Linux内核在编译时也会产生一个System.map文件,便于在oops时帮助定位bug。
按照命令使用的顺序, 介绍gdb常用命令
四、 注释
注意,这种办法意味着你用尽了浑身解数,都无法定位出问题的地方。也就意味着你对你的代码失去了掌控,这几乎是最后一招了。这种方法就是不断注释掉可疑代码片段,或者直接回滚到出问题之前的版本,对比代码不同之处,定位bug。
最后附上一个牛人对精通编程的判断标准:“精通就是,没有你调不出的程序”。
系统
Linux xx 3.2.0-4-686-pae #1 SMP Debian 3.2.60-1+deb7u1 i686 GNU/Linux
编辑器
VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Feb 10 2013 06:46:29)
编译器
Thread model: posix gcc version 4.7.2 (Debian 4.7.2-5)交叉编译工具链忽略不计。
调试器
GNU gdb (GDB) 7.4.1-debian
总的来说, 按照使用率来排序,本人最常用的方法如下:
1. printf 大法(日志)
2. core-dump/map 调试
3. gdb
4. 注释
一、日志
首先来说printf大法,虽然说用printf调试听起来有点low,但是不可否认的是,现实情况中绝大多数的逻辑错误、异常情况通过查看打印输出、日志就能定位bug所在,当然,直接用printf的话稍微有点麻烦。不如自己封装一下:#ifdefine DEBUG_EN #define DEBUG(fmt, args...) \ do { \ printf("DEBUG:%s-%d-%s "fmt, __FILE__, __LINE__, __FUNCTION__, ##args);\ }while(0) #define ERROR(fmt, args...) \ do { \ printf("ERROR:%s-%d-%s "fmt, __FILE__, __LINE__, __FUNCTION__, ##args);\ }while(0) #else #define DEBUG(fmt, args) do{}while(0) #define ERROR(fmt, args) do{}while(0) #endif这样编译的时候, 就可以通过gcc -DDEBUG_EN 打开调试信息输出,当调试完成之后,去掉这个参数即可。这两个宏调用方式与printf完全一样, 输出内容如下图:
当然,对于多线程的情况,可以考虑增加打印tid、加锁、解锁等操作。
二、core-dump/map 调试
当程序运行的过程中异常终止或崩溃,操作系统会将程序当时的内存状态记录下来,保存在一个文件中,这种行为就叫做Core Dump.MAP 文件是程序的全局符号、源文件和代码行号信息的唯一的文本表示方法,是整个程序工程信息的静态文本,通常由linker生成。
core-dump
注意,在编译可执行文件前,要使用-g选项,否则找不到符号,无法精确定位。一般使用"ulimit -c unlimited"打开这个机制,其实这个命令是限制core-dump文件大小。"ulimit -c 0"将禁用这个机制。可通过修改/proc/sys/kernel/core_pattern修改保存路径、文件名等等参数。默认情况下, 这个文件保存在崩溃的可执行程序目录下。得到这个文件之后,执行"gdb
execute-file core-file"即可看到程序崩溃原因以及奔溃前执行的代码。
例如: main.c
#include <stdio.h> int crash_func(int i, int j) { int ret = 0; ret = i / j; return ret; } int main(void) { crash_func(5, 0); return 0; }编译运行这个程序,提示“Floating point exception (core dumped)”,这时执行"gdb main.out core"
map文件
编译可执行程序时增加一个gcc参数"gcc -Wl,-Map,map-file" (-Wl表示将后面的参数传递给链接器ld)。是程序链接的内存映像,表示了某个符号(函数和全局变量等)的地址。值得注意的是,Linux内核在编译时也会产生一个System.map文件,便于在oops时帮助定位bug。
三、gdb
与第二种方法中用到的gdb不同的是,第三种方法通过运行程序,打断点、单步、查看变量的值等方式在运行时定位bug。第二种方式更像是破案,由犯罪现场推导“凶手”。按照命令使用的顺序, 介绍gdb常用命令
file <文件名> | 加载被调试的可执行程序文件 |
b <行号>/<函数名称> | 在第几行或者某个函数第一行代码前设置断点 |
r | 运行 |
s | 单步执行一行代码 |
n | 执行一行代码,执行函数调用(如果有) |
c | 继续运行程序至下一个断点或者结束 |
p<变量名称> | 查看变量值 |
q | 退出 |
四、 注释
注意,这种办法意味着你用尽了浑身解数,都无法定位出问题的地方。也就意味着你对你的代码失去了掌控,这几乎是最后一招了。这种方法就是不断注释掉可疑代码片段,或者直接回滚到出问题之前的版本,对比代码不同之处,定位bug。最后附上一个牛人对精通编程的判断标准:“精通就是,没有你调不出的程序”。
相关文章推荐
- Linux socket 初步
- linux lsof详解
- linux 文件权限
- Linux 执行数学运算
- 10 篇对初学者和专家都有用的 Linux 命令教程
- Linux 与 Windows 对UNICODE 的处理方式
- Ubuntu12.04下QQ完美走起啊!走起啊!有木有啊!
- 解決Linux下Android开发真机调试设备不被识别问题
- 运维入门
- 运维提升
- Linux 自检和 SystemTap
- Ubuntu Linux使用体验
- c语言实现hashmap(转载)
- Linux 信号signal处理机制
- linux下mysql添加用户
- Scientific Linux 5.5 图形安装教程
- 基于 Linux 集群环境上 GPFS 的问题诊断
- 谁是桌面王者?Win PK Linux三大镇山之宝