-
-
Notifications
You must be signed in to change notification settings - Fork 31.4k
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
gh-131591: Implement PEP 768 #131592
base: main
Are you sure you want to change the base?
gh-131591: Implement PEP 768 #131592
Conversation
pablogsal
commented
Mar 22, 2025
•
edited by bedevere-app
bot
Loading
edited by bedevere-app
bot
- Issue: Implement PEP 768 – Safe external debugger interface for CPython #131591
The following commit authors need to sign the Contributor License Agreement: |
@@ -317,6 +318,7 @@ The following implementation-specific options are available:\n\ | |||
-X perf: support the Linux \"perf\" profiler; also PYTHONPERFSUPPORT=1\n\ | |||
-X perf_jit: support the Linux \"perf\" profiler with DWARF support;\n\ | |||
also PYTHON_PERF_JIT_SUPPORT=1\n\ | |||
-X disable-remote-debug: disable remote debugging; also PYTHON_DISABLE_REMOTE_DEBUG\n\ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of the others use _
instead of -
so this should probably be
-X disable-remote-debug: disable remote debugging; also PYTHON_DISABLE_REMOTE_DEBUG\n\ | |
-X disable_remote_debug: disable remote debugging; also PYTHON_DISABLE_REMOTE_DEBUG\n\ |
if (env) { | ||
active = 0; | ||
} | ||
const wchar_t *xoption = config_get_xoption(config, L"disable-remote-debug"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above, all the other X options use underscores so this should probably be
const wchar_t *xoption = config_get_xoption(config, L"disable-remote-debug"); | |
const wchar_t *xoption = config_get_xoption(config, L"disable_remote_debug"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initial observations (I've looked at everything but the interesting stuff, haven't gotten to Python/remote_debugging.c or Lib/test/test_sys.py yet)
int enabled; | ||
int debugger_pending_call; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably be explicit about our sizes:
int enabled; | |
int debugger_pending_call; | |
int32_t enabled; | |
int32_t debugger_pending_call; |
Granted int
is 32 bit on all modern platforms, but given that this goes into the offsets and needs to be read from another process with potentially different integer types (like a 64-bit process attaching to a 32-bit one or vice versa) it's better to be explicit, I think.
Or, if you'd prefer, the offsets themselves could contain the size of the field - but I think that's overkill.
/* Remote debugger support */ | ||
# define MAX_SCRIPT_PATH_SIZE 512 | ||
typedef struct _remote_debugger_support { | ||
int enabled; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this field is unused. You added enabled
here, but never reference it anywhere, and the offsets instead use:
.remote_debugging_enabled = offsetof(PyInterpreterState, config.remote_debug),
I think this line should just be removed (which makes sense, anyway - we want a per-process enablement, not a per-thread enablement).
.debugger_support = { \ | ||
.eval_breaker = offsetof(PyThreadState, eval_breaker), \ | ||
.remote_debugger_support = offsetof(PyThreadState, remote_debugger_support), \ | ||
.remote_debugging_enabled = offsetof(PyInterpreterState, config.remote_debug), \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this field really go into the .debugger_support
section? I think it should probably go into .interpreter_state
instead, since everything else in this struct is specific to a single threadstate, except for this one field being per-interpreter.
@@ -936,7 +936,6 @@ struct _is { | |||
_PyThreadStateImpl _initial_thread; | |||
// _initial_thread should be the last field of PyInterpreterState. | |||
// See https://github.com/python/cpython/issues/127117. | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Accidental change?
|
||
#ifdef Py_REMOTE_DEBUG | ||
const PyConfig *config = _PyInterpreterState_GetConfig(tstate->interp); | ||
if (config->remote_debug) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth doing:
if (config->remote_debug) { | |
if (config->remote_debug == 1) { |
to harden this at least a little bit against heap corruption.
int fd = PyObject_AsFileDescriptor(fileobj); | ||
if (fd == -1) { | ||
PyErr_FormatUnraisable("Error when getting file descriptor for debugger script %s", path); | ||
return 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leaks fileobj
|
||
If no stack profiler is activated, this function has no effect. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What? This seems like a copy-paste error that should just be removed.
If no stack profiler is activated, this function has no effect. |
to how signals are handled. There is no interface to determine when the | ||
code has been executed. The caller is responsible for making sure that | ||
the file still exists whenever the remote process tries to read it and that | ||
it hasn't been overwritten. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably worth documenting that the remote process needs to be CPython, and needs to be Python 3.14+, right?
{ | ||
#ifdef MS_WINDOWS | ||
// Get UTF-16 (wide char) version of the path for Windows | ||
wchar_t *debugger_script_path_w = PyUnicode_AsWideCharString(script, NULL); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Above we said that script
could be either str|bytes
, but I believe PyUnicode_AsWideCharString
only accepts str
. Perhaps on Windows the bytes
option doesn't even make sense, though. Should we document that Unix allows bytes
but Windows does not?
|
||
PyMem_Free(debugger_script_path_w); | ||
#else | ||
const char *debugger_script_path = PyUnicode_AsUTF8(script); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, looks like we're not currently handling bytes
even for Unix.