-
-
Notifications
You must be signed in to change notification settings - Fork 31.8k
Remote PDB can't interrupt an infinite loop in an evaluated command #132975
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
Comments
I'm leaning towards 2. I think As for the concern to interrupt the other programs - Can I would really love if we could avoid multi-threading (at least for now). That's a whole other layer of complexity. |
I strongly advocate for any solution that allows interrupting IO-bound operations. While I'm leaning toward the thread-based approach (option 4), I'm not married to that specific implementation as long as we can reliably interrupt IO. Being able to interrupt IO is absolutely critical for debugging real-world applications. Many of the most frustrating debugging scenarios involve programs that appear completely frozen while waiting on network requests, database operations, or file system access. Without IO interruption capability, users would be forced to kill the entire debugging session when encountering these common situations. In a normal Python process this is possible due to how Python handles signals that are delivered while blocked on long system calls - the syscall returns EINTR and allows Python to react to it. That is why you fan still press Ctrl-C while calling sleep() for example. While threading introduces complexity, I believe a solution that can't handle IO interruption would be a serious functional limitation that would affect many users. The thread has the advantage of working on all OSes and doing what we want. But I can understand if we want to explore other options |
The user controls when There's a good reason to swallow exceptions in general. It might be justifiable to allow
Yes - try:
Hit Ctrl-C and it gets interrupted and you get your PDB prompt back. That's only possible with a signal; option 2 and 3 cannot possibly interrupt IO (the injected script won't get run until after the sleep finishes).
Not generally, but C code that's long running is supposed to periodically poll |
And indeed, the current remote PDB implementation handles calling |
You can't even attach So we are talking about a solution that can interrupt IO operation after I agree a functional limited debugger is frustrating to users, but what's more frustrating is a debugger that can't keep its promise and fails frequently. If we do want a separate thread, we should do all of our communication there - still keep a single socket. We should be able to reuse the existing code to raise all kinds of signals to the main thread. However we are really close to beta freeze, do we want to onboard a even more complicated feature with potential racing issues? pdb has never had anything to do with multi-threading until now. |
While researching, I found that IDLE's REPL faces this same problem - #74112 I've done some experiments. On Windows, On Unix, however, signal.pthread_kill(threading.main_thread().ident, signal.SIGINT) So at this point, the best option that I see is to have PDB run a thread that listens for messages telling it to interrupt the main thread, and for it to call
If you're sure that's what you want, we can do that, but it's considerably more complex. It means that every message except interrupt signals needs to be read by the background thread, re-written to a And if by "do all of our communication there" you mean socket writes as well as socket reads, then it's drastically more complicated, and in fact we either need two background threads instead of one or for the tracing thread to communicate with the single background thread over a separate socket. This is necessary so that the background thread can wake up both when there's messages originating from the tracing thread that need to be written to the client and when there's messages originating from the client that need to be written to the tracing thread. At that point we're pulling in Logically, interrupt signals are out-of-band events, and handling them out-of-band is simpler. Having one socket mimicking what a "normal" non-remote PDB would read on stdin and write to stdout and a separate socket mimicking asynchronous signals seems much simpler and cleaner to me. When PDB is running non-remotely in a terminal, it has two separate input streams for those two separate types of input. I really don't think we should entangle those two input streams in remote PDB's case. |
Okay. Let's do a prototype that has a minimum thread that only does interrupt signal handling and see how that goes. I guess that eliminated some code for interrupt script. Hopefully the thread itself is small enough to be easily maintainable. |
I've hit a complication with the thread approach: if you 'cont'inue the script from a remote PDB and it finishes, it falls off the end of main with the client still attached and the thread still running. If I make the thread a daemon thread things work fine, but those are notoriously problematic and easily lead to crashes after main. If I don't make it a daemon thread, continuing past the end of main results in a deadlock: @gaogaotiantian please let me know if there is some reasonable way to force If there's not, we'll either need to decide to bite the bullet and use a daemon thread, or we'll need to give up on the thread idea. If option (4) isn't workable, option (1) is definitely our second best choice on Unix, but I don't think there's any good option on Windows. Maybe we can use Another option is to use option (1) on Unix, but keep letting Windows do what it's doing today (which can interrupt a 'cont'inued script while it's running Python bytecode, but can't interrupt IO nor any statement executed from a PDB prompt). |
I don't know a lot about how can we detach the thread properly at the finalization phase. I always have concerns about introducing a new thread to handling stuff in pdb. I'm okay to have a simple solution that works on Unix and leave Windows for future. However, if we do swallow exceptions in |
Bug report
Bug description:
As @gaogaotiantian pointed out in #132451 (comment) PDB's new remote attaching feature can enter an uninterruptible infinite loop if you type
while True: pass
at a PDB prompt. We're able to interrupt the script itself, when you 'cont'inue past a breakpoint, but aren't able to interrupt a statement evaluated interactively at a PDB prompt.I have a few different ideas for how this could be fixed, though they all have different tradeoffs.
Options:
PyErr_CheckSignals()
is being called by the main threadKeyboardInterrupt
from an injected scriptsys.remote_exec()
to allowKeyboardInterrupt
to escapeKeyboardInterrupt
will get raised even in places where signals were blocked, potentially leading to a crash where something that should never get an exception got oneset_trace()
is called while it's evaluating a user-supplied statementsignal.raise_signal(signal.SIGINT)
every time it gets a message on the socketI'm currently leaning towards (4) - it's the most complete fix and works on the most platforms and the most scenarios (interrupting both CPU-bound and IO-bound stuff). I plan to prototype this soon.
CPython versions tested on:
CPython main branch
Operating systems tested on:
No response
Linked PRs
The text was updated successfully, but these errors were encountered: