mirror of
https://github.com/python/cpython.git
synced 2025-07-16 07:45:20 +00:00
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:
parent
657976ad95
commit
e34c82abeb
10 changed files with 271 additions and 4 deletions
|
@ -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)
|
||||
|
|
26
Python/clinic/sysmodule.c.h
generated
26
Python/clinic/sysmodule.c.h
generated
|
@ -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]*/
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue