Seeking Stable Python-C++ Mixed Debugging Solution for TVM

Dear TVM Developers,

I’m debugging TVM workflows where Python frontend code calling backend C++ implementations. Specifically, ​I need to step through TVM’s C++ source code during execution of Python code ​ – for example, when running python frontend code tvm.meta_schedule.tune(), I expect the debugger to jump from Python into corresponding C++ functions for line-by-line debugging.

Previously, I successfully used VSCode’s python C++ debugger plugin for this exact purpose. However, this solution has recently stopped working and I cannot restore its functionality.

Given that the python C++ debugger plugin appears to be unmaintained, I no longer consider it a sustainable approach. I would greatly appreciate insights on how others in the community achieve reliable Python-C++ mixed debugging with TVM.

Specifically:

  • What tools/workflows are you currently using?
  • Are there maintained VSCode alternatives or more robust methods?

Thank you for sharing your expertise!

For large projects like TVM, I find print-based debugging is consistently more effective than using a formal debugger :slight_smile:

Printing or logging may be useful for debugging at the functional level. Do you have any recommendations for debugging at the statement level?

Python-C++ Mixed Debugging Configuration in VSCode​

The following .vscode/launch.json configuration enables mixed debugging of TVM’s Python frontend (Python debugger) and C++ backend (GDB) in VSCode.

// .vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        /* 
         * Configuration 1: Hybrid Python + C++ Debugging for TVM.
         * Purpose: Debug both Python frontend and C++ backend simultaneously.
         * Mechanism: Launches Python script while attaching to C++ process.
         * Use: This is a hybrid debugging approach, where users should launch
         *      the Python script first, and then the C++ process is automatically
         *      attached to.
         * Note:
         *   - Requires `benjamin-simmonds.pythoncpp-debug` (Python C++ Debugger)
         *     extension for C++ debugging.
         *   - The following 1.1 and 1.2 configurations are subordinate to this configuration.
         */
        {
            "name": "Full Auto Debug (Python+C++)",
            "type": "pythoncpp",
            "request": "launch",
            "pythonLaunchName": "Python: TVM Frontend",
            "cppAttachName": "C++: TVM Backend",
        },
        /*
         * Configuration 1.1: Python Frontend Debugger.
         * Purpose: Debug Python components of TVM.
         */
        {
            "name": "Python: TVM Frontend",
            "type": "debugpy",
            "request": "launch",
            "program": "${file}",
            "console": "integratedTerminal",
            "justMyCode": false,
            "purpose": [
                "debug-in-terminal"
            ],
            "args": [],
            "env": {},
            "gevent": false
        },
        /*
         * Configuration 1.2: C++ Backend Attach Debugger.
         * Purpose: Debug TVM's C++ runtime.
         * Note:
         *   - Customize `"program"` entry to your venv python executable path.
         */
        {
            "name": "C++: TVM Backend",
            "type": "cppdbg",
            "request": "attach",
            // Customize this to your venv python executable path
            "program": "/home/user/miniconda3/envs/tvm-build-venv/bin/python3",
            "processId": "${command:pickProcess}",
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ]
        },
        /*
         * Configuration 2: Pure C++ Debugging.
         * Purpose: Debug standalone C++ executables.
         * Use: When testing C++ components without Python
         * Note:
         *   - Customize `"program"` entry to your C++ executable path.
         */
        {
            "name": "Pure C++ Debug",
            "type": "cppdbg",
            "request": "launch",
            // Customize `"program"` entry to your C++ executable path.
            "program": "${workspaceFolder}/oot-tvm-example-project/build/oottvm_test",
            "args": [],
            "cwd": "${workspaceFolder}",
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                },
            ],
            "environment": [
                {
                    "name": "EXAMPLEKEY",
                    "value": "EXAMPLEVALUE"
                }
            ]
        },
    ],
}

1. Required VSCode Extensions

Before using this configuration to debug TVM, we should install the following extensions for VSCode:

  • Python Debugger: Search ms-python.debugpy in VSCode extension marketplace.
  • C/C++: Search ms-vscode.cpptools in VSCode extension marketplace.
  • ​Python C++ Debugger​​: Search benjamin-simmonds.pythoncpp-debug in VSCode extension marketplace.

After installing the extensions, we should copy the .vscode/launch.json file to the root directory of your own TVM project. And the next steps will explain some details that you should modify in the launch.json file.

2. Debugging Modes​ and Usage

The current configuration supports two debugging modes:

  • Hybrid Python Frontend + C++ Backend Debugging for TVM
  • Pure C++ Backend Debugging for TVM

As the fowllowing image shows, we can choose the debugging mode in graphical interface. Ther are four choices:

  • Full Auto Debug (Python+C++) is the entry for Hybrid Python Frontend + C++ Backend Debugging.
    • Python: TVM Frontend is responsible for the debugging of the Python Frontend.
    • C++: TVM Backend is responsible for the debugging of the C++ Backend.
  • Pure C++ Debug (C++) is the entry for Pure C++ Backend Debugging.

Image

2.1 Hybrid Python + C++ Debugging for TVM

This is a hybrid debugging approach, and requires users to first launch the Python script, after which the C++ process will be automatically attached.

IMPORTANT

Customize the "program" entry in .vscode/launch.json to point to your Python virtual environment executable. Example path:

"program": "/home/user/miniconda3/envs/tvm-build-venv/bin/python3"

Commands that interact with the Debug Console should be preceded by -exec, example:

-exec p pc or -exec call tvm::Dump(mod)

Example:

Hybrid Debugging Example

2.2 Pure C++ Debugging

This is a pure C++ debugging approach, where users can debug C++ components without Python scripts.

IMPORTANT

Customize the "program" entry in .vscode/launch.json to your C++ executable path. Example path:

"program": "${workspaceFolder}/oot-tvm-example-project/build/oottvm_test"

Example:

Image

You can also check .vscode/launch.json for debugging mode details - we’ve added extensive comments there.

3. Maintenance

This configuration is maintained at:

I was unable to make the Python + C++ debugger work, can you help identifying what is wrong? I am currently using ubuntu and the following script for testing:

import numpy as np
import tvm
from tvm import relax
from tvm.script import relax as R
import tvm.meta_schedule as ms
from tvm.meta_schedule import postproc, schedule_rule
from typing import List
from tvm.meta_schedule import TuneContext
from tvm.meta_schedule.schedule_rule import PyScheduleRule
from tvm.meta_schedule.utils import derived_object
from tvm.tir.schedule import BlockRV, Schedule


@tvm.script.ir_module
class Module:
    @R.function
    def main(
        A: R.Tensor((2048, 2688), "float32"),
        B: R.Tensor((2688, 2304), "float32")) -> R.Tensor((2048, 2304), "float32"):
        with R.dataflow():
            matmul: R.Tensor((2048,2304),dtype="float32") = R.matmul(A, B, out_dtype="void")
            R.output(matmul)
        return matmul

mod = relax.transform.LegalizeOps()(Module)
matmul = mod["matmul"]

target = tvm.target.Target("llvm --num-cores=1")
database = ms.tune_tir(
    mod=matmul,
    target=target,
    max_trials_global=8,
    space=ms.space_generator.PostOrderApply(),
    work_dir="./tune_tests",
)

sch = ms.tir_integration.compile_tir(database, matmul, target)
sch.mod.show()

I have installed TVM Debug mode at a local conda environment ~/miniconda3/envs/tvm/bin/python and am currently working on Ubuntu.

Whenever I run the debugger it the C++ backend seems to attach to multiple processes as shown in the following print:

This stack keeps running for a really long time (much longer than just running the script) and then the script just finishes running, ignoring all breakpoints on both python and C++ scripts.

I have tried a bunch of diferent configurations on the launch.json, including the default configuration of the Python + C++ extension for VSCode. The default configuration was kinda working, but it lacked the flag “justMyCode”: false, so I was not able to debug the C++ scripts (although for some reason even the default configuration doesn`t work somethimes)

I follow the tutorial by @ConvolutedDog and it works on my machine.

Maybe you can try the launch.json configuration from here. Also make sure to check the related VSCode extensions.

(Note: The “Python C++ Debugger” extension for VSCode may not always work reliably, as it hasn’t been maintained for several years.)

If you still can’t get it to work after trying various options, you can use gdb to debug the Python interrupt directly. For example:

$ gdb --args python test.py
# set breakpoints in C++ backend 
$ b task_scheduler.cc:160
$ start
$ c
# then gdb will stop at line 160 of task_scheduler.cc

Let me know if there are any mistakes or if something isn’t clear!

I wasn’t able to make the VSCode extension work, even trying multiple different launch.json configurations. I will stick with gdb for now.

Also, do I need to build TVM using the Debug cmake mode to enable debugging?

You can paste your launch.json here, so I can see what the problem is.

I am using the launch.json from your github:

{
    "version": "0.2.0",
    "configurations": [
        /* 
         * Configuration 1: Hybrid Python + C++ Debugging for TVM.
         * Purpose: Debug both Python frontend and C++ backend simultaneously.
         * Mechanism: Launches Python script while attaching to C++ process.
         * Use: This is a hybrid debugging approach, where users should launch
         *      the Python script first, and then the C++ process is automatically
         *      attached to.
         * Note:
         *   - Requires `benjamin-simmonds.pythoncpp-debug` (Python C++ Debugger)
         *     extension for C++ debugging.
         *   - The following 1.1 and 1.2 configurations are subordinate to this configuration.
         */
        {
            "name": "Full Auto Debug (Python+C++)",
            "type": "pythoncpp",
            "request": "launch",
            "pythonLaunchName": "Python: TVM Frontend",
            "cppAttachName": "C++: TVM Backend",
        },
        /*
         * Configuration 1.1: Python Frontend Debugger.
         * Purpose: Debug Python components of TVM.
         */
        {
            "name": "Python: TVM Frontend",
            "type": "debugpy",
            "request": "launch",
            "program": "${file}",
            "console": "integratedTerminal",
            "justMyCode": false,
            "purpose": [
                "debug-in-terminal"
            ],
            "args": [],
            "env": {},
            "python": "/home/cesar/miniconda3/envs/tvm/bin/python",
            "gevent": false
        },
        /*
         * Configuration 1.2: C++ Backend Attach Debugger.
         * Purpose: Debug TVM's C++ runtime.
         * Note:
         *   - Customize `"program"` entry to your venv python executable path.
         */
        {
            "name": "C++: TVM Backend",
            "type": "cppdbg",
            "request": "attach",
            // Customize this to your venv python executable path
            "program": "/home/cesar/miniconda3/envs/tvm/bin/python",
            "processId": "${command:pickProcess}",
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ]
        },
        /*
         * Configuration 2: Pure C++ Debugging.
         * Purpose: Debug standalone C++ executables.
         * Use: When testing C++ components without Python
         * Note:
         *   - Customize `"program"` entry to your C++ executable path.
         */
        {
            "name": "Pure C++ Debug",
            "type": "cppdbg",
            "request": "launch",
            // Customize `"program"` entry to your C++ executable path.
            "program": "oot-tvm-example-project/build/oottvm_test",
            "args": [],
            "cwd": "${workspaceFolder}",
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                },
            ],
            "environment": [
                {
                    "name": "EXAMPLEKEY",
                    "value": "EXAMPLEVALUE"
                }
            ]
        },
    ],
}

This one doesn’t seem to attach the C++ backend to the correct python runner. I tried the same launch.json but without the flag ‘pyhton’ and setting the enviroment with the Python Enviroment extension, but the results were the same.

I also tried the Python C++ Debugger default launch.json but the results were similar. It seems that the C++ fails to attach to the Python and keeps running endlessly, leading to a crash.