You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何在C语言主函数中调试外部程序?VS Code+GDB调试system调用外部程序的问题及替代方案

在VS Code中调试system()调用的外部程序问题解决

我之前也碰到过和你一模一样的情况:在hello.c里用system("/path/to/world")调用外部程序,在这行设断点后点Step Into,结果弹出Could not load source './stdlib/../sysdeps/posix/system.c': 'SourceRequest' not supported.的错误,根本进不去world的代码里。

为什么用system()没法直接调试外部程序?

原因很简单:system()函数本质是调用系统shell(比如bash)来执行你的world程序,相当于中间多了一个shell进程。GDB默认只会跟踪父进程(也就是hello进程),就算你想跟进,也会先进入shell的代码,而不是直接跳到world的main函数——而且系统libc的源码你大概率没装,所以才会弹出找不到system.c的错误。

替代方案:用fork()+exec系列函数配合GDB的多进程调试

要直接调试被调用的外部程序,我们需要跳过shell这一层,直接创建子进程执行world,同时让GDB跟踪子进程。具体步骤如下:

1. 修改hello.c的代码,替换system()

把原来的system()调用换成fork()+execv()的组合,这样可以直接启动world进程,不需要经过shell:

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char **argv) {
    pid_t pid = fork();
    if (pid == 0) {
        // 子进程:执行world程序
        char *args[] = {"/path/to/world", NULL};
        execv("/path/to/world", args);
        // 如果execv执行失败才会走到这里
        perror("execv failed");
        return 1;
    } else if (pid > 0) {
        // 父进程:等待子进程结束
        wait(NULL);
    } else {
        perror("fork failed");
        return 1;
    }
    return 0;
}

2. 配置VS Code的launch.json,让GDB跟踪子进程

打开VS Code的调试面板,编辑launch.json,添加follow-fork-modedetach-on-fork这两个配置项,告诉GDB跟着子进程走:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Debug Hello",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceFolder}/hello",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                },
                // 关键配置:跟踪子进程,不分离父进程
                {
                    "text": "set follow-fork-mode child",
                    "description": "Make GDB follow the child process"
                },
                {
                    "text": "set detach-on-fork off",
                    "description": "Keep parent process attached"
                }
            ],
            "preLaunchTask": "C/C++: gcc build active file"
        }
    ]
}

3. 编译时添加调试信息

不管是hello.c还是world.c,编译的时候一定要加-g参数,保留调试符号:

gcc -g /path/to/hello.c -o /path/to/hello
gcc -g /path/to/world.c -o /path/to/world

4. 设置断点并调试

  • 直接在world.cmain函数行设置断点,或者在hello.cexecv那行设置断点
  • 启动调试,当程序走到execv时点击Step Into,就能直接进入worldmain函数开始调试了

额外说明

如果只是临时想调试world程序,其实也可以直接在VS Code里配置调试world本身,但如果必须通过hello来触发world的执行,上面的方法就是最靠谱的解决方案。

内容的提问来源于stack exchange,提问作者stackbiz

火山引擎 最新活动