mirror of https://github.com/python/cpython
GH-93841: Allow stats to be turned on and off, cleared and dumped at runtime. (GH-93843)
This commit is contained in:
parent
c7a79bb036
commit
6f8875eba3
|
@ -259,16 +259,16 @@ extern int _PyStaticCode_InternStrings(PyCodeObject *co);
|
|||
#ifdef Py_STATS
|
||||
|
||||
|
||||
#define STAT_INC(opname, name) _py_stats.opcode_stats[(opname)].specialization.name++
|
||||
#define STAT_DEC(opname, name) _py_stats.opcode_stats[(opname)].specialization.name--
|
||||
#define OPCODE_EXE_INC(opname) _py_stats.opcode_stats[(opname)].execution_count++
|
||||
#define CALL_STAT_INC(name) _py_stats.call_stats.name++
|
||||
#define OBJECT_STAT_INC(name) _py_stats.object_stats.name++
|
||||
#define STAT_INC(opname, name) do { if (_py_stats) _py_stats->opcode_stats[opname].specialization.name++; } while (0)
|
||||
#define STAT_DEC(opname, name) do { if (_py_stats) _py_stats->opcode_stats[opname].specialization.name--; } while (0)
|
||||
#define OPCODE_EXE_INC(opname) do { if (_py_stats) _py_stats->opcode_stats[opname].execution_count++; } while (0)
|
||||
#define CALL_STAT_INC(name) do { if (_py_stats) _py_stats->call_stats.name++; } while (0)
|
||||
#define OBJECT_STAT_INC(name) do { if (_py_stats) _py_stats->object_stats.name++; } while (0)
|
||||
#define OBJECT_STAT_INC_COND(name, cond) \
|
||||
do { if (cond) _py_stats.object_stats.name++; } while (0)
|
||||
#define EVAL_CALL_STAT_INC(name) _py_stats.call_stats.eval_calls[(name)]++
|
||||
do { if (_py_stats && cond) _py_stats->object_stats.name++; } while (0)
|
||||
#define EVAL_CALL_STAT_INC(name) do { if (_py_stats) _py_stats->call_stats.eval_calls[name]++; } while (0)
|
||||
#define EVAL_CALL_STAT_INC_IF_FUNCTION(name, callable) \
|
||||
do { if (PyFunction_Check(callable)) _py_stats.call_stats.eval_calls[(name)]++; } while (0)
|
||||
do { if (_py_stats && PyFunction_Check(callable)) _py_stats->call_stats.eval_calls[name]++; } while (0)
|
||||
|
||||
// Used by the _opcode extension which is built as a shared library
|
||||
PyAPI_FUNC(PyObject*) _Py_GetSpecializationStats(void);
|
||||
|
|
|
@ -73,19 +73,22 @@ typedef struct _stats {
|
|||
ObjectStats object_stats;
|
||||
} PyStats;
|
||||
|
||||
PyAPI_DATA(PyStats) _py_stats;
|
||||
|
||||
PyAPI_DATA(PyStats) _py_stats_struct;
|
||||
PyAPI_DATA(PyStats *) _py_stats;
|
||||
|
||||
extern void _Py_StatsClear(void);
|
||||
extern void _Py_PrintSpecializationStats(int to_file);
|
||||
|
||||
#ifdef _PY_INTERPRETER
|
||||
|
||||
#define _Py_INCREF_STAT_INC() _py_stats.object_stats.interpreter_increfs++
|
||||
#define _Py_DECREF_STAT_INC() _py_stats.object_stats.interpreter_decrefs++
|
||||
#define _Py_INCREF_STAT_INC() do { if (_py_stats) _py_stats->object_stats.interpreter_increfs++; } while (0)
|
||||
#define _Py_DECREF_STAT_INC() do { if (_py_stats) _py_stats->object_stats.interpreter_decrefs++; } while (0)
|
||||
|
||||
#else
|
||||
|
||||
#define _Py_INCREF_STAT_INC() _py_stats.object_stats.increfs++
|
||||
#define _Py_DECREF_STAT_INC() _py_stats.object_stats.decrefs++
|
||||
#define _Py_INCREF_STAT_INC() do { if (_py_stats) _py_stats->object_stats.increfs++; } while (0)
|
||||
#define _Py_DECREF_STAT_INC() do { if (_py_stats) _py_stats->object_stats.decrefs++; } while (0)
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
When built with ``-enable-pystats``, ``sys._stats_on()``,
|
||||
``sys._stats_off()``, ``sys._stats_clear()`` and ``sys._stats_dump()``
|
||||
functions have been added to enable gathering stats for parts of programs.
|
|
@ -1310,7 +1310,7 @@ eval_frame_handle_pending(PyThreadState *tstate)
|
|||
do { \
|
||||
frame->prev_instr = next_instr++; \
|
||||
OPCODE_EXE_INC(op); \
|
||||
_py_stats.opcode_stats[lastopcode].pair_count[op]++; \
|
||||
if (_py_stats) _py_stats->opcode_stats[lastopcode].pair_count[op]++; \
|
||||
lastopcode = op; \
|
||||
} while (0)
|
||||
#else
|
||||
|
@ -7790,7 +7790,7 @@ _Py_GetDXProfile(PyObject *self, PyObject *args)
|
|||
PyObject *l = PyList_New(257);
|
||||
if (l == NULL) return NULL;
|
||||
for (i = 0; i < 256; i++) {
|
||||
PyObject *x = getarray(_py_stats.opcode_stats[i].pair_count);
|
||||
PyObject *x = getarray(_py_stats_struct.opcode_stats[i].pair_count);
|
||||
if (x == NULL) {
|
||||
Py_DECREF(l);
|
||||
return NULL;
|
||||
|
@ -7804,7 +7804,7 @@ _Py_GetDXProfile(PyObject *self, PyObject *args)
|
|||
}
|
||||
for (i = 0; i < 256; i++) {
|
||||
PyObject *x = PyLong_FromUnsignedLongLong(
|
||||
_py_stats.opcode_stats[i].execution_count);
|
||||
_py_stats_struct.opcode_stats[i].execution_count);
|
||||
if (x == NULL) {
|
||||
Py_DECREF(counts);
|
||||
Py_DECREF(l);
|
||||
|
|
|
@ -965,6 +965,94 @@ sys_is_finalizing(PyObject *module, PyObject *Py_UNUSED(ignored))
|
|||
return sys_is_finalizing_impl(module);
|
||||
}
|
||||
|
||||
#if defined(Py_STATS)
|
||||
|
||||
PyDoc_STRVAR(sys__stats_on__doc__,
|
||||
"_stats_on($module, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Turns on stats gathering (stats gathering is on by default).");
|
||||
|
||||
#define SYS__STATS_ON_METHODDEF \
|
||||
{"_stats_on", (PyCFunction)sys__stats_on, METH_NOARGS, sys__stats_on__doc__},
|
||||
|
||||
static PyObject *
|
||||
sys__stats_on_impl(PyObject *module);
|
||||
|
||||
static PyObject *
|
||||
sys__stats_on(PyObject *module, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
return sys__stats_on_impl(module);
|
||||
}
|
||||
|
||||
#endif /* defined(Py_STATS) */
|
||||
|
||||
#if defined(Py_STATS)
|
||||
|
||||
PyDoc_STRVAR(sys__stats_off__doc__,
|
||||
"_stats_off($module, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Turns off stats gathering (stats gathering is on by default).");
|
||||
|
||||
#define SYS__STATS_OFF_METHODDEF \
|
||||
{"_stats_off", (PyCFunction)sys__stats_off, METH_NOARGS, sys__stats_off__doc__},
|
||||
|
||||
static PyObject *
|
||||
sys__stats_off_impl(PyObject *module);
|
||||
|
||||
static PyObject *
|
||||
sys__stats_off(PyObject *module, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
return sys__stats_off_impl(module);
|
||||
}
|
||||
|
||||
#endif /* defined(Py_STATS) */
|
||||
|
||||
#if defined(Py_STATS)
|
||||
|
||||
PyDoc_STRVAR(sys__stats_clear__doc__,
|
||||
"_stats_clear($module, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Clears the stats.");
|
||||
|
||||
#define SYS__STATS_CLEAR_METHODDEF \
|
||||
{"_stats_clear", (PyCFunction)sys__stats_clear, METH_NOARGS, sys__stats_clear__doc__},
|
||||
|
||||
static PyObject *
|
||||
sys__stats_clear_impl(PyObject *module);
|
||||
|
||||
static PyObject *
|
||||
sys__stats_clear(PyObject *module, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
return sys__stats_clear_impl(module);
|
||||
}
|
||||
|
||||
#endif /* defined(Py_STATS) */
|
||||
|
||||
#if defined(Py_STATS)
|
||||
|
||||
PyDoc_STRVAR(sys__stats_dump__doc__,
|
||||
"_stats_dump($module, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Dump stats to file, and clears the stats.");
|
||||
|
||||
#define SYS__STATS_DUMP_METHODDEF \
|
||||
{"_stats_dump", (PyCFunction)sys__stats_dump, METH_NOARGS, sys__stats_dump__doc__},
|
||||
|
||||
static PyObject *
|
||||
sys__stats_dump_impl(PyObject *module);
|
||||
|
||||
static PyObject *
|
||||
sys__stats_dump(PyObject *module, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
return sys__stats_dump_impl(module);
|
||||
}
|
||||
|
||||
#endif /* defined(Py_STATS) */
|
||||
|
||||
#if defined(ANDROID_API_LEVEL)
|
||||
|
||||
PyDoc_STRVAR(sys_getandroidapilevel__doc__,
|
||||
|
@ -1011,7 +1099,23 @@ sys_getandroidapilevel(PyObject *module, PyObject *Py_UNUSED(ignored))
|
|||
#define SYS_GETTOTALREFCOUNT_METHODDEF
|
||||
#endif /* !defined(SYS_GETTOTALREFCOUNT_METHODDEF) */
|
||||
|
||||
#ifndef SYS__STATS_ON_METHODDEF
|
||||
#define SYS__STATS_ON_METHODDEF
|
||||
#endif /* !defined(SYS__STATS_ON_METHODDEF) */
|
||||
|
||||
#ifndef SYS__STATS_OFF_METHODDEF
|
||||
#define SYS__STATS_OFF_METHODDEF
|
||||
#endif /* !defined(SYS__STATS_OFF_METHODDEF) */
|
||||
|
||||
#ifndef SYS__STATS_CLEAR_METHODDEF
|
||||
#define SYS__STATS_CLEAR_METHODDEF
|
||||
#endif /* !defined(SYS__STATS_CLEAR_METHODDEF) */
|
||||
|
||||
#ifndef SYS__STATS_DUMP_METHODDEF
|
||||
#define SYS__STATS_DUMP_METHODDEF
|
||||
#endif /* !defined(SYS__STATS_DUMP_METHODDEF) */
|
||||
|
||||
#ifndef SYS_GETANDROIDAPILEVEL_METHODDEF
|
||||
#define SYS_GETANDROIDAPILEVEL_METHODDEF
|
||||
#endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */
|
||||
/*[clinic end generated code: output=98efd34fd9b9b6ab input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=41122dae1bb7158c input=a9049054013a1b77]*/
|
||||
|
|
|
@ -33,7 +33,8 @@ uint8_t _PyOpcode_Adaptive[256] = {
|
|||
|
||||
Py_ssize_t _Py_QuickenedCount = 0;
|
||||
#ifdef Py_STATS
|
||||
PyStats _py_stats = { 0 };
|
||||
PyStats _py_stats_struct = { 0 };
|
||||
PyStats *_py_stats = &_py_stats_struct;
|
||||
|
||||
#define ADD_STAT_TO_DICT(res, field) \
|
||||
do { \
|
||||
|
@ -93,7 +94,7 @@ add_stat_dict(
|
|||
int opcode,
|
||||
const char *name) {
|
||||
|
||||
SpecializationStats *stats = &_py_stats.opcode_stats[opcode].specialization;
|
||||
SpecializationStats *stats = &_py_stats_struct.opcode_stats[opcode].specialization;
|
||||
PyObject *d = stats_to_dict(stats);
|
||||
if (d == NULL) {
|
||||
return -1;
|
||||
|
@ -209,9 +210,18 @@ print_stats(FILE *out, PyStats *stats) {
|
|||
print_object_stats(out, &stats->object_stats);
|
||||
}
|
||||
|
||||
void
|
||||
_Py_StatsClear(void)
|
||||
{
|
||||
_py_stats_struct = (PyStats) { 0 };
|
||||
}
|
||||
|
||||
void
|
||||
_Py_PrintSpecializationStats(int to_file)
|
||||
{
|
||||
if (_py_stats == NULL) {
|
||||
return;
|
||||
}
|
||||
FILE *out = stderr;
|
||||
if (to_file) {
|
||||
/* Write to a file instead of stderr. */
|
||||
|
@ -242,7 +252,7 @@ _Py_PrintSpecializationStats(int to_file)
|
|||
else {
|
||||
fprintf(out, "Specialization stats:\n");
|
||||
}
|
||||
print_stats(out, &_py_stats);
|
||||
print_stats(out, _py_stats);
|
||||
if (out != stderr) {
|
||||
fclose(out);
|
||||
}
|
||||
|
@ -250,8 +260,12 @@ _Py_PrintSpecializationStats(int to_file)
|
|||
|
||||
#ifdef Py_STATS
|
||||
|
||||
#define SPECIALIZATION_FAIL(opcode, kind) _py_stats.opcode_stats[opcode].specialization.failure_kinds[kind]++
|
||||
|
||||
#define SPECIALIZATION_FAIL(opcode, kind) \
|
||||
do { \
|
||||
if (_py_stats) { \
|
||||
_py_stats->opcode_stats[opcode].specialization.failure_kinds[kind]++; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -1909,6 +1909,66 @@ sys_is_finalizing_impl(PyObject *module)
|
|||
return PyBool_FromLong(_Py_IsFinalizing());
|
||||
}
|
||||
|
||||
#ifdef Py_STATS
|
||||
/*[clinic input]
|
||||
sys._stats_on
|
||||
|
||||
Turns on stats gathering (stats gathering is on by default).
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
sys__stats_on_impl(PyObject *module)
|
||||
/*[clinic end generated code: output=aca53eafcbb4d9fe input=8ddc6df94e484f3a]*/
|
||||
{
|
||||
_py_stats = &_py_stats_struct;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
sys._stats_off
|
||||
|
||||
Turns off stats gathering (stats gathering is on by default).
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
sys__stats_off_impl(PyObject *module)
|
||||
/*[clinic end generated code: output=1534c1ee63812214 input=b3e50e71ecf29f66]*/
|
||||
{
|
||||
_py_stats = NULL;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
sys._stats_clear
|
||||
|
||||
Clears the stats.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
sys__stats_clear_impl(PyObject *module)
|
||||
/*[clinic end generated code: output=fb65a2525ee50604 input=3e03f2654f44da96]*/
|
||||
{
|
||||
_Py_StatsClear();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
sys._stats_dump
|
||||
|
||||
Dump stats to file, and clears the stats.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
sys__stats_dump_impl(PyObject *module)
|
||||
/*[clinic end generated code: output=79f796fb2b4ddf05 input=92346f16d64f6f95]*/
|
||||
{
|
||||
_Py_PrintSpecializationStats(1);
|
||||
_Py_StatsClear();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef ANDROID_API_LEVEL
|
||||
/*[clinic input]
|
||||
sys.getandroidapilevel
|
||||
|
@ -1978,6 +2038,12 @@ static PyMethodDef sys_methods[] = {
|
|||
SYS_GET_ASYNCGEN_HOOKS_METHODDEF
|
||||
SYS_GETANDROIDAPILEVEL_METHODDEF
|
||||
SYS_UNRAISABLEHOOK_METHODDEF
|
||||
#ifdef Py_STATS
|
||||
SYS__STATS_ON_METHODDEF
|
||||
SYS__STATS_OFF_METHODDEF
|
||||
SYS__STATS_CLEAR_METHODDEF
|
||||
SYS__STATS_DUMP_METHODDEF
|
||||
#endif
|
||||
{NULL, NULL} // sentinel
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue