您现在的位置是:首页 > 正文

VSCode 联合调试Python/C++

2024-04-01 00:58:03阅读 1


  熟悉深度学习框架的同学都了解,如Tensorflow,Pytorch,Mxnet等都是提供Python前端编程接口,而后端是采用C/C++实现的。想要深入了解这些框架的源码,需要一些工程实践的基础,其中有一项便是Python/C++代码的联合调试。很多IDE如Pycharm,Clion等只支持一种语言的调试,对联合调试支持不好,网上的资料也很少。Visual Studio这个宇宙第一IDE倒是支持联合调试,不过VS不支持跨平台,只能在Windows运行。因此本文就选择了Vscode实现Python/C++代码的联合调试。一是它跨平台,二是通过插件支持多语言代码编辑以及调试。

测试环境

  • Ubuntu 18.04
  • vscode 1.59.1
  • Python 插件 2021.10.1365161279
  • C++插件 1.6.0
  • Python 3.7
  • g++ 7.5.0
  • gdb 8.1.1

编写C++扩展

1. 编写C++实现:myadd.cpp

#include <Python.h>

static PyObject *method_myadd(PyObject *self, PyObject *args){
    int x, y, z = -1;

    /* Parse arguments */
    if(!PyArg_ParseTuple(args, "ii", &x, &y)){
            return NULL;
    }

    /* The actual bit of code I need */
    z = x + y;

    return PyLong_FromLong(z);
}

static PyMethodDef myaddMethods[] = {
    {"myadd", method_myadd, METH_VARARGS, "Python interface for myadd C library function"},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef myaddmodule = {
    PyModuleDef_HEAD_INIT,
    "myadd",
    "Python interface for the myadd C library function",
    -1,
    myaddMethods
};

PyMODINIT_FUNC PyInit_myadd(void) {
    return PyModule_Create(&myaddmodule);
}

2. 编写要调用该扩展的Python脚本: myscript.py

import myadd

print("going to ADD SOME NUMBERS")
x = myadd.myadd(5,6)
print(x)

3. 编写setup脚本

from distutils.core import setup, Extension

def main():
    setup(name="myadd",
          version="1.0.0",
          description="Python interface for the myadd C library function",
          author="wq_0608",
          author_email="xxxx@mail.com",
          ext_modules=[Extension("myadd", ["myadd.cpp"])],
          )


if __name__ == "__main__":
    main()

  最简单的情况,我们使用Extention构造一个C/C++语言的扩展模块。

Extension("myadd", ["myadd.cpp"])

  第一个参数是模块的名称,第二个参数是一个list,里面包含所有的源文件。默认情况下采用c语言,如果我们使用C++语言,可以指定另一个参数language=“c++”

Extension("myadd", ["myadd.cpp"], language="c++")

4. 编译安装

我们可以执行以下命令进行安装:

$ python3 setup.py install
running install
running build
running build_ext...

也可以将它构建为动态链接库,可以从当前目录直接import。

$ python3 setup.py build_ext --inplace

现在myadd是可用的,我们可以运行以下命令检查它是否工作:

$ python3 myscript.py
going to ADD SOME NUMBERS
11

使用gdb启动测试程序

  调试Python程序,无论是手动调试还是IDE集成,都是基于pdb调试器进行的。而对于C++的调试我们则需要gdb(Linux), LLDB(mac), Visual Studio Windows Debugger(Windows)。无论单独用哪一个,都无法实现同时在Python程序和C++程序上打断点。所以只有使用Attach的方法进行调试。好在微软已经为我们开发了现成的一套工具,来在vscode上同时对Python和C++进行调试。

  ptvsd 是 Visual Studio 和 Visual Studio Code 中的 Python 调试器包,该调试引擎构建在开源 pydevd 之上。熟悉vscode python开发的同学可能都知道,ptvsd是用来进行远程调试的。服务器启动程序后,本地开发机通过ptvsd Attach到远程运行的程序上进行调试。它的功能可不止这么简单。ptvsd同时支持pdb,gdb调试器Attach到同一个进程上,以此实现Python和C++的联合调试。

  使用ptvsd联合调试python与c++这篇文章讲述了在VSCode中使用ptvsd联合调试Python与C++,但笔者按照文章尝试后,成功Attach到Python进程,但就是不跳进C++中断点。后来,看到使用gdb与python-gdb联合调试Python/C++这篇文章后遂改用gdb调试。下面以该途径做讲述,如有小伙伴知道怎么解决ptvsd联合调试问题欢迎交流!

1. 安装gdb工具:

根据Python官网使用GDB调试说明,针对不同系统安装不同的gdb工具:

Fedora:

  • sudo yum installgdb python-debuginfo

Ubuntu:

  • sudo apt-get install gdb python3.7-dbg

Centos*: 可以参考centos 7.x 版本下用gdb 调试

  • sudo yum install yum-utils
    sudo debuginfo-install glibc
    sudo yum install gdb python-debuginfo

2. 使用gdb测试是否可用:

$ gdb --args python myscript.py 
GNU gdb (Ubuntu 8.1.1-0ubuntu1) 8.1.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from python...done.
(gdb) b myadd.cpp:7
No source file named myadd.cpp.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (myadd.cpp:7) pending.
(gdb) r
Starting program: /usr/bin/python myscript.py
warning: Error disabling address space randomization: Operation not permitted
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
going to ADD SOME NUMBERS

Breakpoint 1, method_myadd (self=self@entry=0x7f47fdccad10, args=args@entry=0x7f47fed75050) at myadd.cpp:7
7           if(!PyArg_ParseTuple(args, "ii", &x, &y)){
(gdb) b myadd.cpp:12
Breakpoint 2 at 0x7f47fdac07ef: file myadd.cpp, line 12.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /usr/bin/python myscript.py
warning: Error disabling address space randomization: Operation not permitted
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
going to ADD SOME NUMBERS

Breakpoint 1, method_myadd (self=<optimized out>, args=0x7fbd1bf56fa0) at myadd.cpp:7
7           if(!PyArg_ParseTuple(args, "ii", &x, &y)){
(gdb) display z
1: z = <optimized out>
(gdb) n
3       static PyObject *method_myadd(PyObject *self, PyObject *args){
1: z = <optimized out>
(gdb) n
7           if(!PyArg_ParseTuple(args, "ii", &x, &y)){
1: z = -1
(gdb) 

起始有(gdb)的表示在gdb的调试器环境运行命令。其中,

b:打断点

r:运行

display:设定在单步运行或其他情况中,自动显示的对应表达式的内容

n:使用命令n(next)或s(step),它们之间的区别在于:若有函数调用的时候,s会进入该函数而n不会进入该函数。因此,s就类似于VC等工具中的”step in”,n类似与VC等工具中的”step over”

  可以看到基本上已经可以使用gdb来对Python调用的C++共享库进行调试了。更进一步的,如果我们想通过VSCode来对库进行可视化的调试,又该如何处理呢?

GDB中STL的pretty print

  我们在使用gdb时,老是会对其显示的一些STL容器不能很好显示而感到不爽,从gdb7.0开始,支持python脚本导入gdb中,所以为了能让gdb中的vector更好的显示,需要在你的用户目录即~下放置一个.gdbinit文件和python的文件夹,这样操作之后,gdb在启动时就会运行这个.gdbinit从而对STL容器进行pretty print的支持,具体可以参见GDB Wiki的说明,也可参考VS Code中开启gdb的pretty-printer功能

VSCode调试Python调用的C++动态库

1. 打开VSCode->打开工程文件夹

2. 调试配置

根据图1进入launch.json配置页面。
图1
  此处讲解下attach的原理:attach是gdb的一个功能,用于对运行中的程序的状态进行监测,例如一些服务器中的进程不能直接以launch的方式启动,就只能以这种方式来监视。我们的项目中也同理,不能直接通过C++启动,所以需要python首先开启,然后在hello.cpp中设置getchar()或者pause进行中断进程,然后通过attach进程的方式来连接调试。(另外一种更直接的方式是通过上述所说的gdb进行调试,但这种只能通过gdb命令行的方式,可能习惯IDE的人不习惯,所以我找了这种用vscode的方式)

{
      "name": "(gdb) Attach",
      "type": "cppdbg",
      "request": "attach",
      "program": "/usr/bin/python", //要启动的进程
      "processId": "${command:pickProcess}",//表示用手选的方式选择进行调试
      "MIMode": "gdb",
      "additionalSOLibSearchPath": "${workspaceFolder}/build", //动态库所在位置
      "sourceFileMap": {"/build/glibc-S9d2JN": "/usr/src/glibc"}, //调试c时需要用到的库
      "logging": { "engineLogging": true },
      "setupCommands": [  //vscode打开gdb对STL容器的友好显示
          {
              "description": "Enable pretty-printing for gdb",
              "text": "-enable-pretty-printing",
              "ignoreFailures": true
          }
      ]
  }

3. 运行python程序

  由于需要留时间给attach,所以运行python前需要在python脚本或c++代码中加入getchar()sleep()语句空转以等待attach。

$ python myscript.py

  或者在python脚本中打断点并在launch.json增加python程序的调试配置。

    {
         "name": "Python: 当前文件",
         "type": "python",
         "request": "launch",
         "program": "${file}",
         "console": "integratedTerminal",
         "cwd": "${workspaceFolder}/test_demo"
    }

4. attach开始调试

attach前需要找到python进程号,有两种方式。

a. 运行ps aux | grep python:

root      4083  0.0  0.6 970112 205348 ?       Sl   Nov08   0:15 /root/.vscode-server/bin/3866c3553be8b268c8a7f8c0482c0c0177aa8bfa/node /root/.vscode-server/extensions/ms-python.vscode-pylance-2021.11.0/dist/server.bundle.js --cancellationReceive=file:d244b017462526dd28ca64cac58403d8f3ac9bc9f0 --node-ipc --clientProcessId=3954
root      6630  0.5  0.0 862548 16244 ?        Sl   07:37   0:00 /usr/bin/python /root/.vscode-server/extensions/ms-python.python-2021.10.1365161279/pythonFiles/lib/python/debugpy/adapter
root      6639  0.3  0.0 272404 15756 pts/8    Sl   07:37   0:00 /usr/bin/python /root/.vscode-server/extensions/ms-python.python-2021.10.1365161279/pythonFiles/lib/python/debugpy/launcher 33327 -- /home/test_demo/myscript.py
root      6645  4.1  0.1 570468 35372 pts/8    Sl+  07:37   0:01 /usr/bin/python /root/.vscode-server/extensions/ms-python.python-2021.10.1365161279/pythonFiles/lib/python/debugpy --connect 127.0.0.1:37459 --configure-qt none --adapter-access-token e3958bb5dc3c5d3492e9316d6836aa704455f1348874d032ac682b2c734c9479 /home/test_demo/myscript.py
root      6709  0.0  0.0  11472  1092 pts/9    S+   07:37   0:00 grep --color=auto python

含有token的L4行就是目标进程,进程号为6645.

b. 使用VSCode的搜索
在VSCode的debug选择框中(如图2)点击倒三角切换到launch.json中配置的attach,此时会弹出图3中搜索框。
在这里插入图片描述
输入python或者运行的脚本名,选择较大进程号的那个就是目标进程。
图3
正确attach后就会出现图4中红色插头:
图4

5. 完成调试,按红色插头断开连接,python进程继续运行。

参考

  1. 调试Python调用的C++共享库
  2. gdb使用方法简介
  3. gdb调试Python动态链接库
  4. 使用c/c++编写python扩展(二)- Vscode Python C++联合调试
  5. Example debugging mixed Python C++ in VS Code

网站文章

  • 《机器学习高频面试题详解》4.4:模型融合:Bagging

    机械工程师岗,18K x (14-16)薪,七成是基本工资,三成绩效,公积金12%,每月交通补助600元,每日餐补大约20元,试用期半年,试用期内工资80%,宿。三方违约金是一个月工资,但是我现在没有...

    2024-04-01 00:57:57
  • 计算机毕业设计/课程设计开题报告系列-基于JavaWeb的学生选课系统开题报告

    计算机毕业设计/课程设计开题报告系列-基于JavaWeb的学生选课系统开题报告

    本项目是一款基于JavaWeb的学生选课系统,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。

    2024-04-01 00:57:51
  • 为什么要用反射创建对象_为什么几乎所有的Java框架都要用到反射机制,她的魅力在哪里?...

    为什么要用反射创建对象_为什么几乎所有的Java框架都要用到反射机制,她的魅力在哪里?...

    说起Java的反射机制,大家应该不陌生吧,她是Java语言多态的一种体现。但是在面试过程中,我发现很多人都只是停留在听说过的层面,并没有知其所以然,今天我们就来探讨下。大部分人最早接触的反射代码,是J...

    2024-04-01 00:57:43
  • RESTful 规范

    1. 基本概念     REST全称是Representational State Transfer,中文意思是表征性状态转移。指的是一组架构约束条件和原则。如果一个架构符合REST的约束条件和原则,...

    2024-04-01 00:57:17
  • Python中运算符优先级的顺序

    Python中运算符优先级的顺序

    目录(一)引言(二)运算符分类(三)关于三种运算符的优先级列表 在任何语言中运算符都有优先级一说,那在Python中我们常见的运算符优先级又是如何呢,本文我将展开聊聊这个话题 此处我将列出常见运算符,大致分为算数符(比如加减乘除),关系符(比如大于小于等于)以及逻辑符(与非或)......

    2024-04-01 00:57:08
  • 零宽度字符隐写(CTF) 热门推荐

    零宽度字符隐写(CTF) 热门推荐

    文章目录零宽度字符简介用法其他用法例题:[UTCTF2020] zero 零宽度字符简介 零宽度字符是一些不可见的,不可打印的字符。它们存在于页面中主要用于调整字符的显示格式。 常见的零宽度字符及它们...

    2024-04-01 00:57:02
  • 延迟绑定与ret2dlresolve

    延迟绑定与ret2dlresolve

    2024-04-01 00:56:38
  • Java Junit 由浅入深

    Java Junit 由浅入深

    JUnit4的常用方法:fail()方法:是指测试失败assertEquals()方法:测试2个参数(一个是预期值,一个是实际值)是否相等1. @Test : 测试方法,测试程序会运行的方法,后边可以跟参数代表不同的测试,如(expected=XXException.class) 异常测试,(timeout=xxx)超时测试2. @Ignore : 被忽略的测试方法3. @Befo...

    2024-04-01 00:56:31
  • stm32延时函数的三种写法 热门推荐

    我常用的延时函数有3种,一种是空循环,一种是使用systick中断,还有一种是正点原子的延时。 第一种就不做举例,因为平时不常用。但是一些模块给的参考代码的延时函数都会采用这样的方法,直接用就好了,不...

    2024-04-01 00:56:25
  • 在cmd中查询mysql数据库中文显示为“ ?”的解决办法

    在cmd中查询mysql数据库中文显示为“ ?”的解决办法

    解决命令提示符中查询mysql中文显示为问号的方法 今天在使用Navicat给mysql建表的时候输入了一些包含中文的数据,然后想在cmd中操作一下,查询的时候发现中文字段都变成了问号??? 之后我看了一下Navicat中编码方式为UTF-8,而cmd默认编码格式是GBK, 所以只需要在cmd中加上set names gbk; 就能解决了。 ...

    2024-04-01 00:56:00