VSCode调试C语言程序

在研究一段算法时,看不懂代码如何运行的,遂尝试搭建一个简单的调试环境,单步调试观察程序运行情况和变量改变情况。

一、环境介绍

  • Windows11 24H2
  • WSL2
  • Ubuntu 22.04

Linux系统下gdb工具默认安装好,使用VSCode进行WSL远程连接。

二、原理介绍

VSCode进行调试时,需要一个launch.json,这个配置文件描述了使用什么样的调试器,以及进行调试时的其他配置。

如果在VSCode中要进行调试工作,必须要在.vscode文件夹中有一个能完成调试任务的json。

接下来借助我自己写的launch.json来讲解每个部分的功能,也作为一个写调试配置文件的参考。

{
    "version": "0.2.0",   // 使用的 launch.json 版本

    "configurations": [   // 配置调试器的配置列表
        {
            "name": "Hpatchi Debug",   // 调试器配置的名称,可以自定义,可以区别不同的调试器配置
            "type": "cppdbg",   // 调试器类型为 C++ 调试
            "request": "launch",   // 请求启动调试会话
            "program": "${workspaceFolder}/hpatchi",   // 可执行文件的路径,主要的修改点,这里就是我的工作目录下的hpatchi

            // 程序启动时的参数,会将这些参数依次输入给启动调试的程序
            "args": [
                "-f",
                "${workspaceFolder}/TestDemo/FBCM-0056.lbf",
                "${workspaceFolder}/DiffFile",
                "${workspaceFolder}/newFile-00D7.lbf"
            ],
            
            "stopAtEntry": false,   // 是否在程序入口处停止

            "cwd": "${workspaceFolder}",   // 工作目录
            "environment": [],   // 环境变量设置
            "externalConsole": false,   // 是否使用外部控制台

            // 使用 gdb 调试器,也可以设置成别的调试器
            "MIMode": "gdb",

            // 配置 gdb的初始化命令
            "setupCommands": [
                 //启用漂亮打印,输出格式对齐
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                },
                //防止gdb调试进入标准库函数,实测没啥用,可以不加
                {
                    "description": "防止 gdb 打开标准库函数",
                    "text": "-interpreter-exec console \"skip -rfu std::.*\"",
                    "ignoreFailures": false
                }
            ],
            
            "symbolLoadInfo": {
                "loadAll": false,
                "exceptionList": ""
            },
            // 在启动调试会话之前运行的任务,启动在task.json中提前配置好的任务名称。
            //"preLaunchTask": "Build",

            // gdb 调试器的路径,使用什么调试器就换成对应的路径,可以使用 which 命令找到一个程序的位置
            "miDebuggerPath": "/usr/bin/gdb"
        }
    ]
}

三、GDB原理

GDB是默认的c语言调试器,一般和gcc一起使用。

如果要使用gdb进行调试,使用gcc编译时就需要进行一些操作。

使用gcc编译需要在编译命令后加上-g,这样进行编译时gcc就会自动将源代码和编译后的机器语言对应,就可以进行调试追踪。

一定要在.c文件的编译阶段加上-g的命令才能正确生成调试信息,如果在链接阶段加上-g命令是指保留.o文件中的调试信息。即如果不在编译阶段生成调试信息,那这个文件在最后调试时就不会包含调试信息。

推荐在gcc时使用的命令

  • -g 必须加上否则无法进行代码的追踪。
  • -Wall,warn all的缩写,编译时会将所有的warn警告提示出来。
  • -O0 不进行编译优化,防止代码的顺序被调整或者某些调试变量被优化掉。

GCC的编译优化问题

gcc进行编译时会默认进行优化等级为O1的优化,编译器会尝试简单的优化代码流程和变量。

对应的如果进行了优化,部分调试信息就会减少,所以在调试过程中要慎重选择优化等级。

较低的优化等级可以提升编译速度,但是会导致较慢的执行速度或者较大的文件大小。使用比较高的优化等级时可以根据需要优化执行速度和文件大小,但是会牺牲编译的时间。

四、如何在Make项目中进行调试

上面介绍了如果要进行调试需要在执行gcc命令进行编译时加上**-g**的命令。

在Make项目中往往很多文件都是通过隐含规则自动推导出来的,这时候我们就无法显式的加上-g命令来生成调试信息。只有在编译阶段生成了调试信息的文件,在最终调试时才能把代码运行过程和源文件对应起来。

遇到这种情况我们可以在CFLAGS变量中加入-g命令,CFLAGS中的所有参数都会被编译器默认使用,不管是自动推导还是使用gcc命令进行编译。

五、tips

我在调试文件的过程中,只在最后一步可执行文件后面加入了-g命令,导致的后果就是只有这个可执行文件的源文件可以被调试模式追踪,其他的源文件都没法进行调试。

在这里我尝试对makefile进行修改,然后使用了一些命令来查看make将会执行的命令。

make -n可以将make将会执行的命令,不管是显式规则规定的还是自动推导的,所有的命令都会显示出来,然后我将这些信息直接重定向输出到一个文件,接着修改一次makefile,再进行一次输出,我就得到了包含着make执行的命令的两个文件。

使用vimdiff命令就可以在vim中看到两个文件的区别之处,但是vim毕竟是一个命令行工具,如果需要更复杂的文本操作,还是需要使用VSCode,并且VSCode支持更多的插件和操作,自然使用起来就更方便。