From 70709cdd1b033eabc0c1f3ea36f1a9eb61504bf9 Mon Sep 17 00:00:00 2001 From: Dan Yeaw Date: Thu, 26 Dec 2019 15:22:54 -0500 Subject: [PATCH] Bootloader: Add showing traceback in debug windowed mode. When running in Windows in windowed mode, the debug bootloader will now show the Python traceback information. This help debuging errors during pyiboot01_bootstrap. This will allow debugging errors like in Issue #4213. For showing the source lines, the application needs to be frozen with ``--noarchive``. Signed-off-by: Dan Yeaw --- bootloader/src/pyi_launch.c | 52 ++++++++++++++++++++++++++++++++++--- bootloader/src/pyi_python.c | 11 ++++++++ bootloader/src/pyi_python.h | 7 +++++ news/4213.bootloader.rst | 1 + news/4592.bootloader.rst | 1 + 5 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 news/4213.bootloader.rst create mode 100644 news/4592.bootloader.rst diff --git a/bootloader/src/pyi_launch.c b/bootloader/src/pyi_launch.c index 15672fbb06..be3976f216 100644 --- a/bootloader/src/pyi_launch.c +++ b/bootloader/src/pyi_launch.c @@ -364,13 +364,18 @@ int pyi_launch_run_scripts(ARCHIVE_STATUS *status) { unsigned char *data; + const char *pvalue_cchar, *tb_cchar; char buf[PATH_MAX]; + char *char_pvalue, *char_tb, *module_name; size_t namelen; TOC * ptoc = status->tocbuff; PyObject *__main__; PyObject *__file__; PyObject *main_dict; PyObject *code, *retval; + PyObject *ptype, *pvalue, *pvalue_str; + PyObject *ptraceback, *tb, *tb_str; + PyObject *module, *func; __main__ = PI_PyImport_AddModule("__main__"); @@ -417,14 +422,53 @@ pyi_launch_run_scripts(ARCHIVE_STATUS *status) /* Run it */ retval = PI_PyEval_EvalCode(code, main_dict, main_dict); - /* If retval is NULL, an error occured. Otherwise, it is a Python object. + /* If retval is NULL, an error occurred. Otherwise, it is a Python object. * (Since we evaluate module-level code, which is not allowed to return an * object, the Python object returned is always None.) */ if (!retval) { - PI_PyErr_Print(); - /* If the error was SystemExit, PyErr_Print calls exit() without - * returning. So don't print "Failed to execute" on SystemExit. */ FATALERROR("Failed to execute script %s\n", ptoc->name); + + #if defined(WINDOWED) && defined(LAUNCH_DEBUG) + /* If running in windowed mode, sys.stderr will be None + * resp. NullWriter (see pyiboot01_bootstrap.py), thus + * PyErr_Print() below will not show any traceback. With + * debug, print the traceback to a message box. */ + + /* First get the value of the error */ + PI_PyErr_Fetch(&ptype, &pvalue, &ptraceback); + pvalue_str = PI_PyObject_Str(pvalue); + pvalue_cchar = PI_PyUnicode_AsUTF8(pvalue_str); + FATALERROR("Error: %s\n", pvalue_cchar); + Py_DECREF(pvalue_str); + + /* Attempt to get a full traceback, source lines will only + * be available with --noarchive option */ + module_name = "traceback"; + module = PI_PyImport_ImportModule(module_name); + + if (module != NULL) { + func = PI_PyObject_GetAttrString(module, "format_exception"); + if (func) { + tb = PI_PyObject_CallFunctionObjArgs(func, ptype, pvalue, ptraceback, NULL); + tb_str = PI_PyObject_Str(tb); + tb_cchar = PI_PyUnicode_AsUTF8(tb_str); + FATALERROR("Traceback: %s\n", tb_cchar); + Py_DECREF(tb); + Py_DECREF(tb_str); + } + Py_DECREF(func); + } + Py_DECREF(ptype); + Py_DECREF(pvalue); + Py_DECREF(ptraceback); + Py_DECREF(module); + + #else /* if defined(WINDOWED) and defined(LAUNCH_DEBUG) */ + + PI_PyErr_Print(); + + #endif /* if defined(WINDOWED) and defined(LAUNCH_DEBUG) */ + return -1; } free(data); diff --git a/bootloader/src/pyi_python.c b/bootloader/src/pyi_python.c index 165d3609fe..ec4e904d5c 100644 --- a/bootloader/src/pyi_python.c +++ b/bootloader/src/pyi_python.c @@ -57,6 +57,7 @@ DECLPROC(PyDict_GetItemString); DECLPROC(PyErr_Clear); DECLPROC(PyErr_Occurred); DECLPROC(PyErr_Print); +DECLPROC(PyErr_Fetch); DECLPROC(PyImport_AddModule); DECLPROC(PyImport_ExecCodeModule); @@ -66,7 +67,10 @@ DECLPROC(PyList_New); DECLPROC(PyLong_AsLong); DECLPROC(PyModule_GetDict); DECLPROC(PyObject_CallFunction); +DECLPROC(PyObject_CallFunctionObjArgs); DECLPROC(PyObject_SetAttrString); +DECLPROC(PyObject_GetAttrString); +DECLPROC(PyObject_Str); DECLPROC(PyRun_SimpleString); DECLPROC(PySys_AddWarnOption); DECLPROC(PySys_SetArgvEx); @@ -79,6 +83,7 @@ DECLPROC(Py_DecodeLocale); DECLPROC(PyUnicode_FromFormat); DECLPROC(PyUnicode_DecodeFSDefault); DECLPROC(PyUnicode_Decode); +DECLPROC(PyUnicode_AsUTF8); DECLPROC(PyEval_EvalCode); DECLPROC(PyMarshal_ReadObjectFromString); @@ -116,6 +121,7 @@ pyi_python_map_names(HMODULE dll, int pyvers) GETPROC(dll, PyErr_Clear); GETPROC(dll, PyErr_Occurred); GETPROC(dll, PyErr_Print); + GETPROC(dll, PyErr_Fetch); GETPROC(dll, PyImport_AddModule); GETPROC(dll, PyImport_ExecCodeModule); GETPROC(dll, PyImport_ImportModule); @@ -124,7 +130,11 @@ pyi_python_map_names(HMODULE dll, int pyvers) GETPROC(dll, PyLong_AsLong); GETPROC(dll, PyModule_GetDict); GETPROC(dll, PyObject_CallFunction); + GETPROC(dll, PyObject_CallFunctionObjArgs); GETPROC(dll, PyObject_SetAttrString); + GETPROC(dll, PyObject_GetAttrString); + GETPROC(dll, PyObject_Str); + GETPROC(dll, PyRun_SimpleString); GETPROC(dll, PySys_AddWarnOption); @@ -142,6 +152,7 @@ pyi_python_map_names(HMODULE dll, int pyvers) GETPROC(dll, PyUnicode_FromFormat); GETPROC(dll, PyUnicode_Decode); GETPROC(dll, PyUnicode_DecodeFSDefault); + GETPROC(dll, PyUnicode_AsUTF8); VS("LOADER: Loaded functions from Python library.\n"); diff --git a/bootloader/src/pyi_python.h b/bootloader/src/pyi_python.h index 3d71184e9d..30214eb1a8 100644 --- a/bootloader/src/pyi_python.h +++ b/bootloader/src/pyi_python.h @@ -123,6 +123,7 @@ EXTDECLPROC(PyObject *, Py_BuildValue, (char *, ...)); /* Create a Unicode object from the char buffer. The bytes will be interpreted as being UTF-8 encoded. */ EXTDECLPROC(PyObject *, PyUnicode_FromString, (const char *)); EXTDECLPROC(PyObject *, PyObject_CallFunction, (PyObject *, char *, ...)); +EXTDECLPROC(PyObject *, PyObject_CallFunctionObjArgs, (PyObject *, ...)); EXTDECLPROC(PyObject *, PyModule_GetDict, (PyObject *)); EXTDECLPROC(PyObject *, PyDict_GetItemString, (PyObject *, char *)); EXTDECLPROC(void, PyErr_Clear, (void) ); @@ -151,6 +152,12 @@ EXTDECLPROC(PyObject *, PyUnicode_Decode, EXTDECLPROC(PyObject *, PyEval_EvalCode, (PyObject *, PyObject *, PyObject *)); EXTDECLPROC(PyObject *, PyMarshal_ReadObjectFromString, (const char *, size_t)); /* Py_ssize_t */ +/* Used to get traceback information while launching run scripts */ +EXTDECLPROC(void, PyErr_Fetch, (PyObject **, PyObject **, PyObject **)); +EXTDECLPROC(PyObject *, PyObject_Str, (PyObject *)); +EXTDECLPROC(PyObject *, PyObject_GetAttrString, (PyObject *, const char *)); +EXTDECLPROC(const char *, PyUnicode_AsUTF8, (PyObject *)); + /* * Macros for reference counting through exported functions * (that is: without binding to the binary structure of a PyObject. diff --git a/news/4213.bootloader.rst b/news/4213.bootloader.rst new file mode 100644 index 0000000000..acbfdc506c --- /dev/null +++ b/news/4213.bootloader.rst @@ -0,0 +1 @@ +In debug and windowed mode, print traceback to help debug pyiboot01_bootstrap errors. diff --git a/news/4592.bootloader.rst b/news/4592.bootloader.rst new file mode 100644 index 0000000000..acbfdc506c --- /dev/null +++ b/news/4592.bootloader.rst @@ -0,0 +1 @@ +In debug and windowed mode, print traceback to help debug pyiboot01_bootstrap errors.