写点什么

OneFlow 源码一览:GDB 编译调试

作者:OneFlow
  • 2022 年 7 月 14 日
  • 本文字数:4031 字

    阅读完需:约 13 分钟

OneFlow源码一览:GDB编译调试

作者|王益、严浩

翻译|程浩源、董文文

1、GDB Python3


PyTorch 官方发布了如何使用 GDB 对 Python 触发的 C++代码进行调试的指南,详情参考:

https://github.com/pytorch/pytorch/blob/master/CONTRIBUTING.md#gdb-integration


其核心思路是运行gdb python3。在 GDB 会话中,可以为给定的 C++函数名设置断点,如at::Tensor::neg。GDB 当前无法找到这个函数,prompt 中会提示是否在共享库加载时将断点挂起,回答 yes。然后输入run,GDB 会启动 Python 解释器。Python 解释器会提示输入 Python 源码。输入import torch,然后回车。


当 Python 解释器执行 import 语句时,会加载相关的共享库。GDB 会监视加载并设置断点。执行 Python 源码,触发断点,然后打开 GDB prompt 进行 C++调试,例如使用 bt 检查回溯,使用 l 显示 Python 调用的 C++代码。


2、在调试模式下编译 OneFlow


Linux 系统


OneFlow 支持 Linux,暂不支持 macOS 和 Windows。本文主要介绍在 AWS GPU 主机上运行 Amazon Linux 2(类似于 CentOS)。


(base) [wkyi ~]$ cat /etc/os-releaseNAME="Amazon Linux"VERSION="2"ID="amzn"ID_LIKE="centos rhel fedora"VERSION_ID="2"PRETTY_NAME="Amazon Linux 2"ANSI_COLOR="0;33"CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2"HOME_URL="https://amazonlinux.com/"
复制代码


Conda 或 Docker 环境


OneFlow 官方文档建议使用 Conda 或 Docker 镜像:

https://github.com/Oneflow-Inc/oneflow#option-1-build-with-conda-recommended。本次运行使用 Anaconda。使用 Conda 或 Docker 是为了修复 C++编译器和其他构建工具链的版本。使用新版本的 g++需要对源代码进行更新,如 https://github.com/Oneflow-Inc/oneflow/issues/8397


编译调试版本


这里要注意,必须先编译 OneFlow 的调试版本,因为 GDB 需要调试符号才能使 bt 和 l 的输出有意义。


cd ~/w/oneflow/buildCMAKE_BUILD_TYPE=Debug cmake .. -C ../cmake/caches/international/cpu.cmake
复制代码


我安装的是 CPU 版本的 OneFlow,创建了 cpu.cmake 文件。因为我的 AWS 主机不在中国,所以是在 international 目录下创建文件。


报错


在安装报错时,我在 GitHub 上提交了相关 issue

https://github.com/Oneflow-Inc/oneflow/issues?q=is%3Aissue+author%3Awangkuiyi),OneFlow 的研发人员快速给出了回应,向他们致敬!


编译步骤


本小节将展示编译 OneFlow 的具体步骤:


1. 下载安装 Anaconda。默认安装路径是 ~/anaconda3。安装时将环境变量添加到~/.bashrc。然后,获取环境变量或重新连接主机使更改生效。


2. 创建并激活 Conda 环境,具体步骤参考: https://github.com/Oneflow-Inc/conda-env


3. Git clone 源码


mkdir ~/w  cd ~/w  git clone https://github.com/Oneflow-Inc/oneflow


4. 编译 OneFlow


cd oneflow mkdir build cd build CMAKE_BUILD_TYPE=Debug cmake .. -C ../cmake/caches/international/cpu.cmake make -k -j $(nproc)


运行和调试


安装好后,在~/w/oneflow/build 目录中会出现 source.sh文件,这个文件设置了 PYTHONPATH 环境。运行下列命令使设置生效。


source source.sh
复制代码


然后,用 GDB 运行 Python 编辑器。


gdb python3
复制代码


在 GDB prompt 中,我在oneflow::one::Tensor::is_eager设置了一个断点,在共享库加载时会将断点挂起。


(gdb) b oneflow::one::Tensor::is_eagerFunction "oneflow::one::Tensor::is_eager" not defined.Make breakpoint pending on future shared library load? (y or [n]) yBreakpoint 1 (oneflow::one::Tensor::is_eager) pending.
复制代码


再然后,输入run运行 Python 编辑器。在 Python prompt 中,输入oneflow


(gdb) runStarting program: /home/wkyi/anaconda3/envs/oneflow-dev-gcc7-v2/bin/python3Missing separate debuginfos, use: debuginfo-install glibc-2.26-58.amzn2.x86_64[Thread debugging using libthread_db enabled]Using host libthread_db library "/lib64/libthread_db.so.1".Python 3.7.10 (default, Feb 26 2021, 18:47:35)[GCC 7.3.0] :: Anaconda, Inc. on linuxType "help", "copyright", "credits" or "license" for more information.>>> import oneflow
复制代码


导入的时间会比平时更长。如果显示ImportError,要检查是否运行了source source.sh


接下来就可以来创建 tensor 了。


>>> a = oneflow.tensor(1)
Thread 1 "python3" hit Breakpoint 1, oneflow::one::CopyBetweenMirroredTensorAndNumpy<long> (t=..., array=array@entry=0x7fffe5905150, Copy=<optimized out>, Copy@entry=0x7fffefc977e0 <oneflow::BlobNumpyCopyUtil<long>::From(unsigned long, oneflow::NumPyArrayPtr const&)>, modifier=..., block_host_until_done=block_host_until_done@entry=false) at /home/wkyi/w/oneflow/oneflow/api/python/utils/tensor_utils.h:9898 CHECK_OR_RETURN(tensor->is_eager()) << "eager tensors supported only.";
复制代码


输入回车,此行代码执行会触发断点。


上述信息表明在源文件的第 98 行有一个名为tensor->is_eager()的函数oneflow::one::CopyBetweenMirroredTensorAndNumpy


要显示更多内容,可以输入 l。在第 98 行,调用了tensor->is_eager()

(gdb) l93    inline Maybe<void> CopyBetweenMirroredTensorAndNumpy(94        const std::shared_ptr<Tensor>& t, PyObject* array,95        Maybe<void> (*Copy)(uint64_t, const NumPyArrayPtr&), const std::string& modifier,96        bool block_host_until_done) {97      auto tensor = JUST(t->AsMirroredTensor());98      CHECK_OR_RETURN(tensor->is_eager()) << "eager tensors supported only.";99100      if (block_host_until_done) {101        NumPyArrayPtr array_ptr(array);102        const auto& Callback = [array_ptr, Copy](uint64_t ofblob_ptr) {
复制代码


你可能会好奇,为什么在 Python 中创建 tensor 会触发对Tensor::is_eager的调用?可以输入 bt 来显示更多信息。


(gdb) bt#0  oneflow::one::CopyBetweenMirroredTensorAndNumpy<long> (t=..., array=array@entry=0x7fffe5905150, Copy=<optimized out>,    Copy@entry=0x7fffefc977e0 <oneflow::BlobNumpyCopyUtil<long>::From(unsigned long, oneflow::NumPyArrayPtr const&)>, modifier=...,    block_host_until_done=block_host_until_done@entry=false) at /home/wkyi/w/oneflow/oneflow/api/python/utils/tensor_utils.h:98#1  0x00007fffefd5aa5c in oneflow::one::CopyMirroredTensorFromUntypedArray<long> (array=0x7fffe5905150, tensor=...)    at /home/wkyi/w/oneflow/oneflow/api/python/utils/tensor_utils.cpp:61
#13 0x00007fffefbe433f in oneflow::one::functional::tensor (self=<optimized out>, args=<optimized out>, kwargs=<optimized out>) at /home/wkyi/w/oneflow/build/oneflow/api/python/functional/tensor_api.yaml.pybind.cpp:96#14 0x00005555556b98b4 in _PyMethodDef_RawFastCallKeywords () at /tmp/build/80754af9/python_1614362349910/work/Objects/call.c:693#15 0x00005555556b99d1 in _PyCFunction_FastCallKeywords (func=0x7ffdc75675a0, args=<optimized out>, nargs=<optimized out>, kwnames=<optimized out>) at /tmp/build/80754af9/python_1614362349910/work/Objects/call.c:732
#29 0x000055555578c22c in _Py_UnixMain () at /tmp/build/80754af9/python_1614362349910/work/Modules/main.c:3495#30 0x00007ffff783113a in __libc_start_main () from /lib64/libc.so.6#31 0x0000555555730e90 in _start () at ../sysdeps/x86_64/elf/start.S:103
复制代码


调用堆栈的底部是_stack,它是 Python 编辑器的入口点。从上面的代码中可以看到 Python 和 OneFlow 共享库之间的调用边界——Python 中的_PyMethodDef_RawFastCallKeywords函数调用了 OneFlow 的 C++函数oneflow::one::functional::tensor,进而触发了对oneflow::one::Tensor::is_eager的调用


上述内容展示了如何使用 GDB 对 C++编写的 Python 库进行调试。以下将更进一步,展示如何在 VSCode 中调用 GDB,以便更轻松地定位代码。


要想在 Python C++库中使用 GDB,需要进行如下操作:


1. 构建 C++库的调试版本

2. 用 GDB 启动 Python 解释器

3. 在 C++函数中设置断点,例如 b oneflow::one::functional::tensor

4. 在 Python REPL 中运行代码,触发断点,然后 GDB 会在断点处暂停


按照以下方法改进 VSCode 用户界面


1. 安装 Microsoft 的“C/C++”扩展,该扩展支持 GDB 调试器

2. 添加新的调试启动配置

3. 添加下列配置


{   "version": "0.2.0",   "configurations": [       {          "type": "cppdbg",          "request": "launch",          "name": "GDB",          "program": "/home/charlieyan/anaconda3/envs/oneflow-dev-gcc7-v2/bin/python          "cwd": ".",          "environment": [              {                  "name": "PYTHONPATH",                  "value": "/home/charlieyan/proj/oneflow/python"              }          ]       }   ]   }
复制代码


4. 然后就可以启动调试器“GDB”并设置断点。当调试器在断点处暂停时,VSCode 也会跳转到源代码行,侧边栏中的变量会与调用栈一起显示。要想更好地理解代码,也可以单步执行各个函数。



参考来源:

1.https://code.visualstudio.com/docs/cpp/launch-json-reference

2.https://of-worldwide.quip.com/JuQ0AuodVJn4/Use-GDB-to-Walkthrough-OneFlow-Source-Code


(本文经授权后编译发布)


其他人都在看


欢迎下载体验 OneFlow v0.7.0 最新版本:https://github.com/Oneflow-Inc/oneflow/

发布于: 刚刚阅读数: 3
用户头像

OneFlow

关注

不至于成为世界上最快的深度学习框架。 2022.03.23 加入

★ OneFlow深度学习框架:github.com/Oneflow-Inc/oneflow ★ OF云平台:oneflow.cloud

评论

发布
暂无评论
OneFlow源码一览:GDB编译调试_源码_OneFlow_InfoQ写作社区