北冥有鱼 记录生活点滴,分享学习心得

使用gdb调试并阅读TensorFlow源码

Posted by YuChen on March 17, 2020

1. 环境准备

  • docker镜像: registry.baidubce.com/paddlepaddle/paddle:latest-dev-cuda11.4.1-cudnn8-gcc82
  • 容器创建命令:
    1
    
    nvidia-docker run --ulimit core=-1 --cap-add SYS_ADMIN --security-opt seccomp=unconfined --name tf-wz --shm-size="8g" --net=host -d -v $PWD/.ccache:/root/.ccache -v $PWD/.cache:/root/.cache -v $PWD/.tmp:/tmp -v $PWD:/work -v /ssd3/datasets:/work/datasets -it registry.baidubce.com/paddlepaddle/paddle:latest-dev-cuda11.4.1-cudnn8-gcc82 /bin/bash
    

    不使用nvidia-docker命令创建容器的方法(此文不使用,仅记录):

    1
    2
    3
    4
    5
    
    export CUDA_SO="$(\ls /usr/lib64/libcuda* | xargs -I{} echo '-v {}:{}') $(\ls /usr/lib64/libnvidia* | xargs -I{} echo '-v {}:{}')"
    export DEVICES=$(\ls /dev/nvidia* | xargs -I{} echo '--device {}:{}')
    
    sudo /usr/bin/docker run ${CUDA_SO} ${DEVICES} --ulimit core=-1 --cap-add SYS_ADMIN --security-opt seccomp=unconfined --name tf-wz --shm-size="8g" --net=host -d -v /usr/bin/nvidia-smi:/usr/bin/nvidia-smi -v $PWD/.ccache:/root/.ccache -v $PWD/.cache:/root/.cache -v $PWD/.tmp:/tmp -v $PWD:/work -it registry.baidubce.com/paddlepaddle/paddle:latest-dev-cuda11.4.1-cudnn8-gcc82 /bin/bash
    # 进入容器再设置一下环境变量:export LD_LIBRARY_PATH=/usr/lib64:/usr/local/lib:$LD_LIBRARY_PATH
    
  • 进入docker命令:nvidia-docker exec -it tf-wz /bin/bash

注意以下操作均在容器内完成。

2. 获取TensorFlow源码

1
2
3
4
5
git clone  https://github.com/tensorflow/tensorflow.git
# 切换到r1.14分支

git fetch origin r1.14:study_r1.14
git checkout study_r1.14

3. 安装bazel-0.24.1(对应于tf r1.14)

1
2
3
4
# https://github.com/bazelbuild/bazel/releases
wget https://github.com/bazelbuild/bazel/releases/download/0.24.1/bazel-0.24.1-installer-linux-x86_64.sh
chmod +x bazel-0.24.1-installer-linux-x86_64.sh
./bazel-0.24.1-installer-linux-x86_64.sh --user

4. 编译TensorFlow

  • 创建虚环境

    1
    2
    
    mkvirtualenv --python=python3.7 tf-study
    # workon tf-study
    

    virtualenv的安装及使用方法参考此处

  • 安装TensorFlow编译依赖包(python包)

    1
    2
    3
    4
    
    pip install numpy==1.18.5
    pip install -U pip six wheel setuptools mock 'future>=0.17.1'
    pip install -U keras_applications --no-deps
    pip install -U keras_preprocessing --no-deps
    
  • 进行编译选项配置

    1
    2
    3
    
    export PATH=/root/bin:$PATH
    ./configure
    # 根据提示进行配置,主要是遇到“是否编译cuda时”选择“y”,其他默认选择即可。
    
  • 执行编译

    参考此文,为了编译带调试信息的TensorFlow,需要执行如下编译命令:

    1
    2
    
    bazel build -c opt --config=cuda --copt="-g" --cxxopt="-g" //tensorflow/tools/pip_package:build_pip_package
    # tf 2.x: bazel build -c opt --config=cuda --copt="-g" --cxxopt="-g" --cxxopt="-D_GLIBCXX_USE_CXX11_ABI=0" //tensorflow/tools/pip_package:build_pip_package
    

    注意: 编译遇到C++ compilation of rule '//tensorflow/python:bfloat16_lib' failed错误,需要将numpy版本降到1.18.5版本(<1.19.0),然后使用bazel clean命令清空之前的编译结果,最后再重新编译。

5. 构建whl软件包

1
./bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg

6. 安装whl软件包

1
pip install -U /tmp/tensorflow_pkg/tensorflow-*.whl

7. gdb调试python代码方法

7.1 安装python-debuginfo包

1
2
  # apt install python3-dbg
  apt install -y python3.7-dbg

安装完成后,/usr/share/gdb/auto-load/usr/bin/python3.7m-gdb.py即为后续调试python脚本所需的libpython.py

7.2 设置python脚本启动断点

在要调的 python 代码前面加上如下一段代码,用于获取待调试的python脚本进程号并暂停脚本运行,点击查看示例

1
2
3
4
5
import os
PID = os.getpid()
print('Program pid:', PID)
print('Pause here to enter DBG')
os.system("read _")

使用python -m pdb test_debug.py命令执行python脚本,接着执行gdb -p PID即可进行调试。

使用pdd可对python脚本进行调试,使用gdb可对c++库文件进行调试。

os.system("read _")相当于人为地打了一处断点,设置完python调试命令后,在gdb模式中输入c指令(可在输入c指令前设置一些C/C++文件中的断点,如break TF_NewBuffer),然后再在python脚本运行窗口中按Enter键即可让python程序继续运行。

7.3 加载python调试指令

  • 方法一:

为了可以使用py-list之类的python调试指令,进入gdb模式后,需要执行如下代码:

1
2
3
4
5
6
7
(gdb) python
>import sys
# "/Python-3.7.0/Tools/gdb/libpython.py"
>sys.path.append('/Python-3.7.0/Tools/gdb')
>import libpython
>end
(gdb) ...
  • 方法二:

为了方便起见,可以选择将libpython.py文件拷贝到gdb命令执行时所在的目录下,然后执行如下命令完成python调试指令的加载:

1
2
3
4
5
6
(gdb) python
>import sys
>sys.path.append('.')
>import libpython
>end
(gdb) ...
  • 方法三:

为了省略每次运行gdb后都需要进行python调试指令的加载,可以在HOME目录下添加.gdbinit配置文件,内容如下所示:

1
2
3
4
5
6
7
8
9
# 加载python调试指令
python
import sys
sys.path.append('/Python-3.7.0/Tools/gdb')
import libpython
end
# 保存历史命令
set history filename ~/.gdb_history
set history save on

这样每次运行gdb时,即可自动加载python调试指令。

  • 方法四(推荐):

为了最大化减少gdb启动后运行的设置命令数,可以在gdb命令执行时所处目录(一般为用户工程项目路径)下新建.gdbinit配置文件,并将libpython.py放置在同一目录下,最后设置.gdbinit文件为如下内容:

1
2
3
4
5
6
7
8
9
# 加载python调试指令
python
import sys
sys.path.insert(0, ".")
import libpython
end

# 设置tensorflow源码目录,以便查找代码
set directories /work/study/tf-learn/tensorflow/

设置上述gdb初始化配置文件后运行gdb命令时会出现如下类似警告(此时亦可发现当前目录下的.gdbinit文件配置命令并没有执行):

1
2
3
4
5
6
7
warning: File "/work/study/tf-learn/tf-test/.gdbinit" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
        add-auto-load-safe-path /work/study/tf-learn/tf-test/.gdbinit
line to your configuration file "/root/.gdbinit".
To completely disable this security protection add
        set auto-load safe-path /
line to your configuration file "/root/.gdbinit".

根据警告提示,需要在HOME目录下也新建一个.gdbinit配置文件,其内容如下所示:

1
2
3
4
5
# 保存历史命令
set history filename ~/.gdb_history
set history save on
# 自动加载任意目录下的.gdbinit文件配置内容
set auto-load safe-path /

7.4 gdb调试时注意事项:

  • 运行python脚本不能在tensorflow源码根目录下,否则会出错。但是需要在tensorflow源码根目录下存放一个对应的软连接文件(ln -s python脚本文件 tensorflow/python脚本文件),以便使用py-list时可以查看脚本源码内容。
  • 执行gdb -p PID命令最佳方案:在tensorflow源码根目录下运行gdb。否则,需要在gdb模式下使用set directories /work/study/tf-learn/tensorflow/指令设置tensorflow源码根目录路径。
  • TensorFlow的gdb调试方法详见此文档1
  • 给类成员函数设置断点的方法:
    • 需要加上命名空间,写法如b tensorflow::DirectSession::Run
    • 对于带有匿名命名空间的断点设置写法示例:b tensorflow::(anonymous namespace)::ExecutorState::ScheduleReady,其第二个命名空间为namespace {}

7.5 使用GDB dashboard

7.5.1 设置GDB dashboard

执行如下命令,在home目录下设置dashboard配置文件(下载.gdbinit文件到home目录):

1
2
rm -f ~/.gdbinit
wget -P ~ https://git.io/.gdbinit

为了可以加载任意目录下的.gdbinit文件,需要在~/.gdbinit文件中新增如下内容:

1
set auto-load safe-path /

点击此处下载修改后的主目录.gdbinit文件。

7.5.2 打印stl容器的内容

https://sourceware.org/gdb/wiki/STLSupport网站下载stl-views-1.0.3.gdb文件并将其放到所在工作目录下。然后,修改所在工作目录下的.gdbinit内容如下(即增加source stl-views-1.0.3.gdb语句):

1
2
3
4
5
6
7
8
9
10
11
12
# 加载python调试指令
python
import sys
sys.path.insert(0, ".")
import libpython
end

# 设置tensorflow源码目录,以便查找代码
set directories /work/study/tf-learn/tensorflow/

# 设置后可查看stl容器内容
source stl-views-1.0.3.gdb

点击此处下载修改后的工作目录.gdbinit文件。

8. 安装GDB 8.3并高亮显示源码

推荐使用附录中的命令行方式安装gdb-10,其自带源码高亮功能。

8.1 安装支持source-highlight的GDB

  • 源码编译并安装source-highlight
1
2
3
4
5
6
7
8
9
10
11
12
13
14
apt install libboost-dev libboost-regex-dev
# ln -s /usr/lib/x86_64-linux-gnu/libboost_regex.so.1.58.0 /usr/lib/x86_64-linux-gnu/libboost_regex.so

wget http://ftp.gnu.org/gnu/src-highlite/source-highlight-3.1.8.tar.gz
tar zxvf source-highlight-3.1.8.tar.gz

cd source-highlight-3.1.8
autoreconf -i
mkdir build
cd build
../configure
make -j
make install
# export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
  • 源码编译并安装GDB 8.32
1
2
3
4
5
6
7
8
9
10
11
12
13
14
apt install texinfo wget ncurses-dev

wget http://ftp.gnu.org/gnu/gdb/gdb-8.3.tar.gz
tar zxvf gdb-8.3.tar.gz
cd gdb-8.3

mkdir build && cd build/
../configure --prefix=/usr/local/gdb83 --enable-source-highlight=yes --enable-tui=yes --enable-gold=yes --enable-ld=yes --enable-libada --enable-libssp --enable-lto --enable-vtable-verify --enable-werror

make -j
make install
update-alternatives --install /usr/bin/gdb gdb /usr/local/gdb83/bin/gdb 83 --slave /usr/bin/gdbserver gdbserver /usr/local/gdb83/bin/gdbserver --slave /usr/bin/gdb-add-index gdb-add-index /usr/local/gdb83/bin/gdb-add-index
# ln -s /usr/local/gdb83/bin/gdb  /usr/bin/gdb
# ln -s /usr/local/gdb83/bin/gdbserver  /usr/bin/gdbserver

8.2 使用gdb的tui模式

直接使用gdb -p PID调试代码,在需要的时候使用切换键ctrl+x a调出gdbtui,再次使用ctrl+x a则退出gdbtui模式3。示例图如下所示:

gdb tui模式

9. Q&A

  • Q: 运行build_pip_package命令时出现OverflowError: Size does not fit in an unsigned int错误。 A: 需要使用python3.7+进行打包。操作命令如下:

    1
    2
    3
    
    which python3.7 # /usr/local/bin/python3.7
    # 编辑tools/python_bin_path.sh,将其内容修改为:
    # export PYTHON_BIN_PATH="/usr/local/bin/python3.7"
    

    然后再执行打包命令:

    1
    2
    
    ./bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg
    # 这样得到的结果为:tensorflow-1.14.1-cp37-cp37m-linux_x86_64.whl
    

    然而这样打包得到的whl包只能在python3.7环境中进行安装,所以编译时使用python3.7是最好的选择。

附录

下面对本文所使用的docker容器中的一些配置文件进行记录。

apt软件包中国源

修改/etc/apt/sources.list文件,修改后内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
deb-src http://archive.ubuntu.com/ubuntu xenial main restricted #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial universe
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates universe
deb http://mirrors.aliyun.com/ubuntu/ xenial multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse #Added by software-properties
deb http://archive.canonical.com/ubuntu xenial partner
deb-src http://archive.canonical.com/ubuntu xenial partner
deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial-security universe
deb http://mirrors.aliyun.com/ubuntu/ xenial-security multiverse

pip中国源

修改~/.pip/pip.conf文件,修改后内容如下:

1
2
3
[global]
trusted-host = mirrors.aliyun.com
index-url = https://mirrors.aliyun.com/pypi/simple

参考资料





更多文章