[SATYR PATCH 6/6] python: add bindings for core stacktraces

Martin Milata mmilata at redhat.com
Wed Aug 7 15:53:25 UTC 2013


Closes #41.

Signed-off-by: Martin Milata <mmilata at redhat.com>
---
 python/Makefile.am          |   6 ++
 python/py_core_frame.c      | 193 ++++++++++++++++++++++++++++++++++++++++
 python/py_core_frame.h      |  69 ++++++++++++++
 python/py_core_stacktrace.c | 212 ++++++++++++++++++++++++++++++++++++++++++++
 python/py_core_stacktrace.h |  71 +++++++++++++++
 python/py_core_thread.c     | 161 +++++++++++++++++++++++++++++++++
 python/py_core_thread.h     |  66 ++++++++++++++
 python/py_module.c          |  33 +++++++
 tests/python/core.py        | 116 ++++++++++++++++++++++++
 tests/python_bindings.at    |   1 +
 10 files changed, 928 insertions(+)
 create mode 100644 python/py_core_frame.c
 create mode 100644 python/py_core_frame.h
 create mode 100644 python/py_core_stacktrace.c
 create mode 100644 python/py_core_stacktrace.h
 create mode 100644 python/py_core_thread.c
 create mode 100644 python/py_core_thread.h
 create mode 100755 tests/python/core.py

diff --git a/python/Makefile.am b/python/Makefile.am
index 12f408f..95a81c0 100644
--- a/python/Makefile.am
+++ b/python/Makefile.am
@@ -14,6 +14,12 @@ _satyr_la_SOURCES = \
     py_base_thread.c \
     py_base_stacktrace.h \
     py_base_stacktrace.c \
+    py_core_frame.h \
+    py_core_frame.c \
+    py_core_thread.h \
+    py_core_thread.c \
+    py_core_stacktrace.h \
+    py_core_stacktrace.c \
     py_gdb_frame.h \
     py_gdb_frame.c \
     py_gdb_sharedlib.h \
diff --git a/python/py_core_frame.c b/python/py_core_frame.c
new file mode 100644
index 0000000..7abeee8
--- /dev/null
+++ b/python/py_core_frame.c
@@ -0,0 +1,193 @@
+/*
+    py_core_frame.c
+
+    Copyright (C) 2013  Red Hat, Inc.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "py_common.h"
+#include "py_base_frame.h"
+#include "py_core_frame.h"
+
+#include "strbuf.h"
+#include "utils.h"
+#include "core/frame.h"
+
+#define frame_doc "satyr.CoreFrame - class representing a frame in a native executable\n" \
+                  "Usage:\n" \
+                  "satyr.CoreFrame() - creates an empty frame"
+
+#define f_dup_doc "Usage: frame.dup()\n" \
+                  "Returns: satyr.CoreFrame - a new clone of frame\n" \
+                  "Clones the frame object. All new structures are independent " \
+                  "on the original object."
+
+
+static PyMethodDef
+frame_methods[] =
+{
+    /* methods */
+    { "dup", sr_py_core_frame_dup, METH_NOARGS,  f_dup_doc },
+    { NULL },
+};
+
+/* See python/py_common.h and python/py_gdb_frame.c for generic getters/setters documentation. */
+#define GSOFF_PY_STRUCT sr_py_core_frame
+#define GSOFF_PY_MEMBER frame
+#define GSOFF_C_STRUCT sr_core_frame
+GSOFF_START
+GSOFF_MEMBER(address),
+GSOFF_MEMBER(build_id),
+GSOFF_MEMBER(build_id_offset),
+GSOFF_MEMBER(function_name),
+GSOFF_MEMBER(file_name),
+GSOFF_MEMBER(fingerprint),
+GSOFF_MEMBER(fingerprint_hashed)
+GSOFF_END
+
+static PyGetSetDef
+frame_getset[] =
+{
+    SR_ATTRIBUTE_UINT64(address,            "Address of the machine code in memory (long)"      ),
+    SR_ATTRIBUTE_STRING(build_id,           "Build ID of the ELF file (string)"                 ),
+    SR_ATTRIBUTE_UINT64(build_id_offset,    "Offset of the instruction pointer from the start " \
+                                            "of the executable segment (long)"                  ),
+    SR_ATTRIBUTE_STRING(function_name,      "Function name (string)"                            ),
+    SR_ATTRIBUTE_STRING(file_name,          "Name of the executable or shared library (string)" ),
+    SR_ATTRIBUTE_STRING(fingerprint,        "Fingerprint of the current function (string)"      ),
+    SR_ATTRIBUTE_BOOL  (fingerprint_hashed, "True if fingerprint is already hasheed (bool)"     ),
+    { NULL }
+};
+
+PyTypeObject
+sr_py_core_frame_type =
+{
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "satyr.CoreFrame",          /* tp_name */
+    sizeof(struct sr_py_core_frame), /* tp_basicsize */
+    0,                          /* tp_itemsize */
+    sr_py_core_frame_free,      /* tp_dealloc */
+    NULL,                       /* tp_print */
+    NULL,                       /* tp_getattr */
+    NULL,                       /* tp_setattr */
+    NULL,                       /* tp_compare */
+    NULL,                       /* tp_repr */
+    NULL,                       /* tp_as_number */
+    NULL,                       /* tp_as_sequence */
+    NULL,                       /* tp_as_mapping */
+    NULL,                       /* tp_hash */
+    NULL,                       /* tp_call */
+    sr_py_core_frame_str,       /* tp_str */
+    NULL,                       /* tp_getattro */
+    NULL,                       /* tp_setattro */
+    NULL,                       /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT,         /* tp_flags */
+    frame_doc,                  /* tp_doc */
+    NULL,                       /* tp_traverse */
+    NULL,                       /* tp_clear */
+    NULL,                       /* tp_richcompare */
+    0,                          /* tp_weaklistoffset */
+    NULL,                       /* tp_iter */
+    NULL,                       /* tp_iternext */
+    frame_methods,              /* tp_methods */
+    NULL,                       /* tp_members */
+    frame_getset,               /* tp_getset */
+    &sr_py_base_frame_type,     /* tp_base */
+    NULL,                       /* tp_dict */
+    NULL,                       /* tp_descr_get */
+    NULL,                       /* tp_descr_set */
+    0,                          /* tp_dictoffset */
+    NULL,                       /* tp_init */
+    NULL,                       /* tp_alloc */
+    sr_py_core_frame_new,       /* tp_new */
+    NULL,                       /* tp_free */
+    NULL,                       /* tp_is_gc */
+    NULL,                       /* tp_bases */
+    NULL,                       /* tp_mro */
+    NULL,                       /* tp_cache */
+    NULL,                       /* tp_subclasses */
+    NULL,                       /* tp_weaklist */
+};
+
+/* constructor */
+PyObject *
+sr_py_core_frame_new(PyTypeObject *object, PyObject *args, PyObject *kwds)
+{
+    struct sr_py_core_frame *fo = (struct sr_py_core_frame*)
+        PyObject_New(struct sr_py_core_frame, &sr_py_core_frame_type);
+
+    if (!fo)
+        return PyErr_NoMemory();
+
+    fo->frame = sr_core_frame_new();
+
+    return (PyObject*)fo;
+}
+
+/* destructor */
+void
+sr_py_core_frame_free(PyObject *object)
+{
+    struct sr_py_core_frame *this = (struct sr_py_core_frame*)object;
+    sr_core_frame_free(this->frame);
+    PyObject_Del(object);
+}
+
+/* str */
+PyObject *
+sr_py_core_frame_str(PyObject *self)
+{
+    struct sr_py_core_frame *this = (struct sr_py_core_frame*)self;
+    struct sr_strbuf *buf = sr_strbuf_new();
+
+    if (this->frame->address != 0)
+        sr_strbuf_append_strf(buf, "[0x%016"PRIx64"] ", this->frame->address);
+
+    if (this->frame->function_name)
+        sr_strbuf_append_strf(buf, "%s ",  this->frame->function_name);
+
+    if (this->frame->build_id)
+        sr_strbuf_append_strf(buf, "%s+0x%"PRIx64" ", this->frame->build_id,
+                              this->frame->build_id_offset);
+
+    if (this->frame->file_name)
+        sr_strbuf_append_strf(buf, "[%s] ", this->frame->file_name);
+
+    if (this->frame->fingerprint)
+        sr_strbuf_append_strf(buf, "fingerprint: %s (%shashed)", this->frame->fingerprint,
+                              (this->frame->fingerprint_hashed ? "" : "not "));
+
+    char *str = sr_strbuf_free_nobuf(buf);
+    PyObject *result = Py_BuildValue("s", str);
+    free(str);
+    return result;
+}
+
+/* methods */
+PyObject *
+sr_py_core_frame_dup(PyObject *self, PyObject *args)
+{
+    struct sr_py_core_frame *this = (struct sr_py_core_frame*)self;
+    struct sr_py_core_frame *fo = (struct sr_py_core_frame*)
+        PyObject_New(struct sr_py_core_frame, &sr_py_core_frame_type);
+
+    if (!fo)
+        return PyErr_NoMemory();
+
+    fo->frame = sr_core_frame_dup(this->frame, false);
+
+    return (PyObject*)fo;
+}
diff --git a/python/py_core_frame.h b/python/py_core_frame.h
new file mode 100644
index 0000000..dc4a3a2
--- /dev/null
+++ b/python/py_core_frame.h
@@ -0,0 +1,69 @@
+/*
+    py_core_frame.h
+
+    Copyright (C) 2013  Red Hat, Inc.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef SATYR_PY_CORE_FRAME_H
+#define SATYR_PY_CORE_FRAME_H
+
+/**
+ * @file
+ * @brief Python bindings for core frame.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <Python.h>
+#include <structmember.h>
+
+PyTypeObject sr_py_core_frame_type;
+
+/* The beginning of this structure has to have the same layout as
+ * sr_py_base_frame.
+ */
+struct sr_py_core_frame
+{
+    PyObject_HEAD
+    struct sr_core_frame *frame;
+};
+
+/**
+ * Constructor.
+ */
+PyObject *sr_py_core_frame_new(PyTypeObject *object,
+                               PyObject *args, PyObject *kwds);
+
+/**
+ * Destructor.
+ */
+void sr_py_core_frame_free(PyObject *object);
+
+/**
+ * str
+ */
+PyObject *sr_py_core_frame_str(PyObject *self);
+
+/* methods */
+PyObject *sr_py_core_frame_dup(PyObject *self, PyObject *args);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/python/py_core_stacktrace.c b/python/py_core_stacktrace.c
new file mode 100644
index 0000000..976e994
--- /dev/null
+++ b/python/py_core_stacktrace.c
@@ -0,0 +1,212 @@
+/*
+    py_core_stacktrace.c
+
+    Copyright (C) 2013  Red Hat, Inc.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "py_base_stacktrace.h"
+#include "py_core_stacktrace.h"
+#include "py_core_thread.h"
+#include "py_core_frame.h"
+#include "py_common.h"
+#include "strbuf.h"
+#include "core/stacktrace.h"
+#include "core/thread.h"
+#include "stacktrace.h"
+
+#define stacktrace_doc "satyr.CoreStacktrace - class representing a core stacktrace\n" \
+                       "Usage:\n" \
+                       "satyr.CoreStacktrace() - creates an empty stacktrace\n" \
+                       "satyr.CoreStacktrace(json) - creates stacktrace object from JSON string"
+
+#define b_dup_doc "Usage: stacktrace.dup()\n" \
+                  "Returns: satyr.CoreStacktrace - a new clone of core stacktrace\n" \
+                  "Clones the stacktrace object. All new structures are independent " \
+                  "on the original object."
+
+static PyMethodDef
+core_stacktrace_methods[] =
+{
+    /* methods */
+    { "dup", sr_py_core_stacktrace_dup, METH_NOARGS, b_dup_doc },
+    { NULL },
+};
+
+/* See python/py_common.h and python/py_gdb_frame.c for generic getters/setters documentation. */
+#define GSOFF_PY_STRUCT sr_py_core_stacktrace
+#define GSOFF_PY_MEMBER stacktrace
+#define GSOFF_C_STRUCT sr_core_stacktrace
+GSOFF_START
+GSOFF_MEMBER(signal),
+GSOFF_MEMBER(executable)
+GSOFF_END
+
+static PyGetSetDef
+stacktrace_getset[] =
+{
+    SR_ATTRIBUTE_UINT16(signal,     "Signal number (int)"             ),
+    SR_ATTRIBUTE_STRING(executable, "Name of the executable (string)" ),
+    { NULL }
+};
+
+PyTypeObject sr_py_core_stacktrace_type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "satyr.CoreStacktrace",         /* tp_name */
+    sizeof(struct sr_py_core_stacktrace), /* tp_basicsize */
+    0,                              /* tp_itemsize */
+    sr_py_core_stacktrace_free,     /* tp_dealloc */
+    NULL,                           /* tp_print */
+    NULL,                           /* tp_getattr */
+    NULL,                           /* tp_setattr */
+    NULL,                           /* tp_compare */
+    NULL,                           /* tp_repr */
+    NULL,                           /* tp_as_number */
+    NULL,                           /* tp_as_sequence */
+    NULL,                           /* tp_as_mapping */
+    NULL,                           /* tp_hash */
+    NULL,                           /* tp_call */
+    sr_py_core_stacktrace_str,      /* tp_str */
+    NULL,                           /* tp_getattro */
+    NULL,                           /* tp_setattro */
+    NULL,                           /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT,             /* tp_flags */
+    stacktrace_doc,                 /* tp_doc */
+    NULL,                           /* tp_traverse */
+    NULL,                           /* tp_clear */
+    NULL,                           /* tp_richcompare */
+    0,                              /* tp_weaklistoffset */
+    NULL,                           /* tp_iter */
+    NULL,                           /* tp_iternext */
+    core_stacktrace_methods,        /* tp_methods */
+    NULL,                           /* tp_members */
+    stacktrace_getset,              /* tp_getset */
+    &sr_py_multi_stacktrace_type,   /* tp_base */
+    NULL,                           /* tp_dict */
+    NULL,                           /* tp_descr_get */
+    NULL,                           /* tp_descr_set */
+    0,                              /* tp_dictoffset */
+    NULL,                           /* tp_init */
+    NULL,                           /* tp_alloc */
+    sr_py_core_stacktrace_new,      /* tp_new */
+    NULL,                           /* tp_free */
+    NULL,                           /* tp_is_gc */
+    NULL,                           /* tp_bases */
+    NULL,                           /* tp_mro */
+    NULL,                           /* tp_cache */
+    NULL,                           /* tp_subclasses */
+    NULL,                           /* tp_weaklist */
+};
+
+/* constructor */
+PyObject *
+sr_py_core_stacktrace_new(PyTypeObject *object,
+                          PyObject *args,
+                          PyObject *kwds)
+{
+    struct sr_py_core_stacktrace *bo = (struct sr_py_core_stacktrace*)
+        PyObject_New(struct sr_py_core_stacktrace,
+                     &sr_py_core_stacktrace_type);
+
+    if (!bo)
+        return PyErr_NoMemory();
+
+    bo->thread_type = &sr_py_core_thread_type;
+    bo->frame_type = &sr_py_core_frame_type;
+
+    const char *str = NULL;
+    if (!PyArg_ParseTuple(args, "|s", &str))
+        return NULL;
+
+    if (str)
+    {
+        char *error_msg;
+        bo->stacktrace = sr_core_stacktrace_from_json_text(str, &error_msg);
+        if (!bo->stacktrace)
+        {
+            PyErr_SetString(PyExc_ValueError, error_msg);
+            free(error_msg);
+            return NULL;
+        }
+        bo->threads = threads_to_python_list((struct sr_stacktrace *)bo->stacktrace,
+                                             bo->thread_type, bo->frame_type);
+        if (!bo->threads)
+            return NULL;
+    }
+    else
+    {
+        bo->threads = PyList_New(0);
+        bo->stacktrace = sr_core_stacktrace_new();
+    }
+
+    return (PyObject *)bo;
+}
+
+/* destructor */
+void
+sr_py_core_stacktrace_free(PyObject *object)
+{
+    struct sr_py_core_stacktrace *this = (struct sr_py_core_stacktrace*)object;
+    /* the list will decref all of its elements */
+    Py_DECREF(this->threads);
+    this->stacktrace->threads = NULL;
+    sr_core_stacktrace_free(this->stacktrace);
+    PyObject_Del(object);
+}
+
+/* str */
+PyObject *
+sr_py_core_stacktrace_str(PyObject *self)
+{
+    struct sr_py_core_stacktrace *this = (struct sr_py_core_stacktrace *)self;
+    struct sr_strbuf *buf = sr_strbuf_new();
+    sr_strbuf_append_strf(buf, "Core stacktrace with %zd threads",
+                           (ssize_t)(PyList_Size(this->threads)));
+    char *str = sr_strbuf_free_nobuf(buf);
+    PyObject *result = Py_BuildValue("s", str);
+    free(str);
+    return result;
+}
+
+/* methods */
+PyObject *
+sr_py_core_stacktrace_dup(PyObject *self, PyObject *args)
+{
+    struct sr_py_core_stacktrace *this = (struct sr_py_core_stacktrace*)self;
+    if (threads_prepare_linked_list((struct sr_py_multi_stacktrace *)this) < 0)
+        return NULL;
+
+    struct sr_py_core_stacktrace *bo = (struct sr_py_core_stacktrace*)
+        PyObject_New(struct sr_py_core_stacktrace,
+                     &sr_py_core_stacktrace_type);
+
+    if (!bo)
+        return PyErr_NoMemory();
+
+    bo->thread_type = &sr_py_core_thread_type;
+    bo->frame_type = &sr_py_core_frame_type;
+
+    bo->stacktrace = sr_core_stacktrace_dup(this->stacktrace);
+    if (!bo->stacktrace)
+        return NULL;
+
+    bo->threads = threads_to_python_list((struct sr_stacktrace *)bo->stacktrace,
+                                         bo->thread_type, bo->frame_type);
+    if (!bo->threads)
+        return NULL;
+
+    return (PyObject*)bo;
+}
diff --git a/python/py_core_stacktrace.h b/python/py_core_stacktrace.h
new file mode 100644
index 0000000..4cb00ad
--- /dev/null
+++ b/python/py_core_stacktrace.h
@@ -0,0 +1,71 @@
+/*
+    py_core_stacktrace.h
+
+    Copyright (C) 2013  Red Hat, Inc.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef SATYR_PY_CORE_STACKTRACE_H
+#define SATYR_PY_CORE_STACKTRACE_H
+
+/**
+ * @file
+ * @brief Python bindings for Core stack trace.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <Python.h>
+#include <structmember.h>
+
+struct sr_py_core_frame;
+struct sr_py_core_thread;
+
+PyTypeObject sr_py_core_stacktrace_type;
+
+/* The beginning of this structure has to have the same layout as
+ * sr_py_multi_thread_stacktrace.
+ */
+struct sr_py_core_stacktrace
+{
+    PyObject_HEAD
+    struct sr_core_stacktrace *stacktrace;
+    PyObject *threads;
+    PyTypeObject *thread_type;
+    PyTypeObject *frame_type;
+};
+
+/* constructor */
+PyObject *sr_py_core_stacktrace_new(PyTypeObject *object,
+                                    PyObject *args,
+                                    PyObject *kwds);
+
+/* destructor */
+void sr_py_core_stacktrace_free(PyObject *object);
+
+/* str */
+PyObject *sr_py_core_stacktrace_str(PyObject *self);
+
+/* methods */
+PyObject *sr_py_core_stacktrace_dup(PyObject *self, PyObject *args);
+PyObject *sr_py_core_stacktrace_normalize(PyObject *self, PyObject *args);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/python/py_core_thread.c b/python/py_core_thread.c
new file mode 100644
index 0000000..2d27f9f
--- /dev/null
+++ b/python/py_core_thread.c
@@ -0,0 +1,161 @@
+/*
+    py_core_thread.c
+
+    Copyright (C) 2013  Red Hat, Inc.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "py_common.h"
+#include "py_base_thread.h"
+#include "py_core_thread.h"
+#include "py_core_frame.h"
+#include "strbuf.h"
+#include "core/thread.h"
+#include "core/frame.h"
+#include "utils.h"
+
+#define thread_doc "satyr.CoreThread - class representing a thread in a stacktrace\n" \
+                   "Usage:\n" \
+                   "satyr.CoreThread() - creates an empty thread"
+
+#define t_dup_doc "Usage: thread.dup()\n" \
+                  "Returns: satyr.CoreThread - a new clone of thread\n" \
+                  "Clones the thread object. All new structures are independent " \
+                  "on the original object."
+
+static PyMethodDef
+core_thread_methods[] =
+{
+    /* methods */
+    { "dup", sr_py_core_thread_dup, METH_NOARGS, t_dup_doc },
+    { NULL },
+};
+
+PyTypeObject sr_py_core_thread_type =
+{
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "satyr.CoreThread",      /* tp_name */
+    sizeof(struct sr_py_core_thread), /* tp_basicsize */
+    0,                          /* tp_itemsize */
+    sr_py_core_thread_free,     /* tp_dealloc */
+    NULL,                       /* tp_print */
+    NULL,                       /* tp_getattr */
+    NULL,                       /* tp_setattr */
+    NULL,                       /* tp_compare */
+    NULL,                       /* tp_repr */
+    NULL,                       /* tp_as_number */
+    NULL,                       /* tp_as_sequence */
+    NULL,                       /* tp_as_mapping */
+    NULL,                       /* tp_hash */
+    NULL,                       /* tp_call */
+    sr_py_core_thread_str,      /* tp_str */
+    NULL,                       /* tp_getattro */
+    NULL,                       /* tp_setattro */
+    NULL,                       /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT,         /* tp_flags */
+    thread_doc,                 /* tp_doc */
+    NULL,                       /* tp_traverse */
+    NULL,                       /* tp_clear */
+    NULL,                       /* tp_richcompare */
+    0,                          /* tp_weaklistoffset */
+    NULL,                       /* tp_iter */
+    NULL,                       /* tp_iternext */
+    core_thread_methods,        /* tp_methods */
+    NULL,                       /* tp_members */
+    NULL,                       /* tp_getset */
+    &sr_py_base_thread_type,    /* tp_base */
+    NULL,                       /* tp_dict */
+    NULL,                       /* tp_descr_get */
+    NULL,                       /* tp_descr_set */
+    0,                          /* tp_dictoffset */
+    NULL,                       /* tp_init */
+    NULL,                       /* tp_alloc */
+    sr_py_core_thread_new,      /* tp_new */
+    NULL,                       /* tp_free */
+    NULL,                       /* tp_is_gc */
+    NULL,                       /* tp_bases */
+    NULL,                       /* tp_mro */
+    NULL,                       /* tp_cache */
+    NULL,                       /* tp_subclasses */
+    NULL,                       /* tp_weaklist */
+};
+
+/* constructor */
+PyObject *
+sr_py_core_thread_new(PyTypeObject *object, PyObject *args, PyObject *kwds)
+{
+    struct sr_py_core_thread *to = (struct sr_py_core_thread*)
+        PyObject_New(struct sr_py_core_thread,
+                     &sr_py_core_thread_type);
+
+    if (!to)
+        return PyErr_NoMemory();
+
+    to->frame_type = &sr_py_core_frame_type;
+    to->frames = PyList_New(0);
+    to->thread = sr_core_thread_new();
+
+    return (PyObject *)to;
+}
+
+/* destructor */
+void
+sr_py_core_thread_free(PyObject *object)
+{
+    struct sr_py_core_thread *this = (struct sr_py_core_thread *)object;
+    /* the list will decref all of its elements */
+    Py_DECREF(this->frames);
+    this->thread->frames = NULL;
+    sr_core_thread_free(this->thread);
+    PyObject_Del(object);
+}
+
+PyObject *
+sr_py_core_thread_str(PyObject *self)
+{
+    struct sr_py_core_thread *this = (struct sr_py_core_thread *)self;
+    struct sr_strbuf *buf = sr_strbuf_new();
+    sr_strbuf_append_strf(buf, "Thread with %zd frames", (ssize_t)(PyList_Size(this->frames)));
+
+    char *str = sr_strbuf_free_nobuf(buf);
+    PyObject *result = Py_BuildValue("s", str);
+    free(str);
+    return result;
+}
+
+/* methods */
+PyObject *
+sr_py_core_thread_dup(PyObject *self, PyObject *args)
+{
+    struct sr_py_core_thread *this = (struct sr_py_core_thread *)self;
+    if (frames_prepare_linked_list((struct sr_py_base_thread *)this) < 0)
+        return NULL;
+
+    struct sr_py_core_thread *to = (struct sr_py_core_thread*)
+        PyObject_New(struct sr_py_core_thread, &sr_py_core_thread_type);
+
+    if (!to)
+        return PyErr_NoMemory();
+
+    to->frame_type = &sr_py_core_frame_type;
+    to->thread = sr_core_thread_dup(this->thread, false);
+    if (!to->thread)
+        return NULL;
+
+    to->frames = frames_to_python_list((struct sr_thread *)to->thread, to->frame_type);
+
+    return (PyObject *)to;
+}
diff --git a/python/py_core_thread.h b/python/py_core_thread.h
new file mode 100644
index 0000000..2890ff1
--- /dev/null
+++ b/python/py_core_thread.h
@@ -0,0 +1,66 @@
+/*
+    py_core_thread.h
+
+    Copyright (C) 2013  Red Hat, Inc.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef SATYR_PY_CORE_THREAD_H
+#define SATYR_PY_CORE_THREAD_H
+
+/**
+ * @file
+ * @brief Python bindings for Core thread.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <Python.h>
+#include <structmember.h>
+
+PyTypeObject sr_py_core_thread_type;
+
+/* The beginning of this structure has to have the same layout as
+ * sr_py_base_thread.
+ */
+struct sr_py_core_thread
+{
+    PyObject_HEAD
+    struct sr_core_thread *thread;
+    PyObject *frames;
+    PyTypeObject *frame_type;
+};
+
+/* constructor */
+PyObject *sr_py_core_thread_new(PyTypeObject *object,
+                                PyObject *args,
+                                PyObject *kwds);
+
+/* destructor */
+void sr_py_core_thread_free(PyObject *object);
+
+/* str */
+PyObject *sr_py_core_thread_str(PyObject *self);
+
+/* methods */
+PyObject *sr_py_core_thread_dup(PyObject *self, PyObject *args);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/python/py_module.c b/python/py_module.c
index f049a03..23c8882 100644
--- a/python/py_module.c
+++ b/python/py_module.c
@@ -14,6 +14,9 @@
 #include "py_java_frame.h"
 #include "py_java_thread.h"
 #include "py_java_stacktrace.h"
+#include "py_core_frame.h"
+#include "py_core_thread.h"
+#include "py_core_stacktrace.h"
 #include "py_metrics.h"
 
 #include "distance.h"
@@ -136,6 +139,24 @@ init_satyr()
         return;
     }
 
+    if (PyType_Ready(&sr_py_core_frame_type) < 0)
+    {
+        puts("PyType_Ready(&sr_py_core_frame_type) < 0");
+        return;
+    }
+
+    if (PyType_Ready(&sr_py_core_thread_type) < 0)
+    {
+        puts("PyType_Ready(&sr_py_core_thread_type) < 0");
+        return;
+    }
+
+    if (PyType_Ready(&sr_py_core_stacktrace_type) < 0)
+    {
+        puts("PyType_Ready(&sr_py_core_stacktrace_type) < 0");
+        return;
+    }
+
 
     PyObject *module = Py_InitModule("_satyr", module_methods);
     if (!module)
@@ -231,4 +252,16 @@ init_satyr()
     PyModule_AddObject(module, "JavaStacktrace",
                        (PyObject *)&sr_py_java_stacktrace_type);
 
+    Py_INCREF(&sr_py_core_frame_type);
+    PyModule_AddObject(module, "CoreFrame",
+                       (PyObject *)&sr_py_core_frame_type);
+
+    Py_INCREF(&sr_py_core_thread_type);
+    PyModule_AddObject(module, "CoreThread",
+                       (PyObject *)&sr_py_core_thread_type);
+
+    Py_INCREF(&sr_py_core_stacktrace_type);
+    PyModule_AddObject(module, "CoreStacktrace",
+                       (PyObject *)&sr_py_core_stacktrace_type);
+
 }
diff --git a/tests/python/core.py b/tests/python/core.py
new file mode 100755
index 0000000..cf09ba0
--- /dev/null
+++ b/tests/python/core.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+
+import unittest
+from test_helpers import *
+
+contents = load_input_contents('../json_files/core-01')
+threads_expected = 2
+frames_expected = 6
+expected_short_text = '''#1 raise in /usr/lib64/libc-2.15.so
+#2 abort in /usr/lib64/libc-2.15.so
+#3 92ebaf825e4f492952c45189cb9ffc6541f8599b+1123 in /usr/bin/will_abort
+#4 __libc_start_main in /usr/lib64/libc-2.15.so
+'''
+
+class TestCoreStacktrace(BindingsTestCase):
+    def setUp(self):
+        self.trace = satyr.CoreStacktrace(contents)
+
+    def test_correct_thread_count(self):
+        self.assertEqual(len(self.trace.threads), threads_expected)
+
+    def test_correct_frame_count(self):
+        self.assertEqual(frame_count(self.trace), frames_expected)
+
+    def test_dup(self):
+        dup = self.trace.dup()
+        self.assertNotEqual(id(dup.threads), id(self.trace.threads))
+        self.assertEqual(dup.threads, self.trace.threads)
+
+        dup.threads = dup.threads[:5]
+        dup2 = dup.dup()
+        self.assertEqual(len(dup.threads), len(dup2.threads))
+        self.assertNotEqual(id(dup.threads), id(dup2.threads))
+
+    def test_prepare_linked_list(self):
+        dup = self.trace.dup()
+        dup.threads = dup.threads[:5]
+        dup2 = dup.dup()
+        self.assertTrue(len(dup.threads) <= 5)
+
+    def test_str(self):
+        out = str(self.trace)
+        self.assertTrue(('Core stacktrace with %d threads' % threads_expected) in out)
+
+    def test_to_short_text(self):
+        self.assertEqual(self.trace.to_short_text(8), expected_short_text)
+
+    def test_bthash(self):
+        self.assertEqual(self.trace.get_bthash(), '6ebee2edb486ee24a3280c18b0db5353bbc22014')
+
+    def test_getset(self):
+        self.assertGetSetCorrect(self.trace, 'signal', 6, 42)
+        self.assertGetSetCorrect(self.trace, 'executable', '/usr/bin/will_abort', '/bin/true')
+
+    def test_crash_thread(self):
+        self.assertTrue(self.trace.crash_thread is self.trace.threads[1])
+
+class TestCoreThread(BindingsTestCase):
+    def setUp(self):
+        self.thread = satyr.CoreStacktrace(contents).threads[0]
+
+    def test_cmp(self):
+        self.assertEqual(self.thread, self.thread)
+        dup = self.thread.dup()
+        self.assertEqual(self.thread, dup)
+        dup.frames[0].build_id = 'wut'
+        self.assertNotEqual(self.thread, dup)
+
+    def test_duphash(self):
+        expected_plain = '''Thread
+92ebaf825e4f492952c45189cb9ffc6541f8599b+0x2a
+92ebaf825e4f492952c45189cb9ffc6541f8599b+0x29ee
+'''
+        self.assertEqual(self.thread.get_duphash(flags=satyr.DUPHASH_NOHASH, frames=3), expected_plain)
+        self.assertEqual(self.thread.get_duphash(), 'ad8486fa45ff39ffed7a07f3b68b8f406ebe2550')
+
+class TestCoreFrame(BindingsTestCase):
+    def setUp(self):
+        self.frame = satyr.CoreStacktrace(contents).threads[1].frames[0]
+
+    def test_str(self):
+        out = str(self.frame)
+        self.assertEquals(out, '[0x0000003739a35935] raise cc10c72da62c93033e227ffbe2670f2c4fbbde1a+0x35935 '
+                               '[/usr/lib64/libc-2.15.so] '
+                               'fingerprint: f33186a4c862fb0751bca60701f553b829210477 (hashed)')
+
+    def test_dup(self):
+        dup = self.frame.dup()
+        self.assertEqual(dup, self.frame)
+
+        dup.function_name = 'other'
+        self.assertNotEqual(dup, self.frame)
+
+    def test_cmp(self):
+        dup = self.frame.dup()
+        self.assertEqual(dup, dup)
+        self.assertEqual(dup, self.frame)
+        self.assertEqual(dup, self.frame)
+        self.assertNotEqual(id(dup), id(self.frame))
+        dup.function_name = 'another'
+        self.assertNotEqual(dup, self.frame)
+        self.assertFalse(dup > self.frame)
+        self.assertTrue(dup < self.frame)
+
+    def test_getset(self):
+        self.assertGetSetCorrect(self.frame, 'address', 237190207797, 5000)
+        self.assertGetSetCorrect(self.frame, 'build_id', 'cc10c72da62c93033e227ffbe2670f2c4fbbde1a', 'abcdef')
+        self.assertGetSetCorrect(self.frame, 'build_id_offset', 219445, 44)
+        self.assertGetSetCorrect(self.frame, 'function_name', 'raise', 'lower')
+        self.assertGetSetCorrect(self.frame, 'file_name', '/usr/lib64/libc-2.15.so', '/u/l/x')
+        self.assertGetSetCorrect(self.frame, 'fingerprint', 'f33186a4c862fb0751bca60701f553b829210477', 'xxx')
+        self.assertGetSetCorrect(self.frame, 'fingerprint_hashed', True, False)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/python_bindings.at b/tests/python_bindings.at
index c0e12cf..a7ab98d 100644
--- a/tests/python_bindings.at
+++ b/tests/python_bindings.at
@@ -10,4 +10,5 @@ AT_TEST_PYTHON([gdb])
 AT_TEST_PYTHON([koops])
 AT_TEST_PYTHON([python])
 AT_TEST_PYTHON([java])
+AT_TEST_PYTHON([core])
 AT_TEST_PYTHON([metrics])
-- 
1.8.3.1



More information about the Crash-catcher mailing list