cpython/Modules/_remote_debugging/gc_stats.c

153 lines
4.8 KiB
C

/******************************************************************************
* Remote Debugging Module - GC Stats Functions
*
* This file contains functions for reading GC stats from interpreter state.
******************************************************************************/
#include "gc_stats.h"
typedef struct {
PyObject *result;
PyTypeObject *gc_stats_info_type;
bool all_interpreters;
} GetGCStatsContext;
static int
read_gc_stats(struct gc_stats *stats, int64_t iid, PyObject *result,
PyTypeObject *gc_stats_info_type)
{
#define SET_FIELD(converter, expr) do { \
PyObject *value = converter(expr); \
if (value == NULL) { \
goto error; \
} \
PyStructSequence_SetItem(item, field++, value); \
} while (0)
PyObject *item = NULL;
for (unsigned long gen = 0; gen < NUM_GENERATIONS; gen++) {
struct gc_generation_stats *items;
int size;
if (gen == 0) {
items = (struct gc_generation_stats *)stats->young.items;
size = GC_YOUNG_STATS_SIZE;
}
else {
items = (struct gc_generation_stats *)stats->old[gen-1].items;
size = GC_OLD_STATS_SIZE;
}
for (int i = 0; i < size; i++, items++) {
item = PyStructSequence_New(gc_stats_info_type);
if (item == NULL) {
goto error;
}
Py_ssize_t field = 0;
SET_FIELD(PyLong_FromUnsignedLong, gen);
SET_FIELD(PyLong_FromInt64, iid);
SET_FIELD(PyLong_FromInt64, items->ts_start);
SET_FIELD(PyLong_FromInt64, items->ts_stop);
SET_FIELD(PyLong_FromSsize_t, items->collections);
SET_FIELD(PyLong_FromSsize_t, items->collected);
SET_FIELD(PyLong_FromSsize_t, items->uncollectable);
SET_FIELD(PyLong_FromSsize_t, items->candidates);
SET_FIELD(PyFloat_FromDouble, items->duration);
int rc = PyList_Append(result, item);
Py_CLEAR(item);
if (rc < 0) {
goto error;
}
}
}
#undef SET_FIELD
return 0;
error:
Py_XDECREF(item);
return -1;
}
static int
get_gc_stats_from_interpreter_state(RuntimeOffsets *offsets,
uintptr_t interpreter_state_addr,
int64_t iid,
void *context)
{
GetGCStatsContext *ctx = (GetGCStatsContext *)context;
if (!ctx->all_interpreters && iid > 0) {
return 0;
}
uintptr_t gc_stats_addr = 0;
uintptr_t gc_stats_pointer_address = interpreter_state_addr
+ offsets->debug_offsets.interpreter_state.gc
+ offsets->debug_offsets.gc.generation_stats;
if (_Py_RemoteDebug_ReadRemoteMemory(&offsets->handle,
gc_stats_pointer_address,
sizeof(gc_stats_addr),
&gc_stats_addr) < 0) {
set_exception_cause(offsets, PyExc_RuntimeError, "Failed to read GC state address");
return -1;
}
if (gc_stats_addr == 0) {
PyErr_SetString(PyExc_RuntimeError, "GC state address is NULL");
return -1;
}
struct gc_stats stats;
if (_Py_RemoteDebug_ReadRemoteMemory(&offsets->handle,
gc_stats_addr,
sizeof(stats),
&stats) < 0) {
set_exception_cause(offsets, PyExc_RuntimeError, "Failed to read GC state");
return -1;
}
if (read_gc_stats(&stats, iid, ctx->result,
ctx->gc_stats_info_type) < 0) {
set_exception_cause(offsets, PyExc_RuntimeError, "Failed to populate GC stats result");
return -1;
}
return 0;
}
PyObject *
get_gc_stats(RuntimeOffsets *offsets, bool all_interpreters,
PyTypeObject *gc_stats_info_type)
{
uint64_t gc_stats_size = offsets->debug_offsets.gc.generation_stats_size;
if (gc_stats_size != sizeof(struct gc_stats)) {
PyErr_Format(PyExc_RuntimeError,
"Remote gc_stats size (%llu) does not match "
"local size (%zu)",
(unsigned long long)gc_stats_size,
sizeof(struct gc_stats));
set_exception_cause(offsets, PyExc_RuntimeError, "Remote gc_stats size mismatch");
return NULL;
}
PyObject *result = PyList_New(0);
if (result == NULL) {
return NULL;
}
GetGCStatsContext ctx = {
.result = result,
.gc_stats_info_type = gc_stats_info_type,
.all_interpreters = all_interpreters,
};
if (iterate_interpreters(offsets, get_gc_stats_from_interpreter_state,
&ctx) < 0) {
Py_CLEAR(result);
return NULL;
}
return result;
}