Skip to content

python-ecosys/debugpy: Add VS Code debugging support for MicroPython. #1022

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

andrewleech
Copy link
Contributor

This implementation provides a Debug Adapter Protocol (DAP) server that enables VS Code to debug MicroPython code with full breakpoint, stepping, and variable inspection capabilities.

Features:

  • Manual breakpoints via debugpy.breakpoint()
  • Line breakpoints set from VS Code
  • Stack trace inspection
  • Variable scopes (locals/globals)
  • Source code viewing
  • Stepping (into/over/out)
  • Non-blocking architecture for MicroPython's single-threaded environment
  • Conditional debug logging based on VS Code's logToFile setting

Implementation highlights:

  • Uses MicroPython's sys.settrace() for execution monitoring
  • Handles path mapping between VS Code and MicroPython
  • Efficient O(n) fibonacci demo (was O(2^n) recursive)
  • Compatible with MicroPython's limited frame object attributes
  • Comprehensive DAP protocol support

Files:

  • README.md: Setup and usage instructions
  • debugpy/: Core debugging implementation
  • test_vscode.py: VS Code integration test
  • dap_monitor.py: Protocol debugging utility

Usage:

import debugpy
debugpy.listen()          # Start debug server
debugpy.debug_this_thread()  # Enable tracing
debugpy.breakpoint()      # Manual breakpoint

🤖 Generated with Claude Code


Currently only tested on unix port with updates to settrace in micropython/micropython#8767
Should work on any network enabled device however?

This implementation provides a Debug Adapter Protocol (DAP) server that enables
VS Code to debug MicroPython code with full breakpoint, stepping, and variable
inspection capabilities.

Features:
- Manual breakpoints via debugpy.breakpoint()
- Line breakpoints set from VS Code
- Stack trace inspection
- Variable scopes (locals/globals)
- Source code viewing
- Stepping (into/over/out)
- Non-blocking architecture for MicroPython's single-threaded environment
- Conditional debug logging based on VS Code's logToFile setting

Implementation highlights:
- Uses MicroPython's sys.settrace() for execution monitoring
- Handles path mapping between VS Code and MicroPython
- Efficient O(n) fibonacci demo (was O(2^n) recursive)
- Compatible with MicroPython's limited frame object attributes
- Comprehensive DAP protocol support

Files:
- debugpy/: Core debugging implementation
- test_vscode.py: VS Code integration test
- VSCODE_TESTING_GUIDE.md: Setup and usage instructions
- dap_monitor.py: Protocol debugging utility

Usage:
```python
import debugpy
debugpy.listen()          # Start debug server
debugpy.debug_this_thread()  # Enable tracing
debugpy.breakpoint()      # Manual breakpoint
```

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@andrewleech
Copy link
Contributor Author

andrewleech commented Jun 11, 2025

For those interested in AI coding, this was 95% written by Claude Code (Opus and Sonnet 4) as mentioned in the attributions above. I started prompting the build of this just this morning at 5am from my armchair with my infant asleep in my arms, using Termux on my phone to ssh into my linux box.

I had a clone of micropython with my historical work on getting pdb checked out (micropython/micropython#8767 and #499)

I also had a copy of the official cpython debugpy package checked out, this is the package used behind the scenes to drive python debugging in VSCode and similar IDE's. From past reviews I knew debugpy relies on threads, the old pydevd network debug engine as well a large RPC server which runs in a thread.

I figured some of this could be re-implemented if needed, or replaced with other servers already running on micropython :-)

So I started with a claude session in the debugpy folder and performed an initial repo analysis /init before asking:

perform detailed analysis  of the behaviour of this starting with listen mode.  write detailed specifications of the network API exposed and the data formats used there. then  document the pathways taken to get from network API down to the systrace api

Which produced DEBUGPY_ARCHITECTURE_ANALYSIS.md

I then kicked off : [corona@Telie micropython]$ claude --add-dir ~/debugpy/
and gave it this to kick off:

╭──────────────────────────────────────────────────────╮
│ > new  feature task; adding remote python debugging  │
│   support to micropython. I want to attach a         │
│   debugging session in vscode to a micropython       │
│   instance via debugpy.  we have a copy of cpython   │
│   debugpy to reference in ~/debugpy with an          │
│   analysis of its architecture in                    │
│   ~/debugpy/DEBUGPY_ARCHITECTURE_ANALYSIS.md I want  │
│   a minimal port of this library to micropython,     │
│   implementing the network listening interface       │
│   through to the pdb/systrace debug layer.there is   │
│   the micropython pdb implementation available at    │
│   ./lib/micropython-lib/python-stdlib/pdb/pdb.py     │
│   and any other dependencies should searched for     │
│   under ./lib/micropython-lib/ otherwise they might  │
│   need to also be implemented. the new debugpy       │
│   implementation can copy any of the cpython one     │
│   that makes sense, though for micropython smaller   │
│   is better. it should be built in a new folder:     │
│   ./lib/micropython-lib/python-ecosys/debugpy and    │
│   can be tested with the coverage variant of the     │
│   Unix port: /home/corona/micropython4/ports/unix/b  │
│   uild-coverage/micropython/home/corona/micropython  │
│   4/ports/unix/build-coverage/micropython            │
╰──────────────────────────────────────────────────────╯

WIthin just 1 hour of armchair vibe coding I had an initial implementation ready to test, along with test scripts and a written plan.

Around 10 am I was at my desk and had finished my morning meetings, so started testing it in the background while working on my other "real" projects.

It tooks quite a few iterations of testing in vscode for Claude to finish its implementation plan, adding features as it ran test scripts with me hitting the vscode "debug" button in between. Most of these tests failed badly in many different ways, enough that I was quite pessimistic at times because it really looked like it wasn't going to work ..... however I was still able to get other solid work done though during this time (which coincidentally was also using Claude Code; I've had 4 sessions actively on the go today) so I gave Claude a few chances to get it all going after a number of wrong paths were backtracked.

After all that though this screen capture was at 2:12 pm (and yes I ate lunch during that time too):
DebuggingMicropython

It took me a while to realise while reviewing afterwards and cleaning up the git tree that it hasn't actually pulled in pdb as a dependency, but re-implemented a simple version of everything needed but not much more; it's a rather minimal implementation!

@Josverl
Copy link

Josverl commented Jun 11, 2025

Thanks Andrew,
very nice to see.
It took me more than a few attempts to get this running, but very rewarding.
Perhaps rebasing both PRs to master , and some tweaks fix mismatches in the names of the samples to will help there.
Its good to be able to do a even a simple step -by-step , even though there is now even more to be desired.

"configurations": [
{
"name": "Attach to MicroPython",
"type": "python",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"type": "debugpy",

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, yeah I fixed that in the examples file, missed it here

continue

try:
value_str = str(value)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be value_str = repr(value) . without that strings show without quotes etc.

@andrewleech
Copy link
Contributor Author

Thanks @Josverl good to hear it either for you, I still could hardly believe it worked for me!

I'd be interested to hear any notes about what was confusing / difficult to get going to feed into docs.

I assume some of it was getting paths right to import stuff? And/or compiling with the other features needed? Aka things that'll be better once finished and merged...

I will do some testing on hardware too, ensure that does work and document how to get it going.

I did think the branches were pretty well rebased up to date, I'll double check.

Oh yeah I'll eventually look into getting a useful representation of locals too, even if they end up basically just showing the array of values without names as per the current internal representation.

1. Build the MicroPython Unix coverage port:
```bash
cd ports/unix
make CFLAGS_EXTRA="-DMICROPY_PY_SYS_SETTRACE=1"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this flag MICROPY_PY_SYS_SETTRACE conflicts with the referenced PR where this is already set unconditionally

@Josverl
Copy link

Josverl commented Jun 12, 2025

I'd be interested to hear any notes about what was confusing / difficult to get going to feed into docs.

I first got in a tangle by

  • checking out the micropython repo on PR#8767
  • then checking out ./lib/micropython-lib on this PR

blocked most attempts at building as make submodules breaks
After reverting ./lib/micropython-lib, and cloning this PR to a separate folder things got better

Building a firmware with "the updates to settrace in micropython/micropython#8767"
I still ran into build problems , essentially by a double definition of MICROPY_PY_SYS_SETTRACE .
In the readme of this PR it is part of the instructions , while in the "settrace PR" it is unconditionally defined. These two clash.

Matching up the paths vscode / remote paths, was not to difficult.

Open questions/ more play time needed :

  • I am confused by the read-only source used by the debugger to step though. I can see it is retrieved from the remote, but not sure why this is needed , or how to configure it.
  • why JustMycode : false is needed
  • how debugging would work with a .mpy on the remote
  • How to Enable VS Code's built-in DAP logging: ( I do not have these settings in my VSCode)
  • Why the DAP_Monitor does not terminate or reset itself when the remote target exits

📚 likely relevant : What is the Debug Adapter Protocol?

@Josverl
Copy link

Josverl commented Jun 12, 2025

@andrewleech
I have made some more notes after some additional testing I have done.
I have put them in a Gist notes.md for now as I do not want to drop too many comments here.
Would it make sense to start a Discussion on this topic , or do you prefer everything here ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants