summaryrefslogtreecommitdiff
path: root/exception.c
diff options
context:
space:
mode:
Diffstat (limited to 'exception.c')
-rw-r--r--exception.c167
1 files changed, 119 insertions, 48 deletions
diff --git a/exception.c b/exception.c
index 8810304..a2b2683 100644
--- a/exception.c
+++ b/exception.c
@@ -22,69 +22,140 @@
#include "libsigrokdecode.h"
#include <stdarg.h>
#include <glib.h>
-#include <frameobject.h> /* Python header not pulled in by default. */
+
+static char *py_stringify(PyObject *py_obj)
+{
+ PyObject *py_str, *py_bytes;
+ char *str = NULL;
+
+ if (!py_obj)
+ return NULL;
+
+ py_str = PyObject_Str(py_obj);
+ 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 stringify object.");
+ }
+ return str;
+}
+
+static char *py_get_string_attr(PyObject *py_obj, const char *attr)
+{
+ PyObject *py_str, *py_bytes;
+ char *str = NULL;
+
+ 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, ...)
{
- PyObject *etype, *evalue, *etb, *py_str;
- PyTracebackObject *py_tb;
- GString *msg;
va_list args;
- char *ename, *str, *tracestr;
+ PyObject *py_etype, *py_evalue, *py_etraceback;
+ PyObject *py_modname, *py_mod, *py_func, *py_tracefmt;
+ char *msg, *etype_name, *evalue_str, *tracefmt_str;
+ const char *etype_name_fallback;
- if (!PyErr_Occurred())
- /* Nothing is wrong. */
- return;
+ py_etype = py_evalue = py_etraceback = py_mod = py_func = NULL;
- PyErr_Fetch(&etype, &evalue, &etb);
- PyErr_NormalizeException(&etype, &evalue, &etb);
+ va_start(args, format);
+ msg = g_strdup_vprintf(format, args);
+ va_end(args);
- if (!(py_str = PyObject_Str(evalue))) {
- /* Shouldn't happen. */
- srd_dbg("Failed to convert exception value to string.");
- return;
+ 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);
- /* Send the exception error message(s) to srd_err(). */
- if (evalue)
- ename = (char *)Py_TYPE(evalue)->tp_name;
+ 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
- /* Can be NULL. */
- ename = "(unknown exception)";
+ srd_err("%s: %s.", etype_name_fallback, msg);
- msg = g_string_sized_new(128);
- g_string_append(msg, ename);
- g_string_append(msg, ": ");
- va_start(args, format);
- g_string_append_vprintf(msg, format, args);
- va_end(args);
- py_str_as_str(py_str, &str);
- g_string_append(msg, str);
- Py_DecRef(py_str);
- srd_err("%s", 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("%s", msg->str);
- g_free(tracestr);
+ g_free(evalue_str);
+ g_free(etype_name);
+
+ /* If there is no traceback object, we are done. */
+ if (!py_etraceback)
+ goto cleanup;
+
+ py_modname = PyUnicode_FromString("traceback");
+ if (!py_modname)
+ goto cleanup;
+
+ py_mod = PyImport_Import(py_modname);
+ Py_DECREF(py_modname);
+
+ 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)
+ goto cleanup;
+
+ tracefmt_str = py_stringify(py_tracefmt);
+ Py_DECREF(py_tracefmt);
+
+ /* Log the detailed stack trace. */
+ if (tracefmt_str) {
+ srd_dbg("%s", tracefmt_str);
+ g_free(tracefmt_str);
}
- g_free(str);
- g_string_free(msg, TRUE);
- 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();
+
+ g_free(msg);
}