X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=src%2Fpy-common%2Fpy-common.c;h=b1ca34ae455014f87e85116c33fd8b2ab97e4a15;hb=b53cd76821d15e3d80780f5d527aa4074ba71256;hp=4fea4d27b5475ecfa5c6c28208ef8de3a0d79795;hpb=8e01f2d9837d5ba9a85c678121dcc8adf8dbe72e;p=babeltrace.git diff --git a/src/py-common/py-common.c b/src/py-common/py-common.c index 4fea4d27..b1ca34ae 100644 --- a/src/py-common/py-common.c +++ b/src/py-common/py-common.c @@ -26,30 +26,147 @@ #define BT_LOG_TAG "PY-COMMON" #include "logging/log.h" +#include + #include #include "common/assert.h" #include "py-common.h" +/* + * Concatenate strings in list `py_str_list`, returning the result as a + * GString. Remove the trailing \n, if the last string ends with a \n. + */ +static +GString *py_str_list_to_gstring(PyObject *py_str_list, int log_level) +{ + Py_ssize_t i; + GString *gstr; + + gstr = g_string_new(NULL); + if (!gstr) { + BT_LOGE("Failed to allocate a GString."); + goto end; + } + + for (i = 0; i < PyList_Size(py_str_list); i++) { + PyObject *py_str; + const char *str; + + py_str = PyList_GetItem(py_str_list, i); + BT_ASSERT(py_str); + BT_ASSERT(PyUnicode_CheckExact(py_str)); + + /* `str` is owned by `py_str`, not by us */ + str = PyUnicode_AsUTF8(py_str); + if (!str) { + if (BT_LOG_ON_ERROR) { + BT_LOGE_STR("PyUnicode_AsUTF8() failed:"); + PyErr_Print(); + } + + goto error; + } + + /* `str` has a trailing newline */ + g_string_append(gstr, str); + } + + if (gstr->len > 0) { + /* Remove trailing newline if any */ + if (gstr->str[gstr->len - 1] == '\n') { + g_string_truncate(gstr, gstr->len - 1); + } + } + + goto end; + +error: + g_string_free(gstr, TRUE); + gstr = NULL; + +end: + return gstr; +} + BT_HIDDEN -GString *bt_py_common_format_exception(int log_level) +GString *bt_py_common_format_tb(PyObject *py_exc_tb, int log_level) { - PyObject *type = NULL; - PyObject *value = NULL; - PyObject *traceback = NULL; PyObject *traceback_module = NULL; - PyObject *format_exception_func = NULL; + PyObject *format_tb_func = NULL; PyObject *exc_str_list = NULL; GString *msg_buf = NULL; - const char *format_exc_func_name; - Py_ssize_t i; - BT_ASSERT(PyErr_Occurred()); - PyErr_Fetch(&type, &value, &traceback); - BT_ASSERT(type); + BT_ASSERT(py_exc_tb); + + /* + * Import the standard `traceback` module which contains the + * functions to format a traceback. + */ + traceback_module = PyImport_ImportModule("traceback"); + if (!traceback_module) { + BT_LOGE_STR("Failed to import `traceback` module."); + goto error; + } + + format_tb_func = PyObject_GetAttrString(traceback_module, + "format_tb"); + if (!format_tb_func) { + BT_LOGE("Cannot find `format_tb` attribute in `traceback` module."); + goto error; + } + + if (!PyCallable_Check(format_tb_func)) { + BT_LOGE("`traceback.format_tb` attribute is not callable."); + goto error; + } + + /* + * If we are calling format_exception_only, `py_exc_tb` is NULL, so the + * effective argument list is of length 2. + */ + exc_str_list = PyObject_CallFunctionObjArgs( + format_tb_func, py_exc_tb, NULL); + if (!exc_str_list) { + if (BT_LOG_ON_ERROR) { + BT_LOGE("Failed to call `traceback.format_tb` function:"); + PyErr_Print(); + } + + goto error; + } + + msg_buf = py_str_list_to_gstring(exc_str_list, log_level); + if (!msg_buf) { + goto error; + } + + goto end; + +error: + if (msg_buf) { + g_string_free(msg_buf, TRUE); + msg_buf = NULL; + } - /* Make sure `value` is what we expected: an instance of `type` */ - PyErr_NormalizeException(&type, &value, &traceback); +end: + Py_XDECREF(traceback_module); + Py_XDECREF(format_tb_func); + Py_XDECREF(exc_str_list); + + return msg_buf; +} + +BT_HIDDEN +GString *bt_py_common_format_exception(PyObject *py_exc_type, + PyObject *py_exc_value, PyObject *py_exc_tb, + int log_level, bool chain) +{ + PyObject *traceback_module = NULL; + PyObject *format_exception_func = NULL; + PyObject *exc_str_list = NULL; + GString *msg_buf = NULL; + const char *format_exc_func_name; /* * Import the standard `traceback` module which contains the @@ -62,12 +179,12 @@ GString *bt_py_common_format_exception(int log_level) } /* - * `traceback` can be `NULL`, when we fail to call a Python + * `py_exc_tb` can be `NULL`, when we fail to call a Python * function from native code (there is no Python stack at that * point). For example, a function which takes 5 positional * arguments, but 8 were given. */ - format_exc_func_name = traceback ? "format_exception" : + format_exc_func_name = py_exc_tb ? "format_exception" : "format_exception_only"; format_exception_func = PyObject_GetAttrString(traceback_module, format_exc_func_name); @@ -83,8 +200,13 @@ GString *bt_py_common_format_exception(int log_level) goto error; } + /* + * If we are calling format_exception_only, `py_exc_tb` is NULL, so the + * effective argument list is of length 2. + */ exc_str_list = PyObject_CallFunctionObjArgs(format_exception_func, - type, value, traceback, NULL); + py_exc_type, py_exc_value, py_exc_tb, Py_None /* limit */, + chain ? Py_True : Py_False /* chain */, NULL); if (!exc_str_list) { if (BT_LOG_ON_ERROR) { BT_LOGE("Failed to call `traceback.%s` function:", @@ -95,35 +217,9 @@ GString *bt_py_common_format_exception(int log_level) goto error; } - msg_buf = g_string_new(NULL); - - for (i = 0; i < PyList_Size(exc_str_list); i++) { - PyObject *exc_str; - const char *str; - - exc_str = PyList_GetItem(exc_str_list, i); - BT_ASSERT(exc_str); - - /* `str` is owned by `exc_str`, not by us */ - str = PyUnicode_AsUTF8(exc_str); - if (!str) { - if (BT_LOG_ON_ERROR) { - BT_LOGE_STR("PyUnicode_AsUTF8() failed:"); - PyErr_Print(); - } - - goto error; - } - - /* `str` has a trailing newline */ - g_string_append(msg_buf, str); - } - - if (msg_buf->len > 0) { - /* Remove trailing newline if any */ - if (msg_buf->str[msg_buf->len - 1] == '\n') { - g_string_truncate(msg_buf, msg_buf->len - 1); - } + msg_buf = py_str_list_to_gstring(exc_str_list, log_level); + if (!msg_buf) { + goto error; } goto end; @@ -139,6 +235,30 @@ end: Py_XDECREF(format_exception_func); Py_XDECREF(traceback_module); + return msg_buf; +} + +BT_HIDDEN +GString *bt_py_common_format_current_exception(int log_level) +{ + GString *result; + PyObject *py_exc_type = NULL; + PyObject *py_exc_value = NULL; + PyObject *py_exc_tb = NULL; + + BT_ASSERT(PyErr_Occurred()); + PyErr_Fetch(&py_exc_type, &py_exc_value, &py_exc_tb); + BT_ASSERT(py_exc_type); + + /* + * Make sure `py_exc_value` is what we expected: an instance of + * `py_exc_type`. + */ + PyErr_NormalizeException(&py_exc_type, &py_exc_value, &py_exc_tb); + + result = bt_py_common_format_exception(py_exc_type, py_exc_value, + py_exc_tb, log_level, true); + /* * We can safely call PyErr_Restore() because we always call * PyErr_Fetch(), and having an error indicator is a function's @@ -146,7 +266,7 @@ end: * * PyErr_Restore() steals our references. */ - PyErr_Restore(type, value, traceback); + PyErr_Restore(py_exc_type, py_exc_value, py_exc_tb); - return msg_buf; + return result; }