From 5828351d3318405c662ef36b9c5253625cf90f40 Mon Sep 17 00:00:00 2001 From: Nicholas Chaimov Date: Mon, 16 Oct 2023 15:11:26 -0700 Subject: [PATCH] Fix Python profiler for Python 3.11 In Python 3.11, "The PyFrameObject structure members have been removed from the public C API" (https://docs.python.org/3/whatsnew/3.11.html), which breaks the use of frame->f_code in ctau_impl.c. Replace with call to PyFrame_GetCode(). Add implementation of PyFrame_GetCode in terms of older frame->fcode method for Python <=3.8. Tested with Python 3.11, 3.10, 3.8, 2.7. Former-commit-id: 3b1177de8537708f50581c7c6dbbc3ce38b2ab95 --- src/Profile/ctau_impl.c | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/Profile/ctau_impl.c b/src/Profile/ctau_impl.c index 56b227633..3747404b9 100644 --- a/src/Profile/ctau_impl.c +++ b/src/Profile/ctau_impl.c @@ -22,6 +22,19 @@ #define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, #endif +// Python 3.11 compatibility +// This is from https://docs.python.org/3/whatsnew/3.11.html +// to provide backwards compatibility with Python <3.8 +// (Python 3.8 and later provides PyFrame_GetCode natively) +// Python 3.11 and later remove the f_code field of frame +// requiring accessor method instead +#if PY_VERSION_HEX < 0x030900B1 +static inline PyCodeObject* PyFrame_GetCode(PyFrameObject *frame) +{ + Py_INCREF(frame->f_code); + return frame->f_code; +} +#endif /************************ rotatingtree.h *************************/ @@ -353,7 +366,7 @@ normalizeUserObj(PyObject *obj) if (fn->m_self == NULL) { /* built-in function: look up the module name */ PyObject *mod = fn->m_module; - char *modname; + const char * modname; if (mod && PyString_Check(mod)) { modname = PyString_AS_STRING(mod); } @@ -401,7 +414,7 @@ normalizeUserObj(PyObject *obj) static ProfilerEntry *newProfilerEntry(ProfilerObject *pObj, void *key, PyObject *userObj, PyFrameObject *frame, char *cname) { char routine[4096]; - char *co_name, *co_filename; + const char * co_name, * co_filename; int co_firstlineno; ProfilerEntry *self; @@ -428,12 +441,13 @@ static ProfilerEntry *newProfilerEntry(ProfilerObject *pObj, void *key, PyObject self->calls = EMPTY_ROTATING_TREE; if (frame != NULL) { - co_name = PyString_AsString(frame->f_code->co_name); - co_filename = PyString_AsString(frame->f_code->co_filename); + PyCodeObject * codeObj = PyFrame_GetCode(frame); + co_name = PyString_AsString(codeObj->co_name); + co_filename = PyString_AsString(codeObj->co_filename); while (strchr(co_filename,'/')) { co_filename = strchr(co_filename,'/')+1; } - co_firstlineno = frame->f_code->co_firstlineno; + co_firstlineno = codeObj->co_firstlineno; sprintf (routine,"%s [{%s}{%d}]", co_name, co_filename, co_firstlineno); if (strcmp(co_filename,"") != 0) { // suppress "? " TAU_PROFILER_CREATE(handle, routine, "", TAU_PYTHON); @@ -620,15 +634,21 @@ static int profiler_callback(PyObject *self, PyFrameObject *frame, int what, PyO /* the 'frame' of a called function is about to start its execution */ case PyTrace_CALL: /* printf ("Py Enter: %s\n", routine); */ - ptrace_enter_call(self, (void *)frame->f_code, - (PyObject *)frame->f_code, frame, NULL); + { + PyCodeObject * codeObj = PyFrame_GetCode(frame); + ptrace_enter_call(self, (void *)codeObj, + (PyObject *)codeObj, frame, NULL); + } break; /* the 'frame' of a called function is about to finish (either normally or with an exception) */ case PyTrace_RETURN: /* printf ("Py Exit: %s\n", routine); */ - ptrace_leave_call(self, (void *)frame->f_code); + { + PyCodeObject * codeObj = PyFrame_GetCode(frame); + ptrace_leave_call(self, (void *)codeObj); + } break; /* case PyTrace_EXCEPTION: @@ -828,6 +848,8 @@ static PyObject* profiler_disable(ProfilerObject *self, PyObject* noarg) { return Py_None; } +extern void Tau_profile_exit_all_threads(void); + PyDoc_STRVAR(exitAllThreads_doc, "\ exitAllThreads()\n\ \n\