GH-93503: Add thread-specific APIs to set profiling and tracing functions in the C-API (#93504)

* gh-93503: Add APIs to set profiling and tracing functions in all threads in the C-API

* Use a separate API

* Fix NEWS entry

* Add locks around the loop

* Document ignoring exceptions

* Use the new APIs in the sys module

* Update docs
This commit is contained in:
Pablo Galindo Salgado 2022-08-24 23:21:39 +01:00 committed by GitHub
parent 657976ad95
commit e34c82abeb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 271 additions and 4 deletions

View file

@ -96,6 +96,10 @@
#define _Py_atomic_load_relaxed_int32(ATOMIC_VAL) _Py_atomic_load_relaxed(ATOMIC_VAL)
#endif
#define HEAD_LOCK(runtime) \
PyThread_acquire_lock((runtime)->interpreters.mutex, WAIT_LOCK)
#define HEAD_UNLOCK(runtime) \
PyThread_release_lock((runtime)->interpreters.mutex)
/* Forward declarations */
static PyObject *trace_call_function(
@ -6455,6 +6459,27 @@ PyEval_SetProfile(Py_tracefunc func, PyObject *arg)
}
}
void
PyEval_SetProfileAllThreads(Py_tracefunc func, PyObject *arg)
{
PyThreadState *this_tstate = _PyThreadState_GET();
PyInterpreterState* interp = this_tstate->interp;
_PyRuntimeState *runtime = &_PyRuntime;
HEAD_LOCK(runtime);
PyThreadState* ts = PyInterpreterState_ThreadHead(interp);
HEAD_UNLOCK(runtime);
while (ts) {
if (_PyEval_SetProfile(ts, func, arg) < 0) {
_PyErr_WriteUnraisableMsg("in PyEval_SetProfileAllThreads", NULL);
}
HEAD_LOCK(runtime);
ts = PyThreadState_Next(ts);
HEAD_UNLOCK(runtime);
}
}
int
_PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
{
@ -6508,6 +6533,26 @@ PyEval_SetTrace(Py_tracefunc func, PyObject *arg)
}
}
void
PyEval_SetTraceAllThreads(Py_tracefunc func, PyObject *arg)
{
PyThreadState *this_tstate = _PyThreadState_GET();
PyInterpreterState* interp = this_tstate->interp;
_PyRuntimeState *runtime = &_PyRuntime;
HEAD_LOCK(runtime);
PyThreadState* ts = PyInterpreterState_ThreadHead(interp);
HEAD_UNLOCK(runtime);
while (ts) {
if (_PyEval_SetTrace(ts, func, arg) < 0) {
_PyErr_WriteUnraisableMsg("in PyEval_SetTraceAllThreads", NULL);
}
HEAD_LOCK(runtime);
ts = PyThreadState_Next(ts);
HEAD_UNLOCK(runtime);
}
}
int
_PyEval_SetCoroutineOriginTrackingDepth(int depth)

View file

@ -292,6 +292,18 @@ exit:
return return_value;
}
PyDoc_STRVAR(sys__settraceallthreads__doc__,
"_settraceallthreads($module, arg, /)\n"
"--\n"
"\n"
"Set the global debug tracing function in all running threads belonging to the current interpreter.\n"
"\n"
"It will be called on each function call. See the debugger chapter\n"
"in the library manual.");
#define SYS__SETTRACEALLTHREADS_METHODDEF \
{"_settraceallthreads", (PyCFunction)sys__settraceallthreads, METH_O, sys__settraceallthreads__doc__},
PyDoc_STRVAR(sys_gettrace__doc__,
"gettrace($module, /)\n"
"--\n"
@ -312,6 +324,18 @@ sys_gettrace(PyObject *module, PyObject *Py_UNUSED(ignored))
return sys_gettrace_impl(module);
}
PyDoc_STRVAR(sys__setprofileallthreads__doc__,
"_setprofileallthreads($module, arg, /)\n"
"--\n"
"\n"
"Set the profiling function in all running threads belonging to the current interpreter.\n"
"\n"
"It will be called on each function call and return. See the profiler chapter\n"
"in the library manual.");
#define SYS__SETPROFILEALLTHREADS_METHODDEF \
{"_setprofileallthreads", (PyCFunction)sys__setprofileallthreads, METH_O, sys__setprofileallthreads__doc__},
PyDoc_STRVAR(sys_getprofile__doc__,
"getprofile($module, /)\n"
"--\n"
@ -1170,4 +1194,4 @@ sys_getandroidapilevel(PyObject *module, PyObject *Py_UNUSED(ignored))
#ifndef SYS_GETANDROIDAPILEVEL_METHODDEF
#define SYS_GETANDROIDAPILEVEL_METHODDEF
#endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */
/*[clinic end generated code: output=38446a4c76e2f3b6 input=a9049054013a1b77]*/
/*[clinic end generated code: output=322fb0409e376ad4 input=a9049054013a1b77]*/

View file

@ -1021,6 +1021,36 @@ Set the global debug tracing function. It will be called on each\n\
function call. See the debugger chapter in the library manual."
);
/*[clinic input]
sys._settraceallthreads
arg: object
/
Set the global debug tracing function in all running threads belonging to the current interpreter.
It will be called on each function call. See the debugger chapter
in the library manual.
[clinic start generated code]*/
static PyObject *
sys__settraceallthreads(PyObject *module, PyObject *arg)
/*[clinic end generated code: output=161cca30207bf3ca input=5906aa1485a50289]*/
{
PyObject* argument = NULL;
Py_tracefunc func = NULL;
if (arg != Py_None) {
func = trace_trampoline;
argument = arg;
}
PyEval_SetTraceAllThreads(func, argument);
Py_RETURN_NONE;
}
/*[clinic input]
sys.gettrace
@ -1066,6 +1096,35 @@ Set the profiling function. It will be called on each function call\n\
and return. See the profiler chapter in the library manual."
);
/*[clinic input]
sys._setprofileallthreads
arg: object
/
Set the profiling function in all running threads belonging to the current interpreter.
It will be called on each function call and return. See the profiler chapter
in the library manual.
[clinic start generated code]*/
static PyObject *
sys__setprofileallthreads(PyObject *module, PyObject *arg)
/*[clinic end generated code: output=2d61319e27b309fe input=d1a356d3f4f9060a]*/
{
PyObject* argument = NULL;
Py_tracefunc func = NULL;
if (arg != Py_None) {
func = profile_trampoline;
argument = arg;
}
PyEval_SetProfileAllThreads(func, argument);
Py_RETURN_NONE;
}
/*[clinic input]
sys.getprofile
@ -2035,9 +2094,11 @@ static PyMethodDef sys_methods[] = {
SYS_GETSWITCHINTERVAL_METHODDEF
SYS_SETDLOPENFLAGS_METHODDEF
{"setprofile", sys_setprofile, METH_O, setprofile_doc},
SYS__SETPROFILEALLTHREADS_METHODDEF
SYS_GETPROFILE_METHODDEF
SYS_SETRECURSIONLIMIT_METHODDEF
{"settrace", sys_settrace, METH_O, settrace_doc},
SYS__SETTRACEALLTHREADS_METHODDEF
SYS_GETTRACE_METHODDEF
SYS_CALL_TRACING_METHODDEF
SYS__DEBUGMALLOCSTATS_METHODDEF