聊聊关于C/C++调试的看法
只讨论
Linux
环境下的 C/C++ debug
。
在 Windows 环境下开发由很多优秀的 IDE 可以使用,而在 Linux 下开发C/C++,最好的开发环境并不是 IDE,而是一系列工具的组合。不要执着于争论到底是 ide 齐全还是 vim/emacs 效率高,只要用惯、用熟了,效率不会区别太大。确实存在某些场合只能使用 vim ,但这样并不能说明哪种更好。嗯……习惯、喜欢。
当然,vim 等一开始需要花点时间折腾,需要耐心和兴趣,不过维护好自己的vimrc
等配置文件,之后在使用上如何就见仁见智了。
步骤 | 工具 |
---|---|
编辑 | vim/emacs/vscode + plugins |
调试 | gdb / log /valgrind / cppcheck / file / nm / ldd 等 |
编译 | gcc / g++/ cmake / makefile / clang 等 |
链接 | link |
远程调试 | gdb-server / ssh? |
脚本 | shell |
分屏 | tmux |
debug 是什么
调试,是一种发现源代码错误,排查 Bug 的过程,错误的类型有语法错误,逻辑错误,内存泄露等。
debug 的步骤
-
插入打印语句------如
printf、cout
等打印语句,能够显示程序的动态过程,容易检查源程序的有关信息;缺点是大量无关数据,发现 bug 有偶然性。 - 运行部分程序------为了测试某段一段可能出错的代码,执行整个程序的做法是低效率的;因此应设法让程序只执行需要检查的程序段,提高效率。
- 借助调试工具------上面的方法可以说是“人肉debug“,只能在代码量少的情况下发挥作用。大多数语言有专门的调试工具,借助工具调试才是正确的 debug 打开方式。
debug 分类
-
静态调试------插入打印语句
printf、cout
,输出寄存器、关键变量的动态值。(低效) -
动态调试------即使用调试工具分析程序的动态行为。如利用
valgrind
检查内存泄露问题;设置断点
(程序语言自身提供,不是外部工具),分析程序此时的状态;使用内置的宏
。-
使用调试标记
在源代码中使用相应的辅助代码,debug 完成之后 隐藏 这些代码的一种调试策略。借助
#define/#ifdef/#endif
三个编译指令。相当于使用#define
定义宏,#ifdef / #endif
之间包含调试代码。调试完成后,生成发行版时,在编译器命令行参数中设置不提供调试标记,这段代码就会销毁。常用调试标记_DEBUG(VC++)
#ifdef _DEBUG std::cout << "something..." << std::endl; #endif
-
使用调试变量
运行时设置一个
bool
类型的变量,决定特定调试程序段的开关,且该变量可以通过命令行参数设定。#include ... bool _debug_; int main(int argc, char* argv[]) { if(_debug_){ //do something } }
-
使用内置宏
__FILE__ / __FUNCTION__ / __LINE__
自动获取有关模块、函数、宏的信息。{ std::cout << __FILE__ << std::endl; std::cout << __FUNTION__ << std::endl; std::cout << __LINE__ << std::endl; }
-
debug 调试跟踪工具
-
gdb ==> 最重要最常用最基础(Linux)
- 设置断点,且断点可以是条件表达式
- 快速定位 bug,如使用
bt
/where
可以快速找到程序出现core dump
(核心转储)的位置。core dump
就是程序运行中发生execption
,异常退出时,由操作系统把程序当前的内存状况存储在一个core文件
中,又叫做segmentation fault。
-
valgrind ==> 内存
- 内存调试、内存泄漏检测以及性能分析
-
strace
- 跟踪并显示用户程序中的系统调用的详细信息
(parameters/return values/system call/time/…)
- 适用于
a.out/.exe
或运行中的进程,用户可观察程序的运行状态
- 跟踪并显示用户程序中的系统调用的详细信息
-
pstack
查看进程的实时堆栈信息。
静态分析工具
-
cppcheck
- C/C++代码缺陷静态检查,检测未使用的变量,越界访问,内存泄漏等问题。
- 不同于其他编译器和分析工具,
cppcheck
只检查编译器检查不出来的bug,不检查语法错误。作为编译器的一种补充检查。
-
ldd
ldd
命令可以查看当前a.out/.exe
或者动态库需要依赖那些动态库,以及缺少哪些动态库。ldd -r
可以报告缺少的目标对象和函数。 -
file
查看文件类型,对于可执行文件或动态库,可查看使用需要 link 动态库,同时可查看是否包含符号表。
file filename
-
nm
列出目标文件中的符号。通常情况下,对于链接问题可以通过该命令的
-D -u
参数来查看相关符号。nm -CDu filename
-
strip
清理共享库或可执行文件中的符号信息和调试信息,通常在程序发布前使用。
性能调试工具
-
perf ==> CPU
分析应用程序或内核代码性能。对单个程序做函数调用次数、上下文切换次数、中断次数等信息进行统计。
-
prof/gprof
显示程序运行所用的 时间 具体用在什么操作上,可用于性能优化。