Skip to content

Commit 82800d8

Browse files
committed
ceval: stop the world when enabling profiling/tracing for all threads
1 parent c1befd7 commit 82800d8

File tree

2 files changed

+71
-25
lines changed

2 files changed

+71
-25
lines changed

Include/internal/pycore_refcnt.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ typedef struct _PyObjectQueue {
1919
PyObject *objs[PYOBJECT_QUEUE_SIZE];
2020
} _PyObjectQueue;
2121

22+
#define _PyObjectQueue_ForEach(q, obj) \
23+
for (obj = _PyObjectQueue_Pop(q); obj != NULL; obj = _PyObjectQueue_Pop(q))
24+
2225
_PyObjectQueue *_PyObjectQueue_New(void);
2326

2427
static inline void

Python/ceval.c

Lines changed: 68 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "pycore_opcode.h" // EXTRA_CASES
2121
#include "pycore_pyerrors.h" // _PyErr_Fetch()
2222
#include "pycore_pymem.h" // _PyMem_IsPtrFreed()
23+
#include "pycore_refcnt.h" // _PyObjectQueue
2324
#include "pycore_pystate.h" // _PyInterpreterState_GET()
2425
#include "pycore_range.h" // _PyRangeIterObject
2526
#include "pycore_sliceobject.h" // _PyBuildSlice_ConsumeRefs
@@ -2571,6 +2572,28 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
25712572
return result;
25722573
}
25732574

2575+
static PyObject *
2576+
thread_set_profile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
2577+
{
2578+
tstate->c_profilefunc = func;
2579+
PyObject *old_profileobj = tstate->c_profileobj;
2580+
tstate->c_profileobj = Py_XNewRef(arg);
2581+
/* Flag that tracing or profiling is turned on */
2582+
_PyThreadState_UpdateTracingState(tstate);
2583+
return old_profileobj;
2584+
}
2585+
2586+
static PyObject *
2587+
thread_set_trace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
2588+
{
2589+
tstate->c_tracefunc = func;
2590+
PyObject *old_traceobj = tstate->c_traceobj;
2591+
tstate->c_traceobj = Py_XNewRef(arg);
2592+
/* Flag that tracing or profiling is turned on */
2593+
_PyThreadState_UpdateTracingState(tstate);
2594+
return old_traceobj;
2595+
}
2596+
25742597
int
25752598
_PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
25762599
{
@@ -2585,11 +2608,7 @@ _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
25852608
return -1;
25862609
}
25872610

2588-
tstate->c_profilefunc = func;
2589-
PyObject *old_profileobj = tstate->c_profileobj;
2590-
tstate->c_profileobj = Py_XNewRef(arg);
2591-
/* Flag that tracing or profiling is turned on */
2592-
_PyThreadState_UpdateTracingState(tstate);
2611+
PyObject *old_profileobj = thread_set_profile(tstate, func, arg);
25932612

25942613
// gh-98257: Only call Py_XDECREF() once the new profile function is fully
25952614
// set, so it's safe to call sys.setprofile() again (reentrant call).
@@ -2612,20 +2631,32 @@ void
26122631
PyEval_SetProfileAllThreads(Py_tracefunc func, PyObject *arg)
26132632
{
26142633
PyThreadState *this_tstate = _PyThreadState_GET();
2615-
PyInterpreterState* interp = this_tstate->interp;
2634+
if (_PySys_Audit(this_tstate, "sys.setprofile", NULL) < 0) {
2635+
/* Log _PySys_Audit() error */
2636+
_PyErr_WriteUnraisableMsg("in PyEval_SetProfileAllThreads", NULL);
2637+
}
26162638

2639+
PyInterpreterState* interp = this_tstate->interp;
26172640
_PyRuntimeState *runtime = &_PyRuntime;
2641+
2642+
_PyObjectQueue *queue = NULL;
2643+
2644+
_PyRuntimeState_StopTheWorld(&_PyRuntime);
26182645
HEAD_LOCK(runtime);
26192646
PyThreadState* ts = PyInterpreterState_ThreadHead(interp);
2620-
HEAD_UNLOCK(runtime);
2621-
26222647
while (ts) {
2623-
if (_PyEval_SetProfile(ts, func, arg) < 0) {
2624-
_PyErr_WriteUnraisableMsg("in PyEval_SetProfileAllThreads", NULL);
2648+
PyObject *old_profileobj = thread_set_profile(ts, func, arg);
2649+
if (old_profileobj) {
2650+
_PyObjectQueue_Push(&queue, old_profileobj);
26252651
}
2626-
HEAD_LOCK(runtime);
26272652
ts = PyThreadState_Next(ts);
2628-
HEAD_UNLOCK(runtime);
2653+
}
2654+
HEAD_UNLOCK(runtime);
2655+
_PyRuntimeState_StartTheWorld(&_PyRuntime);
2656+
2657+
PyObject *old_profileobj;
2658+
_PyObjectQueue_ForEach(&queue, old_profileobj) {
2659+
Py_DECREF(old_profileobj);
26292660
}
26302661
}
26312662

@@ -2643,16 +2674,11 @@ _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
26432674
return -1;
26442675
}
26452676

2646-
tstate->c_tracefunc = func;
2647-
PyObject *old_traceobj = tstate->c_traceobj;
2648-
tstate->c_traceobj = Py_XNewRef(arg);
2649-
/* Flag that tracing or profiling is turned on */
2650-
_PyThreadState_UpdateTracingState(tstate);
2677+
PyObject *old_traceobj = thread_set_trace(tstate, func, arg);
26512678

26522679
// gh-98257: Only call Py_XDECREF() once the new trace function is fully
26532680
// set, so it's safe to call sys.settrace() again (reentrant call).
26542681
Py_XDECREF(old_traceobj);
2655-
26562682
return 0;
26572683
}
26582684

@@ -2670,20 +2696,37 @@ void
26702696
PyEval_SetTraceAllThreads(Py_tracefunc func, PyObject *arg)
26712697
{
26722698
PyThreadState *this_tstate = _PyThreadState_GET();
2673-
PyInterpreterState* interp = this_tstate->interp;
26742699

2700+
if (_PySys_Audit(this_tstate, "sys.settrace", NULL) < 0) {
2701+
/* Log _PySys_Audit() error */
2702+
_PyErr_WriteUnraisableMsg("in PyEval_SetTraceAllThreads", NULL);
2703+
}
2704+
2705+
PyInterpreterState* interp = this_tstate->interp;
26752706
_PyRuntimeState *runtime = &_PyRuntime;
2707+
2708+
_PyObjectQueue *queue = NULL;
2709+
2710+
_PyMutex_lock(&_PyRuntime.stoptheworld_mutex);
2711+
_PyRuntimeState_StopTheWorld(&_PyRuntime);
2712+
26762713
HEAD_LOCK(runtime);
26772714
PyThreadState* ts = PyInterpreterState_ThreadHead(interp);
2678-
HEAD_UNLOCK(runtime);
2679-
26802715
while (ts) {
2681-
if (_PyEval_SetTrace(ts, func, arg) < 0) {
2682-
_PyErr_WriteUnraisableMsg("in PyEval_SetTraceAllThreads", NULL);
2716+
PyObject *old_traceobj = thread_set_trace(ts, func, arg);
2717+
if (old_traceobj) {
2718+
_PyObjectQueue_Push(&queue, old_traceobj);
26832719
}
2684-
HEAD_LOCK(runtime);
26852720
ts = PyThreadState_Next(ts);
2686-
HEAD_UNLOCK(runtime);
2721+
}
2722+
HEAD_UNLOCK(runtime);
2723+
2724+
_PyRuntimeState_StartTheWorld(&_PyRuntime);
2725+
_PyMutex_unlock(&_PyRuntime.stoptheworld_mutex);
2726+
2727+
PyObject *old_traceobj;
2728+
_PyObjectQueue_ForEach(&queue, old_traceobj) {
2729+
Py_DECREF(old_traceobj);
26872730
}
26882731
}
26892732

0 commit comments

Comments
 (0)