diff --git a/Include/internal/pycore_instruments.h b/Include/internal/pycore_instruments.h index 7658adca719e86..98684ff1024f6e 100644 --- a/Include/internal/pycore_instruments.h +++ b/Include/internal/pycore_instruments.h @@ -62,9 +62,6 @@ extern void _Py_call_instrumentation_exc2(PyThreadState *tstate, int event, _PyInterpreterFrame *frame, _Py_CODEUNIT *instr, PyObject *arg0, PyObject *arg1); -extern int -_Py_Instrumentation_GetLine(PyCodeObject *code, int index); - extern PyObject _PyInstrumentation_MISSING; extern PyObject _PyInstrumentation_DISABLE; @@ -120,6 +117,8 @@ typedef struct _PyCoMonitoringData { uint8_t *per_instruction_tools; } _PyCoMonitoringData; +extern int +_Py_Instrumentation_GetLine(PyCodeObject *code, _PyCoLineInstrumentationData *line_data, int index); #ifdef __cplusplus } diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-09-14-18-33.gh-issue-148037.aP3CSX.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-09-14-18-33.gh-issue-148037.aP3CSX.rst new file mode 100644 index 00000000000000..b0cef595129817 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-09-14-18-33.gh-issue-148037.aP3CSX.rst @@ -0,0 +1 @@ +Remove critical section from :c:func:`!PyCode_Addr2Line` in free-threading. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index f0b2e5f36c6060..acaa9f5501af12 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -1013,14 +1013,18 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) * source location tracking (co_lines/co_positions) ******************/ -static int -_PyCode_Addr2Line(PyCodeObject *co, int addrq) +int +PyCode_Addr2Line(PyCodeObject *co, int addrq) { if (addrq < 0) { return co->co_firstlineno; } - if (co->_co_monitoring && co->_co_monitoring->lines) { - return _Py_Instrumentation_GetLine(co, addrq/sizeof(_Py_CODEUNIT)); + _PyCoMonitoringData *data = _Py_atomic_load_ptr_acquire(&co->_co_monitoring); + if (data) { + _PyCoLineInstrumentationData *lines = _Py_atomic_load_ptr_acquire(&data->lines); + if (lines) { + return _Py_Instrumentation_GetLine(co, lines, addrq/sizeof(_Py_CODEUNIT)); + } } assert(addrq >= 0 && addrq < _PyCode_NBYTES(co)); PyCodeAddressRange bounds; @@ -1035,7 +1039,7 @@ _PyCode_SafeAddr2Line(PyCodeObject *co, int addrq) return co->co_firstlineno; } if (co->_co_monitoring && co->_co_monitoring->lines) { - return _Py_Instrumentation_GetLine(co, addrq/sizeof(_Py_CODEUNIT)); + return _Py_Instrumentation_GetLine(co, co->_co_monitoring->lines, addrq/sizeof(_Py_CODEUNIT)); } if (!(addrq >= 0 && addrq < _PyCode_NBYTES(co))) { return -1; @@ -1045,16 +1049,6 @@ _PyCode_SafeAddr2Line(PyCodeObject *co, int addrq) return _PyCode_CheckLineNumber(addrq, &bounds); } -int -PyCode_Addr2Line(PyCodeObject *co, int addrq) -{ - int lineno; - Py_BEGIN_CRITICAL_SECTION(co); - lineno = _PyCode_Addr2Line(co, addrq); - Py_END_CRITICAL_SECTION(); - return lineno; -} - void _PyLineTable_InitAddressRange(const char *linetable, Py_ssize_t length, int firstlineno, PyCodeAddressRange *range) { diff --git a/Python/instrumentation.c b/Python/instrumentation.c index 32d6a204182892..d06cda7c589c74 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -1285,13 +1285,10 @@ _Py_call_instrumentation_exc2( } int -_Py_Instrumentation_GetLine(PyCodeObject *code, int index) +_Py_Instrumentation_GetLine(PyCodeObject *code, _PyCoLineInstrumentationData *line_data, int index) { - _PyCoMonitoringData *monitoring = code->_co_monitoring; - assert(monitoring != NULL); - assert(monitoring->lines != NULL); + assert(line_data != NULL); assert(index < Py_SIZE(code)); - _PyCoLineInstrumentationData *line_data = monitoring->lines; int line_delta = get_line_delta(line_data, index); int line = compute_line(code, line_delta); return line; @@ -1309,11 +1306,11 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame, _PyCoMonitoringData *monitoring = code->_co_monitoring; _PyCoLineInstrumentationData *line_data = monitoring->lines; PyInterpreterState *interp = tstate->interp; - int line = _Py_Instrumentation_GetLine(code, i); + int line = _Py_Instrumentation_GetLine(code, line_data, i); assert(line >= 0); assert(prev != NULL); int prev_index = (int)(prev - bytecode); - int prev_line = _Py_Instrumentation_GetLine(code, prev_index); + int prev_line = _Py_Instrumentation_GetLine(code, line_data, prev_index); if (prev_line == line) { int prev_opcode = bytecode[prev_index].op.code; /* RESUME and INSTRUMENTED_RESUME are needed for the operation of @@ -1510,11 +1507,9 @@ initialize_tools(PyCodeObject *code) } static void -initialize_lines(PyCodeObject *code, int bytes_per_entry) +initialize_lines(_PyCoLineInstrumentationData *line_data, PyCodeObject *code, int bytes_per_entry) { ASSERT_WORLD_STOPPED_OR_LOCKED(code); - _PyCoLineInstrumentationData *line_data = code->_co_monitoring->lines; - assert(line_data != NULL); line_data->bytes_per_entry = bytes_per_entry; int code_len = (int)Py_SIZE(code); @@ -1655,18 +1650,19 @@ allocate_instrumentation_data(PyCodeObject *code) ASSERT_WORLD_STOPPED_OR_LOCKED(code); if (code->_co_monitoring == NULL) { - code->_co_monitoring = PyMem_Malloc(sizeof(_PyCoMonitoringData)); - if (code->_co_monitoring == NULL) { + _PyCoMonitoringData *monitoring = PyMem_Malloc(sizeof(_PyCoMonitoringData)); + if (monitoring == NULL) { PyErr_NoMemory(); return -1; } - code->_co_monitoring->local_monitors = (_Py_LocalMonitors){ 0 }; - code->_co_monitoring->active_monitors = (_Py_LocalMonitors){ 0 }; - code->_co_monitoring->tools = NULL; - code->_co_monitoring->lines = NULL; - code->_co_monitoring->line_tools = NULL; - code->_co_monitoring->per_instruction_opcodes = NULL; - code->_co_monitoring->per_instruction_tools = NULL; + monitoring->local_monitors = (_Py_LocalMonitors){ 0 }; + monitoring->active_monitors = (_Py_LocalMonitors){ 0 }; + monitoring->tools = NULL; + monitoring->lines = NULL; + monitoring->line_tools = NULL; + monitoring->per_instruction_opcodes = NULL; + monitoring->per_instruction_tools = NULL; + _Py_atomic_store_ptr_release(&code->_co_monitoring, monitoring); } return 0; } @@ -1731,12 +1727,13 @@ update_instrumentation_data(PyCodeObject *code, PyInterpreterState *interp) else { bytes_per_entry = 5; } - code->_co_monitoring->lines = PyMem_Malloc(1 + code_len * bytes_per_entry); - if (code->_co_monitoring->lines == NULL) { + _PyCoLineInstrumentationData *lines = PyMem_Malloc(1 + code_len * bytes_per_entry); + if (lines == NULL) { PyErr_NoMemory(); return -1; } - initialize_lines(code, bytes_per_entry); + initialize_lines(lines, code, bytes_per_entry); + _Py_atomic_store_ptr_release(&code->_co_monitoring->lines, lines); } if (multitools && code->_co_monitoring->line_tools == NULL) { code->_co_monitoring->line_tools = PyMem_Malloc(code_len); diff --git a/Python/legacy_tracing.c b/Python/legacy_tracing.c index 9c72fab298201e..2e29e1aa208ac0 100644 --- a/Python/legacy_tracing.c +++ b/Python/legacy_tracing.c @@ -398,8 +398,8 @@ sys_trace_jump_func( assert(PyCode_Check(code)); /* We can call _Py_Instrumentation_GetLine because we always set * line events for tracing */ - int to_line = _Py_Instrumentation_GetLine(code, to); - int from_line = _Py_Instrumentation_GetLine(code, from); + int to_line = _Py_Instrumentation_GetLine(code, code->_co_monitoring->lines, to); + int from_line = _Py_Instrumentation_GetLine(code, code->_co_monitoring->lines, from); if (to_line != from_line) { /* Will be handled by target INSTRUMENTED_LINE */ return &_PyInstrumentation_DISABLE;