-
-
Notifications
You must be signed in to change notification settings - Fork 32k
gh-131591: Allow pdb to attach to a running process 8000 #132451
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
Changes from 1 commit
5225333
78a3085
90e0a81
e44a670
e837246
27efa97
557a725
7f7584a
5666ffb
325f166
72830e2
27c6780
baaf28a
e61cc31
5d59ce1
09adb2b
600aa05
0601f10
5a1755b
f184e4e
82b71f8
3986c17
2e69667
55adbcc
0bda5c2
5e93247
f799e83
46fb219
1ec9475
662c7eb
ac36d7d
f06d9c2
715af27
c654fdf
205bc55
bbe784b
30cb537
659556f
6c2d970
100be44
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
If `NamedTemporaryFile.__exit__` tries to delete the connect script before the remote process has closed it, a `PermissionError` is raised on Windows. Unfortunately there is no synchronization that we can use to ensure that the file is closed before we try to delete it. This makes the race less likely to occur by making the `with` block contain more code, so that more time passes before we attempt to delete the file. Co-authored-by: Chris Eibl <138194463+chris-eibl@users.noreply.github.com>
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3044,31 +3044,31 @@ def attach(pid): | |
with closing(socket.create_server(("localhost", 0))) as server: | ||
port = server.getsockname()[1] | ||
|
||
with tempfile.NamedTemporaryFile("w", delete_on_close=False) as script: | ||
script.write( | ||
with tempfile.NamedTemporaryFile("w", delete_on_close=False) as connect_script: | ||
connect_script.write( | ||
f'import pdb, sys\n' | ||
f'pdb._connect("localhost", {port}, sys._getframe().f_back)\n' | ||
) | ||
script.close() | ||
sys.remote_exec(pid, script.name) | ||
connect_script.close() | ||
sys.remote_exec(pid, connect_script.name) | ||
|
||
# TODO Add a timeout? Or don't bother since the user can ^C? | ||
client_sock, _ = server.accept() | ||
|
||
with closing(client_sock): | ||
sockfile = client_sock.makefile("rwb") | ||
with closing(client_sock): | ||
sockfile = client_sock.makefile("rwb") | ||
|
||
with closing(sockfile): | ||
with tempfile.NamedTemporaryFile("w", delete_on_close=False) as script: | ||
script.write( | ||
'import pdb, sys\n' | ||
'if inst := pdb.Pdb._last_pdb_instance:\n' | ||
' inst.set_step()\n' | ||
' inst.set_trace(sys._getframe(1))\n' | ||
) | ||
script.close() | ||
with closing(sockfile): | ||
with tempfile.NamedTemporaryFile("w", delete_on_close=False) as interrupt_script: | ||
interrupt_script.write( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay I think I found a problem here. If you do while True:
pass from client, the server will stuck in the loop forever. Because I thought you could simply do a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exceptions aren't propagated out of the script run by There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm. This is sort of a PDB problem in general, isn't it? Signal handlers only run on the main thread, so if you're using PDB in a multi-threaded program and you do while True:
pass on any thread but the main thread, that thread will be stuck forever, right? The only thing that remote PDB is making worse is that getting stuck will happen for all N threads, instead of for N-1 of them 😄 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It might make sense to special case @pablogsal mentioned to me earlier today that he has some ideas about better ways to do the interrupts, but we haven't had a chance to discuss it yet. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's true for multi-thread programs. However, pdb is always horrible at debugging multi-thread programs. There are millions of issues there so that's not our major concern now. I really hope that a single-thread program can work with remote pdb. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could this be fixed by running evaluated code with tracing enabled? Or would that break more than it fixes? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The issue is that the evaluated code is in a trace callback, so it won't call any other callback functions - otherwise it's infinite. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, but we could use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that will disable the breakpoints in the evaluated code? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 8000Aren't they already disabled, since we're running the evaluated code from a trace function and not calling it with If I do:
the breakpoint doesn't get hit. |
||
'import pdb, sys\n' | ||
'if inst := pdb.Pdb._last_pdb_instance:\n' | ||
' inst.set_step()\n' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I based it on the sigint handler that regular PDB installs: def sigint_handler(self, signum, frame):
if self.allow_kbdint:
raise KeyboardInterrupt
self.message("\nProgram interrupted. (Use 'cont' to resume).")
self.set_step()
self.set_trace(frame) If that's not the right thing to do, happy to change it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay that piece of code is from 15 years ago. I don't believe |
||
' inst.set_trace(sys._getframe(1))\n' | ||
) | ||
interrupt_script.close() | ||
|
||
_PdbClient(pid, sockfile, script.name).cmdloop() | ||
_PdbClient(pid, sockfile, interrupt_script.name).cmdloop() | ||
|
||
|
||
# Post-Mortem interface | ||
|
Uh oh!
There was an error while loading. Please reload this page.