Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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