Mastering Python Debugging on Remote SSH with VSCode
Tags: webdev, python, beginners, programming
Published: 2023-05-26 12:27:47

When modifying the BERT application model, I found myself working on code while SSH-logged into EC2 through VSCode. Setting up the debugging environment was somewhat of a challenge, so I've put together this guide to share the process.

## Installation
Generally, four steps are necessary:

### 1. Introduction of debugpy
First, install the `debugpy` debugging tool using the command `pip install debugpy`. If you are building a conda environment using `setup.yml`, add `debugpy` to dependencies, and then execute `conda env create -f setup.yml`.

Below is an example of a conda file description.

```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  # Add
  - pip:
      - transformers==3.2.0
```

### 2. Adding code to start the debug session
Before running the Python program, you need to start `debugpy `and initiate the remote debug session. Add the following code to your execution file.

```py
import debugpy

debugpy.listen(('0.0.0.0', 5678))

print("Waiting for debugger attach")
debugpy.wait_for_client()
```

When remote debugging, breakpoints become active from the moment the listen method of `debugpy` is executed (when `debugpy` starts listening on the remote machine). This is because VSCode (local machine) starts receiving debug information by connecting to `debugpy` (remote machine).

### 3. Setting up SSH Tunneling
To remote debug, you need to connect to `debugpy` on the remote machine from the local machine. To do this, use SSH tunneling with the following command (linking port 5678 of the local machine with port 5678 of the remote machine).

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

### 4. Creation of launch.json
`launch.json` is a file read by VSCode when a debug session starts (when Run and Debug in the sidebar is clicked). This file defines the settings for the debug session and instructs VSCode on how to start the session. When the debug session starts, code execution is paused at the breakpoint you've set.

Describe the following in `launch.json` and place it directly under `.vscode`.

```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>"
                }
            ]
        }
    ]
}
```

In `<current directory>`, set the path output by `pwd`. In the case of Ubuntu, the path would be something like `/home/ubuntu/<your folder>`.

## Debug Execution

### Execution of the Program
First, execute the program.

```bash
$ python main.py
```
If the following is displayed in the terminal after program execution, `debugpy` has started on the remote machine.

```
Waiting for debugger attach
```

### Starting the Debug Session
To start the debug session in VSCode, select "Run and Debug" from the Activity Bar (on the left side of the window). When you click the green play button at the top of the "Run and Debug" panel, the debug settings defined in `launch.json` are executed.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sxggoekvcij28ouopw25.png)

VSCode then connects to the remote machine where `debugpy` is running and begins the debug session. The program is paused at the breakpoint, and you can use the debug tool to step through the code execution.

### Basic Operations
VSCode's debug toolbar includes the following buttons and corresponding commands. These are the basic operations in debugging:

- Continue / Pause (F5): Resume or pause the execution of the program. If it is stopped at a breakpoint, pressing this button will resume execution until the next breakpoint.
- Step Over (F10): Move to the line following the current one. Even if the current line contains a function call, it does not enter inside the function but treats the function call as one step.
- Step Into (F11): Move to the line following the current one. However, if the current line contains a function call, it steps inside that function.
- Step Out (Shift+F11): After executing the remaining part of the current function, it goes back to the line that called the function.
- Restart (Ctrl+Shift+F5): Restart the debug session.
- Stop (Shift+F5): End the debug session.