[SATYR PATCHv2 3/6] python: base classes for threads and stacktraces

Martin Milata mmilata at redhat.com
Mon Jun 24 09:30:08 UTC 2013


Similar to previous commit, base classes were added that are
superclasses of thread and stacktrace classes. The situation is a bit
more complicated here:

BaseThread: methods for all thread types
SingleThreadStacktrace: subclass of BaseThread, base class for
    stacktraces that have only one thread and can be treated as one
MultiThreadStacktrace: base class for stacktrace types that can contain
    multiple threads

Signed-off-by: Martin Milata <mmilata at redhat.com>
---
 python/Makefile.am            |   4 +
 python/py_base_stacktrace.c   | 293 ++++++++++++++++++++++++++++++++++++++++++
 python/py_base_stacktrace.h   |  64 +++++++++
 python/py_base_thread.c       | 205 +++++++++++++++++++++++++++++
 python/py_base_thread.h       |  56 ++++++++
 python/py_gdb_stacktrace.c    |  94 ++++++--------
 python/py_gdb_stacktrace.h    |   9 +-
 python/py_gdb_thread.c        | 161 +++--------------------
 python/py_gdb_thread.h        |  14 +-
 python/py_java_stacktrace.c   | 147 +++------------------
 python/py_java_stacktrace.h   |  10 +-
 python/py_java_thread.c       | 130 ++-----------------
 python/py_java_thread.h       |  14 +-
 python/py_koops_stacktrace.c  | 141 ++------------------
 python/py_koops_stacktrace.h  |   7 +-
 python/py_module.c            |  20 +++
 python/py_python_stacktrace.c | 135 ++-----------------
 python/py_python_stacktrace.h |   7 +-
 18 files changed, 777 insertions(+), 734 deletions(-)
 create mode 100644 python/py_base_stacktrace.c
 create mode 100644 python/py_base_stacktrace.h
 create mode 100644 python/py_base_thread.c
 create mode 100644 python/py_base_thread.h

diff --git a/python/Makefile.am b/python/Makefile.am
index 49ef936..12f408f 100644
--- a/python/Makefile.am
+++ b/python/Makefile.am
@@ -10,6 +10,10 @@ _satyr_la_SOURCES = \
     py_cluster.c \
     py_base_frame.h \
     py_base_frame.c \
+    py_base_thread.h \
+    py_base_thread.c \
+    py_base_stacktrace.h \
+    py_base_stacktrace.c \
     py_gdb_frame.h \
     py_gdb_frame.c \
     py_gdb_sharedlib.h \
diff --git a/python/py_base_stacktrace.c b/python/py_base_stacktrace.c
new file mode 100644
index 0000000..c729335
--- /dev/null
+++ b/python/py_base_stacktrace.c
@@ -0,0 +1,293 @@
+/*
+    py_base_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_thread.h"
+#include "py_base_stacktrace.h"
+
+#include "stacktrace.h"
+#include "thread.h"
+
+#define single_stacktrace_doc "satyr.SingleThreadStacktrace - base class for stacktrace with single thread"
+#define multi_stacktrace_doc "satyr.MultiThreadStacktrace - base class for stacktrace with multiple threads"
+
+#define to_short_text_doc "Usage: stacktrace.to_short_text([max_frames])\n" \
+                          "Returns short text representation of the stacktrace. If max_frames is\n" \
+                          "specified, the result includes only that much topmost frames.\n"
+
+#define threads_doc "A list containing the objects representing threads in the stacktrace."
+
+static PyMethodDef
+single_methods[] =
+{
+    { "to_short_text", sr_py_single_stacktrace_to_short_text, METH_VARARGS, to_short_text_doc },
+    { NULL },
+};
+
+PyTypeObject sr_py_single_stacktrace_type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "satyr.SingleThreadStacktrace", /* tp_name */
+    sizeof(struct sr_py_base_thread), /* tp_basicsize */
+    0,                              /* tp_itemsize */
+    NULL,                           /* 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 */
+    NULL,                           /* tp_str */
+    NULL,                           /* tp_getattro */
+    NULL,                           /* tp_setattro */
+    NULL,                           /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+    single_stacktrace_doc,          /* tp_doc */
+    NULL,                           /* tp_traverse */
+    NULL,                           /* tp_clear */
+    NULL,                           /* tp_richcompare */
+    0,                              /* tp_weaklistoffset */
+    NULL,                           /* tp_iter */
+    NULL,                           /* tp_iternext */
+    single_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 */
+    NULL,                           /* tp_new */
+    NULL,                           /* tp_free */
+    NULL,                           /* tp_is_gc */
+    NULL,                           /* tp_bases */
+    NULL,                           /* tp_mro */
+    NULL,                           /* tp_cache */
+    NULL,                           /* tp_subclasses */
+    NULL,                           /* tp_weaklist */
+};
+
+static PyMemberDef
+multi_members[] =
+{
+    { (char *)"threads", T_OBJECT_EX, offsetof(struct sr_py_multi_stacktrace, threads), 0, (char *)threads_doc },
+    { NULL },
+};
+
+static PyMethodDef
+multi_methods[] =
+{
+    { "to_short_text", sr_py_multi_stacktrace_to_short_text, METH_VARARGS, to_short_text_doc },
+    { NULL },
+};
+
+PyTypeObject sr_py_multi_stacktrace_type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "satyr.MultiThreadStacktrace",  /* tp_name */
+    sizeof(struct sr_py_multi_stacktrace), /* tp_basicsize */
+    0,                              /* tp_itemsize */
+    NULL,                           /* 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 */
+    NULL,                           /* tp_str */
+    NULL,                           /* tp_getattro */
+    NULL,                           /* tp_setattro */
+    NULL,                           /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+    multi_stacktrace_doc,           /* tp_doc */
+    NULL,                           /* tp_traverse */
+    NULL,                           /* tp_clear */
+    NULL,                           /* tp_richcompare */
+    0,                              /* tp_weaklistoffset */
+    NULL,                           /* tp_iter */
+    NULL,                           /* tp_iternext */
+    multi_methods,                  /* tp_methods */
+    multi_members,                  /* tp_members */
+    NULL,                           /* tp_getset */
+    NULL,                           /* tp_base */
+    NULL,                           /* tp_dict */
+    NULL,                           /* tp_descr_get */
+    NULL,                           /* tp_descr_set */
+    0,                              /* tp_dictoffset */
+    NULL,                           /* tp_init */
+    NULL,                           /* tp_alloc */
+    NULL,                           /* tp_new */
+    NULL,                           /* tp_free */
+    NULL,                           /* tp_is_gc */
+    NULL,                           /* tp_bases */
+    NULL,                           /* tp_mro */
+    NULL,                           /* tp_cache */
+    NULL,                           /* tp_subclasses */
+    NULL,                           /* tp_weaklist */
+};
+
+int
+threads_prepare_linked_list(struct sr_py_multi_stacktrace *stacktrace)
+{
+    int i;
+    PyObject *item;
+    struct sr_py_base_thread *current = NULL, *prev = NULL;
+
+    for (i = 0; i < PyList_Size(stacktrace->threads); ++i)
+    {
+        item = PyList_GetItem(stacktrace->threads, i);
+        if (!item)
+            return -1;
+
+        Py_INCREF(item);
+        if (!PyObject_TypeCheck(item, stacktrace->thread_type))
+        {
+            Py_XDECREF(current);
+            Py_XDECREF(prev);
+            PyErr_Format(PyExc_TypeError,
+                         "threads must be a list of %s objects",
+                         stacktrace->thread_type->tp_name);
+            return -1;
+        }
+
+        /* Note: this line is not present in frames_prepare_linked_list. */
+        current = (struct sr_py_base_thread*)item;
+        if (frames_prepare_linked_list(current) < 0)
+            return -1;
+
+        if (i == 0)
+            sr_stacktrace_set_threads(stacktrace->stacktrace, current->thread);
+        else
+            sr_thread_set_next(prev->thread, current->thread);
+
+        Py_XDECREF(prev);
+        prev = current;
+    }
+
+    if (current)
+    {
+        sr_thread_set_next(current->thread, NULL);
+        Py_XDECREF(current);
+    }
+
+    return 0;
+}
+
+int
+threads_free_python_list(struct sr_py_multi_stacktrace *stacktrace)
+{
+    int i;
+    PyObject *item;
+    for (i = 0; i < PyList_Size(stacktrace->threads); ++i)
+    {
+        item = PyList_GetItem(stacktrace->threads, i);
+        if (!item)
+            return -1;
+        Py_DECREF(item);
+    }
+    Py_DECREF(stacktrace->threads);
+
+    return 0;
+}
+
+PyObject *
+threads_to_python_list(struct sr_stacktrace *stacktrace, PyTypeObject *thread_type, PyTypeObject *frame_type)
+{
+    PyObject *result = PyList_New(0);
+    if (!result)
+        return PyErr_NoMemory();
+
+    struct sr_thread *thread = sr_stacktrace_threads(stacktrace);
+    struct sr_py_base_thread *item;
+    while (thread)
+    {
+        item = PyObject_New(struct sr_py_base_thread, thread_type);
+        if (!item)
+            return PyErr_NoMemory();
+
+        /* XXX may need to initialize further */
+        item->thread = thread;
+        item->frames = frames_to_python_list(thread, frame_type);
+        item->frame_type = frame_type;
+        if (!item->frames)
+            return NULL;
+
+        if (PyList_Append(result, (PyObject*)item) < 0)
+            return NULL;
+
+        thread = sr_thread_next(thread);
+    }
+
+    return result;
+}
+
+PyObject *
+sr_py_single_stacktrace_to_short_text(PyObject *self, PyObject *args)
+{
+    int max_frames = 0;
+    if (!PyArg_ParseTuple(args, "|i", &max_frames))
+        return NULL;
+
+    struct sr_py_base_thread *this = (struct sr_py_base_thread *)self;
+    if (frames_prepare_linked_list(this) < 0)
+        return NULL;
+
+    char *text =
+        sr_stacktrace_to_short_text((struct sr_stacktrace *)this->thread, max_frames);
+    if (!text)
+        return NULL;
+
+    PyObject *result = PyString_FromString(text);
+
+    free(text);
+    return result;
+}
+
+PyObject *
+sr_py_multi_stacktrace_to_short_text(PyObject *self, PyObject *args)
+{
+    int max_frames = 0;
+    if (!PyArg_ParseTuple(args, "|i", &max_frames))
+        return NULL;
+
+    struct sr_py_multi_stacktrace *this =
+        (struct sr_py_multi_stacktrace *)self;
+    if (threads_prepare_linked_list(this) < 0)
+        return NULL;
+
+    char *text =
+        sr_stacktrace_to_short_text((struct sr_stacktrace *)this->stacktrace, max_frames);
+    if (!text)
+        return NULL;
+
+    PyObject *result = PyString_FromString(text);
+
+    free(text);
+    return result;
+}
diff --git a/python/py_base_stacktrace.h b/python/py_base_stacktrace.h
new file mode 100644
index 0000000..a0d5e65
--- /dev/null
+++ b/python/py_base_stacktrace.h
@@ -0,0 +1,64 @@
+/*
+    py_base_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_BASE_STACKTRACE_H
+#define SATYR_PY_BASE_STACKTRACE_H
+
+/**
+ * @file
+ * @brief Base classes for stacktrace structures.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "py_base_thread.h"
+
+extern PyTypeObject sr_py_single_stacktrace_type;
+extern PyTypeObject sr_py_multi_stacktrace_type;
+
+/* Python object for multi threaded stacktraces. */
+struct sr_py_multi_stacktrace
+{
+    PyObject_HEAD
+    struct sr_stacktrace *stacktrace;
+    PyObject *threads;
+    PyTypeObject *thread_type;
+    PyTypeObject *frame_type;
+};
+
+/* helpers */
+int threads_prepare_linked_list(struct sr_py_multi_stacktrace *stacktrace);
+int threads_free_python_list(struct sr_py_multi_stacktrace *stacktrace);
+PyObject *threads_to_python_list(struct sr_stacktrace *stacktrace,
+                                 PyTypeObject *thread_type, PyTypeObject *frame_type);
+
+/* methods */
+PyObject *sr_py_single_stacktrace_to_short_text(PyObject *self, PyObject *args);
+PyObject *sr_py_multi_stacktrace_to_short_text(PyObject *self, PyObject *args);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/python/py_base_thread.c b/python/py_base_thread.c
new file mode 100644
index 0000000..6536c52
--- /dev/null
+++ b/python/py_base_thread.c
@@ -0,0 +1,205 @@
+/*
+    py_base_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_base_frame.h"
+
+#include "thread.h"
+#include "frame.h"
+
+#define thread_doc "satyr.BaseThread - base class for threads"
+#define frames_doc "A list containing objects representing frames in a thread."
+
+static PyMethodDef
+thread_methods[] =
+{
+    /* methods */
+    { NULL },
+};
+
+static PyMemberDef
+thread_members[] =
+{
+    { (char *)"frames", T_OBJECT_EX, offsetof(struct sr_py_base_thread, frames), 0, (char *)frames_doc },
+    { NULL },
+};
+
+PyTypeObject
+sr_py_base_thread_type =
+{
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "satyr.BaseThread",         /* tp_name */
+    sizeof(struct sr_py_base_thread), /* tp_basicsize */
+    0,                          /* tp_itemsize */
+    NULL,                       /* tp_dealloc */
+    NULL,                       /* tp_print */
+    NULL,                       /* tp_getattr */
+    NULL,                       /* tp_setattr */
+    (cmpfunc)sr_py_base_thread_cmp, /* tp_compare */
+    NULL,                       /* tp_repr */
+    NULL,                       /* tp_as_number */
+    NULL,                       /* tp_as_sequence */
+    NULL,                       /* tp_as_mapping */
+    NULL,                       /* tp_hash */
+    NULL,                       /* tp_call */
+    NULL,                       /* tp_str */
+    NULL,                       /* tp_getattro */
+    NULL,                       /* tp_setattro */
+    NULL,                       /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+    thread_doc,                  /* tp_doc */
+    NULL,                       /* tp_traverse */
+    NULL,                       /* tp_clear */
+    NULL,                       /* tp_richcompare */
+    0,                          /* tp_weaklistoffset */
+    NULL,                       /* tp_iter */
+    NULL,                       /* tp_iternext */
+    thread_methods,             /* tp_methods */
+    thread_members,             /* tp_members */
+    NULL,                       /* tp_getset */
+    NULL,                       /* tp_base */
+    NULL,                       /* tp_dict */
+    NULL,                       /* tp_descr_get */
+    NULL,                       /* tp_descr_set */
+    0,                          /* tp_dictoffset */
+    NULL,                       /* tp_init */
+    NULL,                       /* tp_alloc */
+    NULL,                       /* tp_new */
+    NULL,                       /* tp_free */
+    NULL,                       /* tp_is_gc */
+    NULL,                       /* tp_bases */
+    NULL,                       /* tp_mro */
+    NULL,                       /* tp_cache */
+    NULL,                       /* tp_subclasses */
+    NULL,                       /* tp_weaklist */
+};
+
+/* helpers */
+int
+frames_prepare_linked_list(struct sr_py_base_thread *thread)
+{
+    int i;
+    PyObject *item;
+    struct sr_py_base_frame *current = NULL, *prev = NULL;
+
+    for (i = 0; i < PyList_Size(thread->frames); ++i)
+    {
+        item = PyList_GetItem(thread->frames, i);
+        if (!item)
+            return -1;
+
+        Py_INCREF(item);
+
+        if (!PyObject_TypeCheck(item, thread->frame_type))
+        {
+            Py_XDECREF(item);
+            Py_XDECREF(prev);
+            PyErr_Format(PyExc_TypeError, "frames must be a list of %s objects",
+                         thread->frame_type->tp_name);
+            return -1;
+        }
+
+        current = (struct sr_py_base_frame*)item;
+        if (i == 0)
+            sr_thread_set_frames(thread->thread, current->frame);
+        else
+            sr_frame_set_next(prev->frame, current->frame);
+
+        Py_XDECREF(prev);
+        prev = current;
+    }
+
+    if (current)
+    {
+        sr_frame_set_next(current->frame, NULL);
+        Py_DECREF(current);
+    }
+
+    return 0;
+}
+
+int
+frames_free_python_list(struct sr_py_base_thread *thread)
+{
+    int i;
+    PyObject *item;
+
+    for (i = 0; i < PyList_Size(thread->frames); ++i)
+    {
+        item = PyList_GetItem(thread->frames, i);
+        if (!item)
+            return -1;
+        Py_DECREF(item);
+    }
+    Py_DECREF(thread->frames);
+
+    return 0;
+}
+
+PyObject *
+frames_to_python_list(struct sr_thread *thread, PyTypeObject *frame_type)
+{
+    PyObject *result = PyList_New(0);
+    if (!result)
+        return NULL;
+
+    struct sr_frame *frame = sr_thread_frames(thread);
+    struct sr_py_base_frame *item;
+    while (frame)
+    {
+        item = PyObject_New(struct sr_py_base_frame, frame_type);
+        if (!item)
+            return PyErr_NoMemory();
+
+        /* XXX may need to initialize item further */
+        /* It would be a good idea to have a common code that is executed each
+         * time new object (e.g. via __new__ or _dup) is created so that we
+         * don't miss setting some attribute. As opposed to using PyObject_New
+         * directly. */
+        item->frame = frame;
+        if (PyList_Append(result, (PyObject *)item) < 0)
+            return NULL;
+
+        frame = sr_frame_next(frame);
+    }
+
+    return result;
+}
+
+/* comparison */
+int
+sr_py_base_thread_cmp(struct sr_py_base_thread *self, struct sr_py_base_thread *other)
+{
+    if (self->ob_type != other->ob_type)
+    {
+        /* distinct types must be unequal */
+        return normalize_cmp(self->ob_type - other->ob_type);
+    }
+
+    if (frames_prepare_linked_list(self) < 0
+        || frames_prepare_linked_list(other) < 0)
+    {
+        /* exception is already set */
+        return -1;
+    }
+
+    return normalize_cmp(sr_thread_cmp(self->thread, other->thread));
+}
diff --git a/python/py_base_thread.h b/python/py_base_thread.h
new file mode 100644
index 0000000..69ae0de
--- /dev/null
+++ b/python/py_base_thread.h
@@ -0,0 +1,56 @@
+/*
+    py_base_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_BASE_THREAD_H
+#define SATYR_PY_BASE_THREAD_H
+
+/**
+ * @file
+ * @brief Base class for threads.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <Python.h>
+#include <structmember.h>
+
+extern PyTypeObject sr_py_base_thread_type;
+
+struct sr_py_base_thread
+{
+    PyObject_HEAD
+    struct sr_thread *thread;
+    PyObject *frames;
+    PyTypeObject *frame_type;
+};
+
+/* helpers */
+int frames_prepare_linked_list(struct sr_py_base_thread *thread);
+int frames_free_python_list(struct sr_py_base_thread *thread);
+PyObject *frames_to_python_list(struct sr_thread *thread, PyTypeObject *frame_type);
+
+int sr_py_base_thread_cmp(struct sr_py_base_thread *self, struct sr_py_base_thread *other);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/python/py_gdb_stacktrace.c b/python/py_gdb_stacktrace.c
index d2f3a5d..8cd82fb 100644
--- a/python/py_gdb_stacktrace.c
+++ b/python/py_gdb_stacktrace.c
@@ -2,6 +2,8 @@
 #include "py_gdb_thread.h"
 #include "py_gdb_frame.h"
 #include "py_gdb_sharedlib.h"
+#include "py_base_thread.h"
+#include "py_base_stacktrace.h"
 #include "strbuf.h"
 #include "gdb/stacktrace.h"
 #include "gdb/thread.h"
@@ -96,7 +98,6 @@ gdb_stacktrace_methods[] =
 static PyMemberDef
 gdb_stacktrace_members[] =
 {
-    { (char *)"threads",     T_OBJECT_EX, offsetof(struct sr_py_gdb_stacktrace, threads),     0,        b_threads_doc     },
     { (char *)"crashframe",  T_OBJECT_EX, offsetof(struct sr_py_gdb_stacktrace, crashframe),  READONLY, b_crashframe_doc  },
     { (char *)"crashthread", T_OBJECT_EX, offsetof(struct sr_py_gdb_stacktrace, crashthread), READONLY, b_crashthread_doc },
     { (char *)"libs",        T_OBJECT_EX, offsetof(struct sr_py_gdb_stacktrace, libs),        0,        b_libs_doc        },
@@ -135,7 +136,7 @@ PyTypeObject sr_py_gdb_stacktrace_type = {
     gdb_stacktrace_methods,               /* tp_methods */
     gdb_stacktrace_members,               /* tp_members */
     NULL,                           /* tp_getset */
-    NULL,                           /* tp_base */
+    &sr_py_multi_stacktrace_type,   /* tp_base */
     NULL,                           /* tp_dict */
     NULL,                           /* tp_descr_get */
     NULL,                           /* tp_descr_set */
@@ -153,50 +154,15 @@ PyTypeObject sr_py_gdb_stacktrace_type = {
 };
 
 /* helpers */
-int
-stacktrace_prepare_linked_list(struct sr_py_gdb_stacktrace *stacktrace)
+static int
+gdb_prepare_linked_lists(struct sr_py_gdb_stacktrace *stacktrace)
 {
-    int i;
-    PyObject *item;
-
-    /* thread */
-    struct sr_py_gdb_thread *current = NULL, *prev = NULL;
-    for (i = 0; i < PyList_Size(stacktrace->threads); ++i)
-    {
-        item = PyList_GetItem(stacktrace->threads, i);
-        if (!item)
-            return -1;
-
-        Py_INCREF(item);
-        if (!PyObject_TypeCheck(item, &sr_py_gdb_thread_type))
-        {
-            Py_XDECREF(current);
-            Py_XDECREF(prev);
-            PyErr_SetString(PyExc_TypeError,
-                            "threads must be a list of satyr.Thread objects");
-            return -1;
-        }
-
-        current = (struct sr_py_gdb_thread*)item;
-        if (thread_prepare_linked_list(current) < 0)
-            return -1;
-
-        if (i == 0)
-            stacktrace->stacktrace->threads = current->thread;
-        else
-            prev->thread->next = current->thread;
-
-        Py_XDECREF(prev);
-        prev = current;
-    }
-
-    if (current)
-    {
-        current->thread->next = NULL;
-        Py_XDECREF(current);
-    }
+    if (threads_prepare_linked_list((struct sr_py_multi_stacktrace *) stacktrace) < 0)
+        return -1;
 
     /* sharedlib */
+    int i;
+    PyObject *item;
     struct sr_py_gdb_sharedlib *currentlib = NULL, *prevlib = NULL;
     for (i = 0; i < PyList_Size(stacktrace->libs); ++i)
     {
@@ -282,8 +248,10 @@ thread_linked_list_to_python_list(struct sr_gdb_stacktrace *stacktrace)
         item = (struct sr_py_gdb_thread*)
             PyObject_New(struct sr_py_gdb_thread, &sr_py_gdb_thread_type);
 
+        item->frame_type = &sr_py_gdb_frame_type;
         item->thread = thread;
-        item->frames = frame_linked_list_to_python_list(thread);
+        item->frames = frames_to_python_list((struct sr_thread *)thread,
+                                             &sr_py_gdb_frame_type);
         if (!item->frames)
             return NULL;
 
@@ -320,6 +288,11 @@ sharedlib_linked_list_to_python_list(struct sr_gdb_stacktrace *stacktrace)
     return result;
 }
 
+/* XXX Why GDB stacktrace needs this function while other types do not? Is it
+ * because the methods here modify the linked lists and other types have no
+ * such methods? Shouldn't we do a copy of the linked list *before* we call the
+ * C function on it?
+ */
 int
 stacktrace_rebuild_thread_python_list(struct sr_py_gdb_stacktrace *stacktrace)
 {
@@ -338,7 +311,8 @@ stacktrace_rebuild_thread_python_list(struct sr_py_gdb_stacktrace *stacktrace)
         return -1;
     }
     stacktrace->stacktrace->threads = newlinkedlist;
-    stacktrace->threads = thread_linked_list_to_python_list(stacktrace->stacktrace);
+    stacktrace->threads = threads_to_python_list((struct sr_stacktrace *)stacktrace->stacktrace,
+                                                 &sr_py_gdb_thread_type, &sr_py_gdb_frame_type);
     return 0;
 }
 
@@ -381,6 +355,8 @@ sr_py_gdb_stacktrace_new(PyTypeObject *object,
     if (!PyArg_ParseTuple(args, "|s", &str))
         return NULL;
 
+    bo->thread_type = &sr_py_gdb_thread_type;
+    bo->frame_type = &sr_py_gdb_frame_type;
     bo->crashframe = (struct sr_py_gdb_frame*)Py_None;
     bo->crashthread = (struct sr_py_gdb_thread*)Py_None;
     if (str)
@@ -443,7 +419,7 @@ PyObject *
 sr_py_gdb_stacktrace_dup(PyObject *self, PyObject *args)
 {
     struct sr_py_gdb_stacktrace *this = (struct sr_py_gdb_stacktrace*)self;
-    if (stacktrace_prepare_linked_list(this) < 0)
+    if (gdb_prepare_linked_lists(this) < 0)
         return NULL;
 
     struct sr_py_gdb_stacktrace *bo = (struct sr_py_gdb_stacktrace*)
@@ -453,6 +429,8 @@ sr_py_gdb_stacktrace_dup(PyObject *self, PyObject *args)
     if (!bo)
         return PyErr_NoMemory();
 
+    bo->thread_type = &sr_py_gdb_thread_type;
+    bo->frame_type = &sr_py_gdb_frame_type;
     bo->stacktrace = sr_gdb_stacktrace_dup(this->stacktrace);
     if (!bo->stacktrace)
         return NULL;
@@ -490,7 +468,7 @@ PyObject *
 sr_py_gdb_stacktrace_find_crash_frame(PyObject *self, PyObject *args)
 {
     struct sr_py_gdb_stacktrace *this = (struct sr_py_gdb_stacktrace *)self;
-    if (stacktrace_prepare_linked_list(this) < 0)
+    if (gdb_prepare_linked_lists(this) < 0)
         return NULL;
 
     /* destroys linked list - need to rebuild python list */
@@ -520,7 +498,7 @@ PyObject *
 sr_py_gdb_stacktrace_find_crash_thread(PyObject *self, PyObject *args)
 {
     struct sr_py_gdb_stacktrace *this = (struct sr_py_gdb_stacktrace*)self;
-    if (stacktrace_prepare_linked_list(this) < 0)
+    if (gdb_prepare_linked_lists(this) < 0)
         return NULL;
 
     /* destroys linked list - need to rebuild python list */
@@ -537,8 +515,10 @@ sr_py_gdb_stacktrace_find_crash_thread(PyObject *self, PyObject *args)
     if (!result)
         return PyErr_NoMemory();
 
+    result->frame_type = &sr_py_gdb_frame_type;
     result->thread = sr_gdb_thread_dup(thread, false);
-    result->frames = frame_linked_list_to_python_list(result->thread);
+    result->frames = frames_to_python_list((struct sr_thread *)result->thread,
+                                           result->frame_type);
     this->crashthread = result;
 
     if (stacktrace_rebuild_thread_python_list(this) < 0)
@@ -551,7 +531,7 @@ PyObject *
 sr_py_gdb_stacktrace_limit_frame_depth(PyObject *self, PyObject *args)
 {
     struct sr_py_gdb_stacktrace *this = (struct sr_py_gdb_stacktrace*)self;
-    if (stacktrace_prepare_linked_list(this) < 0)
+    if (gdb_prepare_linked_lists(this) < 0)
         return NULL;
 
     int depth;
@@ -570,7 +550,7 @@ PyObject *
 sr_py_gdb_stacktrace_quality_simple(PyObject *self, PyObject *args)
 {
     struct sr_py_gdb_stacktrace *this = (struct sr_py_gdb_stacktrace*)self;
-    if (stacktrace_prepare_linked_list(this) < 0)
+    if (gdb_prepare_linked_lists(this) < 0)
         return NULL;
 
     /* does not destroy the linked list */
@@ -582,7 +562,7 @@ PyObject *
 sr_py_gdb_stacktrace_quality_complex(PyObject *self, PyObject *args)
 {
     struct sr_py_gdb_stacktrace *this = (struct sr_py_gdb_stacktrace*)self;
-    if (stacktrace_prepare_linked_list(this) < 0)
+    if (gdb_prepare_linked_lists(this) < 0)
         return NULL;
 
     /* does not destroy the linked list */
@@ -594,7 +574,7 @@ PyObject *
 sr_py_gdb_stacktrace_get_duplication_hash(PyObject *self, PyObject *args)
 {
     struct sr_py_gdb_stacktrace *this = (struct sr_py_gdb_stacktrace*)self;
-    if (stacktrace_prepare_linked_list(this) < 0)
+    if (gdb_prepare_linked_lists(this) < 0)
         return NULL;
 
     /* does not destroy the linked list */
@@ -609,7 +589,7 @@ PyObject *
 sr_py_gdb_stacktrace_find_address(PyObject *self, PyObject *args)
 {
     struct sr_py_gdb_stacktrace *this = (struct sr_py_gdb_stacktrace*)self;
-    if (stacktrace_prepare_linked_list(this) < 0)
+    if (gdb_prepare_linked_lists(this) < 0)
         return NULL;
 
     unsigned long long address;
@@ -642,7 +622,7 @@ PyObject *
 sr_py_gdb_stacktrace_set_libnames(PyObject *self, PyObject *args)
 {
     struct sr_py_gdb_stacktrace *this = (struct sr_py_gdb_stacktrace*)self;
-    if (stacktrace_prepare_linked_list(this) < 0)
+    if (gdb_prepare_linked_lists(this) < 0)
         return NULL;
 
     /* does not destroy the linked list */
@@ -657,7 +637,7 @@ PyObject *
 sr_py_gdb_stacktrace_normalize(PyObject *self, PyObject *args)
 {
     struct sr_py_gdb_stacktrace *this = (struct sr_py_gdb_stacktrace*)self;
-    if (stacktrace_prepare_linked_list(this) < 0)
+    if (gdb_prepare_linked_lists(this) < 0)
         return NULL;
 
     /* destroys the linked list and frees some parts */
@@ -685,7 +665,7 @@ PyObject *
 sr_py_gdb_stacktrace_to_short_text(PyObject *self, PyObject *args)
 {
     struct sr_py_gdb_stacktrace *this = (struct sr_py_gdb_stacktrace*)self;
-    if (stacktrace_prepare_linked_list(this) < 0)
+    if (gdb_prepare_linked_lists(this) < 0)
         return NULL;
 
     int max_frames = 0;
diff --git a/python/py_gdb_stacktrace.h b/python/py_gdb_stacktrace.h
index 1581958..fc2fc8b 100644
--- a/python/py_gdb_stacktrace.h
+++ b/python/py_gdb_stacktrace.h
@@ -37,20 +37,21 @@ struct sr_py_gdb_thread;
 
 PyTypeObject sr_py_gdb_stacktrace_type;
 
+/* The beginning of this structure has to have the same layout as
+ * sr_py_multi_thread_stacktrace.
+ */
 struct sr_py_gdb_stacktrace
 {
     PyObject_HEAD
     struct sr_gdb_stacktrace *stacktrace;
     PyObject *threads;
+    PyTypeObject *thread_type;
+    PyTypeObject *frame_type;
     struct sr_py_gdb_frame *crashframe;
     struct sr_py_gdb_thread *crashthread;
     PyObject *libs;
 };
 
-/* helpers */
-int stacktrace_prepare_linked_list(struct sr_py_gdb_stacktrace *stacktrace);
-PyObject *stacktrace_prepare_thread_list(struct sr_gdb_stacktrace *stacktrace);
-
 /* constructor */
 PyObject *sr_py_gdb_stacktrace_new(PyTypeObject *object,
                                    PyObject *args,
diff --git a/python/py_gdb_thread.c b/python/py_gdb_thread.c
index 711d196..f9fde3c 100644
--- a/python/py_gdb_thread.c
+++ b/python/py_gdb_thread.c
@@ -1,4 +1,5 @@
 #include "py_common.h"
+#include "py_base_thread.h"
 #include "py_gdb_thread.h"
 #include "py_gdb_frame.h"
 #include "strbuf.h"
@@ -30,9 +31,6 @@
 #define t_format_funs_doc "Usage: thread.format_funs()\n" \
                       "Returns: string"
 
-#define t_frames_doc (char *)"A list containing satyr.Frame objects representing " \
-                     "frames in a thread."
-
 static PyMethodDef
 gdb_thread_methods[] =
 {
@@ -44,13 +42,6 @@ gdb_thread_methods[] =
     { NULL },
 };
 
-static PyMemberDef
-gdb_thread_members[] =
-{
-    { (char *)"frames", T_OBJECT_EX, offsetof(struct sr_py_gdb_thread, frames), 0, t_frames_doc },
-    { NULL },
-};
-
 /* See python/py_common.h and python/py_gdb_frame.c for generic getters/setters documentation. */
 #define GSOFF_PY_STRUCT sr_py_gdb_thread
 #define GSOFF_PY_MEMBER thread
@@ -77,7 +68,7 @@ PyTypeObject sr_py_gdb_thread_type =
     NULL,                       /* tp_print */
     NULL,                       /* tp_getattr */
     NULL,                       /* tp_setattr */
-    (cmpfunc)sr_py_gdb_thread_cmp, /* tp_compare */
+    NULL,                       /* tp_compare */
     NULL,                       /* tp_repr */
     NULL,                       /* tp_as_number */
     NULL,                       /* tp_as_sequence */
@@ -97,9 +88,9 @@ PyTypeObject sr_py_gdb_thread_type =
     NULL,                       /* tp_iter */
     NULL,                       /* tp_iternext */
     gdb_thread_methods,         /* tp_methods */
-    gdb_thread_members,         /* tp_members */
+    NULL,                       /* tp_members */
     gdb_thread_getset,          /* tp_getset */
-    NULL,                       /* tp_base */
+    &sr_py_base_thread_type,    /* tp_base */
     NULL,                       /* tp_dict */
     NULL,                       /* tp_descr_get */
     NULL,                       /* tp_descr_set */
@@ -116,119 +107,6 @@ PyTypeObject sr_py_gdb_thread_type =
     NULL,                       /* tp_weaklist */
 };
 
-/* helpers */
-int
-thread_prepare_linked_list(struct sr_py_gdb_thread *thread)
-{
-    int i;
-    PyObject *item;
-    struct sr_py_gdb_frame *current = NULL, *prev = NULL;
-
-    for (i = 0; i < PyList_Size(thread->frames); ++i)
-    {
-        item = PyList_GetItem(thread->frames, i);
-        if (!item)
-            return -1;
-
-        Py_INCREF(item);
-
-        if (!PyObject_TypeCheck(item, &sr_py_gdb_frame_type))
-        {
-            Py_XDECREF(item);
-            Py_XDECREF(prev);
-            PyErr_SetString(PyExc_TypeError, "frames must be a list of satyr.Frame objects");
-            return -1;
-        }
-
-        current = (struct sr_py_gdb_frame*)item;
-        if (i == 0)
-            thread->thread->frames = current->frame;
-        else
-            prev->frame->next = current->frame;
-
-        Py_XDECREF(prev);
-        prev = current;
-    }
-
-    if (current)
-    {
-        current->frame->next = NULL;
-        Py_DECREF(current);
-    }
-
-    return 0;
-}
-
-int
-thread_free_frame_python_list(struct sr_py_gdb_thread *thread)
-{
-    int i;
-    PyObject *item;
-
-    for (i = 0; i < PyList_Size(thread->frames); ++i)
-    {
-        item = PyList_GetItem(thread->frames, i);
-        if (!item)
-            return -1;
-        Py_DECREF(item);
-    }
-    Py_DECREF(thread->frames);
-
-    return 0;
-}
-
-PyObject *
-frame_linked_list_to_python_list(struct sr_gdb_thread *thread)
-{
-    PyObject *result = PyList_New(0);
-    if (!result)
-        return NULL;
-
-    struct sr_gdb_frame *frame = thread->frames;
-    struct sr_py_gdb_frame *item;
-    while (frame)
-    {
-        item = (struct sr_py_gdb_frame*)
-            PyObject_New(struct sr_py_gdb_frame, &sr_py_gdb_frame_type);
-
-        if (!item)
-            return PyErr_NoMemory();
-
-        item->frame = frame;
-        if (PyList_Append(result, (PyObject *)item) < 0)
-            return NULL;
-
-        frame = frame->next;
-    }
-
-    return result;
-}
-
-int
-thread_rebuild_python_list(struct sr_py_gdb_thread *thread)
-{
-    struct sr_gdb_frame *newlinkedlist = sr_gdb_frame_dup(thread->thread->frames, true);
-    if (thread_free_frame_python_list(thread) < 0)
-        return -1;
-    /* linked list */
-    thread->thread->frames = newlinkedlist;
-    /* python list */
-    thread->frames = frame_linked_list_to_python_list(thread->thread);
-    if (!thread->frames)
-    {
-        struct sr_gdb_frame *next;
-        while (newlinkedlist)
-        {
-            next = newlinkedlist->next;
-            sr_gdb_frame_free(newlinkedlist);
-            newlinkedlist = next;
-        }
-        return -1;
-    }
-
-    return 0;
-}
-
 /* constructor */
 PyObject *
 sr_py_gdb_thread_new(PyTypeObject *object, PyObject *args, PyObject *kwds)
@@ -240,6 +118,8 @@ sr_py_gdb_thread_new(PyTypeObject *object, PyObject *args, PyObject *kwds)
     if (!to)
         return PyErr_NoMemory();
 
+    to->frame_type = &sr_py_gdb_frame_type;
+
     const char *str = NULL;
     int only_funs = 0;
     if (!PyArg_ParseTuple(args, "|si", &str, &only_funs))
@@ -262,7 +142,8 @@ sr_py_gdb_thread_new(PyTypeObject *object, PyObject *args, PyObject *kwds)
         {
             to->thread = sr_gdb_thread_parse_funs(str);
         }
-        to->frames = frame_linked_list_to_python_list(to->thread);
+        to->frames = frames_to_python_list((struct sr_thread*)to->thread,
+                                           to->frame_type);
         if (!to->frames)
             return NULL;
     }
@@ -280,7 +161,7 @@ void
 sr_py_gdb_thread_free(PyObject *object)
 {
     struct sr_py_gdb_thread *this = (struct sr_py_gdb_thread *)object;
-    thread_free_frame_python_list(this);
+    frames_free_python_list((struct sr_py_base_thread*) this);
     this->thread->frames = NULL;
     sr_gdb_thread_free(this->thread);
     PyObject_Del(object);
@@ -305,7 +186,7 @@ PyObject *
 sr_py_gdb_thread_dup(PyObject *self, PyObject *args)
 {
     struct sr_py_gdb_thread *this = (struct sr_py_gdb_thread *)self;
-    if (thread_prepare_linked_list(this) < 0)
+    if (frames_prepare_linked_list((struct sr_py_base_thread*) this) < 0)
         return NULL;
 
     struct sr_py_gdb_thread *to = (struct sr_py_gdb_thread*)
@@ -314,33 +195,23 @@ sr_py_gdb_thread_dup(PyObject *self, PyObject *args)
     if (!to)
         return PyErr_NoMemory();
 
+    to->frame_type = &sr_py_gdb_frame_type;
+
     to->thread = sr_gdb_thread_dup(this->thread, false);
     if (!to->thread)
         return NULL;
 
-    to->frames = frame_linked_list_to_python_list(to->thread);
+    to->frames = frames_to_python_list((struct sr_thread*)to->thread,
+                                       to->frame_type);
 
     return (PyObject *)to;
 }
 
-int
-sr_py_gdb_thread_cmp(struct sr_py_gdb_thread *self, struct sr_py_gdb_thread *other)
-{
-    if (thread_prepare_linked_list(self) < 0
-        || thread_prepare_linked_list(other) < 0)
-    {
-        /* exception is already set */
-        return -1;
-    }
-
-    return normalize_cmp(sr_gdb_thread_cmp(self->thread, other->thread));
-}
-
 PyObject *
 sr_py_gdb_thread_quality_counts(PyObject *self, PyObject *args)
 {
     struct sr_py_gdb_thread *this = (struct sr_py_gdb_thread *)self;
-    if (thread_prepare_linked_list(this) < 0)
+    if (frames_prepare_linked_list((struct sr_py_base_thread *)this) < 0)
         return NULL;
 
     int ok = 0, all = 0;
@@ -352,7 +223,7 @@ PyObject *
 sr_py_gdb_thread_quality(PyObject *self, PyObject *args)
 {
     struct sr_py_gdb_thread *this = (struct sr_py_gdb_thread *)self;
-    if (thread_prepare_linked_list(this) < 0)
+    if (frames_prepare_linked_list((struct sr_py_base_thread *)this) < 0)
         return NULL;
 
     return Py_BuildValue("f", sr_gdb_thread_quality(this->thread));
diff --git a/python/py_gdb_thread.h b/python/py_gdb_thread.h
index 00c32b9..39d5163 100644
--- a/python/py_gdb_thread.h
+++ b/python/py_gdb_thread.h
@@ -34,18 +34,17 @@ extern "C" {
 
 PyTypeObject sr_py_gdb_thread_type;
 
+/* The beginning of this structure has to have the same layout as
+ * sr_py_base_thread.
+ */
 struct sr_py_gdb_thread
 {
     PyObject_HEAD
-    PyObject *frames;
     struct sr_gdb_thread *thread;
+    PyObject *frames;
+    PyTypeObject *frame_type;
 };
 
-/* helpers */
-int thread_prepare_linked_list(struct sr_py_gdb_thread *thread);
-PyObject *frame_linked_list_to_python_list(struct sr_gdb_thread *thread);
-int thread_rebuild_python_list(struct sr_py_gdb_thread *thread);
-
 /* constructor */
 PyObject *sr_py_gdb_thread_new(PyTypeObject *object,
                                PyObject *args,
@@ -57,9 +56,6 @@ void sr_py_gdb_thread_free(PyObject *object);
 /* str */
 PyObject *sr_py_gdb_thread_str(PyObject *self);
 
-/* cmp */
-int sr_py_gdb_thread_cmp(struct sr_py_gdb_thread *self, struct sr_py_gdb_thread *other);
-
 /* methods */
 PyObject *sr_py_gdb_thread_dup(PyObject *self, PyObject *args);
 PyObject *sr_py_gdb_thread_quality_counts(PyObject *self, PyObject *args);
diff --git a/python/py_java_stacktrace.c b/python/py_java_stacktrace.c
index 71167e2..7a9477d 100644
--- a/python/py_java_stacktrace.c
+++ b/python/py_java_stacktrace.c
@@ -1,3 +1,4 @@
+#include "py_base_stacktrace.h"
 #include "py_java_stacktrace.h"
 #include "py_java_thread.h"
 #include "py_java_frame.h"
@@ -25,25 +26,15 @@
 #define b_normalize_doc "Usage: stacktrace.normalize()\n" \
                         "Normalizes all threads in the stacktrace."
 
-#define b_threads_doc (char *)"A list containing the satyr.JavaThread objects " \
-                      "representing threads in the stacktrace."
-
 static PyMethodDef
 java_stacktrace_methods[] =
 {
     /* methods */
     { "dup",                  sr_py_java_stacktrace_dup,                  METH_NOARGS,  b_dup_doc                  },
-    { "to_short_text",        sr_py_java_stacktrace_to_short_text,        METH_VARARGS, b_to_short_text            },
 //    { "normalize",            sr_py_java_stacktrace_normalize,            METH_NOARGS,  b_normalize_doc            },
     { NULL },
 };
 
-static PyMemberDef
-java_stacktrace_members[] =
-{
-    { (char *)"threads",     T_OBJECT_EX, offsetof(struct sr_py_java_stacktrace, threads),     0,        b_threads_doc     },
-    { NULL },
-};
 
 PyTypeObject sr_py_java_stacktrace_type = {
     PyObject_HEAD_INIT(NULL)
@@ -75,9 +66,9 @@ PyTypeObject sr_py_java_stacktrace_type = {
     NULL,                           /* tp_iter */
     NULL,                           /* tp_iternext */
     java_stacktrace_methods,        /* tp_methods */
-    java_stacktrace_members,        /* tp_members */
+    NULL,                           /* tp_members */
     NULL,                           /* tp_getset */
-    NULL,                           /* tp_base */
+    &sr_py_multi_stacktrace_type,   /* tp_base */
     NULL,                           /* tp_dict */
     NULL,                           /* tp_descr_get */
     NULL,                           /* tp_descr_set */
@@ -94,98 +85,6 @@ PyTypeObject sr_py_java_stacktrace_type = {
     NULL,                           /* tp_weaklist */
 };
 
-/* helpers */
-int
-java_stacktrace_prepare_linked_list(struct sr_py_java_stacktrace *stacktrace)
-{
-    int i;
-    PyObject *item;
-
-    /* thread */
-    struct sr_py_java_thread *current = NULL, *prev = NULL;
-    for (i = 0; i < PyList_Size(stacktrace->threads); ++i)
-    {
-        item = PyList_GetItem(stacktrace->threads, i);
-        if (!item)
-            return -1;
-
-        Py_INCREF(item);
-        if (!PyObject_TypeCheck(item, &sr_py_java_thread_type))
-        {
-            Py_XDECREF(current);
-            Py_XDECREF(prev);
-            PyErr_SetString(PyExc_TypeError,
-                            "threads must be a list of satyr.JavaThread objects");
-            return -1;
-        }
-
-        current = (struct sr_py_java_thread*)item;
-        if (java_thread_prepare_linked_list(current) < 0)
-            return -1;
-
-        if (i == 0)
-            stacktrace->stacktrace->threads = current->thread;
-        else
-            prev->thread->next = current->thread;
-
-        Py_XDECREF(prev);
-        prev = current;
-    }
-
-    if (current)
-    {
-        current->thread->next = NULL;
-        Py_XDECREF(current);
-    }
-
-    return 0;
-}
-
-int
-java_stacktrace_free_thread_python_list(struct sr_py_java_stacktrace *stacktrace)
-{
-    int i;
-    PyObject *item;
-    for (i = 0; i < PyList_Size(stacktrace->threads); ++i)
-    {
-        item = PyList_GetItem(stacktrace->threads, i);
-        if (!item)
-            return -1;
-        Py_DECREF(item);
-    }
-    Py_DECREF(stacktrace->threads);
-
-    return 0;
-}
-
-PyObject *
-java_thread_linked_list_to_python_list(struct sr_java_stacktrace *stacktrace)
-{
-    PyObject *result = PyList_New(0);
-    if (!result)
-        return PyErr_NoMemory();
-
-    struct sr_java_thread *thread = stacktrace->threads;
-    struct sr_py_java_thread *item;
-    while (thread)
-    {
-        item = (struct sr_py_java_thread*)
-            PyObject_New(struct sr_py_java_thread, &sr_py_java_thread_type);
-
-        item->thread = thread;
-        item->frames = java_frame_linked_list_to_python_list(thread);
-        if (!item->frames)
-            return NULL;
-
-        if (PyList_Append(result, (PyObject*)item) < 0)
-            return NULL;
-
-        thread = thread->next;
-    }
-
-    return result;
-}
-
 /* constructor */
 PyObject *
 sr_py_java_stacktrace_new(PyTypeObject *object,
@@ -199,6 +98,9 @@ sr_py_java_stacktrace_new(PyTypeObject *object,
     if (!bo)
         return PyErr_NoMemory();
 
+    bo->thread_type = &sr_py_java_thread_type;
+    bo->frame_type = &sr_py_java_frame_type;
+
     const char *str = NULL;
     if (!PyArg_ParseTuple(args, "|s", &str))
         return NULL;
@@ -214,7 +116,8 @@ sr_py_java_stacktrace_new(PyTypeObject *object,
             PyErr_SetString(PyExc_ValueError, location.message);
             return NULL;
         }
-        bo->threads = java_thread_linked_list_to_python_list(bo->stacktrace);
+        bo->threads = threads_to_python_list((struct sr_stacktrace *)bo->stacktrace,
+                                             bo->thread_type, bo->frame_type);
         if (!bo->threads)
             return NULL;
     }
@@ -232,7 +135,7 @@ void
 sr_py_java_stacktrace_free(PyObject *object)
 {
     struct sr_py_java_stacktrace *this = (struct sr_py_java_stacktrace*)object;
-    java_stacktrace_free_thread_python_list(this);
+    threads_free_python_list((struct sr_py_multi_stacktrace *)this);
     this->stacktrace->threads = NULL;
     sr_java_stacktrace_free(this->stacktrace);
     PyObject_Del(object);
@@ -257,7 +160,7 @@ PyObject *
 sr_py_java_stacktrace_dup(PyObject *self, PyObject *args)
 {
     struct sr_py_java_stacktrace *this = (struct sr_py_java_stacktrace*)self;
-    if (java_stacktrace_prepare_linked_list(this) < 0)
+    if (threads_prepare_linked_list((struct sr_py_multi_stacktrace *)this) < 0)
         return NULL;
 
     struct sr_py_java_stacktrace *bo = (struct sr_py_java_stacktrace*)
@@ -267,39 +170,21 @@ sr_py_java_stacktrace_dup(PyObject *self, PyObject *args)
     if (!bo)
         return PyErr_NoMemory();
 
+    bo->thread_type = &sr_py_java_thread_type;
+    bo->frame_type = &sr_py_java_frame_type;
+
     bo->stacktrace = sr_java_stacktrace_dup(this->stacktrace);
     if (!bo->stacktrace)
         return NULL;
 
-    bo->threads = java_thread_linked_list_to_python_list(bo->stacktrace);
+    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;
 }
 
-PyObject *
-sr_py_java_stacktrace_to_short_text(PyObject *self, PyObject *args)
-{
-    int max_frames = 0;
-    if (!PyArg_ParseTuple(args, "|i", &max_frames))
-        return NULL;
-
-    struct sr_py_java_stacktrace *this = (struct sr_py_java_stacktrace*)self;
-    if (java_stacktrace_prepare_linked_list(this) < 0)
-        return NULL;
-
-    char *text =
-        sr_stacktrace_to_short_text((struct sr_stacktrace *)this->stacktrace, max_frames);
-    if (!text)
-        return NULL;
-
-    PyObject *result = PyString_FromString(text);
-
-    free(text);
-    return result;
-}
-
 /*
  * Normalization not yet implemente for java stacktraces
  *
@@ -307,7 +192,7 @@ PyObject *
 sr_py_java_stacktrace_normalize(PyObject *self, PyObject *args)
 {
     struct sr_py_java_stacktrace *this = (struct sr_py_java_stacktrace*)self;
-    if (java_stacktrace_prepare_linked_list(this) < 0)
+    if (threads_prepare_linked_list((struct sr_py_multi_stacktrace *)this) < 0)
         return NULL;
 
     struct sr_java_stacktrace *tmp = sr_java_stacktrace_dup(this->stacktrace);
diff --git a/python/py_java_stacktrace.h b/python/py_java_stacktrace.h
index 74996dc..4802d57 100644
--- a/python/py_java_stacktrace.h
+++ b/python/py_java_stacktrace.h
@@ -37,17 +37,18 @@ struct sr_py_java_thread;
 
 PyTypeObject sr_py_java_stacktrace_type;
 
+/* The beginning of this structure has to have the same layout as
+ * sr_py_multi_thread_stacktrace.
+ */
 struct sr_py_java_stacktrace
 {
     PyObject_HEAD
     struct sr_java_stacktrace *stacktrace;
     PyObject *threads;
+    PyTypeObject *thread_type;
+    PyTypeObject *frame_type;
 };
 
-/* helpers */
-int java_stacktrace_prepare_linked_list(struct sr_py_java_stacktrace *stacktrace);
-PyObject *java_stacktrace_prepare_thread_list(struct sr_java_stacktrace *stacktrace);
-
 /* constructor */
 PyObject *sr_py_java_stacktrace_new(PyTypeObject *object,
                                     PyObject *args,
@@ -62,7 +63,6 @@ PyObject *sr_py_java_stacktrace_str(PyObject *self);
 /* methods */
 PyObject *sr_py_java_stacktrace_dup(PyObject *self, PyObject *args);
 PyObject *sr_py_java_stacktrace_normalize(PyObject *self, PyObject *args);
-PyObject *sr_py_java_stacktrace_to_short_text(PyObject *self, PyObject *args);
 
 #ifdef __cplusplus
 }
diff --git a/python/py_java_thread.c b/python/py_java_thread.c
index c960e37..3c8a77b 100644
--- a/python/py_java_thread.c
+++ b/python/py_java_thread.c
@@ -1,4 +1,5 @@
 #include "py_common.h"
+#include "py_base_thread.h"
 #include "py_java_thread.h"
 #include "py_java_frame.h"
 #include "strbuf.h"
@@ -45,13 +46,6 @@ java_thread_methods[] =
     { NULL },
 };
 
-static PyMemberDef
-java_thread_members[] =
-{
-    { (char *)"frames", T_OBJECT_EX, offsetof(struct sr_py_java_thread, frames), 0, t_frames_doc },
-    { NULL },
-};
-
 /* See python/py_common.h and python/py_gdb_frame.c for generic getters/setters documentation. */
 #define GSOFF_PY_STRUCT sr_py_java_thread
 #define GSOFF_PY_MEMBER thread
@@ -78,7 +72,7 @@ PyTypeObject sr_py_java_thread_type =
     NULL,                       /* tp_print */
     NULL,                       /* tp_getattr */
     NULL,                       /* tp_setattr */
-    (cmpfunc)sr_py_java_thread_cmp, /* tp_compare */
+    NULL,                       /* tp_compare */
     NULL,                       /* tp_repr */
     NULL,                       /* tp_as_number */
     NULL,                       /* tp_as_sequence */
@@ -98,9 +92,9 @@ PyTypeObject sr_py_java_thread_type =
     NULL,                       /* tp_iter */
     NULL,                       /* tp_iternext */
     java_thread_methods,        /* tp_methods */
-    java_thread_members,        /* tp_members */
+    NULL,                       /* tp_members */
     java_thread_getset,         /* tp_getset */
-    NULL,                       /* tp_base */
+    &sr_py_base_thread_type,    /* tp_base */
     NULL,                       /* tp_dict */
     NULL,                       /* tp_descr_get */
     NULL,                       /* tp_descr_set */
@@ -117,94 +111,6 @@ PyTypeObject sr_py_java_thread_type =
     NULL,                       /* tp_weaklist */
 };
 
-/* helpers */
-int
-java_thread_prepare_linked_list(struct sr_py_java_thread *thread)
-{
-    int i;
-    PyObject *item;
-    struct sr_py_java_frame *current = NULL, *prev = NULL;
-
-    for (i = 0; i < PyList_Size(thread->frames); ++i)
-    {
-        item = PyList_GetItem(thread->frames, i);
-        if (!item)
-            return -1;
-
-        Py_INCREF(item);
-
-        if (!PyObject_TypeCheck(item, &sr_py_java_frame_type))
-        {
-            Py_XDECREF(item);
-            Py_XDECREF(prev);
-            PyErr_SetString(PyExc_TypeError, "frames must be a list of satyr.JavaFrame objects");
-            return -1;
-        }
-
-        current = (struct sr_py_java_frame*)item;
-        if (i == 0)
-            thread->thread->frames = current->frame;
-        else
-            prev->frame->next = current->frame;
-
-        Py_XDECREF(prev);
-        prev = current;
-    }
-
-    if (current)
-    {
-        current->frame->next = NULL;
-        Py_DECREF(current);
-    }
-
-    return 0;
-}
-
-int
-java_thread_free_frame_python_list(struct sr_py_java_thread *thread)
-{
-    int i;
-    PyObject *item;
-
-    for (i = 0; i < PyList_Size(thread->frames); ++i)
-    {
-        item = PyList_GetItem(thread->frames, i);
-        if (!item)
-            return -1;
-        Py_DECREF(item);
-    }
-    Py_DECREF(thread->frames);
-
-    return 0;
-}
-
-PyObject *
-java_frame_linked_list_to_python_list(struct sr_java_thread *thread)
-{
-    PyObject *result = PyList_New(0);
-    if (!result)
-        return NULL;
-
-    struct sr_java_frame *frame = thread->frames;
-    struct sr_py_java_frame *item;
-    while (frame)
-    {
-        item = (struct sr_py_java_frame*)
-            PyObject_New(struct sr_py_java_frame, &sr_py_java_frame_type);
-
-        if (!item)
-            return PyErr_NoMemory();
-
-        item->frame = frame;
-        if (PyList_Append(result, (PyObject *)item) < 0)
-            return NULL;
-
-        frame = frame->next;
-    }
-
-    return result;
-}
-
 /* constructor */
 PyObject *
 sr_py_java_thread_new(PyTypeObject *object, PyObject *args, PyObject *kwds)
@@ -216,6 +122,8 @@ sr_py_java_thread_new(PyTypeObject *object, PyObject *args, PyObject *kwds)
     if (!to)
         return PyErr_NoMemory();
 
+    to->frame_type = &sr_py_java_frame_type;
+
     const char *str = NULL;
     if (!PyArg_ParseTuple(args, "|s", &str))
         return NULL;
@@ -230,7 +138,7 @@ sr_py_java_thread_new(PyTypeObject *object, PyObject *args, PyObject *kwds)
             PyErr_SetString(PyExc_ValueError, location.message);
             return NULL;
         }
-        to->frames = java_frame_linked_list_to_python_list(to->thread);
+        to->frames = frames_to_python_list((struct sr_thread *)to->thread, to->frame_type);
         if (!to->frames)
             return NULL;
     }
@@ -248,7 +156,7 @@ void
 sr_py_java_thread_free(PyObject *object)
 {
     struct sr_py_java_thread *this = (struct sr_py_java_thread *)object;
-    java_thread_free_frame_python_list(this);
+    frames_free_python_list((struct sr_py_base_thread *)this);
     this->thread->frames = NULL;
     sr_java_thread_free(this->thread);
     PyObject_Del(object);
@@ -275,7 +183,7 @@ PyObject *
 sr_py_java_thread_dup(PyObject *self, PyObject *args)
 {
     struct sr_py_java_thread *this = (struct sr_py_java_thread *)self;
-    if (java_thread_prepare_linked_list(this) < 0)
+    if (frames_prepare_linked_list((struct sr_py_base_thread *)this) < 0)
         return NULL;
 
     struct sr_py_java_thread *to = (struct sr_py_java_thread*)
@@ -284,33 +192,21 @@ sr_py_java_thread_dup(PyObject *self, PyObject *args)
     if (!to)
         return PyErr_NoMemory();
 
+    to->frame_type = &sr_py_java_frame_type;
     to->thread = sr_java_thread_dup(this->thread, false);
     if (!to->thread)
         return NULL;
 
-    to->frames = java_frame_linked_list_to_python_list(to->thread);
+    to->frames = frames_to_python_list((struct sr_thread *)to->thread, to->frame_type);
 
     return (PyObject *)to;
 }
 
-int
-sr_py_java_thread_cmp(struct sr_py_java_thread *self, struct sr_py_java_thread *other)
-{
-    if (java_thread_prepare_linked_list(self) < 0
-        || java_thread_prepare_linked_list(other) < 0)
-    {
-        /* exception is already set */
-        return -1;
-    }
-
-    return normalize_cmp(sr_java_thread_cmp(self->thread, other->thread));
-}
-
 PyObject *
 sr_py_java_thread_quality_counts(PyObject *self, PyObject *args)
 {
     struct sr_py_java_thread *this = (struct sr_py_java_thread *)self;
-    if (java_thread_prepare_linked_list(this) < 0)
+    if (frames_prepare_linked_list((struct sr_py_base_thread *)this) < 0)
         return NULL;
 
     int ok = 0, all = 0;
@@ -322,7 +218,7 @@ PyObject *
 sr_py_java_thread_quality(PyObject *self, PyObject *args)
 {
     struct sr_py_java_thread *this = (struct sr_py_java_thread *)self;
-    if (java_thread_prepare_linked_list(this) < 0)
+    if (frames_prepare_linked_list((struct sr_py_base_thread *)this) < 0)
         return NULL;
 
     return Py_BuildValue("f", sr_java_thread_quality(this->thread));
diff --git a/python/py_java_thread.h b/python/py_java_thread.h
index f9958f6..0abe8bc 100644
--- a/python/py_java_thread.h
+++ b/python/py_java_thread.h
@@ -34,18 +34,17 @@ extern "C" {
 
 PyTypeObject sr_py_java_thread_type;
 
+/* The beginning of this structure has to have the same layout as
+ * sr_py_base_thread.
+ */
 struct sr_py_java_thread
 {
     PyObject_HEAD
-    PyObject *frames;
     struct sr_java_thread *thread;
+    PyObject *frames;
+    PyTypeObject *frame_type;
 };
 
-/* helpers */
-int java_thread_prepare_linked_list(struct sr_py_java_thread *thread);
-PyObject *java_frame_linked_list_to_python_list(struct sr_java_thread *thread);
-int java_thread_rebuild_python_list(struct sr_py_java_thread *thread);
-
 /* constructor */
 PyObject *sr_py_java_thread_new(PyTypeObject *object,
                                 PyObject *args,
@@ -57,9 +56,6 @@ void sr_py_java_thread_free(PyObject *object);
 /* str */
 PyObject *sr_py_java_thread_str(PyObject *self);
 
-/* cmp */
-int sr_py_java_thread_cmp(struct sr_py_java_thread *self, struct sr_py_java_thread *other);
-
 /* methods */
 PyObject *sr_py_java_thread_dup(PyObject *self, PyObject *args);
 PyObject *sr_py_java_thread_quality_counts(PyObject *self, PyObject *args);
diff --git a/python/py_koops_stacktrace.c b/python/py_koops_stacktrace.c
index 5a14066..97a38ef 100644
--- a/python/py_koops_stacktrace.c
+++ b/python/py_koops_stacktrace.c
@@ -1,5 +1,6 @@
 #include "py_koops_frame.h"
 #include "py_koops_stacktrace.h"
+#include "py_base_stacktrace.h"
 #include "py_common.h"
 #include "strbuf.h"
 #include "koops/frame.h"
@@ -37,14 +38,6 @@ koops_stacktrace_methods[] =
     /* methods */
     { "dup",                  sr_py_koops_stacktrace_dup,                  METH_NOARGS,  b_dup_doc                   },
     { "normalize",            sr_py_koops_stacktrace_normalize,            METH_NOARGS,  b_normalize_doc             },
-    { "to_short_text",        sr_py_koops_stacktrace_to_short_text,        METH_VARARGS, b_to_short_text             },
-    { NULL },
-};
-
-static PyMemberDef
-koops_stacktrace_members[] =
-{
-    { (char *)"frames", T_OBJECT_EX, offsetof(struct sr_py_koops_stacktrace, frames), 0, (char *) b_frames_doc },
     { NULL },
 };
 
@@ -86,9 +79,9 @@ PyTypeObject sr_py_koops_stacktrace_type = {
     NULL,                           /* tp_iter */
     NULL,                           /* tp_iternext */
     koops_stacktrace_methods,       /* tp_methods */
-    koops_stacktrace_members,       /* tp_members */
+    NULL,                           /* tp_members */
     koops_stacktrace_getset,        /* tp_getset */
-    NULL,                           /* tp_base */
+    &sr_py_single_stacktrace_type,  /* tp_base */
     NULL,                           /* tp_dict */
     NULL,                           /* tp_descr_get */
     NULL,                           /* tp_descr_set */
@@ -105,49 +98,6 @@ PyTypeObject sr_py_koops_stacktrace_type = {
     NULL,                           /* tp_weaklist */
 };
 
-/* helpers */
-int
-koops_stacktrace_prepare_linked_list(struct sr_py_koops_stacktrace *stacktrace)
-{
-    int i;
-    PyObject *item;
-
-    struct sr_py_koops_frame *current = NULL, *prev = NULL;
-    for (i = 0; i < PyList_Size(stacktrace->frames); ++i)
-    {
-        item = PyList_GetItem(stacktrace->frames, i);
-        if (!item)
-            return -1;
-
-        Py_INCREF(item);
-        if (!PyObject_TypeCheck(item, &sr_py_koops_frame_type))
-        {
-            Py_XDECREF(current);
-            Py_XDECREF(prev);
-            PyErr_SetString(PyExc_TypeError,
-                            "frames must be a list of satyr.KerneloopsFrame objects");
-            return -1;
-        }
-
-        current = (struct sr_py_koops_frame*)item;
-        if (i == 0)
-            stacktrace->stacktrace->frames = current->frame;
-        else
-            prev->frame->next = current->frame;
-
-        Py_XDECREF(prev);
-        prev = current;
-    }
-
-    if (current)
-    {
-        current->frame->next = NULL;
-        Py_XDECREF(current);
-    }
-
-    return 0;
-}
-
 PyObject *
 sr_py_koops_stacktrace_get_modules(PyObject *self, void *data)
 {
@@ -197,52 +147,6 @@ sr_py_koops_stacktrace_set_taint_flags(PyObject *self, PyObject *rhs, void *data
     return -1;
 }
 
-PyObject *
-koops_frame_linked_list_to_python_list(struct sr_koops_stacktrace *stacktrace)
-{
-    PyObject *result = PyList_New(0);
-    if (!result)
-        return PyErr_NoMemory();
-
-    struct sr_koops_frame *frame = stacktrace->frames;
-    struct sr_py_koops_frame *item;
-
-    while(frame)
-    {
-        item = (struct sr_py_koops_frame*)
-            PyObject_New(struct sr_py_koops_frame, &sr_py_koops_frame_type);
-
-        if (!item)
-            return PyErr_NoMemory();
-
-        item->frame = frame;
-        if (PyList_Append(result, (PyObject *)item) < 0)
-            return NULL;
-
-        frame = frame->next;
-    }
-
-    return result;
-}
-
-int
-koops_free_frame_python_list(struct sr_py_koops_stacktrace *stacktrace)
-{
-    int i;
-    PyObject *item;
-
-    for (i = 0; i < PyList_Size(stacktrace->frames); ++i)
-    {
-        item = PyList_GetItem(stacktrace->frames, i);
-        if (!item)
-          return -1;
-        Py_DECREF(item);
-    }
-    Py_DECREF(stacktrace->frames);
-
-    return 0;
-}
-
 /* constructor */
 PyObject *
 sr_py_koops_stacktrace_new(PyTypeObject *object,
@@ -256,6 +160,8 @@ sr_py_koops_stacktrace_new(PyTypeObject *object,
     if (!bo)
         return PyErr_NoMemory();
 
+    bo->frame_type = &sr_py_koops_frame_type;
+
     const char *str = NULL;
     if (!PyArg_ParseTuple(args, "|s", &str))
         return NULL;
@@ -271,7 +177,7 @@ sr_py_koops_stacktrace_new(PyTypeObject *object,
             return NULL;
         }
 
-        bo->frames = koops_frame_linked_list_to_python_list(bo->stacktrace);
+        bo->frames = frames_to_python_list((struct sr_thread *)bo->stacktrace, bo->frame_type);
     }
     else
     {
@@ -287,7 +193,7 @@ void
 sr_py_koops_stacktrace_free(PyObject *object)
 {
     struct sr_py_koops_stacktrace *this = (struct sr_py_koops_stacktrace*)object;
-    koops_free_frame_python_list(this);
+    frames_free_python_list((struct sr_py_base_thread *)this);
     this->stacktrace->frames = NULL;
     sr_koops_stacktrace_free(this->stacktrace);
     PyObject_Del(object);
@@ -312,7 +218,7 @@ PyObject *
 sr_py_koops_stacktrace_dup(PyObject *self, PyObject *args)
 {
     struct sr_py_koops_stacktrace *this = (struct sr_py_koops_stacktrace*)self;
-    if (koops_stacktrace_prepare_linked_list(this) < 0)
+    if (frames_prepare_linked_list((struct sr_py_base_thread *)this) < 0)
         return NULL;
 
     struct sr_py_koops_stacktrace *bo = (struct sr_py_koops_stacktrace*)
@@ -322,11 +228,12 @@ sr_py_koops_stacktrace_dup(PyObject *self, PyObject *args)
     if (!bo)
         return PyErr_NoMemory();
 
+    bo->frame_type = &sr_py_koops_frame_type;
     bo->stacktrace = sr_koops_stacktrace_dup(this->stacktrace);
     if (!bo->stacktrace)
         return NULL;
 
-    bo->frames = koops_frame_linked_list_to_python_list(bo->stacktrace);
+    bo->frames = frames_to_python_list((struct sr_thread *)bo->stacktrace, bo->frame_type);
     if (!bo->frames)
         return NULL;
 
@@ -337,14 +244,14 @@ PyObject *
 sr_py_koops_stacktrace_normalize(PyObject *self, PyObject *args)
 {
     struct sr_py_koops_stacktrace *this = (struct sr_py_koops_stacktrace*)self;
-    if (koops_stacktrace_prepare_linked_list(this) < 0)
+    if (frames_prepare_linked_list((struct sr_py_base_thread *)this) < 0)
         return NULL;
 
     /* destroys the linked list and frees some parts */
     /* need to rebuild python list manually */
     struct sr_koops_stacktrace *tmp = sr_koops_stacktrace_dup(this->stacktrace);
     sr_normalize_koops_stacktrace(tmp);
-    if (koops_free_frame_python_list(this) < 0)
+    if (frames_free_python_list((struct sr_py_base_thread *)this) < 0)
     {
         sr_koops_stacktrace_free(tmp);
         return NULL;
@@ -354,31 +261,9 @@ sr_py_koops_stacktrace_normalize(PyObject *self, PyObject *args)
     tmp->frames = NULL;
     sr_koops_stacktrace_free(tmp);
 
-    this->frames = koops_frame_linked_list_to_python_list(this->stacktrace);
+    this->frames = frames_to_python_list((struct sr_thread *)this->stacktrace, this->frame_type);
     if (!this->frames)
         return NULL;
 
     Py_RETURN_NONE;
 }
-
-PyObject *
-sr_py_koops_stacktrace_to_short_text(PyObject *self, PyObject *args)
-{
-    int max_frames = 0;
-    if (!PyArg_ParseTuple(args, "|i", &max_frames))
-        return NULL;
-
-    struct sr_py_koops_stacktrace *this = (struct sr_py_koops_stacktrace*)self;
-    if (koops_stacktrace_prepare_linked_list(this) < 0)
-        return NULL;
-
-    char *text =
-        sr_stacktrace_to_short_text((struct sr_stacktrace *)this->stacktrace, max_frames);
-    if (!text)
-        return NULL;
-
-    PyObject *result = PyString_FromString(text);
-
-    free(text);
-    return result;
-}
diff --git a/python/py_koops_stacktrace.h b/python/py_koops_stacktrace.h
index 100be66..162ac5e 100644
--- a/python/py_koops_stacktrace.h
+++ b/python/py_koops_stacktrace.h
@@ -34,12 +34,16 @@ extern "C" {
 
 PyTypeObject sr_py_koops_stacktrace_type;
 
+/* The beginning of this structure has to have the same layout as
+ * sr_py_base_thread.
+ */
 struct sr_py_koops_stacktrace
 {
     PyObject_HEAD
+    struct sr_koops_stacktrace *stacktrace;
     PyObject *frames;
+    PyTypeObject *frame_type;
     PyObject *taint_flags;
-    struct sr_koops_stacktrace *stacktrace;
 };
 
 /* constructor */
@@ -62,7 +66,6 @@ int sr_py_koops_stacktrace_set_taint_flags(PyObject *self, PyObject *rhs, void *
 /* methods */
 PyObject *sr_py_koops_stacktrace_dup(PyObject *self, PyObject *args);
 PyObject *sr_py_koops_stacktrace_normalize(PyObject *self, PyObject *args);
-PyObject *sr_py_koops_stacktrace_to_short_text(PyObject *self, PyObject *args);
 
 
 #ifdef __cplusplus
diff --git a/python/py_module.c b/python/py_module.c
index 6759091..5e1f60a 100644
--- a/python/py_module.c
+++ b/python/py_module.c
@@ -1,6 +1,8 @@
 #include <Python.h>
 #include "py_cluster.h"
 #include "py_base_frame.h"
+#include "py_base_thread.h"
+#include "py_base_stacktrace.h"
 #include "py_gdb_frame.h"
 #include "py_gdb_sharedlib.h"
 #include "py_gdb_stacktrace.h"
@@ -35,6 +37,24 @@ init_satyr()
         return;
     }
 
+    if (PyType_Ready(&sr_py_base_thread_type) < 0)
+    {
+        puts("PyType_Ready(&sr_py_base_thread_type) < 0");
+        return;
+    }
+
+    if (PyType_Ready(&sr_py_single_stacktrace_type) < 0)
+    {
+        puts("PyType_Ready(&sr_py_single_stacktrace_type) < 0");
+        return;
+    }
+
+    if (PyType_Ready(&sr_py_multi_stacktrace_type) < 0)
+    {
+        puts("PyType_Ready(&sr_py_multi_stacktrace_type) < 0");
+        return;
+    }
+
     if (PyType_Ready(&sr_py_gdb_frame_type) < 0)
     {
         puts("PyType_Ready(&sr_py_gdb_frame_type) < 0");
diff --git a/python/py_python_stacktrace.c b/python/py_python_stacktrace.c
index e35b0fa..0f16b07 100644
--- a/python/py_python_stacktrace.c
+++ b/python/py_python_stacktrace.c
@@ -1,6 +1,7 @@
 #include "py_common.h"
 #include "py_python_frame.h"
 #include "py_python_stacktrace.h"
+#include "py_base_stacktrace.h"
 #include "utils.h"
 #include "strbuf.h"
 #include "python/frame.h"
@@ -36,18 +37,10 @@ python_stacktrace_methods[] =
 {
     /* methods */
     { "dup",           sr_py_python_stacktrace_dup,           METH_NOARGS,  f_dup_doc },
-    { "to_short_text", sr_py_python_stacktrace_to_short_text, METH_VARARGS, f_to_short_text },
 //    { "normalize", sr_py_python_stacktrace_normalize, METH_NOARGS, f_normalize_doc },
     { NULL },
 };
 
-static PyMemberDef
-python_stacktrace_members[] =
-{
-    { (char *)"frames", T_OBJECT_EX, offsetof(struct sr_py_python_stacktrace, frames), 0, (char *)f_frames_doc },
-    { NULL },
-};
-
 /* See python/py_common.h and python/py_gdb_frame.c for generic getters/setters documentation. */
 #define GSOFF_PY_STRUCT sr_py_python_stacktrace
 #define GSOFF_PY_MEMBER stacktrace
@@ -93,9 +86,9 @@ PyTypeObject sr_py_python_stacktrace_type = {
     NULL,                           /* tp_iter */
     NULL,                           /* tp_iternext */
     python_stacktrace_methods,      /* tp_methods */
-    python_stacktrace_members,      /* tp_members */
+    NULL,                           /* tp_members */
     python_stacktrace_getset,       /* tp_getset */
-    NULL,                           /* tp_base */
+    &sr_py_single_stacktrace_type,  /* tp_base */
     NULL,                           /* tp_dict */
     NULL,                           /* tp_descr_get */
     NULL,                           /* tp_descr_set */
@@ -112,95 +105,6 @@ PyTypeObject sr_py_python_stacktrace_type = {
     NULL,                           /* tp_weaklist */
 };
 
-/* helpers */
-int
-python_stacktrace_prepare_linked_list(struct sr_py_python_stacktrace *stacktrace)
-{
-    int i;
-    PyObject *item;
-
-    struct sr_py_python_frame *current = NULL, *prev = NULL;
-    for (i = 0; i < PyList_Size(stacktrace->frames); ++i)
-    {
-        item = PyList_GetItem(stacktrace->frames, i);
-        if (!item)
-            return -1;
-
-        Py_INCREF(item);
-        if (!PyObject_TypeCheck(item, &sr_py_python_frame_type))
-        {
-            Py_XDECREF(current);
-            Py_XDECREF(prev);
-            PyErr_SetString(PyExc_TypeError,
-                            "frames must be a list of satyr.PythonFrame objects");
-            return -1;
-        }
-
-        current = (struct sr_py_python_frame*)item;
-        if (i == 0)
-            stacktrace->stacktrace->frames = current->frame;
-        else
-            prev->frame->next = current->frame;
-
-        Py_XDECREF(prev);
-        prev = current;
-    }
-
-    if (current)
-    {
-        current->frame->next = NULL;
-        Py_XDECREF(current);
-    }
-
-    return 0;
-}
-
-PyObject *
-python_frame_linked_list_to_python_list(struct sr_python_stacktrace *stacktrace)
-{
-    PyObject *result = PyList_New(0);
-    if (!result)
-        return PyErr_NoMemory();
-
-    struct sr_python_frame *frame = stacktrace->frames;
-    struct sr_py_python_frame *item;
-
-    while(frame)
-    {
-        item = (struct sr_py_python_frame*)
-            PyObject_New(struct sr_py_python_frame, &sr_py_python_frame_type);
-
-        if (!item)
-            return PyErr_NoMemory();
-
-        item->frame = frame;
-        if (PyList_Append(result, (PyObject *)item) < 0)
-            return NULL;
-
-        frame = frame->next;
-    }
-
-    return result;
-}
-
-int
-python_free_frame_python_list(struct sr_py_python_stacktrace *stacktrace)
-{
-    int i;
-    PyObject *item;
-
-    for (i = 0; i < PyList_Size(stacktrace->frames); ++i)
-    {
-        item = PyList_GetItem(stacktrace->frames, i);
-        if (!item)
-          return -1;
-        Py_DECREF(item);
-    }
-    Py_DECREF(stacktrace->frames);
-
-    return 0;
-}
-
 /* constructor */
 PyObject *
 sr_py_python_stacktrace_new(PyTypeObject *object,
@@ -214,6 +118,8 @@ sr_py_python_stacktrace_new(PyTypeObject *object,
     if (!bo)
         return PyErr_NoMemory();
 
+    bo->frame_type = &sr_py_python_frame_type;
+
     const char *str = NULL;
     if (!PyArg_ParseTuple(args, "|s", &str))
         return NULL;
@@ -229,7 +135,7 @@ sr_py_python_stacktrace_new(PyTypeObject *object,
             return NULL;
         }
 
-        bo->frames = python_frame_linked_list_to_python_list(bo->stacktrace);
+        bo->frames = frames_to_python_list((struct sr_thread *)bo->stacktrace, bo->frame_type);
     }
     else
     {
@@ -245,7 +151,7 @@ void
 sr_py_python_stacktrace_free(PyObject *object)
 {
     struct sr_py_python_stacktrace *this = (struct sr_py_python_stacktrace*)object;
-    python_free_frame_python_list(this);
+    frames_free_python_list((struct sr_py_base_thread *)this);
     this->stacktrace->frames = NULL;
     sr_python_stacktrace_free(this->stacktrace);
     PyObject_Del(object);
@@ -270,7 +176,7 @@ PyObject *
 sr_py_python_stacktrace_dup(PyObject *self, PyObject *args)
 {
     struct sr_py_python_stacktrace *this = (struct sr_py_python_stacktrace*)self;
-    if (python_stacktrace_prepare_linked_list(this) < 0)
+    if (frames_prepare_linked_list((struct sr_py_base_thread *)this) < 0)
         return NULL;
 
     struct sr_py_python_stacktrace *bo = (struct sr_py_python_stacktrace*)
@@ -280,39 +186,18 @@ sr_py_python_stacktrace_dup(PyObject *self, PyObject *args)
     if (!bo)
         return PyErr_NoMemory();
 
+    bo->frame_type = &sr_py_python_frame_type;
     bo->stacktrace = sr_python_stacktrace_dup(this->stacktrace);
     if (!bo->stacktrace)
         return NULL;
 
-    bo->frames =  python_frame_linked_list_to_python_list(bo->stacktrace);
+    bo->frames =  frames_to_python_list((struct sr_thread *)bo->stacktrace, bo->frame_type);
     if (!bo->frames)
         return NULL;
 
     return (PyObject*)bo;
 }
 
-PyObject *
-sr_py_python_stacktrace_to_short_text(PyObject *self, PyObject *args)
-{
-    int max_frames = 0;
-    if (!PyArg_ParseTuple(args, "|i", &max_frames))
-        return NULL;
-
-    struct sr_py_python_stacktrace *this = (struct sr_py_python_stacktrace*)self;
-    if (python_stacktrace_prepare_linked_list(this) < 0)
-        return NULL;
-
-    char *text =
-        sr_stacktrace_to_short_text((struct sr_stacktrace *)this->stacktrace, max_frames);
-    if (!text)
-        return NULL;
-
-    PyObject *result = PyString_FromString(text);
-
-    free(text);
-    return result;
-}
-
 /*
  * No python normalization implemented yet
  *
diff --git a/python/py_python_stacktrace.h b/python/py_python_stacktrace.h
index 8075344..faa5e06 100644
--- a/python/py_python_stacktrace.h
+++ b/python/py_python_stacktrace.h
@@ -34,11 +34,15 @@ extern "C" {
 
 PyTypeObject sr_py_python_stacktrace_type;
 
+/* The beginning of this structure has to have the same layout as
+ * sr_py_base_thread.
+ */
 struct sr_py_python_stacktrace
 {
     PyObject_HEAD
-    PyObject *frames;
     struct sr_python_stacktrace *stacktrace;
+    PyObject *frames;
+    PyTypeObject *frame_type;
 };
 
 /* constructor */
@@ -55,7 +59,6 @@ PyObject *sr_py_python_stacktrace_str(PyObject *self);
 /* methods */
 PyObject *sr_py_python_stacktrace_dup(PyObject *self, PyObject *args);
 PyObject *sr_py_python_stacktrace_normalize(PyObject *self, PyObject *args);
-PyObject *sr_py_python_stacktrace_to_short_text(PyObject *self, PyObject *args);
 
 
 #ifdef __cplusplus
-- 
1.7.11.7



More information about the Crash-catcher mailing list