--- /dev/null
+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)
--- /dev/null
+Make :mod:`cProfile` thread-safe on the :term:`free threaded <free
+threading>` build.
}
/*[clinic input]
+@critical_section
_lsprof.Profiler.getstats
cls: defining_class
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);
}
/*[clinic input]
+@critical_section
_lsprof.Profiler._pystart_callback
code: object
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);
}
/*[clinic input]
+@critical_section
_lsprof.Profiler._pythrow_callback
code: object
_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);
}
/*[clinic input]
+@critical_section
_lsprof.Profiler._pyreturn_callback
code: object
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);
}
/*[clinic input]
+@critical_section
_lsprof.Profiler._ccall_callback
code: object
_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);
}
/*[clinic input]
+@critical_section
_lsprof.Profiler._creturn_callback
code: object
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);
/*[clinic input]
+@critical_section
_lsprof.Profiler.enable
subcalls: bool = True
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) {
/*[clinic input]
+@critical_section
_lsprof.Profiler.disable
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,
}
/*[clinic input]
+@critical_section
_lsprof.Profiler.clear
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,
# 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__,
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__,
}
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;
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;
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;
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;
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;
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;
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__,
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__,
exit:
return return_value;
}
-/*[clinic end generated code: output=9e46985561166c37 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=af26a0b0ddcc3351 input=a9049054013a1b77]*/