对subprocess启动的子进程使用VSCode python debugger

文章目录

  • [1 情况概要(和文件结构)](#1 情况概要(和文件结构))
  • [2 具体设置和启动步骤](#2 具体设置和启动步骤)
    • [2.1 具体配置](#2.1 具体配置)
      • [Step 1 针对attach debugger到子进程](#Step 1 针对attach debugger到子进程)
      • [Step 2 针对子进程的暂停](#Step 2 针对子进程的暂停)
      • [(可选) Step 3 判断哪个进程id是需要的子进程](#(可选) Step 3 判断哪个进程id是需要的子进程)
    • [2.2 启动步骤和过程](#2.2 启动步骤和过程)
  • [3 其他问题解决](#3 其他问题解决)
    • 3.1
    • [3.2 ptrace: Operation not permitted](#3.2 ptrace: Operation not permitted)
  • 其他解决方案*2
  • 参考

1 情况概要(和文件结构)

环境:Linux Ubuntu

最近在跑大模型,遇到一份代码的具体程序是通过subprocess.run()来启动的,而vscode的debug功能无法追踪进去。也就是说,我有父进程launch.py来启动subprocess.run(),调用子进程文件run.py。我可以单步调试追踪到subprocess这一步并进入run()函数,但是无法继续进入子进程的工作,无法追踪到run.py看我真正想看的代码。

这里父进程文件和子进程文件的概念我没有细究,大概意思是主动调用subprocess.run()来创建子进程的是父进程文件,被subprocess.run()跑起来的是子进程文件。

附上文件结构的简单示意。

  1. test.shpython launch.py并传一堆参数

  2. launch.py:父进程,主要内容为

python 复制代码
def python_launch(args):
    """
    Vanilla python launcher for degbugging purposes
    """
    # ....
    # 构造cmd
    cmd = f"python {args.run_file}" # 省略一堆参数
    # 启动subprocess
    subprocess.run(cmd, shell=True)
    
  1. run.py:子进程,主要内容为
python 复制代码
def main(cfg):
 
    # 环境变量设置,数据读取,新建文件夹之类的之类的
    # ...
    
    # 初始化trainer并训练
    trainer = build_trainer(cfg)
    trainer.run()

2 具体设置和启动步骤

我们的目标是,我从某个地方把程序起起来并创建了子进程,然后把debugger连接到子进程上,于是它可以在子进程的断点地方停下、正常调试。

那么我们需要做到两件事情:1)把Debugger attach到子进程;2)子进程要能等待我们attach,而不是一股脑往下运行,那就来不及停在断点。

2.1 具体配置

我们分别对这两件事情做配置。

Step 1 针对attach debugger到子进程

采用vscode的python debugger插件,新建debugger config如下(编辑的是launch.json)

json 复制代码
{
    "name": "Python: Attach to Subprocess", //随便起名
    "type": "python",
    "request": "attach", //附加到子进程
    "processId": "${command:pickProcess}" //选择进程id
}

这里用${command:pickProcess}是采用vscode自带的命令,从进程列表中手动选择,而不是写死进程id。

实测这些内容就够了,不需要其他key比如ChatGPT建议的justMyCode和subProcess。

Step 2 针对子进程的暂停

在子进程文件,你需要断点的代码前面,或者索性最前面,加上一行:

python 复制代码
    input("Continue...")

这行会让代码停下,直到你在命令行中随便敲点什么,回车也行,才继续执行。

(可选) Step 3 判断哪个进程id是需要的子进程

因为我实在小白,我不确定哪个进程才是我要的,所以我在子进程文件里加了几行,输出父进程id和子进程id。

python 复制代码
    print(f"Parent PID (PPID): {os.getppid()}")
    print(f"Current PID: {os.getpid()}")

Current PID就是我们要的子进程id

以上,配置完了,我的run.py最终长这样:

python 复制代码
def main(cfg):

    #### for debug #########
    # 输出父进程和子进程id
    print(f"Parent PID (PPID): {os.getppid()}")
    print(f"Current PID: {os.getpid()}")
    
    # 可选,用来检查user id,原因后面说
    print(f"UID: {os.getuid()}, EUID: {os.geteuid()}") 
    
    # 暂停
    input("Continue...")
    #########################
    
    # 环境变量设置,数据读取,新建文件夹之类的之类的
    # ...
    
    trainer = build_trainer(cfg)
    trainer.run()
    

2.2 启动步骤和过程

需要你操作的地方写了人工,其他的是自动执行顺序。

  1. (人工)正常通过命令行执行sh test.sh,启动脚本

  2. 程序进入launch.py,启动subprocess

  3. 程序进入run.py

    3.1. 输出父进程id和子进程id供参考

    3.2. 在input(Continue...)处暂停,等待键入字符才能继续执行

  4. (人工)启动debug的Subprocess Attach,选择子进程,附加到正确的id上,等待...

  5. attach到子进程,可能需要一会。

    在DEBUG CONSOLE里会提示结果,要么是attach成功(如图),要么是弹窗报错然后在DEBUG CONSOLE里看错误信息。

    此时可以看到调试工具栏表示正在执行、没有暂停,因为我们还在被input()暂停着。

  6. (人工)在命令行里敲个回车,跳出input(Continue...)

  7. 断点停止在run.py中的对应位置

  8. (人工)正常调试

3 其他问题解决

3.1

json 复制代码
--- Starting attach to pid: 1103 ---
/bin/sh: 1: gdb: not found

解决方案是安装gdb

sh 复制代码
apt-get update
apt-get upgrade -y
apt-get install gdb

3.2 ptrace: Operation not permitted

解释一下前面python里为什么写了一句进程创建用户id查询

我在第5步attach环节遇到了一个错误,ptrace: Operation not permitted,是个权限错误。

--- Starting attach to pid: 654608 ---
Could not attach to process.  If your uid matches the uid of the target
process, check the setting of /proc/sys/kernel/yama/ptrace_scope, or try
again as the root user.  For more details, see /etc/sysctl.d/10-ptrace.conf
ptrace: Operation not permitted.

问了下ChatGPT:

/proc/sys/kernel/yama/ptrace_scope 文件

这个文件控制了 ptrace 系统调用的访问权限。ptrace 是 Linux 的一个系统调用,允许一个进程跟踪和控制另一个进程,是调试器工作的核心。
ptrace_scope 参数的值

0: 允许任何进程使用 ptrace 附加到其他进程(受用户权限约束)。

1: 仅允许父进程(如直接启动的调试器进程)附加到子进程。

2: 禁止所有非父子关系的调试。

3: 禁止所有 ptrace 附加操作。

默认值通常为 1,为了安全性避免恶意进程滥用 ptrace。

查询了一下这个值(cat /proc/sys/kernel/yama/ptrace_scope),输出是1。但是显然我的父进程和子进程都是同一个user启用的,不懂为什么说我权限错误。

查询进程创建用户有好几个方法,我是在python里写了一句这个。(解释了前文的查询用户id是啥用)

python 复制代码
print(f"UID: {os.getuid()}, EUID: {os.geteuid()}") 

但是到处折腾了一圈别的方案没解决,还是回来把这个值设为0了,真就解决了。

这个值的设置需要root权限。废话,如果本来就是用root在跑的话权限统一也不会报这个错了。

sh 复制代码
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope

ChatGPT也说了,这个值设置为0的话不太安全,建议用完就给它改回去。于是写了个脚本方便切换值,插入在.bashrc文件里(vim ~/.bashrc

ptrace_scope() {
	current_value=$(cat /proc/sys/kernel/yama/ptrace_scope) 
	echo "[INTRO] Script for changing between 0 & 1 value for /proc/sys/kernel/yama/ptrace_scope. The value should vary from [0,3] for different safety levels, default as 1"
    echo; # 换行
    echo "current value = $current_value" # 获取当前值
    
    # 根据当前值,在0和1之间切换
    if [ "$current_value" -eq 1 ]; then
        echo "Now changing to 0..."
        echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
    elif [ "$current_value" -eq 0 ]; then
        echo "Now changing to 1 ..."
        echo 1 | sudo tee /proc/sys/kernel/yama/ptrace_scope
    else
        echo "[Warning] Unknown ptrace_scope current value. Nothing takes effect."
        exit 1
    fi

    # 检查是否执行成功,并提醒恢复0值
    current_value=$(cat /proc/sys/kernel/yama/ptrace_scope)
    echo "[INFO] ptrace_scope changed to $current_value"
    echo;
    if [ "$current_value" -eq 1 ]; then
        echo "[INFO] Safe default setting ^-^"
    elif [ "$current_value" -eq 0 ]; then
        echo "[INFO] Unsafe setting, remember to change back to value = 1."
    fi

    echo;
}

记得source ~/.bashrc使配置生效。

效果如下,通过ptrace_scope change指令实现0-1切换。

其他解决方案*2

方案一(ChatGPT提供,对我不可行)

针对debugger监听子进程这个事情,ChatGPT还给了我一套方案,但用不了。记一下大概配置,哪哪都折腾过了,这个debugpy的wait for client啊就是不知道wait到了什么,直接就监听到了,但我明明还没开debugger的attach!

在子进程文件里写

python 复制代码
import debugpy

int main():
	# 启动调试服务
	debugpy.listen(("0.0.0.0", 5678))
	print("Debugpy is listening on port 5678")
	# 等待客户端连接
	debugpy.wait_for_client()
	print("Debugger is attached!")

	# trainer.......

在debugger config里写一个针对test脚本执行的config,和debug普通python文件一个写法。另外再写一个attach用的config,用的监听路径,大概如下:

json 复制代码
{
    "name": "Python: Attach to Subprocess",
    "type": "python",
    "request": "attach",
    "connect": {
        "host": "localhost",
        "port": 5678
    },
    "justMyCode": false,
    "subProcess": false
}

方案二(Github issue翻到)

还看到一个更暴力的是是直接改写,不用subprocess,见GitHub: embodied-generalist/issues/33

因为它只是inference但是我要train...小白还不知道不开子进程会不会有影响,就没碰

参考

  1. StackOverflow Attaching a VSCode Debugger to a Sub Process in Python
  2. GitHub: embodied-generalist/issues/33
相关推荐
bitcsljl2 分钟前
Linux 命令行快捷键
linux·运维·服务器
ac.char4 分钟前
在 Ubuntu 下使用 Tauri 打包 EXE 应用
linux·运维·ubuntu
Cachel wood24 分钟前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
終不似少年遊*29 分钟前
pyecharts
python·信息可视化·数据分析·学习笔记·pyecharts·使用技巧
Python之栈31 分钟前
【无标题】
数据库·python·mysql
Youkiup32 分钟前
【linux 常用命令】
linux·运维·服务器
qq_2975046135 分钟前
【解决】Linux更新系统内核后Nvidia-smi has failed...
linux·运维·服务器
袁袁袁袁满1 小时前
100天精通Python(爬虫篇)——第113天:‌爬虫基础模块之urllib详细教程大全
开发语言·爬虫·python·网络爬虫·爬虫实战·urllib·urllib模块教程
weixin_437398211 小时前
Linux扩展——shell编程
linux·运维·服务器·bash
小燚~1 小时前
ubuntu开机进入initramfs状态
linux·运维·ubuntu