/*
- * This file is part of the sigrok project.
+ * This file is part of the libsigrokdecode project.
*
* Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
*
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "sigrokdecode.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */
-#include "sigrokdecode-internal.h"
-#include "config.h"
+#include <config.h>
+#include "libsigrokdecode-internal.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */
+#include "libsigrokdecode.h"
#include <stdarg.h>
#include <glib.h>
-#include <frameobject.h> /* Python header not pulled in by default. */
-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);
}