Skip to content

Commit f89e408

Browse files
committed
fixup! fixup! gh-138385: Sample all interpreters in the tachyon profiler
1 parent 88a3621 commit f89e408

2 files changed

Lines changed: 108 additions & 5 deletions

File tree

Modules/_remote_debugging_module.c

Lines changed: 106 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2664,7 +2664,7 @@ _remote_debugging_RemoteUnwinder___init___impl(RemoteUnwinderObject *self,
26642664
@critical_section
26652665
_remote_debugging.RemoteUnwinder.get_stack_trace
26662666
2667-
Returns stack traces for all interpreters and threads in target process.
2667+
Returns stack traces for all interpreters and threads in process.
26682668
26692669
Each element in the returned list is a tuple of (interpreter_id, thread_list), where:
26702670
- interpreter_id is the interpreter identifier
@@ -2708,7 +2708,7 @@ The threads returned depend on the initialization parameters:
27082708

27092709
static PyObject *
27102710
_remote_debugging_RemoteUnwinder_get_stack_trace_impl(RemoteUnwinderObject *self)
2711-
/*[clinic end generated code: output=666192b90c69d567 input=b5b7f2e7e2a91318]*/
2711+
/*[clinic end generated code: output=666192b90c69d567 input=bcff01c73cccc1c0]*/
27122712
{
27132713
PyObject* result = PyList_New(0);
27142714
if (!result) {
@@ -2734,11 +2734,114 @@ _remote_debugging_RemoteUnwinder_get_stack_trace_impl(RemoteUnwinderObject *self
27342734
int64_t interpreter_id = GET_MEMBER(int64_t, interp_state_buffer,
27352735
self->debug_offsets.interpreter_state.id);
27362736

2737-
if (process_interpreter_threads(self, current_interpreter, result, interpreter_id) < 0) {
2737+
// Get code object generation from buffer
2738+
uint64_t code_object_generation = GET_MEMBER(uint64_t, interp_state_buffer,
2739+
self->debug_offsets.interpreter_state.code_object_generation);
2740+
2741+
if (code_object_generation != self->code_object_generation) {
2742+
self->code_object_generation = code_object_generation;
2743+
_Py_hashtable_clear(self->code_object_cache);
2744+
}
2745+
2746+
#ifdef Py_GIL_DISABLED
2747+
// Check TLBC generation and invalidate cache if needed
2748+
uint32_t current_tlbc_generation = GET_MEMBER(uint32_t, interp_state_buffer,
2749+
self->debug_offsets.interpreter_state.tlbc_generation);
2750+
if (current_tlbc_generation != self->tlbc_generation) {
2751+
self->tlbc_generation = current_tlbc_generation;
2752+
_Py_hashtable_clear(self->tlbc_cache);
2753+
}
2754+
#endif
2755+
2756+
// Create a list to hold threads for this interpreter
2757+
PyObject *interpreter_threads = PyList_New(0);
2758+
if (!interpreter_threads) {
2759+
set_exception_cause(self, PyExc_MemoryError, "Failed to create interpreter threads list");
27382760
Py_CLEAR(result);
27392761
goto exit;
27402762
}
27412763

2764+
uintptr_t current_tstate;
2765+
if (self->only_active_thread) {
2766+
// Find the GIL holder for THIS interpreter
2767+
int gil_locked = GET_MEMBER(int, interp_state_buffer,
2768+
self->debug_offsets.interpreter_state.gil_runtime_state_locked);
2769+
2770+
if (!gil_locked) {
2771+
// This interpreter's GIL is not locked, skip it
2772+
Py_DECREF(interpreter_threads);
2773+
goto next_interpreter;
2774+
}
2775+
2776+
// Get the GIL holder for this interpreter
2777+
current_tstate = (uintptr_t)GET_MEMBER(PyThreadState*, interp_state_buffer,
2778+
self->debug_offsets.interpreter_state.gil_runtime_state_holder);
2779+
} else if (self->tstate_addr == 0) {
2780+
// Get all threads for this interpreter
2781+
current_tstate = GET_MEMBER(uintptr_t, interp_state_buffer,
2782+
self->debug_offsets.interpreter_state.threads_head);
2783+
} else {
2784+
// Target specific thread (only process first interpreter)
2785+
current_tstate = self->tstate_addr;
2786+
}
2787+
2788+
while (current_tstate != 0) {
2789+
PyObject* frame_info = unwind_stack_for_thread(self, &current_tstate);
2790+
if (!frame_info) {
2791+
Py_DECREF(interpreter_threads);
2792+
set_exception_cause(self, PyExc_RuntimeError, "Failed to unwind stack for thread");
2793+
Py_CLEAR(result);
2794+
goto exit;
2795+
}
2796+
2797+
if (PyList_Append(interpreter_threads, frame_info) == -1) {
2798+
Py_DECREF(frame_info);
2799+
Py_DECREF(interpreter_threads);
2800+
set_exception_cause(self, PyExc_RuntimeError, "Failed to append thread frame info");
2801+
Py_CLEAR(result);
2802+
goto exit;
2803+
}
2804+
Py_DECREF(frame_info);
2805+
2806+
// If targeting specific thread or only active thread, process just one
2807+
if (self->tstate_addr || self->only_active_thread) {
2808+
break;
2809+
}
2810+
}
2811+
2812+
// Create the InterpreterInfo StructSequence
2813+
RemoteDebuggingState *state = RemoteDebugging_GetStateFromObject((PyObject*)self);
2814+
PyObject *interpreter_info = PyStructSequence_New(state->InterpreterInfo_Type);
2815+
if (!interpreter_info) {
2816+
Py_DECREF(interpreter_threads);
2817+
set_exception_cause(self, PyExc_MemoryError, "Failed to create InterpreterInfo");
2818+
Py_CLEAR(result);
2819+
goto exit;
2820+
}
2821+
2822+
PyObject *interp_id = PyLong_FromLongLong(interpreter_id);
2823+
if (!interp_id) {
2824+
Py_DECREF(interpreter_threads);
2825+
Py_DECREF(interpreter_info);
2826+
set_exception_cause(self, PyExc_MemoryError, "Failed to create interpreter ID");
2827+
Py_CLEAR(result);
2828+
goto exit;
2829+
}
2830+
2831+
PyStructSequence_SetItem(interpreter_info, 0, interp_id); // steals reference
2832+
PyStructSequence_SetItem(interpreter_info, 1, interpreter_threads); // steals reference
2833+
2834+
// Add this interpreter to the result list
2835+
if (PyList_Append(result, interpreter_info) == -1) {
2836+
Py_DECREF(interpreter_info);
2837+
set_exception_cause(self, PyExc_RuntimeError, "Failed to append interpreter info");
2838+
Py_CLEAR(result);
2839+
goto exit;
2840+
}
2841+
Py_DECREF(interpreter_info);
2842+
2843+
next_interpreter:
2844+
27422845
// Get the next interpreter address
27432846
current_interpreter = GET_MEMBER(uintptr_t, interp_state_buffer,
27442847
self->debug_offsets.interpreter_state.next);

Modules/clinic/_remote_debugging_module.c.h

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)