VSCodeを活用したリモートSSH接続下でのPythonデバッグガイド

BERT応用モデルの改修にあたり、VSCodeを使ってEC2にSSHログインした状態でコードを触っているのですが、デバッグ環境の導入に一手間かかったので、今回記事にまとめました。

# 導入手順
大きくわけて、4つの手順が必要となります。

## 1. debugpyの導入
デバッグツール`debugpy`を`pip install debugpy`でインストールします。
`setup.yml`でconda環境を構築する場合は、dependenciesに`debugpy`を追加して、`conda env create -f setup.yml`を実行します。

以下はcondaファイルの記述例です。

```yml:setup.yml
name: tabformer
channels:
  - anaconda
  - pytorch
  - huggingface
  - conda-forge
dependencies:
  - python>=3.8
  - pip>=21.0
  - pytorch=1.7.1=py3.8_cuda11.0.221_cudnn8.0.5_0
  - torchvision
  - pandas
  - scikit-learn
  - transformers
  - numpy
  - libgcc
  - debugpy  # 追加する行
  - pip:
      - transformers==3.2.0
```
## 2. デバッグセッション開始用コードの追加
Pythonプログラムを実行する前に、`debugpy`を起動してリモートデバッグセッションを開始する必要があります。
実行ファイルに以下のコードを追加します。

```python
import debugpy

# 0.0.0.0を指定しているのは、すべてのネットワークインターフェースで接続を待つためです。
# 5678はデフォルトのdebugpyポートですが、必要に応じて変更できます。
debugpy.listen(('0.0.0.0', 5678))

print("Waiting for debugger attach")
debugpy.wait_for_client()
```
リモートデバッグの場合、`debugpy`のlistenメソッドが実行されるとき(`debugpy`がリモートマシンで開始されてポートをリッスンし始めるとき)からブレークポイントが有効になります。
これは、VSCode(ローカルマシン)が`debugpy`(リモートマシン)に接続してデバッグ情報を受信し始めるためです。

## 3. SSHトンネリングの設定
リモートデバッグするためには、ローカルマシンからリモートマシン上の`debugpy`に接続する必要があります。
これを行うために以下のコマンドでSSHトンネリングを使用します(ローカルマシンのポート5678とリモートマシンのポート5678を結びつけます)。

```bash
$ ssh -i ~/.ssh/<pem-key> -L 5678:localhost:5678 ubuntu@<public-ip>
```

## 4. launch.jsonの作成
`launch.json`は、VSCodeによりデバッグセッションが開始されるとき(サイドバーのRun and Debugがクリックされるとき)に読み込まれるファイルです。
このファイルはデバッグセッションの設定を定義し、VSCodeにデバッグセッションをどのように開始するかを指示します。
デバッグセッションを開始すると、設定したブレークポイントでコードの実行が一時停止されます。

`launch.json`に以下の内容を記述し、`.vscode`直下に配置します。

```json:.vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Remote Attach",
            "type": "python",
            "request": "attach",
            "connect": {
                "host": "localhost",
                "port": 5678
            },
            "pathMappings": [
                {
                    "localRoot": "${workspaceFolder}",
                    "remoteRoot": "<current directory>"
                }
            ]
        }
    ]
}
```
`<current directory>`については、`pwd`で出力されるパスを設定します。
Ubuntuの場合は`/home/ubuntu/<your folder>`のようなパスとなります。


# デバッグ実行


## プログラムの実行
まずはプログラムを実行します。

```bash
$ python main.py
```
プログラムの実行後、ターミナルに以下が表示されていれば、`debugpy`がリモートマシンで開始されています。

```
Waiting for debugger attach
```

## デバッグセッションの開始
VSCodeでデバッグセッションを開始するには、Activity Bar(ウィンドウ左側)から"Run and Debug"を選択します。
"Run and Debug"パネルの上部にある緑色の再生ボタンをクリックすると、`launch.json`に定義されたデバッグ設定が実行されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/94a4fe15-d20e-4ded-6ccf-f486b3294057.png" width="600">

これにより、VSCodeは`debugpy`が動作しているリモートマシンに接続し、デバッグセッションを開始します。
プログラムはブレークポイントで一時停止し、デバッグツールを使用してコードの実行をステップバイステップで進めることができます。

## 基本操作
VSCodeのデバッグツールバーには、以下のボタンとそれに対応するコマンドがあります。
これらがデバッグにおける基本操作となります。

- Continue / Pause (F5): プログラムの実行を再開または一時停止します。ブレークポイントで停止した場合、このボタンを押すと次のブレークポイントまで実行が再開されます。
- Step Over (F10): 現在の行の次の行に移動します。現在の行が関数呼び出しを含んでいる場合でも、その関数の内部には入らず、関数呼び出しを一つのステップとして扱います。
- Step Into (F11): 現在の行の次の行に移動します。しかし、現在の行が関数呼び出しを含んでいる場合は、その関数の内部に入ります。
- Step Out (Shift+F11): 現在の関数の残りの部分を実行した後で、その関数を呼び出した行に戻ります。
- Restart (Ctrl+Shift+F5): デバッグセッションを再起動します。
- Stop (Shift+F5): デバッグセッションを終了します。

# 参考資料

https://zv-louis.hatenablog.com/entry/2022/06/17/235623