VSCode 联合调试Python/C++

VSCode 联合调试Python/C++

  • 测试环境
  • 编写C++扩展
    • 1. 编写C++实现:`myadd.cpp`
    • 2. 编写要调用该扩展的Python脚本: `myscript.py`
    • 3. 编写setup脚本
    • 4. 编译安装
  • 使用gdb启动测试程序
    • 1. 安装gdb工具:
    • 2. 使用gdb测试是否可用:
  • GDB中STL的pretty print
  • VSCode调试Python调用的C++动态库
    • 1. 打开VSCode->打开工程文件夹
    • 2. 调试配置
    • 3. 运行python程序
    • 4. attach开始调试
    • 5. 完成调试,按红色插头断开连接,python进程继续运行。
  • 参考

  熟悉深度学习框架的同学都了解,如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 

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="[email protected]",
          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配置页面。
VSCode 联合调试Python/C++_第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中搜索框。
VSCode 联合调试Python/C++_第2张图片
输入python或者运行的脚本名,选择较大进程号的那个就是目标进程。
VSCode 联合调试Python/C++_第3张图片
正确attach后就会出现图4中红色插头:
VSCode 联合调试Python/C++_第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

你可能感兴趣的:(C/C++,Python,vscode,python,c++,gdb)