デバッグビルドしたTensorFlowをVisual Studio Codeでデバッグする（Mac）

TensorFlowの内部構造を理解する場合、TensorFlowのソースコードを読むことが最も重要な調査手段ですが、実際に動かしてみて確かめることで、効率よく内部構造を調査することができます。
TensorFlowを動作させて調査する際、TensorFlowにprint文を追加してデバッグする方法が最初に考えられます。しかし、リリースビルドでさえ時間のかかるTensorFlowを、毎回print文を追加するたびにデバッグビルドするのは効率が悪いです。
そこで本記事では、TensorFlowをデバッグビルドし、Visual Studio Codeを使ってGUIでデバッグする方法を紹介します。

なお本記事では、以下の環境で動作確認しました。

* macOS High Sierra (10.13.5)
* Xcode (9.1)
* TensorFlow (1.8.0、コミットID: 93bc2e2072e0daccbcff7a90d397b704a9e8f778)
* Conda (4.4.10)
  * Python (3.5.5)
  * pip (3.5)


# TensorFlowのデバッグビルド

## TensorFlowのソースコード取得

最初に、TensorFlowのソースコードをGitHubからクローンして取得します。
本記事では、TensorFlowのバージョンが1.8.0の時のソースコードを使用するため、タグv1.8.0が設定されたコミット（コミットID: 93bc2e2072e0daccbcff7a90d397b704a9e8f778）をcheckoutします。

```bash
$ git clone https://github.com/tensorflow/tensorflow.git
$ cd tensorflow
$ git checkout v1.8.0
```

## Bazelのインストール

TensorFlowは、BazelというGoogleが開発しているビルドツールを使ってビルドします。BazelのインストーラはGitHubに置かれているため、[GitHubのReleasesページ](https://github.com/bazelbuild/bazel/releases)から、**bazel-0.15.0-installer-darwin-x86_64.sh** をダウンロードします。
ダウンロード完了後、以下のコマンドを実行してインストールします。

```bash
$ sh bazel-0.15.0-installer-darwin-x86_64.sh
```

## TensorFlowのビルド

TensorFlowをビルドする前に、ビルドするために必要な設定を行います。TensorFlowのビルド時間を短縮したい場合は、不要なものをビルドしないように、サポート機能を無効化すると良いでしょう。今回は、全てのサポートを無効化してビルドしました。

```bash
$ ./configure
Please specify the location of python. [Default is /Users/XXX/anaconda3/envs/tf_build/bin/python]:
Please input the desired Python library path to use.  Default is [/Users/XXX/anaconda3/envs/tf_build/lib/python3.5/site-packages]
Do you wish to build TensorFlow with Google Cloud Platform support? [Y/n]: n
Do you wish to build TensorFlow with Hadoop File System support? [Y/n]: n
Do you wish to build TensorFlow with Amazon S3 File System support? [Y/n]: n
Do you wish to build TensorFlow with Apache Kafka Platform support? [Y/n]: n
Do you wish to build TensorFlow with XLA JIT support? [y/N]:
Do you wish to build TensorFlow with GDR support? [y/N]:
Do you wish to build TensorFlow with VERBS support? [y/N]:
Do you wish to build TensorFlow with OpenCL SYCL support? [y/N]:
Do you wish to build TensorFlow with CUDA support? [y/N]:
Do you wish to download a fresh release of clang? (Experimental) [y/N]:
Do you wish to build TensorFlow with MPI support? [y/N]:
Please specify optimization flags to use during compilation when bazel option "--config=opt" is specified [Default is -march=native]:
Would you like to interactively configure ./WORKSPACE for Android builds? [y/N]:
```

続いてビルドを行います。通常のビルド時に利用するオプションに加えて、デバッグオプションとして ```-c dbg``` や ```--strip=never``` を追加している点に注意してください。

```bash
$ bazel build -c opt -c dbg --strip=never //tensorflow/tools/pip_package:build_pip_package
```

ビルドが完了したら、TensorFlowのPythonパッケージを作成します。

```bash
$ mkdir pkg_out
$ bazel-bin/tensorflow/tools/pip_package/build_pip_package ./pkg_out
```

最後に、作成したPythonパッケージをインストールします。

```bash
$ pip install pkg_out/tensorflow-1.8.0-cp35-cp35m-macosx_10_6_x86_64.whl
```


## インストール確認

デバッグビルドして作ったTensorFlowのPythonのパッケージが動作することを確認します。
動作確認の時、ビルドのコマンドを実行したディレクトリ（リポジトリのトップディレクトリ）と同じディレクトリで、Pythonのプログラムを実行しないように注意してください。tensorflowパッケージをimportする時にエラーが発生してしまいます。

```bash
$ python
>>> import tensorflow as tf
>>>
>>> a = tf.constant("Hello.")
>>>
>>> with tf.Session() as sess:
...   print(sess.run(a))
b'Hello.'
```


# Xcodeのインストール

TensorFlowのコア処理部はC++で書かれているため、C++のデバッガを使ってデバッグをする必要があります。C++コードで書かれたプログラムをMacでデバッグするためには、Xcodeに付随するlldbと呼ばれるデバッガを利用する必要があります。App StoreからXcodeをインストールしましょう。


# Visual Studio Codeの設定

## Visual Studio Codeのダウンロード

Mac版の[Visual Studio Code](https://code.visualstudio.com)をインストールします。

## Visual Studio Codeの設定

Visual Studio CodeからTensorFlowをデバッグできるようにするため、以下の手順でVisual Studio Codeの設定を行います。

1. [File] - [Add Folder to Workspace...] を実行して、TensorFlowリポジトリのトップディレクトリを開きます
2. [Debug] - [Open Configurations...] を実行して、デバッグの設定ファイル ```launch.json``` を作成します
3. デバッグ実行するための2つのデバッグルールを、```launch.json``` に追加します

```launch.json``` の記述例を以下に示します。

TensorFlowのプログラムが動作しているプロセスにアタッチしてデバッグする場合のデバッグルールを ```(lldb) Attach to Process```、TensorFlowをスタンドアローンなプログラムとしてビルドしたものを、Visual Studio Codeから直接実行してデバッグする場合のデバッグルールを ```(lldb) Launch``` として追加しました。本記事では前者のデバッグルールに従ってTensorFlowをデバッグします。

※ デバッグルール ```(lldb) Launch``` を利用する場合は、```"program"``` にデバッグ対象のプログラムを指定する必要がありますので、注意してください。

```json:launch.json

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(lldb) Attach to Process",
            "type": "cppdbg",
            "request": "attach",
            "program": "${workspaceRoot}/bazel-out/darwin-py3-dbg/bin/tensorflow/libtensorflow_framework.so",
            "stopAtEntry": true,
            "cwd": "${workspaceFolder}",
            "externalConsole": true,
            "MIMode": "lldb",
            "processId": "${command:pickProcess}"
        },
        {
            "name": "(lldb) Launch",
            "type": "cppdbg",
            "request": "launch",
            "program": "<Standalone Program>",
            "args": [],
            "stopAtEntry": true,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": true,
            "MIMode": "lldb"
        }
    ]
}
```


# TensorFlowのデバッグ

ようやく、TensorFlowをデバッグする準備が整いました。さていよいよ、TensorFlowのデバッグしてみたいと思います。

## TensorFlowのプロセス確認

本記事では、TensorFlowのプロセスにアタッチすることでデバッグを行います。このため、Pythonプログラムから自身のプロセスIDを確認する必要があります。後ほど、このプロセスに対して、Visual Studio Codeからアタッチするため、終了しないように注意してください。

```bash
$ python
>>> import tensorflow as tf
>>> import os
>>> os.getpid()
38838
>>>
```

## TensorFlowのプロセスにアタッチ

Visual Studio Codeで [Debug] - [Start Debugging] を実行すると、アタッチするプロセスを聞かれますので、先ほど取得したプロセスIDを入力して検索し、デバッグ対象とするプロセスIDであることを確認して選択することで、プロセスのアタッチが完了します。

## ブレークポイントの設定

実際にブレークポイントを設定して、TensorFlow内部の処理をデバッグできることを確認します。ここでは、ブレークポイントを ```DirectSession::Run()``` に設定します。今回は、```tensorflow/core/common_runtime/direct_session.cc``` の622行目に設定しました。

## プログラム実行

先ほど起動したPythonプログラム（プロセスID：38838）に、以下のプログラムを入力して実行すると、設定したブレークポイントでプログラムが止まり、Visual Studio Code側に実行制御が移ります。

```bash
>>> a = tf.constant("Hello.")
>>>
>>> with tf.Session() as sess:
...   print(sess.run(a))
```

以降、Visual Studio Codeを使ってTensorFlow内をデバッグできるようになります。

<img width="1308" alt="tensorflow_debug.png" src="https://qiita-image-store.s3.amazonaws.com/0/38888/320c5556-2907-9ad5-71e6-04efef044494.png">


# 参考情報

* https://www.tensorflow.org/install/install_sources
* https://gist.github.com/Mistobaan/738e76c3a5bb1f9bcc52e2809a23a7a1