summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Include/internal/pycore_code.h16
-rw-r--r--Include/pystats.h13
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2022-06-15-11-16-13.gh-issue-93841.06zqX3.rst3
-rw-r--r--Python/ceval.c6
-rw-r--r--Python/clinic/sysmodule.c.h106
-rw-r--r--Python/specialize.c24
-rw-r--r--Python/sysmodule.c66
7 files changed, 212 insertions, 22 deletions
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 71dd3b3..c975f1c 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -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);
diff --git a/Include/pystats.h b/Include/pystats.h
index bea471b..87e92aa 100644
--- a/Include/pystats.h
+++ b/Include/pystats.h
@@ -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
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-06-15-11-16-13.gh-issue-93841.06zqX3.rst b/Misc/NEWS.d/next/Core and Builtins/2022-06-15-11-16-13.gh-issue-93841.06zqX3.rst
new file mode 100644
index 0000000..179d380
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2022-06-15-11-16-13.gh-issue-93841.06zqX3.rst
@@ -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.
diff --git a/Python/ceval.c b/Python/ceval.c
index bdae333..47dd100 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -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);
diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h
index 6ee3bb2..76b4cc5 100644
--- a/Python/clinic/sysmodule.c.h
+++ b/Python/clinic/sysmodule.c.h
@@ -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]*/
diff --git a/Python/specialize.c b/Python/specialize.c
index c9cf35f..948fb32 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -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;
@@ -210,8 +211,17 @@ print_stats(FILE *out, PyStats *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
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index 8a9a584..444042f 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -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
};