From 703647732359200c54f1d2e695cc3a06b9a96c9a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 29 Apr 2020 03:28:46 +0200 Subject: [PATCH] bpo-40421: Add PyFrame_GetBack() function (GH-19765) New PyFrame_GetBack() function: get the frame next outer frame. Replace frame->f_back with PyFrame_GetBack(frame) in most code but frameobject.c, ceval.c and genobject.c. --- Doc/c-api/reflection.rst | 11 +++++++++++ Doc/whatsnew/3.9.rst | 1 + Include/cpython/frameobject.h | 2 ++ .../2020-04-28-19-29-36.bpo-40421.3uIIaB.rst | 1 + Modules/_tracemalloc.c | 12 ++++++++---- Objects/frameobject.c | 10 ++++++++++ Python/_warnings.c | 18 ++++++++++++------ Python/sysmodule.c | 10 ++++++---- Python/traceback.c | 19 ++++++++++++++----- 9 files changed, 65 insertions(+), 19 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2020-04-28-19-29-36.bpo-40421.3uIIaB.rst diff --git a/Doc/c-api/reflection.rst b/Doc/c-api/reflection.rst index 21d98786091..9207d86012c 100644 --- a/Doc/c-api/reflection.rst +++ b/Doc/c-api/reflection.rst @@ -31,6 +31,17 @@ Reflection See also :c:func:`PyThreadState_GetFrame`. +.. c:function:: int PyFrame_GetBack(PyFrameObject *frame) + + Get the *frame* next outer frame. + + Return a strong reference, or ``NULL`` if *frame* has no outer frame. + + *frame* must not be ``NULL``. + + .. versionadded:: 3.9 + + .. c:function:: int PyFrame_GetCode(PyFrameObject *frame) Get the *frame* code. diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index e26bd473e61..0edb11419c4 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -538,6 +538,7 @@ Build and C API Changes ======================= * New :c:func:`PyFrame_GetCode` function: get a frame code. + New :c:func:`PyFrame_GetBack` function: get the frame next outer frame. (Contributed by Victor Stinner in :issue:`40421`.) * Add :c:func:`PyFrame_GetLineNumber` to the limited C API. diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index e32efac5947..36a51baae87 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -77,6 +77,8 @@ PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *); PyAPI_FUNC(void) _PyFrame_DebugMallocStats(FILE *out); +PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame); + #ifdef __cplusplus } #endif diff --git a/Misc/NEWS.d/next/C API/2020-04-28-19-29-36.bpo-40421.3uIIaB.rst b/Misc/NEWS.d/next/C API/2020-04-28-19-29-36.bpo-40421.3uIIaB.rst new file mode 100644 index 00000000000..aadfb339b17 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-04-28-19-29-36.bpo-40421.3uIIaB.rst @@ -0,0 +1 @@ +New :c:func:`PyFrame_GetBack` function: get the frame next outer frame. diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c index 6f28f7f5757..ea7e0127366 100644 --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -3,7 +3,7 @@ #include "pycore_pymem.h" // _Py_tracemalloc_config #include "pycore_traceback.h" #include "hashtable.h" -#include "frameobject.h" +#include "frameobject.h" // PyFrame_GetBack() #include "clinic/_tracemalloc.c.h" /*[clinic input] @@ -434,15 +434,19 @@ traceback_get_frames(traceback_t *traceback) } PyFrameObject *pyframe = PyThreadState_GetFrame(tstate); - Py_XDECREF(pyframe); // use a borrowed reference - for (; pyframe != NULL; pyframe = pyframe->f_back) { + for (; pyframe != NULL;) { if (traceback->nframe < _Py_tracemalloc_config.max_nframe) { tracemalloc_get_frame(pyframe, &traceback->frames[traceback->nframe]); assert(traceback->frames[traceback->nframe].filename != NULL); traceback->nframe++; } - if (traceback->total_nframe < UINT16_MAX) + if (traceback->total_nframe < UINT16_MAX) { traceback->total_nframe++; + } + + PyFrameObject *back = PyFrame_GetBack(pyframe); + Py_DECREF(pyframe); + pyframe = back; } } diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 6d288b5b059..451c895a77c 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1237,3 +1237,13 @@ PyFrame_GetCode(PyFrameObject *frame) Py_INCREF(code); return code; } + + +PyFrameObject* +PyFrame_GetBack(PyFrameObject *frame) +{ + assert(frame != NULL); + PyFrameObject *back = frame->f_back; + Py_XINCREF(back); + return back; +} diff --git a/Python/_warnings.c b/Python/_warnings.c index 7c15ce0ef89..4d65bb30c8e 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -3,7 +3,7 @@ #include "pycore_interp.h" // PyInterpreterState.warnings #include "pycore_pyerrors.h" #include "pycore_pystate.h" // _PyThreadState_GET() -#include "frameobject.h" +#include "frameobject.h" // PyFrame_GetBack() #include "clinic/_warnings.c.h" #define MODULE_NAME "_warnings" @@ -815,7 +815,9 @@ static PyFrameObject * next_external_frame(PyFrameObject *frame) { do { - frame = frame->f_back; + PyFrameObject *back = PyFrame_GetBack(frame); + Py_DECREF(frame); + frame = back; } while (frame != NULL && is_internal_frame(frame)); return frame; @@ -831,12 +833,15 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno, PyObject *globals; /* Setup globals, filename and lineno. */ - PyFrameObject *f = _PyThreadState_GET()->frame; + PyThreadState *tstate = _PyThreadState_GET(); + PyFrameObject *f = PyThreadState_GetFrame(tstate); // Stack level comparisons to Python code is off by one as there is no // warnings-related stack level to avoid. if (stack_level <= 0 || is_internal_frame(f)) { while (--stack_level > 0 && f != NULL) { - f = f->f_back; + PyFrameObject *back = PyFrame_GetBack(f); + Py_DECREF(f); + f = back; } } else { @@ -857,6 +862,7 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno, Py_DECREF(code); Py_INCREF(*filename); *lineno = PyFrame_GetLineNumber(f); + Py_DECREF(f); } *module = NULL; @@ -868,7 +874,7 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno, if (*registry == NULL) { int rc; - if (PyErr_Occurred()) { + if (_PyErr_Occurred(tstate)) { goto handle_error; } *registry = PyDict_New(); @@ -887,7 +893,7 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno, if (*module == Py_None || (*module != NULL && PyUnicode_Check(*module))) { Py_INCREF(*module); } - else if (PyErr_Occurred()) { + else if (_PyErr_Occurred(tstate)) { goto handle_error; } else { diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 92ea5e7d637..914beb7e127 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -16,7 +16,7 @@ Data members: #include "Python.h" #include "code.h" -#include "frameobject.h" +#include "frameobject.h" // PyFrame_GetBack() #include "pycore_ceval.h" // _Py_RecursionLimitLowerWaterMark() #include "pycore_initconfig.h" #include "pycore_object.h" @@ -1787,14 +1787,17 @@ sys__getframe_impl(PyObject *module, int depth) /*[clinic end generated code: output=d438776c04d59804 input=c1be8a6464b11ee5]*/ { PyThreadState *tstate = _PyThreadState_GET(); - PyFrameObject *f = tstate->frame; + PyFrameObject *f = PyThreadState_GetFrame(tstate); if (_PySys_Audit(tstate, "sys._getframe", "O", f) < 0) { + Py_DECREF(f); return NULL; } while (depth > 0 && f != NULL) { - f = f->f_back; + PyFrameObject *back = PyFrame_GetBack(f); + Py_DECREF(f); + f = back; --depth; } if (f == NULL) { @@ -1802,7 +1805,6 @@ sys__getframe_impl(PyObject *module, int depth) "call stack is not deep enough"); return NULL; } - Py_INCREF(f); return (PyObject*)f; } diff --git a/Python/traceback.c b/Python/traceback.c index 438a2c4fce7..99b63af11f8 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -4,7 +4,7 @@ #include "Python.h" #include "code.h" -#include "frameobject.h" +#include "frameobject.h" // PyFrame_GetBack() #include "structmember.h" // PyMemberDef #include "osdefs.h" // SEP #ifdef HAVE_FCNTL_H @@ -798,22 +798,31 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header) PUTS(fd, "Stack (most recent call first):\n"); } - frame = tstate->frame; + frame = PyThreadState_GetFrame(tstate); if (frame == NULL) { PUTS(fd, "\n"); return; } depth = 0; - while (frame != NULL) { + while (1) { if (MAX_FRAME_DEPTH <= depth) { + Py_DECREF(frame); PUTS(fd, " ...\n"); break; } - if (!PyFrame_Check(frame)) + if (!PyFrame_Check(frame)) { + Py_DECREF(frame); break; + } dump_frame(fd, frame); - frame = frame->f_back; + PyFrameObject *back = PyFrame_GetBack(frame); + Py_DECREF(frame); + + if (back == NULL) { + break; + } + frame = back; depth++; } }