Skip to content

Commit 5bf3a31

Browse files
GH-146527: Add more data to GC statistics and add it to PyDebugOffsets (#146532)
1 parent 087a5f6 commit 5bf3a31

File tree

5 files changed

+203
-74
lines changed

5 files changed

+203
-74
lines changed

Include/internal/pycore_debug_offsets.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ typedef struct _Py_DebugOffsets {
222222
uint64_t size;
223223
uint64_t collecting;
224224
uint64_t frame;
225+
uint64_t generation_stats_size;
226+
uint64_t generation_stats;
225227
} gc;
226228

227229
// Generator object offset;
@@ -373,6 +375,8 @@ typedef struct _Py_DebugOffsets {
373375
.size = sizeof(struct _gc_runtime_state), \
374376
.collecting = offsetof(struct _gc_runtime_state, collecting), \
375377
.frame = offsetof(struct _gc_runtime_state, frame), \
378+
.generation_stats_size = sizeof(struct gc_stats), \
379+
.generation_stats = offsetof(struct _gc_runtime_state, generation_stats), \
376380
}, \
377381
.gen_object = { \
378382
.size = sizeof(PyGenObject), \

Include/internal/pycore_interp_structs.h

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -177,31 +177,54 @@ struct gc_generation {
177177
generations */
178178
};
179179

180-
struct gc_collection_stats {
181-
/* number of collected objects */
182-
Py_ssize_t collected;
183-
/* total number of uncollectable objects (put into gc.garbage) */
184-
Py_ssize_t uncollectable;
185-
// Total number of objects considered for collection and traversed:
186-
Py_ssize_t candidates;
187-
// Duration of the collection in seconds:
188-
double duration;
189-
};
190-
191180
/* Running stats per generation */
192181
struct gc_generation_stats {
182+
PyTime_t ts_start;
183+
PyTime_t ts_stop;
184+
185+
/* heap_size on the start of the collection */
186+
Py_ssize_t heap_size;
187+
188+
/* work_to_do on the start of the collection */
189+
Py_ssize_t work_to_do;
190+
193191
/* total number of collections */
194192
Py_ssize_t collections;
193+
194+
/* total number of visited objects */
195+
Py_ssize_t object_visits;
196+
195197
/* total number of collected objects */
196198
Py_ssize_t collected;
197199
/* total number of uncollectable objects (put into gc.garbage) */
198200
Py_ssize_t uncollectable;
199201
// Total number of objects considered for collection and traversed:
200202
Py_ssize_t candidates;
201-
// Duration of the collection in seconds:
203+
204+
Py_ssize_t objects_transitively_reachable;
205+
Py_ssize_t objects_not_transitively_reachable;
206+
207+
// Total duration of the collection in seconds:
202208
double duration;
203209
};
204210

211+
#ifdef Py_GIL_DISABLED
212+
#define GC_YOUNG_STATS_SIZE 1
213+
#define GC_OLD_STATS_SIZE 1
214+
#else
215+
#define GC_YOUNG_STATS_SIZE 11
216+
#define GC_OLD_STATS_SIZE 3
217+
#endif
218+
struct gc_young_stats_buffer {
219+
struct gc_generation_stats items[GC_YOUNG_STATS_SIZE];
220+
int8_t index;
221+
};
222+
223+
struct gc_old_stats_buffer {
224+
struct gc_generation_stats items[GC_OLD_STATS_SIZE];
225+
int8_t index;
226+
};
227+
205228
enum _GCPhase {
206229
GC_PHASE_MARK = 0,
207230
GC_PHASE_COLLECT = 1
@@ -211,6 +234,11 @@ enum _GCPhase {
211234
signature of gc.collect and change the size of PyStats.gc_stats */
212235
#define NUM_GENERATIONS 3
213236

237+
struct gc_stats {
238+
struct gc_young_stats_buffer young;
239+
struct gc_old_stats_buffer old[2];
240+
};
241+
214242
struct _gc_runtime_state {
215243
/* Is automatic collection enabled? */
216244
int enabled;
@@ -220,7 +248,7 @@ struct _gc_runtime_state {
220248
struct gc_generation old[2];
221249
/* a permanent generation which won't be collected */
222250
struct gc_generation permanent_generation;
223-
struct gc_generation_stats generation_stats[NUM_GENERATIONS];
251+
struct gc_stats generation_stats;
224252
/* true if we are currently running the collector */
225253
int collecting;
226254
// The frame that started the current collection. It might be NULL even when

Modules/gcmodule.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -347,9 +347,9 @@ gc_get_stats_impl(PyObject *module)
347347
/* To get consistent values despite allocations while constructing
348348
the result list, we use a snapshot of the running stats. */
349349
GCState *gcstate = get_gc_state();
350-
for (i = 0; i < NUM_GENERATIONS; i++) {
351-
stats[i] = gcstate->generation_stats[i];
352-
}
350+
stats[0] = gcstate->generation_stats.young.items[gcstate->generation_stats.young.index];
351+
stats[1] = gcstate->generation_stats.old[0].items[gcstate->generation_stats.old[0].index];
352+
stats[2] = gcstate->generation_stats.old[1].items[gcstate->generation_stats.old[1].index];
353353

354354
PyObject *result = PyList_New(0);
355355
if (result == NULL)

0 commit comments

Comments
 (0)