本帖最后由 老叶子 于 2025-10-16 14:36 编辑
请原谅我用这个标题吸引大家进来,其实本文是一篇外文翻译,内容取自cortex-debug github-wiki(Cortex Debug Under the hood)(标题翻译的也没错吧)
翻译的初衷是因为在学习cortex-debug的过程中,发现多数文章仅仅只是给一个配置好的launch.json模板,没有对于cortex-debug更详细的介绍。这篇文章正好可以补足这一点,而且中文互联网上还没有此文的翻译,那么我就结合自己的理解,对文章做一下解读,如有错误,还请指出
Debug file(s)
巧妇难为无米之炊,既然debug,首先要有调试文件。调试文件由程序和调试信息组成,程序烧录到芯片内,调试信息供GDB使用。当两者在一个文件内时,可以使用launch.json中executable属性,当两者不在一起时,loadFiles属性指定待烧录的程序,symboFiles属性指定调试信息。下面两种表达方式是等价的 [C] 纯文本查看 复制代码 {
"executable": "./aaa.elf",
}
{
"loadFiles": [
"./aaa.elf",
],
"symbolFiles": [
"./aaa.elf",
],
}虽然原文指出“loadFiles可以是 E LF、二进制或 intel 十六进制格式文件“,但作者经过测试,只支持elf、hex格式,bin文件由于不含有地址信息,无法烧录。 举例几个使用loadFiles、symboFiles的场景: 同时调试flash上的多个程序,比如bootload、app,launch.json如下配置 [C] 纯文本查看 复制代码 {
"loadFiles": [
"./bootload.elf",
"./app.elf",
],
"symbolFiles": [
"./bootload.elf",
"./app.elf",
],
}当在boot工程中开启调试时,若转到app工程,vscode会自动打开app工程中对应的源文件。 还有一种情景,bootload是外购的程序,不包含调试信息(已经被strip了),app存放于外置norFlash中,这种情景下不需要向flash中烧录app,只烧录bootload。无法调试bootliad,只能调试app [C] 纯文本查看 复制代码 {
"loadFiles": [
"./bootload—_strip.elf",
],
"symbolFiles": [
"./app.elf",
],
}launch.json没有executable属性会出现警告,为消除警告,可以将executable的值留空。loadFiles、symbolFiles属性的优先级大于executable属性 [C] 纯文本查看 复制代码 {
"executable": "",
"loadFiles": [
"./bootload.elf",
"./app.elf",
],
"symbolFiles": [
"./bootload.elf",
"./app.elf",
],
}编译时务必使用-g选项,使文件携带调试信息。 调试文件必须是固定地址(没使用-fPIC选项),才能使用executable、loadFiles、symbolFiles属性。对于动态加载程序,可以在postLaunchCommands属性中执行add-symbol-file命令进行调试,具体请参考以下链接 Body part
调试时共有五部分参与,他们之间是线性关系
vscode只是个文本编辑器,并不是IDE,所以并没有调试功能。但vscode为各种语言的调试适配器(Debug Adapter)预留了接口,从而在vscode上实现调试功能。launch.json就是调试适配器的配置文件,cortex-debug就是嵌入式C语言的调试适配器。关于vscode调试功能(如按钮功能、断点、数据检查)如何使用,请参考以下链接 对于C语言程序,launch.json内的属性可分为三部分 vscode通用属性,如type、name、preLaunchTask。此部分属性说明请参考以下连接 调试C/C++的属性,此处的C程序指在Windows、Linux上运行的C程序。此部分属性由C/C++插件提供,说明请参考以下连接 调试嵌入式C/C++的属性。此部分属性由cortex-debug插件提供,说明请参考以下连接 调试嵌入式代码时,launch.json中可以使用vscode的通用属性,但能不能使用C/C++插件的属性,作者没有做过实验,望了解的朋友解答一下。 vscode与调试适配器之间有专用的通讯协议(原文中有链接)。在通讯时,vscode为客户端,调试适配器为服务端,两者间通讯的过程在“调试控制台”用蓝色字体表示。“From client xxx”即为调试适配器从vscode接收到的内容。“To client xxx”即为cortex-debug发向vscode的内容。 在下图中,用户在“调试控制台”输入GDB命令“print retry_count = 123”。通过GDB将变量在内存中的值改变,并将结果反映在调试UI
更常见的情况,并不需要用户输入命令,cortex-debug会直接控制GDB,比如调试配置阶段,调试UI操作
cortex-debug与gdb交互过程在“调试控制台”用红色字体表示,两者间使用的不是常见的GDB/CLI(Command-Line Interface)命令,而是方便解析的GDB/MI(Machine Interface)命令,两者功能相同,格式不同 关于GDB/MI更多内容,请参考以下链接 cortex-debug发向gdb的格式为:数字序号+“-”+命令,如42-interpreter-exec console "print retry_count = 123" gdb返回的格式为:->+具体内容,如-> =memory-changed,thread-group="i1",addr="0x20004f0c",len="0x4" ->+数字序号+^done,表示此命令执行完毕,如-> 2^done Startup process
1.启动GDB 2.通过objdump读取反汇编内容,通过nm读取符号表 3.启动GDBserver 步骤4-8是原文“Collect gdb+gdb-server commands for start”的内容。在此处,特意说明了在launch.json中request的值不论是launch还是attach,对cortex-debug的影响并不大,原文如下: 4.一些杂七杂八的命令,作者也看不懂 5.连接GDBserver与GDB 6.执行launch.json中preLaunchCommands属性的命令,作者在这使用echo打印了一句话 7.执行内置启动命令,内置启动命令可以用overrideLaunchCommands属性覆盖。内置命令如下 monitor reset halt load monitor reset halt wiki也解释了这三句命令的含义,大意为:重置停止设备-->烧录程序-->重置停止设备。monitor开头的命令为gdb传递给gdbserver,由gdbserver执行的命令。 调试包含了烧录,所以在preLaunchTask属性指定的任务中,仅编译即可 8.执行launch.json中postLaunchCommands属性的命令,作者在这使用echo打印了一句话 9.(可选)调试起始选项。由launch.json中的属性决定其行为 runToEntryPoint:程序一直运行,直到运行到指定函数处暂停 breakAfterReset:在程序第一句处暂停 postStartSessionCommands:用户给定的命令。如果此命令与断点无关,则在程序第一句处暂停 以上属性均没有:程序一直运行,直到第一个断点 Restart or Reset
重启(restart)与重置(reset)在调试中是两个概念。 重启是vscode控制的东西,指的是关闭本次调试,并开启下一次调试,受preLaunchTask属性影响 重置是cortex-debug控制的东西,指的是芯片的重启
重置的具体步骤为: 1.执行launch.json中preRestartCommands属性的命令 2.执行内置启动命令,内置启动命令可以用overrideRestartCommands属性覆盖。内置命令如下 monitor reset halt 3.执行launch.json中postRestartCommands属性的命令 注意!! - 重置并不会断开GDB、GDBserver之间的链接,所以Startup process步骤中的命令不会被再次执行
- Startup process步骤中也有monitor reset halt命令,但是并不会触发preRestartCommands、postRestartCommands属性
(完)
|