mirror of
https://github.com/python/cpython.git
synced 2025-11-24 20:30:18 +00:00
[3.14] gh-116738: make cProfile module thread-safe (GH-138229) (#138575)
gh-116738: make `cProfile` module thread-safe (GH-138229)
(cherry picked from commit 8554c0917e)
Co-authored-by: Alper <alperyoney@fb.com>
Co-authored-by: Kumar Aditya <kumaraditya@python.org>
This commit is contained in:
parent
e51acb3fa6
commit
4429554223
4 changed files with 100 additions and 14 deletions
43
Lib/test/test_free_threading/test_cprofile.py
Normal file
43
Lib/test/test_free_threading/test_cprofile.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import unittest
|
||||
|
||||
from test.support import threading_helper
|
||||
|
||||
import cProfile
|
||||
import pstats
|
||||
|
||||
|
||||
NTHREADS = 10
|
||||
INSERT_PER_THREAD = 1000
|
||||
|
||||
|
||||
@threading_helper.requires_working_threading()
|
||||
class TestCProfile(unittest.TestCase):
|
||||
def test_cprofile_racing_list_insert(self):
|
||||
def list_insert(lst):
|
||||
for i in range(INSERT_PER_THREAD):
|
||||
lst.insert(0, i)
|
||||
|
||||
lst = []
|
||||
|
||||
with cProfile.Profile() as pr:
|
||||
threading_helper.run_concurrently(
|
||||
worker_func=list_insert, nthreads=NTHREADS, args=(lst,)
|
||||
)
|
||||
pr.create_stats()
|
||||
ps = pstats.Stats(pr)
|
||||
stats_profile = ps.get_stats_profile()
|
||||
list_insert_profile = stats_profile.func_profiles[
|
||||
"<method 'insert' of 'list' objects>"
|
||||
]
|
||||
# Even though there is no explicit recursive call to insert,
|
||||
# cProfile may record some calls as recursive due to limitations
|
||||
# in its handling of multithreaded programs. This issue is not
|
||||
# directly related to FT Python itself; however, it tends to be
|
||||
# more noticeable when using FT Python. Therefore, consider only
|
||||
# the calls section and disregard the recursive part.
|
||||
list_insert_ncalls = list_insert_profile.ncalls.split("/")[0]
|
||||
self.assertEqual(
|
||||
int(list_insert_ncalls), NTHREADS * INSERT_PER_THREAD
|
||||
)
|
||||
|
||||
self.assertEqual(len(lst), NTHREADS * INSERT_PER_THREAD)
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
Make :mod:`cProfile` thread-safe on the :term:`free threaded <free
|
||||
threading>` build.
|
||||
|
|
@ -534,6 +534,7 @@ static int statsForEntry(rotating_node_t *node, void *arg)
|
|||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
_lsprof.Profiler.getstats
|
||||
|
||||
cls: defining_class
|
||||
|
|
@ -565,7 +566,7 @@ profiler_subentry objects:
|
|||
|
||||
static PyObject *
|
||||
_lsprof_Profiler_getstats_impl(ProfilerObject *self, PyTypeObject *cls)
|
||||
/*[clinic end generated code: output=1806ef720019ee03 input=445e193ef4522902]*/
|
||||
/*[clinic end generated code: output=1806ef720019ee03 input=3dc69eb85ed73d91]*/
|
||||
{
|
||||
statscollector_t collect;
|
||||
collect.state = _PyType_GetModuleState(cls);
|
||||
|
|
@ -613,6 +614,7 @@ setBuiltins(ProfilerObject *pObj, int nvalue)
|
|||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
_lsprof.Profiler._pystart_callback
|
||||
|
||||
code: object
|
||||
|
|
@ -624,7 +626,7 @@ _lsprof.Profiler._pystart_callback
|
|||
static PyObject *
|
||||
_lsprof_Profiler__pystart_callback_impl(ProfilerObject *self, PyObject *code,
|
||||
PyObject *instruction_offset)
|
||||
/*[clinic end generated code: output=5fec8b7ad5ed25e8 input=b166e6953c579cda]*/
|
||||
/*[clinic end generated code: output=5fec8b7ad5ed25e8 input=b61a0e79cf1f8499]*/
|
||||
{
|
||||
ptrace_enter_call((PyObject*)self, (void *)code, code);
|
||||
|
||||
|
|
@ -632,6 +634,7 @@ _lsprof_Profiler__pystart_callback_impl(ProfilerObject *self, PyObject *code,
|
|||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
_lsprof.Profiler._pythrow_callback
|
||||
|
||||
code: object
|
||||
|
|
@ -645,7 +648,7 @@ static PyObject *
|
|||
_lsprof_Profiler__pythrow_callback_impl(ProfilerObject *self, PyObject *code,
|
||||
PyObject *instruction_offset,
|
||||
PyObject *exception)
|
||||
/*[clinic end generated code: output=0a32988919dfb94c input=fd728fc2c074f5e6]*/
|
||||
/*[clinic end generated code: output=0a32988919dfb94c input=60c7f272206d3758]*/
|
||||
{
|
||||
ptrace_enter_call((PyObject*)self, (void *)code, code);
|
||||
|
||||
|
|
@ -653,6 +656,7 @@ _lsprof_Profiler__pythrow_callback_impl(ProfilerObject *self, PyObject *code,
|
|||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
_lsprof.Profiler._pyreturn_callback
|
||||
|
||||
code: object
|
||||
|
|
@ -667,7 +671,7 @@ _lsprof_Profiler__pyreturn_callback_impl(ProfilerObject *self,
|
|||
PyObject *code,
|
||||
PyObject *instruction_offset,
|
||||
PyObject *retval)
|
||||
/*[clinic end generated code: output=9e2f6fc1b882c51e input=667ffaeb2fa6fd1f]*/
|
||||
/*[clinic end generated code: output=9e2f6fc1b882c51e input=0ddcc1ec53faa928]*/
|
||||
{
|
||||
ptrace_leave_call((PyObject*)self, (void *)code);
|
||||
|
||||
|
|
@ -703,6 +707,7 @@ PyObject* get_cfunc_from_callable(PyObject* callable, PyObject* self_arg, PyObje
|
|||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
_lsprof.Profiler._ccall_callback
|
||||
|
||||
code: object
|
||||
|
|
@ -717,7 +722,7 @@ static PyObject *
|
|||
_lsprof_Profiler__ccall_callback_impl(ProfilerObject *self, PyObject *code,
|
||||
PyObject *instruction_offset,
|
||||
PyObject *callable, PyObject *self_arg)
|
||||
/*[clinic end generated code: output=152db83cabd18cad input=0e66687cfb95c001]*/
|
||||
/*[clinic end generated code: output=152db83cabd18cad input=2fc1e0630ee5e32b]*/
|
||||
{
|
||||
if (self->flags & POF_BUILTINS) {
|
||||
PyObject* cfunc = get_cfunc_from_callable(callable, self_arg, self->missing);
|
||||
|
|
@ -733,6 +738,7 @@ _lsprof_Profiler__ccall_callback_impl(ProfilerObject *self, PyObject *code,
|
|||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
_lsprof.Profiler._creturn_callback
|
||||
|
||||
code: object
|
||||
|
|
@ -748,7 +754,7 @@ _lsprof_Profiler__creturn_callback_impl(ProfilerObject *self, PyObject *code,
|
|||
PyObject *instruction_offset,
|
||||
PyObject *callable,
|
||||
PyObject *self_arg)
|
||||
/*[clinic end generated code: output=1e886dde8fed8fb0 input=b18afe023746923a]*/
|
||||
/*[clinic end generated code: output=1e886dde8fed8fb0 input=bdc246d6b5b8714a]*/
|
||||
{
|
||||
if (self->flags & POF_BUILTINS) {
|
||||
PyObject* cfunc = get_cfunc_from_callable(callable, self_arg, self->missing);
|
||||
|
|
@ -780,6 +786,7 @@ static const struct {
|
|||
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
_lsprof.Profiler.enable
|
||||
|
||||
subcalls: bool = True
|
||||
|
|
@ -796,7 +803,7 @@ Start collecting profiling information.
|
|||
static PyObject *
|
||||
_lsprof_Profiler_enable_impl(ProfilerObject *self, int subcalls,
|
||||
int builtins)
|
||||
/*[clinic end generated code: output=1e747f9dc1edd571 input=9ab81405107ab7f1]*/
|
||||
/*[clinic end generated code: output=1e747f9dc1edd571 input=0b88115b1c796173]*/
|
||||
{
|
||||
int all_events = 0;
|
||||
if (setSubcalls(self, subcalls) < 0 || setBuiltins(self, builtins) < 0) {
|
||||
|
|
@ -869,6 +876,7 @@ flush_unmatched(ProfilerObject *pObj)
|
|||
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
_lsprof.Profiler.disable
|
||||
|
||||
Stop collecting profiling information.
|
||||
|
|
@ -876,7 +884,7 @@ Stop collecting profiling information.
|
|||
|
||||
static PyObject *
|
||||
_lsprof_Profiler_disable_impl(ProfilerObject *self)
|
||||
/*[clinic end generated code: output=838cffef7f651870 input=05700b3fc68d1f50]*/
|
||||
/*[clinic end generated code: output=838cffef7f651870 input=f7e4787cae20f7f6]*/
|
||||
{
|
||||
if (self->flags & POF_EXT_TIMER) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
|
|
@ -928,6 +936,7 @@ _lsprof_Profiler_disable_impl(ProfilerObject *self)
|
|||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
_lsprof.Profiler.clear
|
||||
|
||||
Clear all profiling information collected so far.
|
||||
|
|
@ -935,7 +944,7 @@ Clear all profiling information collected so far.
|
|||
|
||||
static PyObject *
|
||||
_lsprof_Profiler_clear_impl(ProfilerObject *self)
|
||||
/*[clinic end generated code: output=dd1c668fb84b1335 input=fbe1f88c28be4f98]*/
|
||||
/*[clinic end generated code: output=dd1c668fb84b1335 input=4aab219d5d7a9bec]*/
|
||||
{
|
||||
if (self->flags & POF_EXT_TIMER) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
|
|
|
|||
42
Modules/clinic/_lsprof.c.h
generated
42
Modules/clinic/_lsprof.c.h
generated
|
|
@ -6,6 +6,7 @@ preserve
|
|||
# include "pycore_gc.h" // PyGC_Head
|
||||
# include "pycore_runtime.h" // _Py_ID()
|
||||
#endif
|
||||
#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
|
||||
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
|
||||
|
||||
PyDoc_STRVAR(_lsprof_Profiler_getstats__doc__,
|
||||
|
|
@ -45,11 +46,18 @@ _lsprof_Profiler_getstats_impl(ProfilerObject *self, PyTypeObject *cls);
|
|||
static PyObject *
|
||||
_lsprof_Profiler_getstats(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
|
||||
if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) {
|
||||
PyErr_SetString(PyExc_TypeError, "getstats() takes no arguments");
|
||||
return NULL;
|
||||
goto exit;
|
||||
}
|
||||
return _lsprof_Profiler_getstats_impl((ProfilerObject *)self, cls);
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = _lsprof_Profiler_getstats_impl((ProfilerObject *)self, cls);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(_lsprof_Profiler__pystart_callback__doc__,
|
||||
|
|
@ -76,7 +84,9 @@ _lsprof_Profiler__pystart_callback(PyObject *self, PyObject *const *args, Py_ssi
|
|||
}
|
||||
code = args[0];
|
||||
instruction_offset = args[1];
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = _lsprof_Profiler__pystart_callback_impl((ProfilerObject *)self, code, instruction_offset);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
|
|
@ -109,7 +119,9 @@ _lsprof_Profiler__pythrow_callback(PyObject *self, PyObject *const *args, Py_ssi
|
|||
code = args[0];
|
||||
instruction_offset = args[1];
|
||||
exception = args[2];
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = _lsprof_Profiler__pythrow_callback_impl((ProfilerObject *)self, code, instruction_offset, exception);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
|
|
@ -143,7 +155,9 @@ _lsprof_Profiler__pyreturn_callback(PyObject *self, PyObject *const *args, Py_ss
|
|||
code = args[0];
|
||||
instruction_offset = args[1];
|
||||
retval = args[2];
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = _lsprof_Profiler__pyreturn_callback_impl((ProfilerObject *)self, code, instruction_offset, retval);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
|
|
@ -178,7 +192,9 @@ _lsprof_Profiler__ccall_callback(PyObject *self, PyObject *const *args, Py_ssize
|
|||
instruction_offset = args[1];
|
||||
callable = args[2];
|
||||
self_arg = args[3];
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = _lsprof_Profiler__ccall_callback_impl((ProfilerObject *)self, code, instruction_offset, callable, self_arg);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
|
|
@ -215,7 +231,9 @@ _lsprof_Profiler__creturn_callback(PyObject *self, PyObject *const *args, Py_ssi
|
|||
instruction_offset = args[1];
|
||||
callable = args[2];
|
||||
self_arg = args[3];
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = _lsprof_Profiler__creturn_callback_impl((ProfilerObject *)self, code, instruction_offset, callable, self_arg);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
|
|
@ -299,7 +317,9 @@ _lsprof_Profiler_enable(PyObject *self, PyObject *const *args, Py_ssize_t nargs,
|
|||
goto exit;
|
||||
}
|
||||
skip_optional_pos:
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = _lsprof_Profiler_enable_impl((ProfilerObject *)self, subcalls, builtins);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
|
|
@ -320,7 +340,13 @@ _lsprof_Profiler_disable_impl(ProfilerObject *self);
|
|||
static PyObject *
|
||||
_lsprof_Profiler_disable(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
return _lsprof_Profiler_disable_impl((ProfilerObject *)self);
|
||||
PyObject *return_value = NULL;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = _lsprof_Profiler_disable_impl((ProfilerObject *)self);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(_lsprof_Profiler_clear__doc__,
|
||||
|
|
@ -338,7 +364,13 @@ _lsprof_Profiler_clear_impl(ProfilerObject *self);
|
|||
static PyObject *
|
||||
_lsprof_Profiler_clear(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
return _lsprof_Profiler_clear_impl((ProfilerObject *)self);
|
||||
PyObject *return_value = NULL;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = _lsprof_Profiler_clear_impl((ProfilerObject *)self);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(profiler_init__doc__,
|
||||
|
|
@ -444,4 +476,4 @@ skip_optional_pos:
|
|||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=9e46985561166c37 input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=af26a0b0ddcc3351 input=a9049054013a1b77]*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue