Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Cleanup init of interp->pystats_struct.
Add the _PyStats_InterpInit() function which will set pystats_enabled
flag and allocate pystats_struct as required.  We shouldn't be putting
logic in _PyObject_InitState().  This also fixes a bug where
interp->pystats_struct was allocated repeated rather than once per
interpreter.
  • Loading branch information
nascheme committed Oct 23, 2025
commit 0e0926777488d48e8b96101a2664fe4fa84be41d
1 change: 1 addition & 0 deletions Include/internal/pycore_stats.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ PyAPI_FUNC(PyObject*) _Py_GetSpecializationStats(void);
RARE_EVENT_INTERP_INC(interp, name); \
} while (0); \

PyStatus _PyStats_InterpInit(PyInterpreterState *);
bool _PyStats_ThreadInit(PyInterpreterState *, _PyThreadStateImpl *);
void _PyStats_ThreadFini(_PyThreadStateImpl *);
void _PyStats_Attach(_PyThreadStateImpl *);
Expand Down
7 changes: 0 additions & 7 deletions Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -2418,13 +2418,6 @@ _PyObject_InitState(PyInterpreterState *interp)
if (refchain_init(interp) < 0) {
return _PyStatus_NO_MEMORY();
}
#endif
#ifdef Py_STATS
if (interp->config._pystats) {
// start with pystats enabled, can be disabled via sys._stats_off()
// this needs to be set before the first tstate is created
interp->pystats_enabled = 1;
}
#endif
return _PyStatus_OK();
}
Expand Down
17 changes: 17 additions & 0 deletions Python/pylifecycle.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "pycore_runtime.h" // _Py_ID()
#include "pycore_runtime_init.h" // _PyRuntimeState_INIT
#include "pycore_setobject.h" // _PySet_NextEntry()
#include "pycore_stats.h" // _PyStats_InterpInit()
#include "pycore_sysmodule.h" // _PySys_ClearAttrString()
#include "pycore_traceback.h" // _Py_DumpTracebackThreads()
#include "pycore_typeobject.h" // _PyTypes_InitTypes()
Expand Down Expand Up @@ -652,6 +653,14 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
return status;
}

#ifdef Py_STATS
// initialize pystats. This must be done after the settings are loaded.
status = _PyStats_InterpInit(interp);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
#endif

// initialize the interp->obmalloc state. This must be done after
// the settings are loaded (so that feature_flags are set) but before
// any calls are made to obmalloc functions.
Expand Down Expand Up @@ -2349,6 +2358,14 @@ new_interpreter(PyThreadState **tstate_p,
return status;
}

#ifdef Py_STATS
// initialize pystats. This must be done after the settings are loaded.
status = _PyStats_InterpInit(interp);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
#endif

// initialize the interp->obmalloc state. This must be done after
// the settings are loaded (so that feature_flags are set) but before
// any calls are made to obmalloc functions.
Expand Down
33 changes: 21 additions & 12 deletions Python/pystats.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "pycore_pyatomic_ft_wrappers.h"
#include "pycore_pylifecycle.h" // _PyOS_URandomNonblock()
#include "pycore_tstate.h"
#include "pycore_initconfig.h" // _PyStatus_OK()
#include "pycore_uop_metadata.h" // _PyOpcode_uop_name
#include "pycore_uop_ids.h" // MAX_UOP_ID
#include "pycore_pystate.h" // _PyThreadState_GET()
Expand Down Expand Up @@ -725,23 +726,31 @@ _Py_PrintSpecializationStats(int to_file)
return 1;
}

PyStatus
_PyStats_InterpInit(PyInterpreterState *interp)
{
if (interp->config._pystats) {
// start with pystats enabled, can be disabled via sys._stats_off()
// this needs to be set before the first tstate is created
interp->pystats_enabled = 1;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, we use PyMem_RawCalloc to allocate new interp memory. Then the default value of interp->pystats_enabled is 0, so if interp->config._pystats is false, we don't need to initialize it.

interp->pystats_struct = PyMem_RawCalloc(1, sizeof(PyStats));
if (interp->pystats_struct == NULL) {
return _PyStatus_ERR("out-of-memory while initializing interpreter");
}
}
return _PyStatus_OK();
}

bool
_PyStats_ThreadInit(PyInterpreterState *interp, _PyThreadStateImpl *tstate)
{
STATS_LOCK(interp);
if (interp->pystats_enabled) {
PyStats *s = PyMem_RawCalloc(1, sizeof(PyStats));
if (s == NULL) {
STATS_UNLOCK(interp);
#ifdef Py_GIL_DISABLED
if (FT_ATOMIC_LOAD_INT_RELAXED(interp->pystats_enabled)) {
assert(interp->pystats_struct != NULL);
tstate->pystats_struct = PyMem_RawCalloc(1, sizeof(PyStats));
if (tstate->pystats_struct == NULL) {
return false;
}
FT_ATOMIC_STORE_PTR_RELAXED(interp->pystats_struct, s);
}
STATS_UNLOCK(interp);
#ifdef Py_GIL_DISABLED
tstate->pystats_struct = PyMem_RawCalloc(1, sizeof(PyStats));
if (tstate->pystats_struct == NULL) {
return false;
}
#endif
return true;
Expand Down