diff --git a/source/blender/python/BPY_extern.hh b/source/blender/python/BPY_extern.hh index 159f8e47078..7140b1d5b76 100644 --- a/source/blender/python/BPY_extern.hh +++ b/source/blender/python/BPY_extern.hh @@ -56,6 +56,14 @@ void BPY_thread_restore(BPy_ThreadStatePtr tstate); } \ (void)0 +/** + * Print the Python backtrace of the current thread state. + * + * Should be safe to call from anywhere at any point, may not output anything if there is no valid + * python thread state available. + */ +void BPY_thread_backtrace_print(); + void BPY_text_free_code(Text *text); /** * Needed so the #Main pointer in `bpy.data` doesn't become out of date. diff --git a/source/blender/python/generic/bpy_threads.cc b/source/blender/python/generic/bpy_threads.cc index fb3e7e4ddde..6a932c3397a 100644 --- a/source/blender/python/generic/bpy_threads.cc +++ b/source/blender/python/generic/bpy_threads.cc @@ -32,3 +32,30 @@ void BPY_thread_restore(BPy_ThreadStatePtr tstate) PyEval_RestoreThread((PyThreadState *)tstate); } } + +void BPY_thread_backtrace_print() +{ + PyThreadState *tstate = PyGILState_GetThisThreadState(); + + if (tstate) { + PyFrameObject *frame = PyThreadState_GetFrame(tstate); + + printf(frame ? "Python stack trace:\n" : "No Python stack trace available.\n"); + + while (frame) { + PyCodeObject *frame_co = PyFrame_GetCode(frame); + int line = PyFrame_GetLineNumber(frame); + const char *filename = PyUnicode_AsUTF8(frame_co->co_filename); + const char *funcname = PyUnicode_AsUTF8(frame_co->co_name); + printf(" %s:%d %s\n", filename, line, funcname); + Py_DECREF(frame_co); + PyFrameObject *frame_back = PyFrame_GetBack(frame); + Py_DECREF(frame); + frame = frame_back; + } + printf("\n"); + } + else { + printf("No Python thread state available.\n"); + } +}