Skip to content
43 changes: 43 additions & 0 deletions Lib/test/test_free_threading/test_cprofile.py
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Make :mod:`cProfile` thread-safe on the :term:`free threaded <free
threading>` build.
27 changes: 18 additions & 9 deletions Modules/_lsprof.c
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,7 @@ static int statsForEntry(rotating_node_t *node, void *arg)
}

/*[clinic input]
@critical_section
_lsprof.Profiler.getstats

cls: defining_class
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -613,6 +614,7 @@ setBuiltins(ProfilerObject *pObj, int nvalue)
}

/*[clinic input]
@critical_section
_lsprof.Profiler._pystart_callback

code: object
Expand All @@ -624,14 +626,15 @@ _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);

Py_RETURN_NONE;
}

/*[clinic input]
@critical_section
_lsprof.Profiler._pythrow_callback

code: object
Expand All @@ -645,14 +648,15 @@ 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);

Py_RETURN_NONE;
}

/*[clinic input]
@critical_section
_lsprof.Profiler._pyreturn_callback

code: object
Expand All @@ -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);

Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand All @@ -733,6 +738,7 @@ _lsprof_Profiler__ccall_callback_impl(ProfilerObject *self, PyObject *code,
}

/*[clinic input]
@critical_section
_lsprof.Profiler._creturn_callback

code: object
Expand All @@ -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);
Expand Down Expand Up @@ -780,6 +786,7 @@ static const struct {


/*[clinic input]
@critical_section
_lsprof.Profiler.enable

subcalls: bool = True
Expand All @@ -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) {
Expand Down Expand Up @@ -869,14 +876,15 @@ flush_unmatched(ProfilerObject *pObj)


/*[clinic input]
@critical_section
_lsprof.Profiler.disable

Stop collecting profiling information.
[clinic start generated code]*/

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,
Expand Down Expand Up @@ -928,14 +936,15 @@ _lsprof_Profiler_disable_impl(ProfilerObject *self)
}

/*[clinic input]
@critical_section
_lsprof.Profiler.clear

Clear all profiling information collected so far.
[clinic start generated code]*/

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,
Expand Down
42 changes: 37 additions & 5 deletions Modules/clinic/_lsprof.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading