Skip to content

Commit efa7bf3

Browse files
committed
gh-135953: Simplify GC markers in the tachyon profiler
1 parent f6dd9c1 commit efa7bf3

File tree

3 files changed

+16
-21
lines changed

3 files changed

+16
-21
lines changed

Lib/profiling/sampling/gecko_collector.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,6 @@ def collect(self, stack_frames):
141141
for thread_info in interpreter_info.threads:
142142
frames = thread_info.frame_info
143143
tid = thread_info.thread_id
144-
gc_collecting = thread_info.gc_collecting
145144

146145
# Initialize thread if needed
147146
if tid not in self.threads:
@@ -197,16 +196,23 @@ def collect(self, stack_frames):
197196
self._add_marker(tid, "Waiting for GIL", self.gil_wait_start.pop(tid),
198197
current_time, CATEGORY_GIL)
199198

200-
# Track GC events - attribute to all threads that hold the GIL during GC
201-
# (GC is interpreter-wide but runs on whichever thread(s) have the GIL)
202-
# If GIL switches during GC, multiple threads will get GC markers
203-
if gc_collecting and has_gil:
204-
# Start GC marker if not already started for this thread
199+
# Track GC events by detecting <GC> frames in the stack trace
200+
# This leverages the improved GC frame tracking from commit 336366fd7ca
201+
# which precisely identifies the thread that initiated GC collection
202+
has_gc_frame = False
203+
if frames:
204+
for frame_info in frames:
205+
# Check if this is a GC frame (funcname == "<GC>", filename == "~", lineno == 0)
206+
if frame_info.funcname == "<GC>":
207+
has_gc_frame = True
208+
break
209+
210+
if has_gc_frame:
211+
# This thread initiated GC collection
205212
if tid not in self.gc_start_per_thread:
206213
self.gc_start_per_thread[tid] = current_time
207214
elif tid in self.gc_start_per_thread:
208-
# End GC marker if it was running for this thread
209-
# (either GC finished or thread lost GIL)
215+
# End GC marker when no more GC frames are detected
210216
self._add_marker(tid, "GC Collecting", self.gc_start_per_thread.pop(tid),
211217
current_time, CATEGORY_GC)
212218

Lib/test/test_profiling/test_sampling_profiler.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,13 @@ def __repr__(self):
6363
class MockThreadInfo:
6464
"""Mock ThreadInfo for testing since the real one isn't accessible."""
6565

66-
def __init__(self, thread_id, frame_info, status=0, gc_collecting=False): # Default to THREAD_STATE_RUNNING (0)
66+
def __init__(self, thread_id, frame_info, status=0): # Default to THREAD_STATE_RUNNING (0)
6767
self.thread_id = thread_id
6868
self.frame_info = frame_info
6969
self.status = status
70-
self.gc_collecting = gc_collecting
7170

7271
def __repr__(self):
73-
return f"MockThreadInfo(thread_id={self.thread_id}, frame_info={self.frame_info}, status={self.status}, gc_collecting={self.gc_collecting})"
72+
return f"MockThreadInfo(thread_id={self.thread_id}, frame_info={self.frame_info}, status={self.status})"
7473

7574

7675
class MockInterpreterInfo:
@@ -2742,7 +2741,6 @@ def __init__(self, thread_id, frame_info, status):
27422741
self.thread_id = thread_id
27432742
self.frame_info = frame_info
27442743
self.status = status
2745-
self.gc_collecting = False
27462744

27472745
# Create test data: active thread (HAS_GIL | ON_CPU), idle thread (neither), and another active thread
27482746
ACTIVE_STATUS = THREAD_STATUS_HAS_GIL | THREAD_STATUS_ON_CPU # Has GIL and on CPU

Modules/_remote_debugging_module.c

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,6 @@ static PyStructSequence_Field ThreadInfo_fields[] = {
186186
{"thread_id", "Thread ID"},
187187
{"status", "Thread status (flags: HAS_GIL, ON_CPU, UNKNOWN or legacy enum)"},
188188
{"frame_info", "Frame information"},
189-
{"gc_collecting", "Whether GC is collecting (interpreter-level)"},
190189
{NULL}
191190
};
192191

@@ -2827,18 +2826,10 @@ unwind_stack_for_thread(
28272826
goto error;
28282827
}
28292828

2830-
PyObject *py_gc_collecting = PyBool_FromLong(gc_collecting);
2831-
if (py_gc_collecting == NULL) {
2832-
set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create gc_collecting");
2833-
Py_DECREF(py_status);
2834-
goto error;
2835-
}
2836-
28372829
// py_status contains status flags (bitfield)
28382830
PyStructSequence_SetItem(result, 0, thread_id);
28392831
PyStructSequence_SetItem(result, 1, py_status); // Steals reference
28402832
PyStructSequence_SetItem(result, 2, frame_info); // Steals reference
2841-
PyStructSequence_SetItem(result, 3, py_gc_collecting); // Steals reference
28422833

28432834
cleanup_stack_chunks(&chunks);
28442835
return result;

0 commit comments

Comments
 (0)