Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork8.5k
mpremote: Add automatic PTY device detection for QEMU#18327
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:master
Are you sure you want to change the base?
Conversation
codecovbot commentedOct 25, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@## master #18327 +/- ##======================================= Coverage 98.38% 98.38% ======================================= Files 171 171 Lines 22297 22297 ======================================= Hits 21936 21936 Misses 361 361 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
github-actionsbot commentedOct 25, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Code size report: |
agatti commentedOct 26, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I believe you can test the RV32/RV64 targets with fake time tests that would validate those changes. Now that that#18234 has been merged, the QEMU target has the time module compiled in, so If I'm not mistaken the test runner terminates the whole lot if there isn't any output either within 60s since the test start, or if there's no output after 60s since the characters were seen on STDOUT. If it's the latter case, then it won't take long to come up with a test that loops N times waiting 50s on each iteration, to see if your changes work :) |
Thanks@agatti for the suggestion! Branch rebased on upstream/master. Validated the PTY fix with a timeout stress test on QEMU MPS2-AN385 (ARM Cortex-M7). Test setupBuilt the QEMU port: cd mpy-cross&& makecd ../ports/qemumake submodulesmake BOARD=MPS2_AN385 Started QEMU with PTY serial redirection: make BOARD=MPS2_AN385 run# Output: char device redirected to /dev/pts/36 (label serial0)Ran test script via mpremote connected to the PTY: mpremote connect /dev/pts/36 run test_pty_timeout.py Test code (temporary, not committed)"""One-off test to validate PTY communication with long delays.Tests that mpremote can read output from QEMU PTY devices evenwith delays approaching the 60s timeout threshold."""importtimeprint("Starting PTY timeout test")foriinrange(3):print(f"Iteration{i} starting")time.sleep(50)# 50s per iteration - just under 60s timeoutprint(f"Iteration{i} complete")print("Test completed successfully") ResultsWithout fix (commit27b7bf3): mpremote times out during iteration 1 because With fix (commit45aaf12): All 3 iterations (150 seconds total) complete without timeout. |
PTY devices used by QEMU don't reliably report inWaiting() status.This adds automatic PTY detection (Linux /dev/pts/* with major 136)and uses blocking reads for PTYs while maintaining non-blockingbehavior for real serial devices.Fixes intermittent hangs when running tests against QEMU targets.See PRmicropython#18327 for detailed analysis and validation.Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Applies the same PTY detection fix to pyboard.py as mpremote.The official test suite uses pyboard.py (tests/run-tests.py),so both tools need the fix.See PRmicropython#18327 for detailed analysis and validation.Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
@iabdalkader this was one of my distractions that got in the way of updating the openmv unix port PR - I'd appreciate a review in the hope it'll fix the issue you currently patch for inhttps://github.com/openmv/openmv/blob/master/tools/mpremote-qemu-serial.patch? |
iabdalkader commentedNov 1, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I don't much about FWIW I had a test case bundle here that reproduces the issue:#18234 (comment) Note, it seems to make the CI tests here hang maybe. |
Applies the same PTY detection fix to pyboard.py as mpremote.The official test suite uses pyboard.py (tests/run-tests.py),so both tools need the fix.See PRmicropython#18327 for detailed analysis and validation.Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Uh oh!
There was an error while loading.Please reload this page.
tools: Add automatic PTY device detection for QEMU
Summary
Both
mpremoteandpyboard.pycan hang when connecting to PTY devices (QEMU serial output) due to PTYinWaiting()timing behavior. While the official test suite usually passes (short tests + timing workarounds mask the issue), long-running operations and heavy serial traffic expose intermittent hangs. This adds automatic PTY detection to both tools and uses blocking reads for PTY devices while maintaining non-blocking behavior for real serial devices.Impact:
Problem
PTY (pseudo-terminal) devices used by QEMU don't reliably report
inWaiting()status. Since commit0d46e45a1 switched to non-blocking reads, mpremote can fail on PTY devices:Why it fails (when it does):
inWaiting()behavior depends on timing - when it's called relative to when data arrivesinWaiting() > 0before readingThe timing-dependent nature:
inWaiting()behavior depends on OS scheduling and data arrival timinginWaiting()returns 0 while data is actually buffered (tests fail)Current workarounds:
Why Official Tests Usually Pass
The official MicroPython test suite runs against QEMU (using
pyboard.pyfrom line 369 oftests/run-tests.py) and generally passes in CI, despite this underlying PTY issue. Several factors mask the problem:What exposes the bug reliably:
The fix eliminates the timing dependency entirely by using blocking reads (which pyserial handles correctly for PTYs) instead of polling
inWaiting().Root Cause Technical Details
PTY devices (Unix98 pseudo-terminals at
/dev/pts/N) have timing-dependent I/O behavior:inWaiting()read(1)blockingselect()The race condition:
Stock mpremote code path:
The code checks
inWaiting()before reading, which is unreliable for PTY devices due to timing.Solution
Auto-detect PTY devices and use appropriate read strategy:
inWaiting()check (unchanged)Detection method:
/dev/pts/[0-9]+(Linux Unix98 PTY)Addressing the Original Concern
The original commit0d46e45a1 introduced the
inWaiting()check to solve a legitimate problem:Our fix respects this concern by relying on pyserial's timeout mechanism:
interCharTimeout: 1- Blocking reads return empty after 1 second if no data arrivesread_until()timeout checks - Function returns on timeout (default 10s)The difference is:
inWaiting()check (commit0d46e45 behavior preserved)inWaiting()timing issues)Both approaches prevent indefinite blocking - the PTY path just uses the timeout mechanism that was already present in the code.
Patch benefits:
OpenMV Testing
The primary validation comes from OpenMV's production use case, which consistently exposed the issue:
Test environment:
Results:
OpenMV has been maintaining a manual patch for this issue:tools/mpremote-qemu-serial.patch
This PR upstreams their fix with automatic detection, allowing them to remove the downstream patch.
Why Long-Running Tests Matter
The timing-dependent nature of this issue means:
OpenMV's 180-second continuous workload provided the consistent reproduction needed to identify and fix the issue.
Compatibility
/dev/pts/*)/dev/ttyUSB*,/dev/ttyACM*)inWaiting()as beforeSafety guarantees:
os.major()missing → Falls back (Python 3.3+ has it)stat()errors → Falls backPerformance
statcall)statcall)Serial devices: Zero performance impact - code path unchanged, still uses efficient non-blocking reads with
inWaiting()checks.PTY devices: Improved performance by eliminating 10ms sleep polling.
Platform Notes
Linux-specific: Current implementation detects Linux Unix98 PTY (major 136). Other platforms fall back to serial behavior.
Background
OpenMV (https://github.com/openmv/openmv) uses QEMU for CI/CD testing of their MicroPython-based vision firmware. They've maintained a manual patch to work around this issue. This PR upstreams their fix with automatic detection.
References
Checklist
TL;DR: mpremote and pyboard.py can hang intermittently on QEMU PTY devices due to timing-dependent
inWaiting()behavior. While official tests usually pass (short duration + timing workarounds mask it), long-running operations expose the issue reliably. This adds automatic PTY detection (path + device major number) and uses blocking reads for PTY while keeping non-blocking behavior for real serial. Eliminates timing dependency. Fully backward compatible with graceful fallback. Validated with OpenMV's 180s continuous workload (81/81 tests passed).