vscode使用attach process调试进程

本文介绍如何搭建s3fs-fuse文件系统,并详细记录了从源码下载、编译配置到调试的全过程。此外,还分享了具体的调试步骤及测试方法。

https://note.youdao.com/s/UjVWPaA5 格式乱了,可以看下笔记地址

这里以s3fs为例

s3用的是minio,默认配置启动即可,启动minio后,执行

echo minioadmin:minioadmin >~/.passwd-s3fs

后面s3fs需要用到。

开始干活吧

1、下载源码

git clone GitHub - s3fs-fuse/s3fs-fuse: FUSE-based file system backed by Amazon S3

2、创建json文件

  • tasks.json

{ "tasks": [ { "label": "build_debug", // 任务名称,调试时可以指定不用任务进行处理 "type": "shell", // [shell, process], 定义任务作为作为进程运行还是在shell中作为命令运行; (测试没看出啥区别...) "command": "make", // 要执行的命令,可以是外部程序或者是shell命令。这里使用make编译命令 "problemMatcher": [ // 要使用的问题匹配程序。可以是一个字符串或一个问题匹配程序定义,也可以是一个字符串数组和多个问题匹配程序。 "$gcc" ], "group": { // 定义此任务属于的执行组。它支持 "build" 以将其添加到生成组,也支持 "test" 以将其添加到测试组。 "kind": "build", "isDefault": true }, "presentation": { // 配置用于显示任务输出并读取其输入的面板 "echo": true, // 控制是否将执行的命令显示到面板中。默认值为“true”。 "reveal": "always", // 控制运行任务的终端是否显示。可按选项 "revealProblems" 进行替代。默认设置为“始终”。 "focus": false, // 控制面板是否获取焦点。默认值为“false”。如果设置为“true”,面板也会显示。 "panel": "shared", // 控制是否在任务间共享面板。同一个任务使用相同面板还是每次运行时新创建一个面板。 "showReuseMessage": true, // 控制是否显示“终端将被任务重用,按任意键关闭”提示 "clear": false // 运行前清屏 } }, { "label": "build_release",//可以手动执行 "type": "shell", "command": "make", "args": ["CFLAGS = -O2"], // 编译参数, 替换makefile中让CFLAGS字段 "dependsOn":["build_clean"], // 指定依赖让task, 即会先执行build_clean,然后再执行build_release "problemMatcher": [ "$gcc" ], "group": { "kind": "build", "isDefault": true } }, { "label": "build_clean",//可以手动执行 "type": "shell", "command": "make", "args": ["clean"], // 相当于执行 make clean命令 "problemMatcher": [ "$gcc" ], "group": { "kind": "build", "isDefault": true } } ], "version": "2.0.0" }

launch.json

{ "configurations": [ { "name": "(gdb) Attach", "type": "cppdbg", "request": "attach", "program": "${workspaceFolder}/src/s3fs", "processId": "${command:pickProcess}", "MIMode": "gdb", "setupCommands": [ { "description": "Enable pretty-printing for gdb", "text": "-enable-pretty-printing", "ignoreFailures": true } ] } ] }

3、启动s3fs

./src/s3fs -o passwd_file=/home/xxx/.passwd-s3fs -s -o url=http://127.0.0.1:9000 -o use_path_request_style -f -o curldbg -o dbglevel=debug -o umask=0 -o mp_umask=0 bucketname /mnt/s3

4、打断点

这里以ls列出桶内容为例

断点这里打在s3fs_readdir方法里

 

5、attach process

按F5

然后选择自己要调试的进程

因为需要超级用户身份运行,所以会弹出输入密码的框

6、执行对应测试

cd /mnt/s3

ls

OK

<think>我们正在处理一个关于在VSCode调试基于exec系统调用的多进程程序的问题。用户希望了解如何配置VSCode调试这样的程序。 关键点: 1. 程序使用exec系统调用创建多个进程。 2. 需要在VSCode调试这些进程。 解决方案思路: 在VSCode调试进程程序,通常有两种主要方式: a. 使用"attach"模式,将调试器附加到已经运行的子进程上。 b. 使用VSCode的多目标调试配置,同时启动多个进程并分别调试。 但是,由于程序是通过exec系统调用创建子进程,这意味着子进程会替换为新的程序。因此,我们需要在子进程被创建并执行exec之后,让调试器能够附加到这些子进程上。 具体步骤: 1. 在父进程代码中,在fork之后、exec之前,插入一个等待调试器附加的代码(例如,一个循环等待某个条件满足,或者使用一个信号量)。这样,当子进程被创建但尚未执行exec时,我们可以让子进程暂停,以便我们有时间附加调试器。 2. 配置VSCode调试设置,使用"attach"模式附加到子进程。 然而,VSCode也支持通过launch.json配置进行多进程调试。我们可以设置一个主配置(用于父进程)和多个子配置(用于子进程),但需要手动指定每个子进程进程ID,或者使用自动化脚本。 另一种更简单的方法是使用VSCode的"compound"配置,同时启动多个调试会话。但是,对于动态创建的子进程,我们需要在运行时获取子进程的PID并附加调试器。 推荐方法: 使用VSCode的"Native Attach"配置,结合在代码中插入等待点。 步骤详解: 步骤1:修改代码 在子进程的exec调用之前,加入一个等待调试器附加的代码。例如,在Linux下,我们可以使用一个循环等待某个文件被创建,或者使用一个无限循环并配合信号来中断。但更简单的方法是使用一个条件变量,并通过环境变量控制是否等待。 例如,在子进程中: ```c if (getenv("WAIT_FOR_DEBUGGER")) { printf("子进程 %d 正在等待调试器附加...\n", getpid()); while (1) { sleep(1); // 等待直到被调试器中断 } } // 然后执行exec ``` 这样,当设置了环境变量WAIT_FOR_DEBUGGER时,子进程会在执行exec之前进入等待状态。我们可以在这个循环期间附加调试器,然后打断循环继续执行。 步骤2:配置VSCode的launch.json 我们需要两个配置: - 一个用于启动父进程使用"launch"类型)。 - 另一个用于附加到子进程使用"attach"类型)。 但是,由于子进程的PID是动态的,我们可以先运行父进程,然后从终端输出中获取子进程的PID,再手动附加。但这样比较麻烦。 为了自动化,我们可以使用一个脚本,在父进程运行后,监控子进程的创建,并自动附加调试器。然而,VSCode本身不直接支持这种动态附加,但我们可以通过扩展或多调试会话来实现。 替代方案:使用VSCode的"preLaunchTask"和"postDebugTask"结合脚本自动化。 另一种方法是使用gdb的follow-fork-mode和detach-on-fork设置,但VSCode的C++调试器(基于gdb或lldb)支持这些设置。 具体配置(使用gdb): 在launch.json中,我们可以为父进程设置一个配置,使用gdb的"fork"模式: ```json { "name": "(gdb) 启动父进程", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/parent_program", "args": [], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [{"name": "WAIT_FOR_DEBUGGER", "value": "1"}], "externalConsole": false, "MIMode": "gdb", "setupCommands": [ { "description": "为 gdb 启用整齐打印", "text": "-enable-pretty-printing", "ignoreFailures": true }, { "text": "set detach-on-fork off", // 不要分离fork出的进程 "ignoreFailures": true }, { "text": "set follow-fork-mode child", // 跟踪子进程(这样当父进程fork后,调试器会跟随子进程) "ignoreFailures": true } ] } ``` 但是,这样配置后,调试器会跟随子进程,而父进程会继续运行。如果我们希望同时调试进程和子进程,那么需要多个调试会话。 注意:上面的配置中,我们设置了环境变量WAIT_FOR_DEBUGGER=1,这样每个子进程都会在exec之前等待。但是,在gdb跟随子进程后,我们可以在子进程的等待循环中断点,然后修改代码跳过等待(例如,设置一个变量来打破循环),然后继续。 然而,这种方法只能调试一个子进程(因为follow-fork-mode只能选择一个进程跟随)。 如果我们想调试多个子进程,或者同时调试进程和子进程,我们需要使用调试会话。 步骤3:使用多个调试会话 我们可以配置一个"compound"启动配置,同时启动多个调试器。但是,子进程的PID是动态的,所以我们需要在运行时获取PID并附加。 这里,我们可以编写一个脚本,该脚本在父进程运行时监听并获取子进程的PID,然后启动一个新的调试会话附加到该子进程。但VSCode调试API允许通过扩展实现,但手动配置较复杂。 简化方法: 我们可以让每个子进程在启动时输出自己的PID,并写入一个文件,然后使用一个VSCode任务来监视这个文件,当有新的PID写入时,自动启动附加调试器的会话。但这需要编写扩展,超出了简单配置的范围。 因此,对于大多数情况,建议使用第一种方法:只调试一个子进程,或者使用手动附加。 手动附加步骤: 1. 运行父进程(不调试),或者使用上面的gdb配置运行父进程(但只跟随一个子进程)。 2. 当子进程打印出等待信息(和PID)时,记住PID。 3. 在VSCode中,添加一个"attach"类型的调试配置,附加到该PID。 但是,如果我们需要调试多个子进程,就需要多次手动附加。 自动化附加的参考方法(使用脚本): 我们可以编写一个脚本,使用ptrace或其他机制在子进程创建时暂停它,然后自动启动调试器附加。例如,使用gdb的--pid参数。然后,我们可以将这个脚本作为父进程的启动器。 在Linux中,我们可以使用gdbattach命令,但需要知道PID。 另一种工具:`gdb --attach-pid` 但是需要提前知道PID,而PID是动态的。 使用gdb的catch命令:在gdb中,我们可以设置捕获fork事件,然后自动附加到子进程。但是,这需要我们在gdb中运行父进程,并在fork事件发生时执行命令。 例如,在gdb中: ``` catch fork ``` 然后,在捕获到fork事件时,我们可以使用: ``` set follow-fork-mode child ``` 但是,这只能跟随一个子进程。 考虑到这些复杂性,如果用户需要同时调试多个进程,可能需要使用更专业的工具或编写特定的脚本。 总结:在VSCode调试基于exec的多进程程序,可以按照以下步骤: 1. 在子进程的代码中,在fork之后、exec之前,加入等待调试器附加的代码(例如,一个循环,通过环境变量控制)。 2. 在父进程的launch.json配置中,设置环境变量WAIT_FOR_DEBUGGER=1,并配置gdb的fork模式(如上述配置)。 3. 运行调试,当父进程创建子进程时,gdb会跟随子进程(根据follow-fork-mode设置),然后我们可以在等待循环处中断,并修改循环条件(例如,设置一个变量)以继续执行。 4. 如果需要同时调试进程和子进程,则需要在父进程fork后,使用另一个调试会话附加到父进程(因为gdb跟随子进程后,父进程会继续运行,但不再被调试)。 对于同时调试多个进程,目前VSCode原生支持有限,可能需要依赖多个调试会话手动附加。 另外,注意:在子进程中,如果我们在等待循环中,可以手动附加调试器(不使用gdb的follow-fork-mode): - 先运行父进程(不调试),子进程进入等待循环。 - 然后,在VSCode中,使用"attach"配置附加到子进程的PID。 - 附加后,在循环处设置断点,然后修改代码跳出循环(例如,设置一个标志),继续调试。 这样,我们就可以调试进程了,而父进程不受影响。 因此,根据用户的具体需求,我们可以提供不同的方案。 考虑到用户可能希望同时调试进程和子进程,我们建议使用两个独立的调试会话:一个调试进程,另一个在子进程等待时附加到子进程。 步骤: 1. 在代码中,子进程等待调试器附加(使用循环,并打印PID)。 2. 配置父进程调试会话(普通的launch配置),但不设置gdb的fork模式(即默认跟随父进程)。 3. 运行父进程调试会话,当子进程被创建并打印等待信息(包括PID)时,暂停父进程(如果需要,可以在fork后设置断点)。 4. 复制子进程的PID,然后创建一个新的调试会话(attach类型),附加到该PID。 5. 在子进程调试会话中,跳出等待循环,继续调试。 这样,我们就同时调试了父进程和子进程。 但是,如果子进程很快,可能来不及附加,所以等待循环是必要的。 最后,我们提供两个配置示例: 父进程配置(launch.json): ```json { "name": "调试进程", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/parent_program", "args": [], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [{"name": "WAIT_FOR_DEBUGGER", "value": "1"}], "externalConsole": false, "MIMode": "gdb" } ``` 子进程附加配置(需要提前知道PID,所以通常先运行父进程,然后从输出中获取PID,再手动附加。为了简化,我们可以先创建好附加配置,然后在附加时输入PID): ```json { "name": "附加到子进程", "type": "cppdbg", "request": "attach", "program": "${workspaceFolder}/child_program", // 这里填写子进程的程序路径,如果和父进程相同,则填写父进程的程序路径 "processId": "${command:pickProcess}", // 使用VSCode进程选择器 "MIMode": "gdb" } ``` 注意:子进程的程序路径。因为exec会替换为新的程序,所以如果子进程执行的是另一个程序,那么这里要填写那个程序的路径。 操作步骤: 1. 启动父进程调试会话。 2. 当控制台输出子进程的PID(例如,“子进程 1234 正在等待调试器附加...”),我们复制这个PID。 3. 切换到“附加到子进程”的调试配置,运行,然后会提示选择进程,我们输入或选择这个PID。 4. 附加成功后,在子进程的等待循环处中断,然后我们可以设置一个断点,并修改循环条件(例如,在调试控制台设置一个变量)来跳出循环,然后继续。 注意:在子进程中,我们可能无法直接修改循环条件(因为代码可能没有符号信息?),所以确保子进程的程序在编译时带有调试信息(-g)。 另外,我们也可以不使用环境变量,而是通过条件编译或其他方式控制等待循环。 最后,如果用户需要自动化,可以考虑编写一个VSCode扩展来监听子进程的创建并自动附加,但这比较复杂。 相关问题: 根据用户的问题,我们生成以下相关问题: 1. 如何在C/C++程序中实现子进程调试等待点? 2. VSCode中如何配置多进程调试(同时调试进程和子进程)? 3. 如何使用gdb的follow-fork-mode调试进程? 4. 在Linux中,如何获取子进程的PID以便附加调试器? 5. 如何避免在调试进程程序时出现竞争条件(例如子进程调试器附加前已经退出)?</think>### 在 VSCode调试基于 exec 的多进程程序 在 VSCode调试使用 `exec` 系统调用的多进程程序,需配置 `launch.json` 文件并理解进程调试机制。以下是具体步骤: --- #### **1. 配置 launch.json 文件** 在 `.vscode/launch.json` 中添加以下配置: ```json { "version": "0.2.0", "configurations": [ { "name": "调试进程", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/parent_program", "args": [], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "MIMode": "gdb", "setupCommands": [ { "description": "启用子进程调试", "text": "set detach-on-fork off", "ignoreFailures": false }, { "text": "set follow-fork-mode parent", "ignoreFailures": true } ] } ] } ``` 关键参数说明: - `set detach-on-fork off`:禁止调试器在 fork 后分离 - `set follow-fork-mode parent`:默认跟踪父进程(可选 `child` 跟踪子进程) - `MIMode`:指定调试器(gdb 或 lldb) --- #### **2. 在代码中添加调试钩子** 在子进程的 `exec` 调用前插入等待逻辑: ```c #include <unistd.h> int main() { pid_t pid = fork(); if (pid == 0) { // 子进程 printf("子进程 PID=%d 等待调试器附加...\n", getpid()); volatile int wait = 1; while (wait); // 循环等待调试器修改变量 execvp("child_program", argv); // 替换为实际程序 } // 父进程继续执行... } ``` 编译时需添加调试符号:`gcc -g parent.c -o parent_program` --- #### **3. 调试操作流程** 1. **启动父进程调试** 按 F5 启动调试VSCode 会停在父进程代码中 2. **附加到子进程** 当子进程打印 PID 并阻塞时: - 在 VSCode 中创建 **附加配置**: ```json { "name": "附加到子进程", "type": "cppdbg", "request": "attach", "program": "${workspaceFolder}/child_program", "processId": "${command:pickProcess}", "MIMode": "gdb" } ``` - 选择该配置 → 运行 → 输入子进程 PID 3. **继续执行子进程** 附加后: - 在调试控制台输入:`set var wait = 0` 解除阻塞 - 设置断点后按 Continue (F5) --- #### **4. 处理 exec 的特殊性** `exec` 会替换进程内存空间,需注意: 1. 附加到子进程后**立即设置断点**在 `exec` **之后**的代码 2. 若子进程崩溃,使用 `catch exec` 捕获 exec 事件: ```gdb catch exec commands silent # 在此处设置后续断点 continue end ``` --- #### **5. 替代方案:gdbserver** 对嵌入式或远程调试: ```bash # 目标机器 gdbserver :9090 ./child_program # VSCode launch.json { "name": "远程调试进程", "type": "cppdbg", "request": "launch", "program": "/path/to/child_program", "miDebuggerServerAddress": "192.168.x.x:9090", "MIMode": "gdb" } ``` --- ### 关键调试技巧 - **多窗口调试**:同时打开父/子进程调试窗口(`View → Run`) - **进程列表**:使用 `-exec info inferiors` (gdb) 查看调试中的进程 - **信号处理**:在 `launch.json` 中添加 `"signalHandling": {"raise": false}` 避免 SIGTRAP 干扰 - **VSCode 扩展**:安装 **C/C++** (ms-vscode.cpptools) 和 **CodeLLDB** (vadimcn.vscode-lldb) > 示例调试流程: > ![调试流程图](https://example.com/debug_flow.png) > *图:父子进程调试状态切换示意*[^1] ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值