Closes #41.
Signed-off-by: Martin Milata mmilata@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])