X-Git-Url: https://sigrok.org/gitweb/?p=libsigrokdecode.git;a=blobdiff_plain;f=exception.c;h=9f8ee4ceb17b028179446b786d53ce318b7fbfb8;hp=1f30c80d95a440b2a8ab7c62fb08e1d24bc463bd;hb=a27981c145cd9a3709673339dc455f3a0d5c3745;hpb=55c3c5f4b9d38b85fae2c39a8a6150b4c50b1bdb diff --git a/exception.c b/exception.c index 1f30c80..9f8ee4c 100644 --- a/exception.c +++ b/exception.c @@ -1,5 +1,5 @@ /* - * This file is part of the sigrok project. + * This file is part of the libsigrokdecode project. * * Copyright (C) 2012 Bert Vermeulen * @@ -17,74 +17,156 @@ * along with this program. If not, see . */ -#include "sigrokdecode.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */ -#include "sigrokdecode-internal.h" -#include "config.h" +#include +#include "libsigrokdecode-internal.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */ +#include "libsigrokdecode.h" #include #include -#include /* Python header not pulled in by default. */ -SRD_PRIV void catch_exception(const char *format, ...) +static char *py_stringify(PyObject *py_obj) { - PyObject *etype, *evalue, *etb, *py_str; - PyTracebackObject *py_tb; - GString *msg; - va_list args; - char *ename, *str, *tracestr; + PyObject *py_str, *py_bytes; + char *str = NULL; + + /* Note: Caller already ran PyGILState_Ensure(). */ + + if (!py_obj) + return NULL; - if (!PyErr_Occurred()) - /* Nothing is wrong. */ - return; + py_str = PyObject_Str(py_obj); + if (!py_str || !PyUnicode_Check(py_str)) + goto cleanup; - PyErr_Fetch(&etype, &evalue, &etb); - PyErr_NormalizeException(&etype, &evalue, &etb); + py_bytes = PyUnicode_AsUTF8String(py_str); + if (!py_bytes) + goto cleanup; - if (!(py_str = PyObject_Str(evalue))) { - /* Shouldn't happen. */ - srd_dbg("Failed to convert exception value to string."); - return; + str = g_strdup(PyBytes_AsString(py_bytes)); + Py_DECREF(py_bytes); + +cleanup: + Py_XDECREF(py_str); + if (!str) { + PyErr_Clear(); + srd_dbg("Failed to stringify object."); } - msg = g_string_sized_new(128); + return str; +} + +static char *py_get_string_attr(PyObject *py_obj, const char *attr) +{ + PyObject *py_str, *py_bytes; + char *str = NULL; + + /* Note: Caller already ran PyGILState_Ensure(). */ + + if (!py_obj) + return NULL; + + py_str = PyObject_GetAttrString(py_obj, attr); + if (!py_str || !PyUnicode_Check(py_str)) + goto cleanup; + + py_bytes = PyUnicode_AsUTF8String(py_str); + if (!py_bytes) + goto cleanup; + + str = g_strdup(PyBytes_AsString(py_bytes)); + Py_DECREF(py_bytes); + +cleanup: + Py_XDECREF(py_str); + if (!str) { + PyErr_Clear(); + srd_dbg("Failed to get object attribute %s.", attr); + } + + return str; +} + +/** @private */ +SRD_PRIV void srd_exception_catch(const char *format, ...) +{ + int i, ret; + va_list args; + PyObject *py_etype, *py_evalue, *py_etraceback; + PyObject *py_mod, *py_func, *py_tracefmt; + char *msg, *etype_name, *evalue_str, *outstr; + const char *etype_name_fallback; + PyGILState_STATE gstate; + GString *s; + + py_etype = py_evalue = py_etraceback = py_mod = py_func = NULL; + va_start(args, format); - g_string_vprintf(msg, format, args); + msg = g_strdup_vprintf(format, args); va_end(args); - /* Can be NULL. */ - if (evalue) - ename = (char *)Py_TYPE(evalue)->tp_name; + gstate = PyGILState_Ensure(); + + PyErr_Fetch(&py_etype, &py_evalue, &py_etraceback); + if (!py_etype) { + /* No current exception, so just print the message. */ + srd_err("%s.", msg); + goto cleanup; + } + PyErr_NormalizeException(&py_etype, &py_evalue, &py_etraceback); + + etype_name = py_get_string_attr(py_etype, "__name__"); + evalue_str = py_stringify(py_evalue); + etype_name_fallback = (etype_name) ? etype_name : "(unknown exception)"; + + if (evalue_str) + srd_err("%s: %s: %s", etype_name_fallback, msg, evalue_str); else - ename = "(unknown exception)"; - - /* Send the exception error message(s) to srd_err(). */ - py_str_as_str(py_str, &str); - g_string_append(msg, ename); - g_string_append(msg, ": "); - g_string_append(msg, str); - Py_DecRef(py_str); - srd_err(msg->str); - - /* Send a more precise error location to srd_dbg(), if we have it. */ - if (etb && etb != Py_None) { - tracestr = NULL; - py_tb = (PyTracebackObject *) etb; - py_str = PyUnicode_FromFormat("%U:%d in %U", - py_tb->tb_frame->f_code->co_filename, - py_tb->tb_frame->f_lineno, - py_tb->tb_frame->f_code->co_name); - py_str_as_str(py_str, &tracestr); - Py_DecRef(py_str); - g_string_printf(msg, "%s in %s: %s", ename, tracestr, str); - srd_dbg(msg->str); - g_free(tracestr); + srd_err("%s: %s.", etype_name_fallback, msg); + + g_free(evalue_str); + g_free(etype_name); + + /* If there is no traceback object, we are done. */ + if (!py_etraceback) + goto cleanup; + + py_mod = py_import_by_name("traceback"); + if (!py_mod) + goto cleanup; + + py_func = PyObject_GetAttrString(py_mod, "format_exception"); + if (!py_func || !PyCallable_Check(py_func)) + goto cleanup; + + /* Call into Python to format the stack trace. */ + py_tracefmt = PyObject_CallFunctionObjArgs(py_func, + py_etype, py_evalue, py_etraceback, NULL); + if (!py_tracefmt || !PyList_Check(py_tracefmt)) + goto cleanup; + + s = g_string_sized_new(128); + for (i = 0; i < PyList_Size(py_tracefmt); i++) { + ret = py_listitem_as_str(py_tracefmt, i, &outstr); + if (ret == 0) { + s = g_string_append(s, outstr); + g_free(outstr); + } } - g_free(str); - g_string_free(msg, TRUE); + srd_err("%s", s->str); + g_string_free(s, TRUE); + + Py_DECREF(py_tracefmt); - Py_XDECREF(etype); - Py_XDECREF(evalue); - Py_XDECREF(etb); +cleanup: + Py_XDECREF(py_func); + Py_XDECREF(py_mod); + Py_XDECREF(py_etraceback); + Py_XDECREF(py_evalue); + Py_XDECREF(py_etype); /* Just in case. */ PyErr_Clear(); + + PyGILState_Release(gstate); + + g_free(msg); }