相关概念
一般来说我们分析BUG,大概分为两种
- 根据软件内部已有的插桩日志,记录关键节点信息、重要函数的输入输出,根据这些信息判断bug
- 调试(Debug),让代码一步一步执行,跟踪程序的运行过程。比如,可以让程序停在某个地方,查看当前所有变量的值,或者内存中的数据,根据这些信息分析bug。
日志
日志这种方式,优点在于离线,但更多基于完备的日志信息,依赖代码内部的插桩日志的详尽程度,如果现存日志未捕获bug信息的日志的话,那么bug分析的形式就变成了如下,中间反复验证猜测编译及bug复现实际上是效率非常低的。
Created with Rapha?l 2.3.0
开始分析
加日志
复现bug
确认现有日志是否可定位问题?
结束
yes
no
调试
调试(Debug)是软件开发的基石之一,调试则依赖调试器(debugger) 常见的debugger有以下几种
名称 | 特点 |
---|
Visual Studio windows Debugger | 如名,windows 平台常见debugger | GDB | Linux 下使用最多的一款调试器,各个平台都有其实现 | LLDB | Mac OS 常用调试器 |
总的来说,gdb主要用于以下几个功能
- 程序启动时,可以按照我们自定义的要求运行程序,例如设置参数和环境变量;
- 可使被调试程序在指定代码处暂停运行,并查看当前程序的运行状态(例如当前变量的值,函数的执行结果等),即支持断点调试;
- 程序执行过程中,可以改变某个变量的值,从而尝试复现bug或者修改程序中出现的逻辑错误。
基础的概念了解到这里,下面进入实操。
实操
vscode C++插件
切换debug模式
基本类似gcc里面的-g -O0,注意不同编译器可能不同实现
windows平台
由于windows平台我们各个sdk编译器使用的都是msvc,windows平台使用的debug为Visual Studio windows Debugger。 我们可以直接使用vs这个ide直接进行debug,不需要任何配置,也可以使用vscode 配置c++插件 进行debug,由于ide debug很简单,再加上个人主要以vscode主力开发工具,本文也主要以vscode为例。
点击侧边栏运行与调试、添加配置 添加windows debugger,附加启动选择看需求
- 附加:附加到已运行的程序进行调试
- 启动:新启动一个程序从main开始调试
{
"version": "0.2.0",
"configurations": [
{
"name": "(Windows) 启动",
"type": "cppvsdbg",
"request": "launch",
"program": "${workspaceFolder}/build/install/app/tutorial_cpp.exe",
"args": [],
"stopAtEntry": true,
"cwd": "${workspaceFolder}/build/install/app/",
"environment": [],
"console": "externalTerminal"
}
]
}
F5开始调试,基本的断点,条件断点,堆栈,寄存器,变量监控,堆栈显示都支持
x86 linux
与windows大同小异,只是vscode c++插件的 lanuch配置有稍许区别,选择GDB 启动
{
"name": "(gdb) 启动",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/linux/install/app/tutorial_cpp",
"args": ["astraplus_intrinsic.txt","0"],
"stopAtEntry": true,
"cwd": "${workspaceFolder}/build/install/app/",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
F5开始调试
其他
需要注意,linux使用的是gdb,vscode除了自身的一些debug支持,还另外支持gdb的命令,在调试控制台输入-exec <command>
gdb也是一个功能非常强大的调试系统,比如 内存读写断点,这个在分析堆栈溢出或者数组越界读写导致的一些莫名奇妙的bug非常有效。 除此之外还有很多内容,可以按需自行了解。
AM、HSI、RK等嵌入式 linux
嵌入式设备调试依赖gdb和gdbserver远程调试,gdb和gdbserver一般作为交叉编译链的一部分。
直接将对应嵌入式设备的gdbserve拷贝到板端,设置PATH或者直接gdbserver丢到bin/目录下。
注意debug模式
gdbserver 10.10.71.195:9995 ./tutorial_cpp 1 1 1 astraplus_intrinsic.txt 0
ip指定为板端ip,端口自定,tutorial_cpp 为执行app,后面的为app参数 板端会提示如下信息
Process ./tutorial_cpp created; pid = 7501 Listening on port 9995
接下来配置,编译端的launch配置
{
"name": "(gdb) 启动",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/install/app/cli",
"args": ["1","1","1","astraplus_intrinsic.txt","0"],
"stopAtEntry": true,
"cwd": "${workspaceFolder}/build/install/app/",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "/home/keras/wangcc/toolchains/gcc-linaro-aarch64-linux-gnu-7.3.1-2018.05/bin/aarch64-linux-gnu-gdb",
"miDebuggerServerAddress": "10.10.71.195:9995",
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
F5开始调试,载入dll后,板端就会提示如下信息
Listening on port 9555 Remote debugging from host 1.1.1.1
debug方法,与x86 linux没有本质区别,只是加上了gdbserver以支持远程调试。
调试的不足
如上文,debug主要依赖编译器生成的调试信息以及低优化等级(-g -O0),来获取较好的调试体验,但是生产环境优化等级都为-O3,也就是生产环境和测试环境的不一致,在某些bug类型会导致生产环境容易复现,测试无法复现的问题,比如性能类bug,虽然可以把debug版本调整优化等级,以尽可能与生产环境一致,但这种调试体验可能较差,编译器指令乱序、寄存器优化,有可能会导致调试时代码并没有按源代码顺序执行,变量无法读取的问题。基本属于核心不可调和的矛盾,这种时候只能日志及调试各种方式都利用上了。
|