diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fffae4a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/satyr-0.13.tar.xz diff --git a/.satyr.metadata b/.satyr.metadata new file mode 100644 index 0000000..bdf2891 --- /dev/null +++ b/.satyr.metadata @@ -0,0 +1 @@ +3a1e70739102d91879ca625670e8efe60817b608 SOURCES/satyr-0.13.tar.xz diff --git a/README.md b/README.md deleted file mode 100644 index 0e7897f..0000000 --- a/README.md +++ /dev/null @@ -1,5 +0,0 @@ -The master branch has no content - -Look at the c7 branch if you are working with CentOS-7, or the c4/c5/c6 branch for CentOS-4, 5 or 6 - -If you find this file in a distro specific branch, it means that no content has been checked in yet diff --git a/SOURCES/satyr-0.13-Add-support-for-Ruby-report-type.patch b/SOURCES/satyr-0.13-Add-support-for-Ruby-report-type.patch new file mode 100644 index 0000000..bd1c13e --- /dev/null +++ b/SOURCES/satyr-0.13-Add-support-for-Ruby-report-type.patch @@ -0,0 +1,2586 @@ +From 28db4ce2840d74f06b55c1e2c7c48f228708fb1d Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Wed, 28 Jan 2015 16:11:15 +0100 +Subject: [PATCH] Add support for Ruby report type + +Related: #1334604 + +Signed-off-by: Martin Milata +--- + include/Makefile.am | 5 + + include/report_type.h | 1 + + include/ruby/frame.h | 99 ++++++ + include/ruby/stacktrace.h | 73 ++++ + lib/Makefile.am | 2 + + lib/abrt.c | 31 ++ + lib/generic_frame.c | 1 + + lib/generic_frame.h | 3 +- + lib/generic_stacktrace.c | 1 + + lib/generic_stacktrace.h | 3 +- + lib/generic_thread.c | 1 + + lib/generic_thread.h | 3 +- + lib/report.c | 3 + + lib/ruby_frame.c | 504 +++++++++++++++++++++++++++ + lib/ruby_stacktrace.c | 387 ++++++++++++++++++++ + tests/Makefile.am | 3 + + tests/ruby_frame.at | 285 +++++++++++++++ + tests/ruby_stacktrace.at | 316 +++++++++++++++++ + tests/ruby_stacktraces/ruby-01 | 23 ++ + tests/ruby_stacktraces/ruby-01-expected-json | 97 ++++++ + tests/ruby_stacktraces/ruby-02 | 37 ++ + tests/ruby_stacktraces/ruby-02-expected-json | 158 +++++++++ + tests/ruby_stacktraces/ruby-03 | 4 + + tests/ruby_stacktraces/ruby-03-expected-json | 19 + + tests/ruby_stacktraces/ruby-04 | 39 +++ + tests/ruby_stacktraces/ruby-04-expected-json | 166 +++++++++ + tests/testsuite.at | 2 + + 27 files changed, 2263 insertions(+), 3 deletions(-) + create mode 100644 include/ruby/frame.h + create mode 100644 include/ruby/stacktrace.h + create mode 100644 lib/ruby_frame.c + create mode 100644 lib/ruby_stacktrace.c + create mode 100644 tests/ruby_frame.at + create mode 100644 tests/ruby_stacktrace.at + create mode 100644 tests/ruby_stacktraces/ruby-01 + create mode 100644 tests/ruby_stacktraces/ruby-01-expected-json + create mode 100644 tests/ruby_stacktraces/ruby-02 + create mode 100644 tests/ruby_stacktraces/ruby-02-expected-json + create mode 100644 tests/ruby_stacktraces/ruby-03 + create mode 100644 tests/ruby_stacktraces/ruby-03-expected-json + create mode 100644 tests/ruby_stacktraces/ruby-04 + create mode 100644 tests/ruby_stacktraces/ruby-04-expected-json + +diff --git a/include/Makefile.am b/include/Makefile.am +index f72ec66..bcffe31 100644 +--- a/include/Makefile.am ++++ b/include/Makefile.am +@@ -49,3 +49,8 @@ pythonheadersdir = $(includedir)/satyr/python + pythonheaders_HEADERS = \ + python/frame.h \ + python/stacktrace.h ++ ++rubyheadersdir = $(includedir)/satyr/ruby ++rubyheaders_HEADERS = \ ++ ruby/frame.h \ ++ ruby/stacktrace.h +diff --git a/include/report_type.h b/include/report_type.h +index a51620d..0d411ee 100644 +--- a/include/report_type.h ++++ b/include/report_type.h +@@ -32,6 +32,7 @@ enum sr_report_type + SR_REPORT_KERNELOOPS, + SR_REPORT_JAVA, + SR_REPORT_GDB, ++ SR_REPORT_RUBY, + + /* Keep this the last entry. */ + SR_REPORT_NUM +diff --git a/include/ruby/frame.h b/include/ruby/frame.h +new file mode 100644 +index 0000000..ffc3c67 +--- /dev/null ++++ b/include/ruby/frame.h +@@ -0,0 +1,99 @@ ++/* ++ ruby_frame.h ++ ++ Copyright (C) 2015 ABRT Team ++ Copyright (C) 2015 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_RUBY_FRAME_H ++#define SATYR_RUBY_FRAME_H ++ ++/** ++ * @file ++ * @brief Ruby frame structure and related routines. ++ */ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "../report_type.h" ++#include ++#include ++ ++struct sr_location; ++struct sr_strbuf; ++struct sr_json_value; ++ ++struct sr_ruby_frame ++{ ++ enum sr_report_type type; ++ ++ char *file_name; ++ ++ uint32_t file_line; ++ ++ bool special_function; ++ ++ char *function_name; ++ ++ uint32_t block_level; ++ ++ uint32_t rescue_level; ++ ++ struct sr_ruby_frame *next; ++}; ++ ++struct sr_ruby_frame * ++sr_ruby_frame_new(); ++ ++void ++sr_ruby_frame_init(struct sr_ruby_frame *frame); ++ ++void ++sr_ruby_frame_free(struct sr_ruby_frame *frame); ++ ++struct sr_ruby_frame * ++sr_ruby_frame_dup(struct sr_ruby_frame *frame, bool siblings); ++ ++int ++sr_ruby_frame_cmp(struct sr_ruby_frame *frame1, struct sr_ruby_frame *frame2); ++ ++int ++sr_ruby_frame_cmp_distance(struct sr_ruby_frame *frame1, ++ struct sr_ruby_frame *frame2); ++ ++struct sr_ruby_frame * ++sr_ruby_frame_append(struct sr_ruby_frame *dest, ++ struct sr_ruby_frame *item); ++ ++struct sr_ruby_frame * ++sr_ruby_frame_parse(const char **input, struct sr_location *location); ++ ++char * ++sr_ruby_frame_to_json(struct sr_ruby_frame *frame); ++ ++struct sr_ruby_frame * ++sr_ruby_frame_from_json(struct sr_json_value *root, char **error_message); ++ ++void ++sr_ruby_frame_append_to_str(struct sr_ruby_frame *frame, struct sr_strbuf *dest); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/include/ruby/stacktrace.h b/include/ruby/stacktrace.h +new file mode 100644 +index 0000000..caa3e0b +--- /dev/null ++++ b/include/ruby/stacktrace.h +@@ -0,0 +1,73 @@ ++/* ++ ruby_stacktrace.h ++ ++ Copyright (C) 2015 ABRT Team ++ Copyright (C) 2015 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_RUBY_STACKTRACE_H ++#define SATYR_RUBY_STACKTRACE_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "../report_type.h" ++#include ++ ++struct sr_ruby_frame; ++struct sr_location; ++struct sr_json_value; ++ ++struct sr_ruby_stacktrace ++{ ++ enum sr_report_type type; ++ ++ char *exception_name; ++ ++ struct sr_ruby_frame *frames; ++}; ++ ++struct sr_ruby_stacktrace * ++sr_ruby_stacktrace_new(); ++ ++void ++sr_ruby_stacktrace_init(struct sr_ruby_stacktrace *stacktrace); ++ ++void ++sr_ruby_stacktrace_free(struct sr_ruby_stacktrace *stacktrace); ++ ++struct sr_ruby_stacktrace * ++sr_ruby_stacktrace_dup(struct sr_ruby_stacktrace *stacktrace); ++ ++struct sr_ruby_stacktrace * ++sr_ruby_stacktrace_parse(const char **input, ++ struct sr_location *location); ++ ++char * ++sr_ruby_stacktrace_get_reason(struct sr_ruby_stacktrace *stacktrace); ++ ++char * ++sr_ruby_stacktrace_to_json(struct sr_ruby_stacktrace *stacktrace); ++ ++struct sr_ruby_stacktrace * ++sr_ruby_stacktrace_from_json(struct sr_json_value *root, char **error_message); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/lib/Makefile.am b/lib/Makefile.am +index f798347..26016f2 100644 +--- a/lib/Makefile.am ++++ b/lib/Makefile.am +@@ -57,6 +57,8 @@ libsatyr_conv_la_SOURCES = \ + python_stacktrace.c \ + report.c \ + rpm.c \ ++ ruby_frame.c \ ++ ruby_stacktrace.c \ + sha1.c \ + strbuf.c \ + unstrip.c \ +diff --git a/lib/abrt.c b/lib/abrt.c +index 4599e2c..a07d44a 100644 +--- a/lib/abrt.c ++++ b/lib/abrt.c +@@ -30,6 +30,7 @@ + #include "python/stacktrace.h" + #include "koops/stacktrace.h" + #include "java/stacktrace.h" ++#include "ruby/stacktrace.h" + #include "json.h" + #include "location.h" + #include +@@ -567,6 +568,34 @@ sr_abrt_report_from_dir(const char *directory, + } + } + ++ /* Ruby stacktrace. */ ++ if (report->report_type == SR_REPORT_RUBY) ++ { ++ char *backtrace_contents = file_contents(directory, "backtrace", ++ error_message); ++ if (!backtrace_contents) ++ { ++ sr_report_free(report); ++ return NULL; ++ } ++ ++ /* Parse the Ruby stacktrace. */ ++ struct sr_location location; ++ sr_location_init(&location); ++ const char *contents_pointer = backtrace_contents; ++ report->stacktrace = (struct sr_stacktrace *)sr_ruby_stacktrace_parse( ++ &contents_pointer, ++ &location); ++ ++ free(backtrace_contents); ++ if (!report->stacktrace) ++ { ++ *error_message = sr_location_to_string(&location); ++ sr_report_free(report); ++ return NULL; ++ } ++ } ++ + return report; + } + +@@ -581,6 +610,8 @@ sr_abrt_type_from_analyzer(const char *analyzer) + return SR_REPORT_KERNELOOPS; + else if (0 == strncmp(analyzer, "Java", 4)) + return SR_REPORT_JAVA; ++ else if (0 == strncmp(analyzer, "Ruby", 4)) ++ return SR_REPORT_RUBY; + + return SR_REPORT_INVALID; + } +diff --git a/lib/generic_frame.c b/lib/generic_frame.c +index eb644b5..d5512c9 100644 +--- a/lib/generic_frame.c ++++ b/lib/generic_frame.c +@@ -34,6 +34,7 @@ static struct frame_methods* dtable[SR_REPORT_NUM] = + [SR_REPORT_KERNELOOPS] = &koops_frame_methods, + [SR_REPORT_JAVA] = &java_frame_methods, + [SR_REPORT_GDB] = &gdb_frame_methods, ++ [SR_REPORT_RUBY] = &ruby_frame_methods, + }; + + void +diff --git a/lib/generic_frame.h b/lib/generic_frame.h +index a61626d..e9f392d 100644 +--- a/lib/generic_frame.h ++++ b/lib/generic_frame.h +@@ -48,7 +48,8 @@ struct frame_methods + }; + + extern struct frame_methods core_frame_methods, python_frame_methods, +- koops_frame_methods, gdb_frame_methods, java_frame_methods; ++ koops_frame_methods, gdb_frame_methods, java_frame_methods, ++ ruby_frame_methods; + + void + frame_append_bthash_text(struct sr_frame *frame, enum sr_bthash_flags flags, +diff --git a/lib/generic_stacktrace.c b/lib/generic_stacktrace.c +index feb1dbc..f98a482 100644 +--- a/lib/generic_stacktrace.c ++++ b/lib/generic_stacktrace.c +@@ -40,6 +40,7 @@ static struct stacktrace_methods* dtable[SR_REPORT_NUM] = + [SR_REPORT_KERNELOOPS] = &koops_stacktrace_methods, + [SR_REPORT_JAVA] = &java_stacktrace_methods, + [SR_REPORT_GDB] = &gdb_stacktrace_methods, ++ [SR_REPORT_RUBY] = &ruby_stacktrace_methods, + }; + + /* In case stacktrace type supports only one thread, return the stacktrace itself. */ +diff --git a/lib/generic_stacktrace.h b/lib/generic_stacktrace.h +index 8e3d629..6e1a444 100644 +--- a/lib/generic_stacktrace.h ++++ b/lib/generic_stacktrace.h +@@ -55,7 +55,8 @@ struct stacktrace_methods + }; + + extern struct stacktrace_methods core_stacktrace_methods, python_stacktrace_methods, +- koops_stacktrace_methods, gdb_stacktrace_methods, java_stacktrace_methods; ++ koops_stacktrace_methods, gdb_stacktrace_methods, java_stacktrace_methods, ++ ruby_stacktrace_methods; + + /* Macros to generate accessors for the "threads" member. */ + #define DEFINE_THREADS_FUNC(name, concrete_t) \ +diff --git a/lib/generic_thread.c b/lib/generic_thread.c +index 59fff1b..16748b5 100644 +--- a/lib/generic_thread.c ++++ b/lib/generic_thread.c +@@ -131,6 +131,7 @@ static struct thread_methods* dtable[SR_REPORT_NUM] = + [SR_REPORT_KERNELOOPS] = &koops_thread_methods, + [SR_REPORT_JAVA] = &java_thread_methods, + [SR_REPORT_GDB] = &gdb_thread_methods, ++ [SR_REPORT_RUBY] = &ruby_thread_methods, + }; + + struct sr_frame * +diff --git a/lib/generic_thread.h b/lib/generic_thread.h +index 005e5ac..8635ad2 100644 +--- a/lib/generic_thread.h ++++ b/lib/generic_thread.h +@@ -56,7 +56,8 @@ struct thread_methods + }; + + extern struct thread_methods core_thread_methods, python_thread_methods, +- koops_thread_methods, gdb_thread_methods, java_thread_methods; ++ koops_thread_methods, gdb_thread_methods, java_thread_methods, ++ ruby_thread_methods; + + /* Macros to generate accessors for the "frames" member. */ + #define DEFINE_FRAMES_FUNC(name, concrete_t) \ +diff --git a/lib/report.c b/lib/report.c +index 411df64..614b213 100644 +--- a/lib/report.c ++++ b/lib/report.c +@@ -42,6 +42,7 @@ static char *report_types[] = + [SR_REPORT_KERNELOOPS] = "kerneloops", + [SR_REPORT_JAVA] = "java", + [SR_REPORT_GDB] = "gdb", ++ [SR_REPORT_RUBY] = "ruby", + NULL + }; + +@@ -189,6 +190,7 @@ sr_report_to_json(struct sr_report *report) + case SR_REPORT_PYTHON: + case SR_REPORT_KERNELOOPS: + case SR_REPORT_JAVA: ++ case SR_REPORT_RUBY: + report_type = sr_report_type_to_string(report->report_type); + reason = sr_stacktrace_get_reason(report->stacktrace); + break; +@@ -383,6 +385,7 @@ sr_report_from_json(struct sr_json_value *root, char **error_message) + case SR_REPORT_PYTHON: + case SR_REPORT_KERNELOOPS: + case SR_REPORT_JAVA: ++ case SR_REPORT_RUBY: + report->stacktrace = sr_stacktrace_from_json(report->report_type, problem, error_message); + break; + default: +diff --git a/lib/ruby_frame.c b/lib/ruby_frame.c +new file mode 100644 +index 0000000..9a9a317 +--- /dev/null ++++ b/lib/ruby_frame.c +@@ -0,0 +1,504 @@ ++/* ++ ruby_frame.c ++ ++ Copyright (C) 2015 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 "ruby/frame.h" ++#include "utils.h" ++#include "location.h" ++#include "strbuf.h" ++#include "json.h" ++#include "generic_frame.h" ++#include "thread.h" ++#include "stacktrace.h" ++#include "internal_utils.h" ++#include ++#include ++#include ++ ++/* Method table */ ++ ++static void ++ruby_append_bthash_text(struct sr_ruby_frame *frame, enum sr_bthash_flags flags, ++ struct sr_strbuf *strbuf); ++static void ++ruby_append_duphash_text(struct sr_ruby_frame *frame, enum sr_duphash_flags flags, ++ struct sr_strbuf *strbuf); ++ ++DEFINE_NEXT_FUNC(ruby_next, struct sr_frame, struct sr_ruby_frame) ++DEFINE_SET_NEXT_FUNC(ruby_set_next, struct sr_frame, struct sr_ruby_frame) ++ ++struct frame_methods ruby_frame_methods = ++{ ++ .append_to_str = (append_to_str_fn_t) sr_ruby_frame_append_to_str, ++ .next = (next_frame_fn_t) ruby_next, ++ .set_next = (set_next_frame_fn_t) ruby_set_next, ++ .cmp = (frame_cmp_fn_t) sr_ruby_frame_cmp, ++ .cmp_distance = (frame_cmp_fn_t) sr_ruby_frame_cmp_distance, ++ .frame_append_bthash_text = ++ (frame_append_bthash_text_fn_t) ruby_append_bthash_text, ++ .frame_append_duphash_text = ++ (frame_append_duphash_text_fn_t) ruby_append_duphash_text, ++ .frame_free = (frame_free_fn_t) sr_ruby_frame_free, ++}; ++ ++/* Public functions */ ++ ++struct sr_ruby_frame * ++sr_ruby_frame_new() ++{ ++ struct sr_ruby_frame *frame = ++ sr_malloc(sizeof(struct sr_ruby_frame)); ++ ++ sr_ruby_frame_init(frame); ++ return frame; ++} ++ ++void ++sr_ruby_frame_init(struct sr_ruby_frame *frame) ++{ ++ memset(frame, 0, sizeof(struct sr_ruby_frame)); ++ frame->type = SR_REPORT_RUBY; ++} ++ ++void ++sr_ruby_frame_free(struct sr_ruby_frame *frame) ++{ ++ if (!frame) ++ return; ++ ++ free(frame->file_name); ++ free(frame->function_name); ++ free(frame); ++} ++ ++struct sr_ruby_frame * ++sr_ruby_frame_dup(struct sr_ruby_frame *frame, bool siblings) ++{ ++ struct sr_ruby_frame *result = sr_ruby_frame_new(); ++ memcpy(result, frame, sizeof(struct sr_ruby_frame)); ++ ++ /* Handle siblings. */ ++ if (siblings) ++ { ++ if (result->next) ++ result->next = sr_ruby_frame_dup(result->next, true); ++ } ++ else ++ result->next = NULL; /* Do not copy that. */ ++ ++ /* Duplicate all strings. */ ++ if (result->file_name) ++ result->file_name = sr_strdup(result->file_name); ++ ++ if (result->function_name) ++ result->function_name = sr_strdup(result->function_name); ++ ++ return result; ++} ++ ++int ++sr_ruby_frame_cmp(struct sr_ruby_frame *frame1, ++ struct sr_ruby_frame *frame2) ++{ ++ /* function_name */ ++ int function_name = sr_strcmp0(frame1->function_name, ++ frame2->function_name); ++ if (function_name != 0) ++ return function_name; ++ ++ /* file_name */ ++ int file_name = sr_strcmp0(frame1->file_name, ++ frame2->file_name); ++ if (file_name != 0) ++ return file_name; ++ ++ /* file_line */ ++ int file_line = frame1->file_line - frame2->file_line; ++ if (file_line != 0) ++ return file_line; ++ ++ /* special_function */ ++ int special_function = frame1->special_function - frame2->special_function; ++ if (special_function != 0) ++ return special_function; ++ ++ /* block_level */ ++ int block_level = frame1->block_level - frame2->block_level; ++ if (block_level != 0) ++ return block_level; ++ ++ /* rescue_level */ ++ int rescue_level = frame1->rescue_level - frame2->rescue_level; ++ if (rescue_level != 0) ++ return rescue_level; ++ ++ return 0; ++} ++ ++int ++sr_ruby_frame_cmp_distance(struct sr_ruby_frame *frame1, ++ struct sr_ruby_frame *frame2) ++{ ++ /* function_name */ ++ int function_name = sr_strcmp0(frame1->function_name, ++ frame2->function_name); ++ if (function_name != 0) ++ return function_name; ++ ++ /* file_name */ ++ int file_name = sr_strcmp0(frame1->file_name, ++ frame2->file_name); ++ if (file_name != 0) ++ return file_name; ++ ++ /* special_function */ ++ int special_function = frame1->special_function - frame2->special_function; ++ if (special_function != 0) ++ return special_function; ++ ++ return 0; ++} ++ ++struct sr_ruby_frame * ++sr_ruby_frame_append(struct sr_ruby_frame *dest, ++ struct sr_ruby_frame *item) ++{ ++ if (!dest) ++ return item; ++ ++ struct sr_ruby_frame *dest_loop = dest; ++ while (dest_loop->next) ++ dest_loop = dest_loop->next; ++ ++ dest_loop->next = item; ++ return dest; ++} ++ ++struct sr_ruby_frame * ++sr_ruby_frame_parse(const char **input, ++ struct sr_location *location) ++{ ++ const char *local_input = *input; ++ struct sr_ruby_frame *frame = sr_ruby_frame_new(); ++ ++ /* take everything before the backtick ++ * /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require' ++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ */ ++ char *filename_lineno_in = NULL; ++ if (!sr_parse_char_cspan(&local_input, "`", &filename_lineno_in)) ++ { ++ location->message = sr_strdup("Unable to find the '`' character " ++ "identifying the beginning of function name."); ++ goto fail; ++ } ++ ++ size_t l = strlen(filename_lineno_in); ++ location->column += l; ++ ++ char *p = filename_lineno_in + l; ++ ++ /* /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require' ++ * ^^^^ ++ */ ++ p -= strlen(":in "); ++ if (p < filename_lineno_in || 0 != strcmp(":in ", p)) ++ { ++ location->column -= strlen(":in "); ++ location->message = sr_strdup("Unable to find ':in ' preceding the " ++ "backtick character."); ++ goto fail; ++ } ++ ++ /* Find the beginning of the line number. */ ++ *p = '\0'; ++ do { ++ p--; ++ } while (p >= filename_lineno_in && isdigit(*p)); ++ p++; ++ ++ /* /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require' ++ * ^^ ++ */ ++ char *p_copy = p; ++ int lineno_len = sr_parse_uint32((const char **)&p_copy, &frame->file_line); ++ if (lineno_len <= 0) ++ { ++ location->message = sr_strdup("Unable to find line number before ':in '"); ++ goto fail; ++ } ++ ++ /* /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require' ++ * ^ ++ */ ++ p--; ++ if (p < filename_lineno_in || *p != ':') ++ { ++ location->column -= lineno_len; ++ location->message = sr_strdup("Unable to fin the ':' character " ++ "preceding the line number"); ++ goto fail; ++ } ++ ++ /* Everything before the colon is the file name. */ ++ *p = '\0'; ++ frame->file_name = filename_lineno_in; ++ filename_lineno_in = NULL; ++ ++ if(!sr_skip_char(&local_input, '`')) ++ { ++ location->message = sr_strdup("Unable to find the '`' character " ++ "identifying the beginning of function name."); ++ goto fail; ++ } ++ ++ location->column++; ++ ++ /* The part in quotes can look like: ++ * `rescue in rescue in block (3 levels) in func' ++ * parse the number of rescues and blocks ++ */ ++ while (sr_skip_string(&local_input, "rescue in ")) ++ { ++ frame->rescue_level++; ++ location->column += strlen("rescue in "); ++ } ++ ++ if (sr_skip_string(&local_input, "block in ")) ++ { ++ frame->block_level = 1; ++ location->column += strlen("block in"); ++ } ++ else if(sr_skip_string(&local_input, "block (")) ++ { ++ location->column += strlen("block ("); ++ ++ int len = sr_parse_uint32(&local_input, &frame->block_level); ++ if (len == 0 || !sr_skip_string(&local_input, " levels) in ")) ++ { ++ location->message = sr_strdup("Unable to parse block depth."); ++ goto fail; ++ } ++ location->column += len + strlen(" levels) in "); ++ } ++ ++ if (sr_skip_char(&local_input, '<')) ++ { ++ location->column++; ++ frame->special_function = true; ++ } ++ ++ if (!sr_parse_char_cspan(&local_input, "'>", &frame->function_name)) ++ { ++ location->message = sr_strdup("Unable to find the \"'\" character " ++ "delimiting the function name."); ++ goto fail; ++ } ++ location->column += strlen(frame->function_name); ++ ++ if (frame->special_function) ++ { ++ if (!sr_skip_char(&local_input, '>')) ++ { ++ location->message = sr_strdup("Unable to find the \">\" character " ++ "delimiting the function name."); ++ goto fail; ++ } ++ location->column++; ++ } ++ ++ if (!sr_skip_char(&local_input, '\'')) ++ { ++ location->message = sr_strdup("Unable to find the \"'\" character " ++ "delimiting the function name."); ++ goto fail; ++ } ++ location->column++; ++ ++ *input = local_input; ++ return frame; ++ ++fail: ++ sr_ruby_frame_free(frame); ++ free(filename_lineno_in); ++ return NULL; ++} ++ ++char * ++sr_ruby_frame_to_json(struct sr_ruby_frame *frame) ++{ ++ struct sr_strbuf *strbuf = sr_strbuf_new(); ++ ++ /* Source file name. */ ++ if (frame->file_name) ++ { ++ sr_strbuf_append_str(strbuf, ", \"file_name\": "); ++ sr_json_append_escaped(strbuf, frame->file_name); ++ sr_strbuf_append_str(strbuf, "\n"); ++ } ++ ++ /* Source file line. */ ++ if (frame->file_line) ++ { ++ sr_strbuf_append_strf(strbuf, ++ ", \"file_line\": %"PRIu32"\n", ++ frame->file_line); ++ } ++ ++ /* Function name / special function. */ ++ if (frame->function_name) ++ { ++ if (frame->special_function) ++ sr_strbuf_append_str(strbuf, ", \"special_function\": "); ++ else ++ sr_strbuf_append_str(strbuf, ", \"function_name\": "); ++ ++ sr_json_append_escaped(strbuf, frame->function_name); ++ sr_strbuf_append_str(strbuf, "\n"); ++ } ++ ++ /* Block level. */ ++ if (frame->block_level > 0) ++ { ++ sr_strbuf_append_strf(strbuf, ++ ", \"block_level\": %"PRIu32"\n", ++ frame->block_level); ++ } ++ ++ /* Rescue level. */ ++ if (frame->rescue_level > 0) ++ { ++ sr_strbuf_append_strf(strbuf, ++ ", \"rescue_level\": %"PRIu32"\n", ++ frame->rescue_level); ++ } ++ ++ ++ strbuf->buf[0] = '{'; ++ sr_strbuf_append_char(strbuf, '}'); ++ return sr_strbuf_free_nobuf(strbuf); ++} ++ ++struct sr_ruby_frame * ++sr_ruby_frame_from_json(struct sr_json_value *root, char **error_message) ++{ ++ if (!JSON_CHECK_TYPE(root, SR_JSON_OBJECT, "frame")) ++ return NULL; ++ ++ struct sr_ruby_frame *result = sr_ruby_frame_new(); ++ struct sr_json_value *val; ++ ++ /* Source file name */ ++ if ((val = json_element(root, "file_name"))) ++ { ++ if (!JSON_CHECK_TYPE(val, SR_JSON_STRING, "file_name")) ++ goto fail; ++ ++ result->file_name = sr_strdup(val->u.string.ptr); ++ } ++ ++ /* Function name / special function. */ ++ if ((val = json_element(root, "function_name"))) ++ { ++ if (!JSON_CHECK_TYPE(val, SR_JSON_STRING, "function_name")) ++ goto fail; ++ ++ result->special_function = false; ++ result->function_name = sr_strdup(val->u.string.ptr); ++ } ++ else if ((val = json_element(root, "special_function"))) ++ { ++ if (!JSON_CHECK_TYPE(val, SR_JSON_STRING, "special_function")) ++ goto fail; ++ ++ result->special_function = true; ++ result->function_name = sr_strdup(val->u.string.ptr); ++ } ++ ++ bool success = ++ JSON_READ_UINT32(root, "file_line", &result->file_line) && ++ JSON_READ_UINT32(root, "block_level", &result->block_level) && ++ JSON_READ_UINT32(root, "rescue_level", &result->rescue_level); ++ ++ if (!success) ++ goto fail; ++ ++ return result; ++ ++fail: ++ sr_ruby_frame_free(result); ++ return NULL; ++} ++ ++void ++sr_ruby_frame_append_to_str(struct sr_ruby_frame *frame, ++ struct sr_strbuf *dest) ++{ ++ for (int i = 0; i < frame->rescue_level; i++) ++ { ++ sr_strbuf_append_str(dest, "rescue in "); ++ } ++ ++ if (frame->block_level == 1) ++ { ++ sr_strbuf_append_str(dest, "block in "); ++ } ++ else if (frame->block_level > 1) ++ { ++ sr_strbuf_append_strf(dest, "block (%u levels) in ", (unsigned)frame->block_level); ++ } ++ ++ sr_strbuf_append_strf(dest, "%s%s%s", ++ (frame->special_function ? "<" : ""), ++ (frame->function_name ? frame->function_name : "??"), ++ (frame->special_function ? ">" : "")); ++ ++ if (frame->file_name) ++ { ++ sr_strbuf_append_strf(dest, " in %s", frame->file_name); ++ ++ if (frame->file_line) ++ { ++ sr_strbuf_append_strf(dest, ":%d", frame->file_line); ++ } ++ } ++} ++ ++static void ++ruby_append_bthash_text(struct sr_ruby_frame *frame, enum sr_bthash_flags flags, ++ struct sr_strbuf *strbuf) ++{ ++ sr_strbuf_append_strf(strbuf, ++ "%s, %"PRIu32", %s, %d, %"PRIu32", %"PRIu32"\n", ++ OR_UNKNOWN(frame->file_name), ++ frame->file_line, ++ OR_UNKNOWN(frame->function_name), ++ frame->special_function, ++ frame->block_level, ++ frame->rescue_level); ++} ++ ++static void ++ruby_append_duphash_text(struct sr_ruby_frame *frame, enum sr_duphash_flags flags, ++ struct sr_strbuf *strbuf) ++{ ++ /* filename:line */ ++ sr_strbuf_append_strf(strbuf, "%s:%"PRIu32"\n", ++ OR_UNKNOWN(frame->file_name), ++ frame->file_line); ++} +diff --git a/lib/ruby_stacktrace.c b/lib/ruby_stacktrace.c +new file mode 100644 +index 0000000..4adde08 +--- /dev/null ++++ b/lib/ruby_stacktrace.c +@@ -0,0 +1,387 @@ ++/* ++ ruby_stacktrace.c ++ ++ Copyright (C) 2015 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 "ruby/stacktrace.h" ++#include "ruby/frame.h" ++#include "location.h" ++#include "utils.h" ++#include "json.h" ++#include "sha1.h" ++#include "report_type.h" ++#include "strbuf.h" ++#include "generic_stacktrace.h" ++#include "generic_thread.h" ++#include "internal_utils.h" ++#include ++#include ++#include ++#include ++ ++/* Method tables */ ++ ++static void ++ruby_append_bthash_text(struct sr_ruby_stacktrace *stacktrace, enum sr_bthash_flags flags, ++ struct sr_strbuf *strbuf); ++ ++DEFINE_FRAMES_FUNC(ruby_frames, struct sr_ruby_stacktrace) ++DEFINE_SET_FRAMES_FUNC(ruby_set_frames, struct sr_ruby_stacktrace) ++DEFINE_PARSE_WRAPPER_FUNC(ruby_parse, SR_REPORT_RUBY) ++ ++struct thread_methods ruby_thread_methods = ++{ ++ .frames = (frames_fn_t) ruby_frames, ++ .set_frames = (set_frames_fn_t) ruby_set_frames, ++ .cmp = (thread_cmp_fn_t) NULL, ++ .frame_count = (frame_count_fn_t) thread_frame_count, ++ .next = (next_thread_fn_t) thread_no_next_thread, ++ .set_next = (set_next_thread_fn_t) NULL, ++ .thread_append_bthash_text = ++ (thread_append_bthash_text_fn_t) thread_no_bthash_text, ++ .thread_free = (thread_free_fn_t) sr_ruby_stacktrace_free, ++ .remove_frame = (remove_frame_fn_t) thread_remove_frame, ++ .remove_frames_above = ++ (remove_frames_above_fn_t) thread_remove_frames_above, ++ .thread_dup = (thread_dup_fn_t) sr_ruby_stacktrace_dup, ++ .normalize = (normalize_fn_t) thread_no_normalization, ++}; ++ ++struct stacktrace_methods ruby_stacktrace_methods = ++{ ++ .parse = (parse_fn_t) ruby_parse, ++ .parse_location = (parse_location_fn_t) sr_ruby_stacktrace_parse, ++ .to_short_text = (to_short_text_fn_t) stacktrace_to_short_text, ++ .to_json = (to_json_fn_t) sr_ruby_stacktrace_to_json, ++ .from_json = (from_json_fn_t) sr_ruby_stacktrace_from_json, ++ .get_reason = (get_reason_fn_t) sr_ruby_stacktrace_get_reason, ++ .find_crash_thread = (find_crash_thread_fn_t) stacktrace_one_thread_only, ++ .threads = (threads_fn_t) stacktrace_one_thread_only, ++ .set_threads = (set_threads_fn_t) NULL, ++ .stacktrace_free = (stacktrace_free_fn_t) sr_ruby_stacktrace_free, ++ .stacktrace_append_bthash_text = ++ (stacktrace_append_bthash_text_fn_t) ruby_append_bthash_text, ++}; ++ ++/* Public functions */ ++ ++struct sr_ruby_stacktrace * ++sr_ruby_stacktrace_new() ++{ ++ struct sr_ruby_stacktrace *stacktrace = ++ sr_malloc(sizeof(struct sr_ruby_stacktrace)); ++ ++ sr_ruby_stacktrace_init(stacktrace); ++ return stacktrace; ++} ++ ++void ++sr_ruby_stacktrace_init(struct sr_ruby_stacktrace *stacktrace) ++{ ++ memset(stacktrace, 0, sizeof(struct sr_ruby_stacktrace)); ++ stacktrace->type = SR_REPORT_RUBY; ++} ++ ++void ++sr_ruby_stacktrace_free(struct sr_ruby_stacktrace *stacktrace) ++{ ++ if (!stacktrace) ++ return; ++ ++ while (stacktrace->frames) ++ { ++ struct sr_ruby_frame *frame = stacktrace->frames; ++ stacktrace->frames = frame->next; ++ sr_ruby_frame_free(frame); ++ } ++ ++ free(stacktrace->exception_name); ++ free(stacktrace); ++} ++ ++struct sr_ruby_stacktrace * ++sr_ruby_stacktrace_dup(struct sr_ruby_stacktrace *stacktrace) ++{ ++ struct sr_ruby_stacktrace *result = sr_ruby_stacktrace_new(); ++ memcpy(result, stacktrace, sizeof(struct sr_ruby_stacktrace)); ++ ++ if (result->exception_name) ++ result->exception_name = sr_strdup(result->exception_name); ++ ++ if (result->frames) ++ result->frames = sr_ruby_frame_dup(result->frames, true); ++ ++ return result; ++} ++ ++struct sr_ruby_stacktrace * ++sr_ruby_stacktrace_parse(const char **input, ++ struct sr_location *location) ++{ ++ const char *local_input = *input; ++ struct sr_ruby_stacktrace *stacktrace = sr_ruby_stacktrace_new(); ++ char *message_and_class = NULL; ++ ++ /* /some/thing.rb:13:in `method': exception message (Exception::Class)\n\tfrom ... ++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ */ ++ stacktrace->frames = sr_ruby_frame_parse(&local_input, location); ++ if (!stacktrace->frames) ++ { ++ location->message = sr_asprintf("Topmost stacktrace frame not found: %s", ++ location->message ? location->message : "(unknown reason)"); ++ goto fail; ++ } ++ ++ /* /some/thing.rb:13:in `method': exception message (Exception::Class)\n\tfrom ... ++ * ^^ ++ */ ++ if (!sr_skip_string(&local_input, ": ")) ++ { ++ location->message = sr_strdup("Unable to find the colon after first function name."); ++ goto fail; ++ } ++ location->column += strlen(": "); ++ ++ /* /some/thing.rb:13:in `method': exception message (Exception::Class)\n\tfrom ... ++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ */ ++ if (!sr_parse_char_cspan(&local_input, "\t", &message_and_class)) ++ { ++ location->message = sr_strdup("Unable to find the exception type and message."); ++ goto fail; ++ } ++ ++ /* /some/thing.rb:13:in `method': exception message (Exception::Class)\n\tfrom ... ++ * ^^ ++ */ ++ size_t l = strlen(message_and_class); ++ location->column += l; ++ ++ char *p = message_and_class + l - 1; ++ if (p < message_and_class || *p != '\n') ++ { ++ location->column--; ++ location->message = sr_strdup("Unable to find the new line character after " ++ "the end of exception class"); ++ goto fail; ++ } ++ ++ /* /some/thing.rb:13:in `method': exception message (Exception::Class)\n\tfrom ... ++ * ^ ++ */ ++ p--; ++ if (p < message_and_class || *p != ')') ++ { ++ location->column -= 2; ++ location->message = sr_strdup("Unable to find the ')' character identifying " ++ "the end of exception class"); ++ goto fail; ++ } ++ ++ /* /some/thing.rb:13:in `method': exception message (Exception::Class)\n\tfrom ... ++ * ^^^^^^^^^^^^^^^^ ++ */ ++ *p = '\0'; ++ do { ++ p--; ++ } while (p >= message_and_class && *p != '('); ++ p++; ++ ++ if (strlen(p) <= 0) ++ { ++ location->message = sr_strdup("Unable to find the '(' character identifying " ++ "the beginning of the exception class"); ++ goto fail; ++ } ++ stacktrace->exception_name = sr_strdup(p); ++ ++ /* /some/thing.rb:13:in `method': exception message (Exception::Class)\n\tfrom ... ++ * ^ ++ */ ++ p--; ++ if (p < message_and_class || *p != '(') ++ { ++ location->message = sr_strdup("Unable to find the '(' character identifying " ++ "the beginning of the exception class"); ++ goto fail; ++ } ++ ++ /* Throw away the message, it may contain sensitive data. */ ++ free(message_and_class); ++ message_and_class = p = NULL; ++ ++ /* /some/thing.rb:13:in `method': exception message (Exception::Class)\n\tfrom ... ++ * we're here now, increment the line ^^ ++ */ ++ location->column = 0; ++ location->line++; ++ ++ struct sr_ruby_frame *last_frame = stacktrace->frames; ++ while (*local_input) ++ { ++ /* The exception message can continue on the lines after the topmost frame ++ * - skip those. ++ */ ++ int skipped = sr_skip_string(&local_input, "\tfrom "); ++ if (!skipped) ++ { ++ location->message = sr_strdup("Frame header not found."); ++ goto fail; ++ } ++ location->column += skipped; ++ ++ last_frame->next = sr_ruby_frame_parse(&local_input, location); ++ if (!last_frame->next) ++ { ++ /* location->message is already set */ ++ goto fail; ++ } ++ ++ /* Eat newline (except at the end of file). */ ++ if (!sr_skip_char(&local_input, '\n') && *local_input != '\0') ++ { ++ location->message = sr_strdup("Expected newline after stacktrace frame."); ++ goto fail; ++ } ++ location->column = 0; ++ location->line++; ++ last_frame = last_frame->next; ++ } ++ ++ *input = local_input; ++ return stacktrace; ++ ++fail: ++ sr_ruby_stacktrace_free(stacktrace); ++ free(message_and_class); ++ return NULL; ++} ++ ++char * ++sr_ruby_stacktrace_to_json(struct sr_ruby_stacktrace *stacktrace) ++{ ++ struct sr_strbuf *strbuf = sr_strbuf_new(); ++ ++ /* Exception class name. */ ++ if (stacktrace->exception_name) ++ { ++ sr_strbuf_append_str(strbuf, ", \"exception_name\": "); ++ sr_json_append_escaped(strbuf, stacktrace->exception_name); ++ sr_strbuf_append_str(strbuf, "\n"); ++ } ++ ++ /* Frames. */ ++ if (stacktrace->frames) ++ { ++ struct sr_ruby_frame *frame = stacktrace->frames; ++ sr_strbuf_append_str(strbuf, ", \"stacktrace\":\n"); ++ while (frame) ++ { ++ if (frame == stacktrace->frames) ++ sr_strbuf_append_str(strbuf, " [ "); ++ else ++ sr_strbuf_append_str(strbuf, " , "); ++ ++ char *frame_json = sr_ruby_frame_to_json(frame); ++ char *indented_frame_json = sr_indent_except_first_line(frame_json, 8); ++ sr_strbuf_append_str(strbuf, indented_frame_json); ++ free(indented_frame_json); ++ free(frame_json); ++ frame = frame->next; ++ if (frame) ++ sr_strbuf_append_str(strbuf, "\n"); ++ } ++ ++ sr_strbuf_append_str(strbuf, " ]\n"); ++ } ++ ++ if (strbuf->len > 0) ++ strbuf->buf[0] = '{'; ++ else ++ sr_strbuf_append_char(strbuf, '{'); ++ ++ sr_strbuf_append_char(strbuf, '}'); ++ return sr_strbuf_free_nobuf(strbuf); ++} ++ ++struct sr_ruby_stacktrace * ++sr_ruby_stacktrace_from_json(struct sr_json_value *root, char **error_message) ++{ ++ if (!JSON_CHECK_TYPE(root, SR_JSON_OBJECT, "stacktrace")) ++ return NULL; ++ ++ struct sr_ruby_stacktrace *result = sr_ruby_stacktrace_new(); ++ ++ /* Exception name. */ ++ if (!JSON_READ_STRING(root, "exception_name", &result->exception_name)) ++ goto fail; ++ ++ /* Frames. */ ++ struct sr_json_value *stacktrace = json_element(root, "stacktrace"); ++ if (stacktrace) ++ { ++ if (!JSON_CHECK_TYPE(stacktrace, SR_JSON_ARRAY, "stacktrace")) ++ goto fail; ++ ++ struct sr_json_value *frame_json; ++ FOR_JSON_ARRAY(stacktrace, frame_json) ++ { ++ struct sr_ruby_frame *frame = sr_ruby_frame_from_json(frame_json, ++ error_message); ++ ++ if (!frame) ++ goto fail; ++ ++ result->frames = sr_ruby_frame_append(result->frames, frame); ++ } ++ } ++ ++ return result; ++ ++fail: ++ sr_ruby_stacktrace_free(result); ++ return NULL; ++} ++ ++char * ++sr_ruby_stacktrace_get_reason(struct sr_ruby_stacktrace *stacktrace) ++{ ++ char *exc = "Unknown error"; ++ char *file = ""; ++ uint32_t line = 0; ++ ++ struct sr_ruby_frame *frame = stacktrace->frames; ++ if (frame) ++ { ++ file = frame->file_name; ++ line = frame->file_line; ++ } ++ ++ if (stacktrace->exception_name) ++ exc = stacktrace->exception_name; ++ ++ return sr_asprintf("%s in %s:%"PRIu32, exc, file, line); ++} ++ ++static void ++ruby_append_bthash_text(struct sr_ruby_stacktrace *stacktrace, enum sr_bthash_flags flags, ++ struct sr_strbuf *strbuf) ++{ ++ sr_strbuf_append_strf(strbuf, "Exception: %s\n", OR_UNKNOWN(stacktrace->exception_name)); ++ sr_strbuf_append_char(strbuf, '\n'); ++} +diff --git a/tests/Makefile.am b/tests/Makefile.am +index d69f493..0eb33a5 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -1,5 +1,6 @@ + EXTRA_DIST = gdb_stacktraces \ + java_stacktraces \ ++ ruby_stacktraces \ + json_files \ + kerneloopses \ + programs \ +@@ -50,6 +51,8 @@ TESTSUITE_AT = \ + core_frame.at \ + core_thread.at \ + core_stacktrace.at \ ++ ruby_frame.at \ ++ ruby_stacktrace.at \ + normalize.at \ + metrics.at \ + cluster.at \ +diff --git a/tests/ruby_frame.at b/tests/ruby_frame.at +new file mode 100644 +index 0000000..1b1b9d0 +--- /dev/null ++++ b/tests/ruby_frame.at +@@ -0,0 +1,285 @@ ++# Checking the satyr. -*- Autotest -*- ++ ++AT_BANNER([Ruby frames]) ++ ++AT_TESTFUN([sr_ruby_frame_parse], ++[[ ++#include "ruby/frame.h" ++#include "utils.h" ++#include "location.h" ++#include ++ ++void ++check(char *input, char *file, unsigned line, ++ char *function, bool special, unsigned block_level, unsigned rescue_level) ++{ ++ struct sr_location location; ++ sr_location_init(&location); ++ ++ struct sr_ruby_frame *frame; ++ frame = sr_ruby_frame_parse(&input, &location); ++ ++ assert(frame); ++ assert(frame->file_name && 0 == strcmp(frame->file_name, file)); ++ assert(frame->file_line == line); ++ assert(frame->function_name && 0 == strcmp(frame->function_name, function)); ++ assert(!!frame->special_function == !!special); ++ assert(frame->block_level == block_level); ++ assert(frame->rescue_level == rescue_level); ++ assert(*input == '\0'); ++ ++ sr_ruby_frame_free(frame); ++} ++ ++int ++main(void) ++{ ++ check("/usr/share/ruby/vendor_ruby/will_crash.rb:13:in `rescue in block (2 levels) in func'", ++ "/usr/share/ruby/vendor_ruby/will_crash.rb", 13, "func", false, 2, 1); ++ ++ check("/home/u/work/will:crash/will_crash.rb:30:in `block in '", ++ "/home/u/work/will:crash/will_crash.rb", 30, "class:WillClass", true, 1, 0); ++ ++ check("./will_ruby_raise:8:in `
'", ++ "./will_ruby_raise", 8, "main", true, 0, 0); ++ ++ /* try failing */ ++ struct sr_location location; ++ sr_location_init(&location); ++ char *input = "i;dikasdfxc"; ++ ++ struct sr_ruby_frame *frame = sr_ruby_frame_parse(&input, &location); ++ assert(!frame); ++ assert(location.message); ++ ++ return 0; ++} ++]]) ++ ++AT_TESTFUN([sr_ruby_frame_cmp], ++[[ ++#include "ruby/frame.h" ++#include "utils.h" ++#include "location.h" ++#include ++ ++int ++main(void) ++{ ++ struct sr_location location; ++ char *line = "/usr/share/ruby/vendor_ruby/will_crash.rb:13:in `rescue in block (2 levels) in func'"; ++ char *input; ++ ++ sr_location_init(&location); ++ input = line; ++ struct sr_ruby_frame *frame1 = sr_ruby_frame_parse(&input, &location); ++ assert(frame1); ++ ++ sr_location_init(&location); ++ input = line; ++ struct sr_ruby_frame *frame2 = sr_ruby_frame_parse(&input, &location); ++ assert(frame1); ++ ++ assert(0 == sr_ruby_frame_cmp(frame1, frame2)); ++ assert(frame1 != frame2); ++ ++ frame2->file_line = 9000; ++ assert(0 != sr_ruby_frame_cmp(frame1, frame2)); ++ ++ sr_ruby_frame_free(frame1); ++ sr_ruby_frame_free(frame2); ++ ++ return 0; ++} ++]]) ++ ++AT_TESTFUN([sr_ruby_frame_dup], ++[[ ++#include "ruby/frame.h" ++#include "utils.h" ++#include "location.h" ++#include ++ ++int ++main(void) ++{ ++ struct sr_location location; ++ char *line = "/usr/share/ruby/vendor_ruby/will_crash.rb:13:in `rescue in block (2 levels) in func'"; ++ char *input; ++ ++ sr_location_init(&location); ++ input = line; ++ struct sr_ruby_frame *frame1 = sr_ruby_frame_parse(&input, &location); ++ assert(frame1); ++ ++ struct sr_ruby_frame *frame2 = sr_ruby_frame_dup(frame1, false); ++ ++ assert(0 == sr_ruby_frame_cmp(frame1, frame2)); ++ assert(frame1 != frame2); ++ assert(frame1->function_name != frame2->function_name); ++ ++ return 0; ++} ++]]) ++ ++AT_TESTFUN([sr_ruby_frame_append], ++[[ ++#include "ruby/frame.h" ++#include "utils.h" ++#include "location.h" ++#include ++ ++int ++main(void) ++{ ++ struct sr_ruby_frame *frame1 = sr_ruby_frame_new(); ++ struct sr_ruby_frame *frame2 = sr_ruby_frame_new(); ++ ++ assert(frame1->next == frame2->next); ++ assert(frame1->next == NULL); ++ ++ sr_ruby_frame_append(frame1, frame2); ++ assert(frame1->next == frame2); ++ ++ sr_ruby_frame_free(frame1); ++ sr_ruby_frame_free(frame2); ++ ++ return 0; ++} ++]]) ++ ++AT_TESTFUN([sr_ruby_frame_to_json], ++[[ ++#include "ruby/frame.h" ++#include "utils.h" ++#include "location.h" ++#include ++ ++int ++main(void) ++{ ++ struct sr_location location; ++ char *line = "/usr/share/ruby/vendor_ruby/will_crash.rb:13:in `rescue in block (2 levels) in func'"; ++ char *input; ++ ++ sr_location_init(&location); ++ input = line; ++ struct sr_ruby_frame *frame1 = sr_ruby_frame_parse(&input, &location); ++ assert(frame1); ++ ++ char *expected = "{ \"file_name\": \"/usr/share/ruby/vendor_ruby/will_crash.rb\"\n" ++ ", \"file_line\": 13\n" ++ ", \"function_name\": \"func\"\n" ++ ", \"block_level\": 2\n" ++ ", \"rescue_level\": 1\n" ++ "}"; ++ ++ char *json = sr_ruby_frame_to_json(frame1); ++ assert(0 == strcmp(json, expected)); ++ ++ return 0; ++} ++]]) ++ ++AT_TESTFUN([sr_ruby_frame_from_json], ++[[ ++#include "ruby/frame.h" ++#include "utils.h" ++#include "json.h" ++#include "location.h" ++#include ++ ++void ++check(char *input) ++{ ++ struct sr_location location; ++ sr_location_init(&location); ++ ++ struct sr_ruby_frame *frame1 = sr_ruby_frame_parse(&input, &location); ++ assert(frame1); ++ ++ char *json = sr_ruby_frame_to_json(frame1); ++ char *error = NULL; ++ ++ struct sr_json_value *root = sr_json_parse(json, &error); ++ assert(root); ++ struct sr_ruby_frame *frame2 = sr_ruby_frame_from_json(root, &error); ++ assert(frame2); ++ assert(0 == sr_ruby_frame_cmp(frame1, frame2)); ++ ++ return 0; ++} ++ ++int ++main(void) ++{ ++ check("/usr/share/ruby/vendor_ruby/will_crash.rb:13:in `rescue in block (2 levels) in func'"); ++ check("/usr/share/ruby/vendor:ruby/will_crash.rb:13:in `block (22 levels) in '"); ++ ++ return 0; ++} ++]]) ++ ++AT_TESTFUN([sr_ruby_frame_append_to_str], ++[[ ++#include "ruby/frame.h" ++#include "utils.h" ++#include "location.h" ++#include "strbuf.h" ++#include ++ ++int ++main(void) ++{ ++ struct sr_location location; ++ char *line = "/usr/share/ruby/vendor_ruby/will_crash.rb:13:in `rescue in block (2 levels) in func'"; ++ char *input; ++ ++ sr_location_init(&location); ++ input = line; ++ struct sr_ruby_frame *frame1 = sr_ruby_frame_parse(&input, &location); ++ assert(frame1); ++ ++ struct sr_strbuf *strbuf = sr_strbuf_new(); ++ sr_ruby_frame_append_to_str(frame1, strbuf); ++ char *result = sr_strbuf_free_nobuf(strbuf); ++ ++ assert(0 == strcmp(result, "rescue in block (2 levels) in func in /usr/share/ruby/vendor_ruby/will_crash.rb:13")); ++ ++ return 0; ++} ++]]) ++ ++AT_TESTFUN([sr_ruby_frame_generic_functions], ++[[ ++#include "ruby/frame.h" ++#include "frame.h" ++#include "utils.h" ++#include "location.h" ++#include "strbuf.h" ++#include ++ ++int ++main(void) ++{ ++ struct sr_location location; ++ char *line = "/usr/share/ruby/vendor_ruby/will_crash.rb:13:in `rescue in block (2 levels) in func'"; ++ char *input; ++ ++ sr_location_init(&location); ++ input = line; ++ struct sr_ruby_frame *frame1 = sr_ruby_frame_parse(&input, &location); ++ assert(frame1); ++ ++ assert(sr_frame_next(frame1) == NULL); ++ ++ struct sr_strbuf *strbuf = sr_strbuf_new(); ++ sr_frame_append_to_str((struct sr_frame*)frame1, strbuf); ++ char *result = sr_strbuf_free_nobuf(strbuf); ++ ++ assert(0 == strcmp(result, "rescue in block (2 levels) in func in /usr/share/ruby/vendor_ruby/will_crash.rb:13")); ++ ++ sr_frame_free((struct sr_frame*)frame1); ++ return 0; ++} ++]]) +diff --git a/tests/ruby_stacktrace.at b/tests/ruby_stacktrace.at +new file mode 100644 +index 0000000..5a2d901 +--- /dev/null ++++ b/tests/ruby_stacktrace.at +@@ -0,0 +1,316 @@ ++# Checking the satyr. -*- Autotest -*- ++ ++AT_BANNER([Ruby stacktrace]) ++ ++AT_TESTFUN([sr_ruby_stacktrace_parse], ++[[ ++#include "ruby/stacktrace.h" ++#include "ruby/frame.h" ++#include "utils.h" ++#include "location.h" ++#include ++#include ++#include ++ ++void ++check(char *filename, char *exc_name, unsigned frame_count, struct sr_ruby_frame *top_frame, struct sr_ruby_frame *second_frame, struct sr_ruby_frame *bottom_frame) ++{ ++ char *error_message = NULL; ++ const char *file_contents = sr_file_to_string(filename, &error_message); ++ const char *input = file_contents; ++ struct sr_location location; ++ sr_location_init(&location); ++ ++ struct sr_ruby_stacktrace *stacktrace = sr_ruby_stacktrace_parse(&input, &location); ++ assert(stacktrace); ++ assert(*input == '\0'); ++ assert(0 == strcmp(stacktrace->exception_name, exc_name)); ++ ++ struct sr_ruby_frame *frame = stacktrace->frames; ++ int i = 0; ++ while (frame) ++ { ++ if (i==0 && top_frame) ++ { ++ assert(sr_ruby_frame_cmp(frame, top_frame) == 0); ++ } ++ else if (i == 1 && second_frame) ++ { ++ assert(sr_ruby_frame_cmp(frame, second_frame) == 0); ++ } ++ ++ frame = frame->next; ++ i++; ++ } ++ ++ assert(i == frame_count); ++ if (frame && bottom_frame) ++ { ++ assert(sr_ruby_frame_cmp(frame, bottom_frame) == 0); ++ } ++ ++ sr_ruby_stacktrace_free(stacktrace); ++ free(file_contents); ++} ++ ++int ++main(void) ++{ ++ struct sr_ruby_frame top = { ++ .type = SR_REPORT_RUBY, ++ .file_name = "/usr/share/ruby/vendor_ruby/will_crash.rb", ++ .file_line = 13, ++ .special_function = false, ++ .function_name = "func", ++ .block_level = 2, ++ .rescue_level = 1 ++ }; ++ struct sr_ruby_frame second = { ++ .type = SR_REPORT_RUBY, ++ .file_name = "/usr/share/ruby/vendor_ruby/will_crash.rb", ++ .file_line = 10, ++ .special_function = false, ++ .function_name = "func", ++ .block_level = 2, ++ .rescue_level = 0 ++ }; ++ struct sr_ruby_frame bottom = { ++ .type = SR_REPORT_RUBY, ++ .file_name = "/usr/bin/will_ruby_raise", ++ .file_line = 8, ++ .special_function = true, ++ .function_name = "main", ++ .block_level = 0, ++ .rescue_level = 0 ++ }; ++ check("../../ruby_stacktraces/ruby-01", "Wrap::MyException", 21, &top, &second, &bottom); ++ ++ struct sr_ruby_frame top2 = { ++ .type = SR_REPORT_RUBY, ++ .file_name = "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/class_module.rb", ++ .file_line = 173, ++ .special_function = false, ++ .function_name = "aref_prefix", ++ .block_level = 0, ++ .rescue_level = 0 ++ }; ++ struct sr_ruby_frame second2 = { ++ .type = SR_REPORT_RUBY, ++ .file_name = "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/class_module.rb", ++ .file_line = 181, ++ .special_function = false, ++ .function_name = "aref", ++ .block_level = 0, ++ .rescue_level = 0 ++ }; ++ check("../../ruby_stacktraces/ruby-02", "NotImplementedError", 37, &top2, &second2, NULL); ++ ++ check("../../ruby_stacktraces/ruby-03", "RuntimeError", 4, NULL, NULL, NULL); ++ ++ struct sr_ruby_frame top4 = { ++ .type = SR_REPORT_RUBY, ++ .file_name = "/usr/share/rubygems/rubygems/basic_specification.rb", ++ .file_line = 50, ++ .special_function = false, ++ .function_name = "contains_requirable_file?", ++ .block_level = 3, ++ .rescue_level = 0 ++ }; ++ struct sr_ruby_frame second4 = { ++ .type = SR_REPORT_RUBY, ++ .file_name = "/usr/share/rubygems/rubygems/basic_specification.rb", ++ .file_line = 50, ++ .special_function = false, ++ .function_name = "each", ++ .block_level = 0, ++ .rescue_level = 0 ++ }; ++ check("../../ruby_stacktraces/ruby-04", "SignalException", 39, &top4, &second4, NULL); ++ ++ return 0; ++} ++]]) ++ ++AT_TESTFUN([sr_ruby_stacktrace_dup], ++[[ ++#include "ruby/stacktrace.h" ++#include "ruby/frame.h" ++#include "utils.h" ++#include "location.h" ++#include ++#include ++#include ++ ++void ++check(char *filename) ++{ ++ char *error_message = NULL; ++ const char *file_contents = sr_file_to_string(filename, &error_message); ++ const char *input = file_contents; ++ struct sr_location location; ++ sr_location_init(&location); ++ ++ struct sr_ruby_stacktrace *stacktrace1 = sr_ruby_stacktrace_parse(&input, &location); ++ struct sr_ruby_stacktrace *stacktrace2 = sr_ruby_stacktrace_dup(stacktrace1); ++ ++ assert(stacktrace1 != stacktrace2); ++ assert(0 == strcmp(stacktrace1->exception_name, stacktrace2->exception_name)); ++ ++ struct sr_ruby_frame *f1 = stacktrace1->frames; ++ struct sr_ruby_frame *f2 = stacktrace2->frames; ++ ++ while (f1 && f2) ++ { ++ assert(0 == sr_ruby_frame_cmp(f1, f2)); ++ f1 = f1->next; ++ f2 = f2->next; ++ } ++ assert(f1 == NULL); ++ assert(f2 == NULL); ++ ++ sr_ruby_stacktrace_free(stacktrace1); ++ sr_ruby_stacktrace_free(stacktrace2); ++ free(file_contents); ++} ++ ++int ++main(void) ++{ ++ check("../../ruby_stacktraces/ruby-01"); ++ check("../../ruby_stacktraces/ruby-02"); ++ check("../../ruby_stacktraces/ruby-03"); ++ check("../../ruby_stacktraces/ruby-04"); ++ ++ return 0; ++} ++]]) ++ ++AT_TESTFUN([sr_ruby_stacktrace_get_reason], ++[[ ++#include "ruby/stacktrace.h" ++#include "ruby/frame.h" ++#include "utils.h" ++#include "location.h" ++#include ++#include ++#include ++ ++int ++main(void) ++{ ++ char *error_message = NULL; ++ const char *file_contents = sr_file_to_string("../../ruby_stacktraces/ruby-03", &error_message); ++ const char *input = file_contents; ++ struct sr_location location; ++ sr_location_init(&location); ++ struct sr_ruby_stacktrace *stacktrace1 = sr_ruby_stacktrace_parse(&input, &location); ++ ++ char *reason = sr_ruby_stacktrace_get_reason(stacktrace1); ++ char *expected = "RuntimeError in /usr/share/gems/gems/openshift-origin-node-1.18.0.1/lib/openshift-origin-node/utils/tc.rb:103"; ++ assert(0 == strcmp(reason, expected)); ++ ++ sr_ruby_stacktrace_free(stacktrace1); ++ free(file_contents); ++ ++ return 0; ++} ++]]) ++ ++AT_TESTFUN([sr_ruby_stacktrace_to_json], ++[[ ++#include "ruby/stacktrace.h" ++#include "ruby/frame.h" ++#include "utils.h" ++#include "location.h" ++#include ++#include ++#include ++ ++void ++check(char *filename, char *json_filename) ++{ ++ char *error_message = NULL; ++ const char *file_contents = sr_file_to_string(filename, &error_message); ++ const char *input = file_contents; ++ struct sr_location location; ++ sr_location_init(&location); ++ ++ struct sr_ruby_stacktrace *stacktrace1 = sr_ruby_stacktrace_parse(&input, &location); ++ ++ char *expected = sr_file_to_string(json_filename, &error_message); ++ char *json = sr_ruby_stacktrace_to_json(stacktrace1); ++ ++ assert(0 == strcmp(json, expected)); ++ ++ sr_ruby_stacktrace_free(stacktrace1); ++ free(json); ++ free(file_contents); ++} ++ ++int ++main(void) ++{ ++ check("../../ruby_stacktraces/ruby-01", "../../ruby_stacktraces/ruby-01-expected-json"); ++ check("../../ruby_stacktraces/ruby-02", "../../ruby_stacktraces/ruby-02-expected-json"); ++ check("../../ruby_stacktraces/ruby-03", "../../ruby_stacktraces/ruby-03-expected-json"); ++ check("../../ruby_stacktraces/ruby-04", "../../ruby_stacktraces/ruby-04-expected-json"); ++ ++ return 0; ++} ++]]) ++ ++AT_TESTFUN([sr_ruby_stacktrace_from_json], ++[[ ++#include "ruby/stacktrace.h" ++#include "ruby/frame.h" ++#include "utils.h" ++#include "location.h" ++#include ++#include ++#include ++ ++void ++check(char *filename) ++{ ++ char *error_message = NULL; ++ const char *file_contents = sr_file_to_string(filename, &error_message); ++ const char *input = file_contents; ++ struct sr_location location; ++ sr_location_init(&location); ++ ++ struct sr_ruby_stacktrace *stacktrace1 = sr_ruby_stacktrace_parse(&input, &location); ++ ++ char *json = sr_ruby_stacktrace_to_json(stacktrace1); ++ struct sr_ruby_stacktrace *stacktrace2 = sr_stacktrace_from_json_text(SR_REPORT_RUBY, json, &error_message); ++ ++ assert(0 == strcmp(stacktrace1->exception_name, stacktrace2->exception_name)); ++ ++ struct sr_ruby_frame *f1 = stacktrace1->frames; ++ struct sr_ruby_frame *f2 = stacktrace2->frames; ++ ++ while (f1 && f2) ++ { ++ assert(0 == sr_ruby_frame_cmp(f1, f2)); ++ f1 = f1->next; ++ f2 = f2->next; ++ } ++ assert(f1 == NULL); ++ assert(f2 == NULL); ++ ++ sr_ruby_stacktrace_free(stacktrace1); ++ sr_ruby_stacktrace_free(stacktrace2); ++ free(json); ++ free(file_contents); ++} ++ ++int ++main(void) ++{ ++ check("../../ruby_stacktraces/ruby-01"); ++ check("../../ruby_stacktraces/ruby-02"); ++ check("../../ruby_stacktraces/ruby-03"); ++ check("../../ruby_stacktraces/ruby-04"); ++ ++ return 0; ++} ++]]) +diff --git a/tests/ruby_stacktraces/ruby-01 b/tests/ruby_stacktraces/ruby-01 +new file mode 100644 +index 0000000..a635dd9 +--- /dev/null ++++ b/tests/ruby_stacktraces/ruby-01 +@@ -0,0 +1,23 @@ ++/usr/share/ruby/vendor_ruby/will_crash.rb:13:in `rescue in block (2 levels) in func': Exception ++successfully ++raised. (Wrap::MyException) ++ from /usr/share/ruby/vendor_ruby/will_crash.rb:10:in `block (2 levels) in func' ++ from /usr/share/ruby/vendor_ruby/will_crash.rb:9:in `times' ++ from /usr/share/ruby/vendor_ruby/will_crash.rb:9:in `block in func' ++ from /usr/share/ruby/vendor_ruby/will_crash.rb:8:in `times' ++ from /usr/share/ruby/vendor_ruby/will_crash.rb:8:in `func' ++ from /usr/share/ruby/vendor_ruby/will_crash.rb:30:in `block in ' ++ from /usr/share/ruby/vendor_ruby/will_crash.rb:28:in `times' ++ from /usr/share/ruby/vendor_ruby/will_crash.rb:28:in `rescue in rescue in rescue in ' ++ from /usr/share/ruby/vendor_ruby/will_crash.rb:25:in `rescue in rescue in ' ++ from /usr/share/ruby/vendor_ruby/will_crash.rb:22:in `rescue in ' ++ from /usr/share/ruby/vendor_ruby/will_crash.rb:19:in `' ++ from /usr/share/ruby/vendor_ruby/will_crash.rb:6:in `' ++ from /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require' ++ from /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require' ++ from /usr/bin/will_ruby_raise:11:in `block (2 levels) in ' ++ from /usr/bin/will_ruby_raise:10:in `times' ++ from /usr/bin/will_ruby_raise:10:in `block in ' ++ from /usr/bin/will_ruby_raise:9:in `times' ++ from /usr/bin/will_ruby_raise:9:in `' ++ from /usr/bin/will_ruby_raise:8:in `
' +diff --git a/tests/ruby_stacktraces/ruby-01-expected-json b/tests/ruby_stacktraces/ruby-01-expected-json +new file mode 100644 +index 0000000..493fb18 +--- /dev/null ++++ b/tests/ruby_stacktraces/ruby-01-expected-json +@@ -0,0 +1,97 @@ ++{ "exception_name": "Wrap::MyException" ++, "stacktrace": ++ [ { "file_name": "/usr/share/ruby/vendor_ruby/will_crash.rb" ++ , "file_line": 13 ++ , "function_name": "func" ++ , "block_level": 2 ++ , "rescue_level": 1 ++ } ++ , { "file_name": "/usr/share/ruby/vendor_ruby/will_crash.rb" ++ , "file_line": 10 ++ , "function_name": "func" ++ , "block_level": 2 ++ } ++ , { "file_name": "/usr/share/ruby/vendor_ruby/will_crash.rb" ++ , "file_line": 9 ++ , "function_name": "times" ++ } ++ , { "file_name": "/usr/share/ruby/vendor_ruby/will_crash.rb" ++ , "file_line": 9 ++ , "function_name": "func" ++ , "block_level": 1 ++ } ++ , { "file_name": "/usr/share/ruby/vendor_ruby/will_crash.rb" ++ , "file_line": 8 ++ , "function_name": "times" ++ } ++ , { "file_name": "/usr/share/ruby/vendor_ruby/will_crash.rb" ++ , "file_line": 8 ++ , "function_name": "func" ++ } ++ , { "file_name": "/usr/share/ruby/vendor_ruby/will_crash.rb" ++ , "file_line": 30 ++ , "special_function": "class:WillClass" ++ , "block_level": 1 ++ } ++ , { "file_name": "/usr/share/ruby/vendor_ruby/will_crash.rb" ++ , "file_line": 28 ++ , "function_name": "times" ++ } ++ , { "file_name": "/usr/share/ruby/vendor_ruby/will_crash.rb" ++ , "file_line": 28 ++ , "special_function": "class:WillClass" ++ , "rescue_level": 3 ++ } ++ , { "file_name": "/usr/share/ruby/vendor_ruby/will_crash.rb" ++ , "file_line": 25 ++ , "special_function": "class:WillClass" ++ , "rescue_level": 2 ++ } ++ , { "file_name": "/usr/share/ruby/vendor_ruby/will_crash.rb" ++ , "file_line": 22 ++ , "special_function": "class:WillClass" ++ , "rescue_level": 1 ++ } ++ , { "file_name": "/usr/share/ruby/vendor_ruby/will_crash.rb" ++ , "file_line": 19 ++ , "special_function": "class:WillClass" ++ } ++ , { "file_name": "/usr/share/ruby/vendor_ruby/will_crash.rb" ++ , "file_line": 6 ++ , "special_function": "top (required)" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/core_ext/kernel_require.rb" ++ , "file_line": 55 ++ , "function_name": "require" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/core_ext/kernel_require.rb" ++ , "file_line": 55 ++ , "function_name": "require" ++ } ++ , { "file_name": "/usr/bin/will_ruby_raise" ++ , "file_line": 11 ++ , "special_function": "module:WillModule" ++ , "block_level": 2 ++ } ++ , { "file_name": "/usr/bin/will_ruby_raise" ++ , "file_line": 10 ++ , "function_name": "times" ++ } ++ , { "file_name": "/usr/bin/will_ruby_raise" ++ , "file_line": 10 ++ , "special_function": "module:WillModule" ++ , "block_level": 1 ++ } ++ , { "file_name": "/usr/bin/will_ruby_raise" ++ , "file_line": 9 ++ , "function_name": "times" ++ } ++ , { "file_name": "/usr/bin/will_ruby_raise" ++ , "file_line": 9 ++ , "special_function": "module:WillModule" ++ } ++ , { "file_name": "/usr/bin/will_ruby_raise" ++ , "file_line": 8 ++ , "special_function": "main" ++ } ] ++} +\ No newline at end of file +diff --git a/tests/ruby_stacktraces/ruby-02 b/tests/ruby_stacktraces/ruby-02 +new file mode 100644 +index 0000000..dd24a02 +--- /dev/null ++++ b/tests/ruby_stacktraces/ruby-02 +@@ -0,0 +1,37 @@ ++/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/class_module.rb:173:in `aref_prefix': missing aref_prefix for RDoc::SingleClass (NotImplementedError) ++ from /usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/class_module.rb:181:in `aref' ++ from /usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/template/darkfish/class.rhtml:47:in `block in generate_class' ++ from /usr/share/ruby/erb.rb:850:in `eval' ++ from /usr/share/ruby/erb.rb:850:in `result' ++ from /usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb:724:in `template_result' ++ from /usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb:703:in `block in render_template' ++ from /usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb:698:in `open' ++ from /usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb:698:in `open' ++ from /usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb:698:in `render_template' ++ from /usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb:351:in `generate_class' ++ from /usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb:371:in `block in generate_class_files' ++ from /usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb:368:in `each' ++ from /usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb:368:in `generate_class_files' ++ from /usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb:245:in `generate' ++ from /usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/rubygems_hook.rb:137:in `block in document' ++ from /usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/rubygems_hook.rb:134:in `chdir' ++ from /usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/rubygems_hook.rb:134:in `document' ++ from /usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/rubygems_hook.rb:202:in `generate' ++ from /usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/rubygems_hook.rb:56:in `block in generation_hook' ++ from /usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/rubygems_hook.rb:55:in `each' ++ from /usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/rubygems_hook.rb:55:in `generation_hook' ++ from /usr/local/share/ruby/site_ruby/rubygems/request_set.rb:188:in `call' ++ from /usr/local/share/ruby/site_ruby/rubygems/request_set.rb:188:in `block in install' ++ from /usr/local/share/ruby/site_ruby/rubygems/request_set.rb:187:in `each' ++ from /usr/local/share/ruby/site_ruby/rubygems/request_set.rb:187:in `install' ++ from /usr/local/share/ruby/site_ruby/rubygems/dependency_installer.rb:394:in `install' ++ from /usr/local/share/ruby/site_ruby/rubygems/commands/update_command.rb:211:in `update_gem' ++ from /usr/local/share/ruby/site_ruby/rubygems/commands/update_command.rb:223:in `block in update_gems' ++ from /usr/local/share/ruby/site_ruby/rubygems/commands/update_command.rb:222:in `each' ++ from /usr/local/share/ruby/site_ruby/rubygems/commands/update_command.rb:222:in `update_gems' ++ from /usr/local/share/ruby/site_ruby/rubygems/commands/update_command.rb:98:in `execute' ++ from /usr/local/share/ruby/site_ruby/rubygems/command.rb:307:in `invoke_with_build_args' ++ from /usr/local/share/ruby/site_ruby/rubygems/command_manager.rb:168:in `process_args' ++ from /usr/local/share/ruby/site_ruby/rubygems/command_manager.rb:138:in `run' ++ from /usr/local/share/ruby/site_ruby/rubygems/gem_runner.rb:54:in `run' ++ from /bin/gem:21:in `
' +\ No newline at end of file +diff --git a/tests/ruby_stacktraces/ruby-02-expected-json b/tests/ruby_stacktraces/ruby-02-expected-json +new file mode 100644 +index 0000000..1bef95c +--- /dev/null ++++ b/tests/ruby_stacktraces/ruby-02-expected-json +@@ -0,0 +1,158 @@ ++{ "exception_name": "NotImplementedError" ++, "stacktrace": ++ [ { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/class_module.rb" ++ , "file_line": 173 ++ , "function_name": "aref_prefix" ++ } ++ , { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/class_module.rb" ++ , "file_line": 181 ++ , "function_name": "aref" ++ } ++ , { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/template/darkfish/class.rhtml" ++ , "file_line": 47 ++ , "function_name": "generate_class" ++ , "block_level": 1 ++ } ++ , { "file_name": "/usr/share/ruby/erb.rb" ++ , "file_line": 850 ++ , "function_name": "eval" ++ } ++ , { "file_name": "/usr/share/ruby/erb.rb" ++ , "file_line": 850 ++ , "function_name": "result" ++ } ++ , { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb" ++ , "file_line": 724 ++ , "function_name": "template_result" ++ } ++ , { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb" ++ , "file_line": 703 ++ , "function_name": "render_template" ++ , "block_level": 1 ++ } ++ , { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb" ++ , "file_line": 698 ++ , "function_name": "open" ++ } ++ , { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb" ++ , "file_line": 698 ++ , "function_name": "open" ++ } ++ , { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb" ++ , "file_line": 698 ++ , "function_name": "render_template" ++ } ++ , { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb" ++ , "file_line": 351 ++ , "function_name": "generate_class" ++ } ++ , { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb" ++ , "file_line": 371 ++ , "function_name": "generate_class_files" ++ , "block_level": 1 ++ } ++ , { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb" ++ , "file_line": 368 ++ , "function_name": "each" ++ } ++ , { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb" ++ , "file_line": 368 ++ , "function_name": "generate_class_files" ++ } ++ , { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/generator/darkfish.rb" ++ , "file_line": 245 ++ , "function_name": "generate" ++ } ++ , { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/rubygems_hook.rb" ++ , "file_line": 137 ++ , "function_name": "document" ++ , "block_level": 1 ++ } ++ , { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/rubygems_hook.rb" ++ , "file_line": 134 ++ , "function_name": "chdir" ++ } ++ , { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/rubygems_hook.rb" ++ , "file_line": 134 ++ , "function_name": "document" ++ } ++ , { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/rubygems_hook.rb" ++ , "file_line": 202 ++ , "function_name": "generate" ++ } ++ , { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/rubygems_hook.rb" ++ , "file_line": 56 ++ , "function_name": "generation_hook" ++ , "block_level": 1 ++ } ++ , { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/rubygems_hook.rb" ++ , "file_line": 55 ++ , "function_name": "each" ++ } ++ , { "file_name": "/usr/share/gems/gems/rdoc-4.1.1/lib/rdoc/rubygems_hook.rb" ++ , "file_line": 55 ++ , "function_name": "generation_hook" ++ } ++ , { "file_name": "/usr/local/share/ruby/site_ruby/rubygems/request_set.rb" ++ , "file_line": 188 ++ , "function_name": "call" ++ } ++ , { "file_name": "/usr/local/share/ruby/site_ruby/rubygems/request_set.rb" ++ , "file_line": 188 ++ , "function_name": "install" ++ , "block_level": 1 ++ } ++ , { "file_name": "/usr/local/share/ruby/site_ruby/rubygems/request_set.rb" ++ , "file_line": 187 ++ , "function_name": "each" ++ } ++ , { "file_name": "/usr/local/share/ruby/site_ruby/rubygems/request_set.rb" ++ , "file_line": 187 ++ , "function_name": "install" ++ } ++ , { "file_name": "/usr/local/share/ruby/site_ruby/rubygems/dependency_installer.rb" ++ , "file_line": 394 ++ , "function_name": "install" ++ } ++ , { "file_name": "/usr/local/share/ruby/site_ruby/rubygems/commands/update_command.rb" ++ , "file_line": 211 ++ , "function_name": "update_gem" ++ } ++ , { "file_name": "/usr/local/share/ruby/site_ruby/rubygems/commands/update_command.rb" ++ , "file_line": 223 ++ , "function_name": "update_gems" ++ , "block_level": 1 ++ } ++ , { "file_name": "/usr/local/share/ruby/site_ruby/rubygems/commands/update_command.rb" ++ , "file_line": 222 ++ , "function_name": "each" ++ } ++ , { "file_name": "/usr/local/share/ruby/site_ruby/rubygems/commands/update_command.rb" ++ , "file_line": 222 ++ , "function_name": "update_gems" ++ } ++ , { "file_name": "/usr/local/share/ruby/site_ruby/rubygems/commands/update_command.rb" ++ , "file_line": 98 ++ , "function_name": "execute" ++ } ++ , { "file_name": "/usr/local/share/ruby/site_ruby/rubygems/command.rb" ++ , "file_line": 307 ++ , "function_name": "invoke_with_build_args" ++ } ++ , { "file_name": "/usr/local/share/ruby/site_ruby/rubygems/command_manager.rb" ++ , "file_line": 168 ++ , "function_name": "process_args" ++ } ++ , { "file_name": "/usr/local/share/ruby/site_ruby/rubygems/command_manager.rb" ++ , "file_line": 138 ++ , "function_name": "run" ++ } ++ , { "file_name": "/usr/local/share/ruby/site_ruby/rubygems/gem_runner.rb" ++ , "file_line": 54 ++ , "function_name": "run" ++ } ++ , { "file_name": "/bin/gem" ++ , "file_line": 21 ++ , "special_function": "main" ++ } ] ++} +\ No newline at end of file +diff --git a/tests/ruby_stacktraces/ruby-03 b/tests/ruby_stacktraces/ruby-03 +new file mode 100644 +index 0000000..d931254 +--- /dev/null ++++ b/tests/ruby_stacktraces/ruby-03 +@@ -0,0 +1,4 @@ ++/usr/share/gems/gems/openshift-origin-node-1.18.0.1/lib/openshift-origin-node/utils/tc.rb:103:in `get_interface_mtu': Unable to determine external network interface IP address. (RuntimeError) ++ from /usr/share/gems/gems/openshift-origin-node-1.18.0.1/lib/openshift-origin-node/utils/tc.rb:77:in `initialize' ++ from /sbin/oo-admin-ctl-tc:24:in `new' ++ from /sbin/oo-admin-ctl-tc:24:in `
' +\ No newline at end of file +diff --git a/tests/ruby_stacktraces/ruby-03-expected-json b/tests/ruby_stacktraces/ruby-03-expected-json +new file mode 100644 +index 0000000..a1fa1f8 +--- /dev/null ++++ b/tests/ruby_stacktraces/ruby-03-expected-json +@@ -0,0 +1,19 @@ ++{ "exception_name": "RuntimeError" ++, "stacktrace": ++ [ { "file_name": "/usr/share/gems/gems/openshift-origin-node-1.18.0.1/lib/openshift-origin-node/utils/tc.rb" ++ , "file_line": 103 ++ , "function_name": "get_interface_mtu" ++ } ++ , { "file_name": "/usr/share/gems/gems/openshift-origin-node-1.18.0.1/lib/openshift-origin-node/utils/tc.rb" ++ , "file_line": 77 ++ , "function_name": "initialize" ++ } ++ , { "file_name": "/sbin/oo-admin-ctl-tc" ++ , "file_line": 24 ++ , "function_name": "new" ++ } ++ , { "file_name": "/sbin/oo-admin-ctl-tc" ++ , "file_line": 24 ++ , "special_function": "main" ++ } ] ++} +\ No newline at end of file +diff --git a/tests/ruby_stacktraces/ruby-04 b/tests/ruby_stacktraces/ruby-04 +new file mode 100644 +index 0000000..12388a7 +--- /dev/null ++++ b/tests/ruby_stacktraces/ruby-04 +@@ -0,0 +1,39 @@ ++/usr/share/rubygems/rubygems/basic_specification.rb:50:in `block (3 levels) in contains_requirable_file?': SIGTERM (SignalException) ++ from /usr/share/rubygems/rubygems/basic_specification.rb:50:in `each' ++ from /usr/share/rubygems/rubygems/basic_specification.rb:50:in `any?' ++ from /usr/share/rubygems/rubygems/basic_specification.rb:50:in `block (2 levels) in contains_requirable_file?' ++ from /usr/share/rubygems/rubygems/basic_specification.rb:49:in `each' ++ from /usr/share/rubygems/rubygems/basic_specification.rb:49:in `any?' ++ from /usr/share/rubygems/rubygems/basic_specification.rb:49:in `block in contains_requirable_file?' ++ from /usr/share/rubygems/rubygems/basic_specification.rb:45:in `each' ++ from /usr/share/rubygems/rubygems/basic_specification.rb:45:in `any?' ++ from /usr/share/rubygems/rubygems/basic_specification.rb:45:in `contains_requirable_file?' ++ from /usr/share/rubygems/rubygems/specification.rb:898:in `block in find_inactive_by_path' ++ from /usr/share/rubygems/rubygems/specification.rb:897:in `each' ++ from /usr/share/rubygems/rubygems/specification.rb:897:in `find' ++ from /usr/share/rubygems/rubygems/specification.rb:897:in `find_inactive_by_path' ++ from /usr/share/rubygems/rubygems.rb:183:in `try_activate' ++ from /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:132:in `rescue in require' ++ from /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:144:in `require' ++ from /usr/share/gems/gems/compass-0.12.2/lib/compass/sass_extensions/sprites/engines/chunky_png_engine.rb:2:in `' ++ from /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require' ++ from /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require' ++ from /usr/share/gems/gems/compass-0.12.2/lib/compass/sass_extensions/sprites/engines.rb:25:in `' ++ from /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require' ++ from /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require' ++ from /usr/share/gems/gems/compass-0.12.2/lib/compass/sass_extensions/sprites.rb:18:in `' ++ from /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require' ++ from /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require' ++ from /usr/share/gems/gems/compass-0.12.2/lib/compass/sass_extensions.rb:10:in `' ++ from /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require' ++ from /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require' ++ from /usr/share/gems/gems/compass-0.12.2/lib/compass.rb:5:in `block in ' ++ from /usr/share/gems/gems/compass-0.12.2/lib/compass.rb:4:in `each' ++ from /usr/share/gems/gems/compass-0.12.2/lib/compass.rb:4:in `' ++ from /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require' ++ from /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require' ++ from /usr/share/gems/gems/compass-0.12.2/bin/compass:20:in `block in ' ++ from /usr/share/gems/gems/compass-0.12.2/bin/compass:8:in `fallback_load_path' ++ from /usr/share/gems/gems/compass-0.12.2/bin/compass:19:in `' ++ from /usr/bin/compass:23:in `load' ++ from /usr/bin/compass:23:in `
' +\ No newline at end of file +diff --git a/tests/ruby_stacktraces/ruby-04-expected-json b/tests/ruby_stacktraces/ruby-04-expected-json +new file mode 100644 +index 0000000..22bca55 +--- /dev/null ++++ b/tests/ruby_stacktraces/ruby-04-expected-json +@@ -0,0 +1,166 @@ ++{ "exception_name": "SignalException" ++, "stacktrace": ++ [ { "file_name": "/usr/share/rubygems/rubygems/basic_specification.rb" ++ , "file_line": 50 ++ , "function_name": "contains_requirable_file?" ++ , "block_level": 3 ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/basic_specification.rb" ++ , "file_line": 50 ++ , "function_name": "each" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/basic_specification.rb" ++ , "file_line": 50 ++ , "function_name": "any?" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/basic_specification.rb" ++ , "file_line": 50 ++ , "function_name": "contains_requirable_file?" ++ , "block_level": 2 ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/basic_specification.rb" ++ , "file_line": 49 ++ , "function_name": "each" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/basic_specification.rb" ++ , "file_line": 49 ++ , "function_name": "any?" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/basic_specification.rb" ++ , "file_line": 49 ++ , "function_name": "contains_requirable_file?" ++ , "block_level": 1 ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/basic_specification.rb" ++ , "file_line": 45 ++ , "function_name": "each" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/basic_specification.rb" ++ , "file_line": 45 ++ , "function_name": "any?" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/basic_specification.rb" ++ , "file_line": 45 ++ , "function_name": "contains_requirable_file?" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/specification.rb" ++ , "file_line": 898 ++ , "function_name": "find_inactive_by_path" ++ , "block_level": 1 ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/specification.rb" ++ , "file_line": 897 ++ , "function_name": "each" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/specification.rb" ++ , "file_line": 897 ++ , "function_name": "find" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/specification.rb" ++ , "file_line": 897 ++ , "function_name": "find_inactive_by_path" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems.rb" ++ , "file_line": 183 ++ , "function_name": "try_activate" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/core_ext/kernel_require.rb" ++ , "file_line": 132 ++ , "function_name": "require" ++ , "rescue_level": 1 ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/core_ext/kernel_require.rb" ++ , "file_line": 144 ++ , "function_name": "require" ++ } ++ , { "file_name": "/usr/share/gems/gems/compass-0.12.2/lib/compass/sass_extensions/sprites/engines/chunky_png_engine.rb" ++ , "file_line": 2 ++ , "special_function": "top (required)" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/core_ext/kernel_require.rb" ++ , "file_line": 55 ++ , "function_name": "require" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/core_ext/kernel_require.rb" ++ , "file_line": 55 ++ , "function_name": "require" ++ } ++ , { "file_name": "/usr/share/gems/gems/compass-0.12.2/lib/compass/sass_extensions/sprites/engines.rb" ++ , "file_line": 25 ++ , "special_function": "top (required)" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/core_ext/kernel_require.rb" ++ , "file_line": 55 ++ , "function_name": "require" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/core_ext/kernel_require.rb" ++ , "file_line": 55 ++ , "function_name": "require" ++ } ++ , { "file_name": "/usr/share/gems/gems/compass-0.12.2/lib/compass/sass_extensions/sprites.rb" ++ , "file_line": 18 ++ , "special_function": "top (required)" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/core_ext/kernel_require.rb" ++ , "file_line": 55 ++ , "function_name": "require" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/core_ext/kernel_require.rb" ++ , "file_line": 55 ++ , "function_name": "require" ++ } ++ , { "file_name": "/usr/share/gems/gems/compass-0.12.2/lib/compass/sass_extensions.rb" ++ , "file_line": 10 ++ , "special_function": "top (required)" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/core_ext/kernel_require.rb" ++ , "file_line": 55 ++ , "function_name": "require" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/core_ext/kernel_require.rb" ++ , "file_line": 55 ++ , "function_name": "require" ++ } ++ , { "file_name": "/usr/share/gems/gems/compass-0.12.2/lib/compass.rb" ++ , "file_line": 5 ++ , "special_function": "top (required)" ++ , "block_level": 1 ++ } ++ , { "file_name": "/usr/share/gems/gems/compass-0.12.2/lib/compass.rb" ++ , "file_line": 4 ++ , "function_name": "each" ++ } ++ , { "file_name": "/usr/share/gems/gems/compass-0.12.2/lib/compass.rb" ++ , "file_line": 4 ++ , "special_function": "top (required)" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/core_ext/kernel_require.rb" ++ , "file_line": 55 ++ , "function_name": "require" ++ } ++ , { "file_name": "/usr/share/rubygems/rubygems/core_ext/kernel_require.rb" ++ , "file_line": 55 ++ , "function_name": "require" ++ } ++ , { "file_name": "/usr/share/gems/gems/compass-0.12.2/bin/compass" ++ , "file_line": 20 ++ , "special_function": "top (required)" ++ , "block_level": 1 ++ } ++ , { "file_name": "/usr/share/gems/gems/compass-0.12.2/bin/compass" ++ , "file_line": 8 ++ , "function_name": "fallback_load_path" ++ } ++ , { "file_name": "/usr/share/gems/gems/compass-0.12.2/bin/compass" ++ , "file_line": 19 ++ , "special_function": "top (required)" ++ } ++ , { "file_name": "/usr/bin/compass" ++ , "file_line": 23 ++ , "function_name": "load" ++ } ++ , { "file_name": "/usr/bin/compass" ++ , "file_line": 23 ++ , "special_function": "main" ++ } ] ++} +\ No newline at end of file +diff --git a/tests/testsuite.at b/tests/testsuite.at +index 904fe64..f27ed13 100644 +--- a/tests/testsuite.at ++++ b/tests/testsuite.at +@@ -15,6 +15,8 @@ m4_include([koops_stacktrace.at]) + m4_include([core_frame.at]) + m4_include([core_thread.at]) + m4_include([core_stacktrace.at]) ++m4_include([ruby_frame.at]) ++m4_include([ruby_stacktrace.at]) + m4_include([operating_system.at]) + m4_include([normalize.at]) + m4_include([metrics.at]) +-- +1.8.3.1 + diff --git a/SOURCES/satyr-0.13-Check-the-return-value-of-sr_parse_char_cspan.patch b/SOURCES/satyr-0.13-Check-the-return-value-of-sr_parse_char_cspan.patch new file mode 100644 index 0000000..5fd7e50 --- /dev/null +++ b/SOURCES/satyr-0.13-Check-the-return-value-of-sr_parse_char_cspan.patch @@ -0,0 +1,38 @@ +From 309b839354b171cd03955537102ca73bc14b7f58 Mon Sep 17 00:00:00 2001 +From: Matej Habrnal +Date: Mon, 16 May 2016 14:19:34 +0200 +Subject: [PATCH] Check the return value of sr_parse_char_cspan + +Related to: #1336390 + +Signed-off-by: Matej Habrnal +--- + lib/python_stacktrace.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/lib/python_stacktrace.c b/lib/python_stacktrace.c +index 99aa52c..557b728 100644 +--- a/lib/python_stacktrace.c ++++ b/lib/python_stacktrace.c +@@ -231,9 +231,15 @@ sr_python_stacktrace_parse(const char **input, + } + + /* Parse exception name. */ +- sr_parse_char_cspan(&local_input, +- ":\n", +- &stacktrace->exception_name); ++ if (!sr_parse_char_cspan(&local_input, ":\n", &stacktrace->exception_name)) ++ { ++ ++ location->message = "Unable to find the ':\\n' characters " ++ "identifying the end of exception name."; ++ sr_python_stacktrace_free(stacktrace); ++ return NULL; ++ ++ } + + *input = local_input; + return stacktrace; +-- +1.8.3.1 + diff --git a/SOURCES/satyr-0.13-Fix-defects-found-by-coverity.patch b/SOURCES/satyr-0.13-Fix-defects-found-by-coverity.patch new file mode 100644 index 0000000..4fa343c --- /dev/null +++ b/SOURCES/satyr-0.13-Fix-defects-found-by-coverity.patch @@ -0,0 +1,282 @@ +From 481ac50e3e9ad53e4f07dce04facfaef5a2cdcfd Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Thu, 5 Mar 2015 14:08:15 +0100 +Subject: [PATCH] Fix defects found by coverity + +Resolves: #1336390 + +Signed-off-by: Martin Milata + +Conflicts: + lib/abrt.c +--- + lib/core_stacktrace.c | 8 ++++++++ + lib/core_unwind.c | 6 ++++-- + lib/java_frame.c | 3 +++ + lib/koops_stacktrace.c | 3 +++ + lib/python_frame.c | 26 ++++++++++++++------------ + lib/rpm.c | 4 +++- + lib/utils.c | 1 + + python/py_gdb_stacktrace.c | 8 ++++++++ + satyr.c | 3 +++ + 9 files changed, 47 insertions(+), 15 deletions(-) + +diff --git a/lib/core_stacktrace.c b/lib/core_stacktrace.c +index f7fd369..aeb97e4 100644 +--- a/lib/core_stacktrace.c ++++ b/lib/core_stacktrace.c +@@ -192,7 +192,10 @@ sr_core_stacktrace_from_json(struct sr_json_value *root, + if (crash_thread) + { + if (!JSON_CHECK_TYPE(crash_thread, SR_JSON_BOOLEAN, "crash_thread")) ++ { ++ sr_core_thread_free(thread); + goto fail; ++ } + + if (crash_thread->u.boolean) + result->crash_thread = thread; +@@ -296,6 +299,7 @@ sr_core_stacktrace_create(const char *gdb_stacktrace_text, + { + warn("Unable to parse unstrip output."); + ++ sr_gdb_stacktrace_free(gdb_stacktrace); + return NULL; + } + +@@ -332,6 +336,8 @@ sr_core_stacktrace_create(const char *gdb_stacktrace_text, + core_frame->function_name = + sr_strdup(gdb_frame->function_name); + } ++ ++ core_thread->frames = sr_core_frame_append(core_thread->frames, core_frame); + } + + core_stacktrace->threads = +@@ -341,6 +347,8 @@ sr_core_stacktrace_create(const char *gdb_stacktrace_text, + gdb_thread = gdb_thread->next; + } + ++ sr_unstrip_free(unstrip); ++ sr_gdb_stacktrace_free(gdb_stacktrace); + return core_stacktrace; + } + +diff --git a/lib/core_unwind.c b/lib/core_unwind.c +index c30f9db..b8c0235 100644 +--- a/lib/core_unwind.c ++++ b/lib/core_unwind.c +@@ -127,6 +127,7 @@ find_elf_core (Dwfl_Module *mod, void **userdata, const char *modname, + { + warn("Unable to open executable '%s': %s", executable_file, + elf_errmsg(-1)); ++ close(fd); + return -1; + } + +@@ -399,9 +400,10 @@ struct sr_core_stacktrace * + sr_core_stacktrace_from_gdb(const char *gdb_output, const char *core_file, + const char *exe_file, char **error_msg) + { ++ /* I'm not going to rewrite it now since the function is not being used. */ ++ assert(error_msg); + /* Initialize error_msg to 'no error'. */ +- if (error_msg) +- *error_msg = NULL; ++ *error_msg = NULL; + + struct core_handle *ch = open_coredump(core_file, exe_file, error_msg); + if (*error_msg) +diff --git a/lib/java_frame.c b/lib/java_frame.c +index ee97572..d03f86a 100644 +--- a/lib/java_frame.c ++++ b/lib/java_frame.c +@@ -359,7 +359,10 @@ sr_java_frame_parse_exception(const char **input, + if (exception->next == NULL) + exception->next = parsed; + else ++ { ++ assert(frame); + frame->next = parsed; ++ } + + frame = parsed; + } +diff --git a/lib/koops_stacktrace.c b/lib/koops_stacktrace.c +index 611c7d1..01baa79 100644 +--- a/lib/koops_stacktrace.c ++++ b/lib/koops_stacktrace.c +@@ -506,7 +506,10 @@ taint_flags_to_json(struct sr_koops_stacktrace *stacktrace) + } + + if (strbuf->len == 0) ++ { ++ sr_strbuf_free(strbuf); + return sr_strdup("[]"); ++ } + + sr_strbuf_append_char(strbuf, ']'); + char *result = sr_strbuf_free_nobuf(strbuf); +diff --git a/lib/python_frame.c b/lib/python_frame.c +index b0540fe..4b4be8a 100644 +--- a/lib/python_frame.c ++++ b/lib/python_frame.c +@@ -220,11 +220,10 @@ sr_python_frame_parse(const char **input, + /* Parse file name */ + if (!sr_parse_char_cspan(&local_input, "\"", &frame->file_name)) + { +- sr_python_frame_free(frame); + location->message = sr_asprintf("Unable to find the '\"' character " + "identifying the beginning of file name."); + +- return NULL; ++ goto fail; + } + + if (strlen(frame->file_name) > 0 && +@@ -243,7 +242,7 @@ sr_python_frame_parse(const char **input, + if (0 == sr_skip_string(&local_input, "\", line ")) + { + location->message = sr_asprintf("Line separator not found."); +- return NULL; ++ goto fail; + } + + location->column += strlen("\", line "); +@@ -253,7 +252,7 @@ sr_python_frame_parse(const char **input, + if (0 == length) + { + location->message = sr_asprintf("Line number not found."); +- return NULL; ++ goto fail; + } + + location->column += length; +@@ -263,7 +262,7 @@ sr_python_frame_parse(const char **input, + if (local_input[0] != '\n') + { + location->message = sr_asprintf("Function name separator not found."); +- return NULL; ++ goto fail; + } + + /* The last frame of SyntaxError stack trace does not have +@@ -280,11 +279,10 @@ sr_python_frame_parse(const char **input, + /* Parse function name */ + if (!sr_parse_char_cspan(&local_input, "\n", &frame->function_name)) + { +- sr_python_frame_free(frame); + location->message = sr_asprintf("Unable to find the newline character " + "identifying the end of function name."); + +- return NULL; ++ goto fail; + } + + location->column += strlen(frame->function_name); +@@ -301,19 +299,23 @@ sr_python_frame_parse(const char **input, + } + } + +- sr_skip_char(&local_input, '\n'); +- sr_location_add(location, 1, 0); ++ if (sr_skip_char(&local_input, '\n')) ++ sr_location_add(location, 1, 0); + + /* Parse source code line (optional). */ + if (4 == sr_skip_string(&local_input, " ")) + { +- sr_parse_char_cspan(&local_input, "\n", &frame->line_contents); +- sr_skip_char(&local_input, '\n'); +- sr_location_add(location, 1, 0); ++ if (sr_parse_char_cspan(&local_input, "\n", &frame->line_contents) ++ && sr_skip_char(&local_input, '\n')) ++ sr_location_add(location, 1, 0); + } + + *input = local_input; + return frame; ++ ++fail: ++ sr_python_frame_free(frame); ++ return NULL; + } + + char * +diff --git a/lib/rpm.c b/lib/rpm.c +index f8f1fa6..920e145 100644 +--- a/lib/rpm.c ++++ b/lib/rpm.c +@@ -199,7 +199,9 @@ sr_rpm_package_sort(struct sr_rpm_package *packages) + array[loop]->next = NULL; + } + +- return array[0]; ++ struct sr_rpm_package *result = array[0]; ++ free(array); ++ return result; + } + + static struct sr_rpm_package * +diff --git a/lib/utils.c b/lib/utils.c +index fa3c0a0..bdafaa7 100644 +--- a/lib/utils.c ++++ b/lib/utils.c +@@ -354,6 +354,7 @@ sr_string_to_file(const char *filename, + filename, + error); + ++ close(fd); + return false; + } + +diff --git a/python/py_gdb_stacktrace.c b/python/py_gdb_stacktrace.c +index c799e06..5ed3533 100644 +--- a/python/py_gdb_stacktrace.c ++++ b/python/py_gdb_stacktrace.c +@@ -7,6 +7,7 @@ + #include "strbuf.h" + #include "gdb/stacktrace.h" + #include "gdb/thread.h" ++#include "gdb/frame.h" + #include "gdb/sharedlib.h" + #include "location.h" + #include "normalize.h" +@@ -408,13 +409,20 @@ sr_py_gdb_stacktrace_find_crash_frame(PyObject *self, PyObject *args) + PyObject_New(struct sr_py_gdb_frame, &sr_py_gdb_frame_type); + + if (!result) ++ { ++ sr_gdb_frame_free(frame); + return PyErr_NoMemory(); ++ } + + result->frame = frame; + this->crashframe = result; + + if (stacktrace_rebuild_thread_python_list(this) < 0) ++ { ++ sr_gdb_frame_free(frame); ++ Py_DECREF(result); + return NULL; ++ } + + return (PyObject *)result; + } +diff --git a/satyr.c b/satyr.c +index 6e1ed28..44bf869 100644 +--- a/satyr.c ++++ b/satyr.c +@@ -210,6 +210,9 @@ debug_normalize(int argc, char **argv) + struct sr_strbuf *strbuf = sr_strbuf_new(); + sr_gdb_thread_append_to_str(thread, strbuf, false); + puts(strbuf->buf); ++ ++ free(text); ++ sr_strbuf_free(strbuf); + } + + static void +-- +1.8.3.1 + diff --git a/SOURCES/satyr-0.13-Honor-frame-number-limit-in-GDB-core-unwinder.patch b/SOURCES/satyr-0.13-Honor-frame-number-limit-in-GDB-core-unwinder.patch new file mode 100644 index 0000000..e0aeed1 --- /dev/null +++ b/SOURCES/satyr-0.13-Honor-frame-number-limit-in-GDB-core-unwinder.patch @@ -0,0 +1,90 @@ +From d94a7b5c516c0446d1c49e8446707a64b4b45148 Mon Sep 17 00:00:00 2001 +From: Jakub Filak +Date: Tue, 2 Aug 2016 08:50:10 +0200 +Subject: [PATCH] Honor frame number limit in GDB core unwinder + +Related to #1260074 + +Signed-off-by: Jakub Filak +--- + lib/core_unwind.c | 15 +++++++++++++++ + lib/core_unwind_elfutils.c | 6 ++---- + lib/internal_unwind.h | 5 +++++ + 3 files changed, 22 insertions(+), 4 deletions(-) + +diff --git a/lib/core_unwind.c b/lib/core_unwind.c +index b8c0235..05b94a5 100644 +--- a/lib/core_unwind.c ++++ b/lib/core_unwind.c +@@ -429,6 +429,8 @@ sr_core_stacktrace_from_gdb(const char *gdb_output, const char *core_file, + { + struct sr_core_thread *core_thread = sr_core_thread_new(); + ++ unsigned long nframes = CORE_STACKTRACE_FRAME_LIMIT; ++ struct sr_gdb_frame *top_frame = gdb_thread->frames; + for (struct sr_gdb_frame *gdb_frame = gdb_thread->frames; + gdb_frame; + gdb_frame = gdb_frame->next) +@@ -436,6 +438,19 @@ sr_core_stacktrace_from_gdb(const char *gdb_output, const char *core_file, + if (gdb_frame->signal_handler_called) + continue; + ++ if (nframes) ++ --nframes; ++ else ++ top_frame = top_frame->next; ++ } ++ ++ for (struct sr_gdb_frame *gdb_frame = top_frame; ++ gdb_frame; ++ gdb_frame = gdb_frame->next) ++ { ++ if (gdb_frame->signal_handler_called) ++ continue; ++ + struct sr_core_frame *core_frame = resolve_frame(ch->dwfl, + gdb_frame->address, false); + +diff --git a/lib/core_unwind_elfutils.c b/lib/core_unwind_elfutils.c +index 1482dba..d273a03 100644 +--- a/lib/core_unwind_elfutils.c ++++ b/lib/core_unwind_elfutils.c +@@ -33,8 +33,6 @@ + #include + #include + +-#define FRAME_LIMIT 256 +- + struct frame_callback_arg + { + struct sr_core_frame **frames_tail; +@@ -84,8 +82,8 @@ frame_callback(Dwfl_Frame *frame, void *data) + static void + truncate_long_thread(struct sr_core_thread *thread, struct frame_callback_arg *frame_arg) + { +- /* Truncate the stacktrace to FRAME_LIMIT least recent frames. */ +- while (thread->frames && frame_arg->nframes > FRAME_LIMIT) ++ /* Truncate the stacktrace to CORE_STACKTRACE_FRAME_LIMIT least recent frames. */ ++ while (thread->frames && frame_arg->nframes > CORE_STACKTRACE_FRAME_LIMIT) + { + struct sr_core_frame *old_frame = thread->frames; + thread->frames = old_frame->next; +diff --git a/lib/internal_unwind.h b/lib/internal_unwind.h +index 84261c6..b9e162a 100644 +--- a/lib/internal_unwind.h ++++ b/lib/internal_unwind.h +@@ -66,6 +66,11 @@ _set_error(char **error_msg, const char *fmt, ...) __sr_printf(2, 3); + } \ + } while(0) + ++/* This macro is used as a limit for the number of function frames included in ++ * every single thread of a core stacktrace generated by satyr. ++ */ ++#define CORE_STACKTRACE_FRAME_LIMIT 256 ++ + struct exe_mapping_data + { + uint64_t start; +-- +2.17.1 + diff --git a/SOURCES/satyr-0.13-abrt-refactorize-unwinding-from-core-hook.patch b/SOURCES/satyr-0.13-abrt-refactorize-unwinding-from-core-hook.patch new file mode 100644 index 0000000..c1821fd --- /dev/null +++ b/SOURCES/satyr-0.13-abrt-refactorize-unwinding-from-core-hook.patch @@ -0,0 +1,99 @@ +From 89e0a7d0a195605585b80157ce23be1fb03f04a2 Mon Sep 17 00:00:00 2001 +From: Jakub Filak +Date: Wed, 13 May 2015 10:45:32 +0200 +Subject: [PATCH] abrt: refactorize unwinding from core hook + +ABRT needs to save files from its core hook with O_EXCL and with right +mode and owner and there might be more constraints ABRT needs to +respect in future. + +This patch breaks granularity of the abrt functions to allow ABRT to save the +core backtrace data in the way it need by providing a function returning +the data instead of directly saving the data to a file. + +Signed-off-by: Jakub Filak +--- + include/abrt.h | 6 ++++++ + lib/abrt.c | 29 ++++++++++++++++++++++------- + 2 files changed, 28 insertions(+), 7 deletions(-) + +diff --git a/include/abrt.h b/include/abrt.h +index 5116446..ca0748c 100644 +--- a/include/abrt.h ++++ b/include/abrt.h +@@ -44,6 +44,12 @@ sr_abrt_create_core_stacktrace_from_gdb(const char *directory, + bool hash_fingerprints, + char **error_message); + ++char * ++sr_abrt_get_core_stacktrace_from_core_hook(pid_t thread_id, ++ const char *executable, ++ int signum, ++ char **error_message); ++ + bool + sr_abrt_create_core_stacktrace_from_core_hook(const char *directory, + pid_t thread_id, +diff --git a/lib/abrt.c b/lib/abrt.c +index 86e9082..4599e2c 100644 +--- a/lib/abrt.c ++++ b/lib/abrt.c +@@ -162,12 +162,11 @@ sr_abrt_create_core_stacktrace(const char *directory, + error_message); + } + +-bool +-sr_abrt_create_core_stacktrace_from_core_hook(const char *directory, +- pid_t thread_id, +- const char *executable, +- int signum, +- char **error_message) ++char * ++sr_abrt_get_core_stacktrace_from_core_hook(pid_t thread_id, ++ const char *executable, ++ int signum, ++ char **error_message) + { + + struct sr_core_stacktrace *core_stacktrace; +@@ -180,12 +179,29 @@ sr_abrt_create_core_stacktrace_from_core_hook(const char *directory, + fulfill_missing_values(core_stacktrace); + + char *json = sr_core_stacktrace_to_json(core_stacktrace); ++ sr_core_stacktrace_free(core_stacktrace); + + // Add newline to the end of core stacktrace file to make text + // editors happy. + json = sr_realloc(json, strlen(json) + 2); + strcat(json, "\n"); + ++ return json; ++; ++} ++ ++bool ++sr_abrt_create_core_stacktrace_from_core_hook(const char *directory, ++ pid_t thread_id, ++ const char *executable, ++ int signum, ++ char **error_message) ++{ ++ char *json = sr_abrt_get_core_stacktrace_from_core_hook(thread_id, ++ executable, ++ signum, ++ error_message); ++ + char *core_backtrace_filename = sr_build_path(directory, "core_backtrace", NULL); + bool success = sr_string_to_file(core_backtrace_filename, + json, +@@ -193,7 +209,6 @@ sr_abrt_create_core_stacktrace_from_core_hook(const char *directory, + + free(core_backtrace_filename); + free(json); +- sr_core_stacktrace_free(core_stacktrace); + return success; + } + +-- +2.4.3 + diff --git a/SOURCES/satyr-0.13-better-inf-recursion-handling.patch b/SOURCES/satyr-0.13-better-inf-recursion-handling.patch new file mode 100644 index 0000000..ba945f4 --- /dev/null +++ b/SOURCES/satyr-0.13-better-inf-recursion-handling.patch @@ -0,0 +1,109 @@ +From 9639948f3fdda4afcf25e7080aa87d8ad2fbeb34 Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Wed, 27 Aug 2014 13:55:50 +0200 +Subject: [SATYR PATCH] Better handling of infinite recursion + +unwind: Append threads/frames in O(1) +unwind: throw away the most recent frames +unwind: lower stacktrace length limit to 256 + +Fixes #179. + +Signed-off-by: Martin Milata +--- + lib/core_unwind_elfutils.c | 36 ++++++++++++++++++++---------------- + 1 file changed, 20 insertions(+), 16 deletions(-) + +diff --git a/lib/core_unwind_elfutils.c b/lib/core_unwind_elfutils.c +index 43d66be..bbd4813 100644 +--- a/lib/core_unwind_elfutils.c ++++ b/lib/core_unwind_elfutils.c +@@ -29,18 +29,18 @@ + #include + #include + +-#define FRAME_LIMIT 1024 ++#define FRAME_LIMIT 256 + + struct frame_callback_arg + { +- struct sr_core_thread *thread; ++ struct sr_core_frame **frames_tail; + char *error_msg; + unsigned nframes; + }; + + struct thread_callback_arg + { +- struct sr_core_stacktrace *stacktrace; ++ struct sr_core_thread **threads_tail; + char *error_msg; + }; + +@@ -70,15 +70,9 @@ frame_callback(Dwfl_Frame *frame, void *data) + return CB_STOP_UNWIND; + } + +- frame_arg->thread->frames = +- sr_core_frame_append(frame_arg->thread->frames, result); +- +- /* Avoid huge stacktraces from programs stuck in infinite recursion. */ ++ *frame_arg->frames_tail = result; ++ frame_arg->frames_tail = &result->next; + frame_arg->nframes++; +- if (frame_arg->nframes >= FRAME_LIMIT) +- { +- return CB_STOP_UNWIND; +- } + + return DWARF_CB_OK; + } +@@ -99,7 +93,7 @@ unwind_thread(Dwfl_Thread *thread, void *data) + + struct frame_callback_arg frame_arg = + { +- .thread = result, ++ .frames_tail = &(result->frames), + .error_msg = NULL, + .nframes = 0 + }; +@@ -121,14 +115,24 @@ unwind_thread(Dwfl_Thread *thread, void *data) + goto abort; + } + +- if (!error_msg && !frame_arg.thread->frames) ++ if (!error_msg && !result->frames) + { + set_error("No frames found for thread id %d", (int)result->id); + goto abort; + } + +- thread_arg->stacktrace->threads = +- sr_core_thread_append(thread_arg->stacktrace->threads, result); ++ /* Truncate the stacktrace to FRAME_LIMIT least recent frames. */ ++ while (result->frames && frame_arg.nframes > FRAME_LIMIT) ++ { ++ struct sr_core_frame *old_frame = result->frames; ++ result->frames = old_frame->next; ++ sr_core_frame_free(old_frame); ++ frame_arg.nframes--; ++ } ++ ++ *thread_arg->threads_tail = result; ++ thread_arg->threads_tail = &result->next; ++ + return DWARF_CB_OK; + + abort: +@@ -166,7 +170,7 @@ sr_parse_coredump(const char *core_file, + + struct thread_callback_arg thread_arg = + { +- .stacktrace = stacktrace, ++ .threads_tail = &(stacktrace->threads), + .error_msg = NULL + }; + +-- +1.9.3 + diff --git a/SOURCES/satyr-0.13-core_unwind-fix-the-missing-frame-build_id-and-file.patch b/SOURCES/satyr-0.13-core_unwind-fix-the-missing-frame-build_id-and-file.patch new file mode 100644 index 0000000..92b4fc5 --- /dev/null +++ b/SOURCES/satyr-0.13-core_unwind-fix-the-missing-frame-build_id-and-file.patch @@ -0,0 +1,49 @@ +From d34232858880672a9e6b88ff5ebd966969aabb9f Mon Sep 17 00:00:00 2001 +From: Jakub Filak +Date: Mon, 7 Sep 2015 08:08:16 +0200 +Subject: [PATCH] core_unwind: fix the missing frame build_id and file_name + +The documentation of dwfl_module_build_id() says: + + This returns 0 when the module's main ELF file has not yet been loaded + and its build ID bits were not reported. To ensure the ID is always + returned when determinable, call dwfl_module_getelf first. + + /usr/include/elfutils/libdwfl.h + +ABRT upstream commit 88c6b3683a129c0e369071204b29fbca94772d3b verifies +this patch. + +Discovered by Richard Marko +Resolved by Mark Wielaard + +Related: #1210599 + +Signed-off-by: Jakub Filak +--- + lib/core_unwind.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/lib/core_unwind.c b/lib/core_unwind.c +index cf9973a..c30f9db 100644 +--- a/lib/core_unwind.c ++++ b/lib/core_unwind.c +@@ -278,9 +278,14 @@ resolve_frame(Dwfl *dwfl, Dwarf_Addr ip, bool minus_one) + int ret; + const unsigned char *build_id_bits; + const char *filename, *funcname; +- GElf_Addr bid_addr; ++ GElf_Addr bias, bid_addr; + Dwarf_Addr start; + ++ /* Initialize the module's main Elf for dwfl_module_build_id and dwfl_module_info */ ++ /* No need to deallocate the variable 'bias' and the return value.*/ ++ if (NULL == dwfl_module_getelf(mod, &bias)) ++ warn("The module's main Elf was not found"); ++ + ret = dwfl_module_build_id(mod, &build_id_bits, &bid_addr); + if (ret > 0) + { +-- +2.4.3 + diff --git a/SOURCES/satyr-0.13-debug-unwinding-from-core-hook-using-satyr-binary.patch b/SOURCES/satyr-0.13-debug-unwinding-from-core-hook-using-satyr-binary.patch new file mode 100644 index 0000000..21bd205 --- /dev/null +++ b/SOURCES/satyr-0.13-debug-unwinding-from-core-hook-using-satyr-binary.patch @@ -0,0 +1,133 @@ +From 455c47f1f18858c439ff0a0b833ce93c9f79045f Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Mon, 24 Nov 2014 14:38:06 +0100 +Subject: [PATCH 4/5] debug unwinding from core hook using satyr binary + +Write "|/usr/bin/satyr debug unwind-from-hook %e %i %s" to +/proc/sys/kernel/core_pattern in order to use this. Before kernel 3.18 +the %i flag is not available - you can use %p for single-thread +processes. + +Related to abrt/abrt#829. + +Signed-off-by: Martin Milata +--- + satyr.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 81 insertions(+) + +diff --git a/satyr.c b/satyr.c +index 7bdaa45..6e1ed28 100644 +--- a/satyr.c ++++ b/satyr.c +@@ -20,6 +20,8 @@ + #include "gdb/stacktrace.h" + #include "gdb/thread.h" + #include "gdb/frame.h" ++#include "core/unwind.h" ++#include "core/stacktrace.h" + #include "utils.h" + #include "location.h" + #include "strbuf.h" +@@ -37,6 +39,7 @@ + #include + #include + #include ++#include + + static char *g_program_name; + +@@ -275,6 +278,82 @@ debug_duphash(int argc, char **argv) + } + + static void ++debug_unwind_from_hook(int argc, char **argv) ++{ ++ if (argc != 3) ++ { ++ fprintf(stderr, "Wrong number of arguments.\n"); ++ fprintf(stderr, "Usage: satyr debug unwind-from-hook " ++ " \n"); ++ exit(1); ++ } ++ ++ char *executable = argv[0]; ++ char *end; ++ unsigned long tid = strtoul(argv[1], &end, 10); ++ if (*end != '\0') ++ { ++ fprintf(stderr, "Wrong tid\n"); ++ exit(1); ++ } ++ ++ unsigned long signum = strtoul(argv[2], &end, 10); ++ if (*end != '\0') ++ { ++ fprintf(stderr, "Wrong signal number\n"); ++ exit(1); ++ } ++ ++ char *error_message = NULL; ++ struct sr_core_stacktrace *core_stacktrace; ++ ++ core_stacktrace = sr_core_stacktrace_from_core_hook(tid, executable, signum, ++ &error_message); ++ if (!core_stacktrace) ++ { ++ fprintf(stderr, "Unwind failed: %s\n", error_message); ++ exit(1); ++ } ++ ++ char *json = sr_core_stacktrace_to_json(core_stacktrace); ++ // Add newline to the end of core stacktrace file to make text ++ // editors happy. ++ json = sr_realloc(json, strlen(json) + 2); ++ strcat(json, "\n"); ++ ++ time_t t = time(NULL); ++ struct tm *tm = localtime(&t); ++ char core_backtrace_filename[256]; ++ ++ if (tm == NULL) ++ { ++ perror("localtime"); ++ exit(1); ++ } ++ ++ if (strftime(core_backtrace_filename, sizeof(core_backtrace_filename), ++ "/tmp/satyr-core-stacktrace-%F-%T.json", tm) == 0) ++ { ++ fprintf(stderr, "stftime failed\n"); ++ exit(1); ++ } ++ ++ ++ bool success = sr_string_to_file(core_backtrace_filename, ++ json, ++ &error_message); ++ ++ if (!success) ++ { ++ fprintf(stderr, "Failed to write stacktrace: %s\n", error_message); ++ exit(1); ++ } ++ ++ free(json); ++ sr_core_stacktrace_free(core_stacktrace); ++} ++ ++static void + debug(int argc, char **argv) + { + /* Debug command requires a subcommand. */ +@@ -288,6 +367,8 @@ debug(int argc, char **argv) + debug_normalize(argc - 1, argv + 1); + else if (0 == strcmp(argv[0], "duphash")) + debug_duphash(argc - 1, argv + 1); ++ else if (0 == strcmp(argv[0], "unwind-from-hook")) ++ debug_unwind_from_hook(argc - 1, argv + 1); + else + { + fprintf(stderr, "Unknown debug subcommand.\n"); +-- +2.4.3 + diff --git a/SOURCES/satyr-0.13-disable-fingerprints.patch b/SOURCES/satyr-0.13-disable-fingerprints.patch new file mode 100644 index 0000000..3f8c6be --- /dev/null +++ b/SOURCES/satyr-0.13-disable-fingerprints.patch @@ -0,0 +1,98 @@ +From 58e874e4a28ce00623db208475b0d9fd22d601ac Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Tue, 14 Jan 2014 13:40:45 +0100 +Subject: [SATYR PATCH 3/3] abrt: disable fingerprinting of core stacktraces + +Closes rhbz#1052402. + +Signed-off-by: Martin Milata +--- + include/abrt.h | 2 ++ + lib/abrt.c | 2 ++ + lib/core_fingerprint.c | 25 +++++++++++++++++++++++++ + 3 files changed, 29 insertions(+) + +diff --git a/include/abrt.h b/include/abrt.h +index 1ef7876..0a8f5ac 100644 +--- a/include/abrt.h ++++ b/include/abrt.h +@@ -31,6 +31,8 @@ bool + sr_abrt_print_report_from_dir(const char *directory, + char **error_message); + ++/* NOTE: the hash_fingerprints argument has no effect because fingerprint ++ * generation is disabled. */ + bool + sr_abrt_create_core_stacktrace(const char *directory, + bool hash_fingerprints, +diff --git a/lib/abrt.c b/lib/abrt.c +index 8c006fa..39bc45d 100644 +--- a/lib/abrt.c ++++ b/lib/abrt.c +@@ -87,11 +87,13 @@ create_core_stacktrace(const char *directory, const char *gdb_output, + if (!core_stacktrace) + return false; + ++#if 0 + sr_core_fingerprint_generate(core_stacktrace, + error_message); + + if (hash_fingerprints) + sr_core_fingerprint_hash(core_stacktrace); ++#endif + + char *json = sr_core_stacktrace_to_json(core_stacktrace); + +diff --git a/lib/core_fingerprint.c b/lib/core_fingerprint.c +index 8c5a228..3c7b5ba 100644 +--- a/lib/core_fingerprint.c ++++ b/lib/core_fingerprint.c +@@ -17,6 +17,8 @@ + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ ++#include "config.h" ++ + #include "core/fingerprint.h" + #include "core/stacktrace.h" + #include "core/frame.h" +@@ -33,6 +35,8 @@ + #include + #include + ++#if HAVE_LIBOPCODES ++ + static void + fingerprint_add_bool(struct sr_strbuf *buffer, + const char *name, +@@ -552,6 +556,27 @@ sr_core_fingerprint_generate_for_binary(struct sr_core_thread *thread, + return true; + } + ++#else // HAVE_LIBOPCODES ++ ++bool ++sr_core_fingerprint_generate(struct sr_core_stacktrace *stacktrace, ++ char **error_message) ++{ ++ *error_message = sr_strdup("satyr compiled without libopcodes"); ++ return false; ++} ++ ++bool ++sr_core_fingerprint_generate_for_binary(struct sr_core_thread *thread, ++ const char *binary_path, ++ char **error_message) ++{ ++ *error_message = sr_strdup("satyr compiled without libopcodes"); ++ return false; ++} ++ ++#endif // HAVE_LIBOPCODES ++ + static void + hash_frame (struct sr_core_frame *frame) + { +-- +1.8.3.1 + diff --git a/SOURCES/satyr-0.13-disable-hook-unwind-on-kernels-w-o-PTRACE_SEIZE.patch b/SOURCES/satyr-0.13-disable-hook-unwind-on-kernels-w-o-PTRACE_SEIZE.patch new file mode 100644 index 0000000..c181365 --- /dev/null +++ b/SOURCES/satyr-0.13-disable-hook-unwind-on-kernels-w-o-PTRACE_SEIZE.patch @@ -0,0 +1,73 @@ +From c1497bdb2a1deaa467fa75a80b1fb993a7d500f5 Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Mon, 8 Dec 2014 15:37:17 +0100 +Subject: [PATCH 5/5] Disable hook unwind on kernels w/o PTRACE_SEIZE + +Related to abrt/abrt#829. + +Signed-off-by: Martin Milata +--- + lib/core_unwind.c | 7 ++++--- + lib/core_unwind_elfutils.c | 6 ++++++ + 2 files changed, 10 insertions(+), 3 deletions(-) + +diff --git a/lib/core_unwind.c b/lib/core_unwind.c +index 8599f0f..cf9973a 100644 +--- a/lib/core_unwind.c ++++ b/lib/core_unwind.c +@@ -25,6 +25,7 @@ + #include + #include + #include /* struct elf_prstatus */ ++#include /* PTRACE_SEIZE */ + + #include "utils.h" + #include "core/unwind.h" +@@ -52,19 +53,19 @@ sr_parse_coredump(const char *coredump_filename, + + #endif /* !defined WITH_LIBDWFL && !defined WITH_LIBUNWIND */ + +-#if !defined WITH_LIBDWFL ++#if (!defined WITH_LIBDWFL || !defined PTRACE_SEIZE) + + struct sr_core_stacktrace * + sr_core_stacktrace_from_core_hook(pid_t thread_id, + const char *executable_filename, + int signum, +- char **error_message); ++ char **error_message) + { + *error_message = sr_asprintf("satyr is built without live process unwind support"); + return NULL; + } + +-#endif /* !defined WITH_LIBDWFL */ ++#endif /* !defined WITH_LIBDWFL || !defined PTRACE_SEIZE */ + + /* FIXME: is there another way to pass the executable name to the find_elf + * callback? */ +diff --git a/lib/core_unwind_elfutils.c b/lib/core_unwind_elfutils.c +index eb366cd..1482dba 100644 +--- a/lib/core_unwind_elfutils.c ++++ b/lib/core_unwind_elfutils.c +@@ -212,6 +212,10 @@ fail: + return stacktrace; + } + ++/* If PTRACE_SEIZE is not defined (kernel < 3.4), stub function from ++ * core_unwind.c is used. */ ++#ifdef PTRACE_SEIZE ++ + struct sr_core_stacktrace * + sr_core_stacktrace_from_core_hook(pid_t tid, + const char *executable, +@@ -344,4 +348,6 @@ fail: + return stacktrace; + } + ++#endif /* PTRACE_SEIZE */ ++ + #endif /* WITH_LIBDWFL */ +-- +2.4.3 + diff --git a/SOURCES/satyr-0.13-dont-free-gdb-stacktrace.patch b/SOURCES/satyr-0.13-dont-free-gdb-stacktrace.patch new file mode 100644 index 0000000..163ffb9 --- /dev/null +++ b/SOURCES/satyr-0.13-dont-free-gdb-stacktrace.patch @@ -0,0 +1,30 @@ +From 969aedadec7f1f693f53ddaf8dcb1f1d59d5ac72 Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Tue, 23 Sep 2014 18:22:27 +0200 +Subject: [SATYR PATCH] Don't free gdb stacktrace if no crash thread found + +Possibly related to rhbz#1145697. + +Signed-off-by: Martin Milata +--- + lib/gdb_stacktrace.c | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/lib/gdb_stacktrace.c b/lib/gdb_stacktrace.c +index 8e73e21..3b2465a 100644 +--- a/lib/gdb_stacktrace.c ++++ b/lib/gdb_stacktrace.c +@@ -558,10 +558,7 @@ sr_gdb_stacktrace_to_short_text(struct sr_gdb_stacktrace *stacktrace, + crash_thread = sr_gdb_stacktrace_find_crash_thread(stacktrace); + + if (!crash_thread) +- { +- sr_gdb_stacktrace_free(stacktrace); + return NULL; +- } + + struct sr_gdb_thread *optimized_thread + = sr_gdb_thread_get_optimized(crash_thread, stacktrace->libs, +-- +1.9.3 + diff --git a/SOURCES/satyr-0.13-elfutils-0.158.patch b/SOURCES/satyr-0.13-elfutils-0.158.patch new file mode 100644 index 0000000..fafd6a1 --- /dev/null +++ b/SOURCES/satyr-0.13-elfutils-0.158.patch @@ -0,0 +1,26 @@ +From 8e872be02ea4f231347aa47f38b4418175c0e43d Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Wed, 8 Jan 2014 17:27:47 +0100 +Subject: [PATCH] unwind: fix build against elfutils-0.158 + +Signed-off-by: Martin Milata +--- + lib/core_unwind.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/core_unwind.c b/lib/core_unwind.c +index d0c7aec..7910254 100644 +--- a/lib/core_unwind.c ++++ b/lib/core_unwind.c +@@ -206,7 +206,7 @@ open_coredump(const char *elf_file, const char *exe_file, char **error_msg) + ch->cb.section_address = dwfl_offline_section_address; + ch->dwfl = dwfl_begin(&ch->cb); + +- if (dwfl_core_file_report(ch->dwfl, ch->eh) == -1) ++ if (dwfl_core_file_report(ch->dwfl, ch->eh, exe_file) == -1) + { + set_error_dwfl("dwfl_core_file_report"); + goto fail_dwfl; +-- +1.8.3.1 + diff --git a/SOURCES/satyr-0.13-elfutils-unwinder.patch b/SOURCES/satyr-0.13-elfutils-unwinder.patch new file mode 100644 index 0000000..690fd45 --- /dev/null +++ b/SOURCES/satyr-0.13-elfutils-unwinder.patch @@ -0,0 +1,260 @@ +From 17853e2316aed32d56343b2c4a73fa82b43a078d Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Mon, 21 Oct 2013 14:53:02 +0200 +Subject: [SATYR PATCH 2/3] unwind: Port to current API of elfutils unwinder + +Related to rhbz#1051569. + +Signed-off-by: Martin Milata +--- + configure.ac | 2 +- + lib/core_unwind_elfutils.c | 164 +++++++++++++++++++++++++++++---------------- + lib/internal_unwind.h | 2 +- + 3 files changed, 110 insertions(+), 58 deletions(-) + +diff --git a/configure.ac b/configure.ac +index af6cb3f..cf3a193 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -104,7 +104,7 @@ AC_CHECK_LIB([elf], [main]) + AC_CHECK_LIB([dw], [main]) + AC_CHECK_LIB([dwfl], [main]) + AC_CHECK_LIB([dl], [main]) +-AC_CHECK_FUNC(dwfl_frame_state_core, AC_DEFINE(HAVE_DWFL_FRAME_STATE_CORE, [], [Have function dwfl_frame_state_core for coredump unwinding])) ++AC_CHECK_FUNC(dwfl_getthreads, AC_DEFINE(HAVE_DWFL_NEXT_THREAD, [], [Have function dwfl_getthreads for coredump unwinding])) + + # libunwind + AC_CHECK_HEADERS([libunwind-coredump.h]) +diff --git a/lib/core_unwind_elfutils.c b/lib/core_unwind_elfutils.c +index 9a489ac..a8d8b3f 100644 +--- a/lib/core_unwind_elfutils.c ++++ b/lib/core_unwind_elfutils.c +@@ -29,59 +29,108 @@ + #include + #include + +-static struct sr_core_thread * +-unwind_thread(Dwfl *dwfl, Dwfl_Frame_State *state, char **error_msg) ++struct frame_callback_arg + { +- struct sr_core_frame *head = NULL, *tail = NULL; +- pid_t tid = 0; ++ struct sr_core_thread *thread; ++ char *error_msg; ++}; + +- if (state) ++struct thread_callback_arg ++{ ++ struct sr_core_stacktrace *stacktrace; ++ char *error_msg; ++}; ++ ++static int CB_STOP_UNWIND = DWARF_CB_ABORT+1; ++ ++static int ++frame_callback(Dwfl_Frame *frame, void *data) ++{ ++ struct frame_callback_arg *frame_arg = data; ++ char **error_msg = &frame_arg->error_msg; ++ ++ Dwarf_Addr pc; ++ bool minus_one; ++ if (!dwfl_frame_pc(frame, &pc, &minus_one)) + { +- tid = dwfl_frame_tid_get(state); ++ set_error_dwfl("dwfl_frame_pc"); ++ return DWARF_CB_ABORT; + } + +- while (state) ++ Dwfl *dwfl = dwfl_thread_dwfl(dwfl_frame_thread(frame)); ++ struct sr_core_frame *result = resolve_frame(dwfl, pc, minus_one); ++ ++ /* Do not unwind below __libc_start_main. */ ++ if (0 == sr_strcmp0(result->function_name, "__libc_start_main")) + { +- Dwarf_Addr pc; +- bool minus_one; +- if (!dwfl_frame_state_pc(state, &pc, &minus_one)) +- { +- warn("Failed to obtain PC: %s", dwfl_errmsg(-1)); +- break; +- } +- +- struct sr_core_frame *frame = resolve_frame(dwfl, pc, minus_one); +- list_append(head, tail, frame); +- +- /* Do not unwind below __libc_start_main. */ +- if (0 == sr_strcmp0(frame->function_name, "__libc_start_main")) +- break; +- +- if (!dwfl_frame_unwind(&state)) +- { +- warn("Cannot unwind frame: %s", dwfl_errmsg(-1)); +- break; +- } ++ sr_core_frame_free(result); ++ return CB_STOP_UNWIND; + } + +- if (!error_msg && !head) ++ frame_arg->thread->frames = ++ sr_core_frame_append(frame_arg->thread->frames, result); ++ ++ return DWARF_CB_OK; ++} ++ ++static int ++unwind_thread(Dwfl_Thread *thread, void *data) ++{ ++ struct thread_callback_arg *thread_arg = data; ++ char **error_msg = &thread_arg->error_msg; ++ ++ struct sr_core_thread *result = sr_core_thread_new(); ++ if (!result) + { +- set_error("No frames found for thread id %d", (int)tid); ++ set_error("Failed to initialize stacktrace memory"); ++ return DWARF_CB_ABORT; + } ++ result->id = (int64_t)dwfl_thread_tid(thread); ++ ++ struct frame_callback_arg frame_arg = ++ { ++ .thread = result, ++ .error_msg = NULL ++ }; + +- struct sr_core_thread *thread = sr_core_thread_new(); +- thread->id = (int64_t)tid; +- thread->frames = head; +- return thread; ++ int ret = dwfl_thread_getframes(thread, frame_callback, &frame_arg); ++ if (ret == -1) ++ { ++ warn("dwfl_thread_getframes failed for thread id %d: %s", ++ (int)result->id, dwfl_errmsg(-1)); ++ } ++ else if (ret == DWARF_CB_ABORT) ++ { ++ *error_msg = frame_arg.error_msg; ++ goto abort; ++ } ++ else if (ret != 0 && ret != CB_STOP_UNWIND) ++ { ++ *error_msg = sr_strdup("Unknown error in dwfl_thread_getframes"); ++ goto abort; ++ } ++ ++ if (!error_msg && !frame_arg.thread->frames) ++ { ++ set_error("No frames found for thread id %d", (int)result->id); ++ goto abort; ++ } ++ ++ thread_arg->stacktrace->threads = ++ sr_core_thread_append(thread_arg->stacktrace->threads, result); ++ return DWARF_CB_OK; ++ ++abort: ++ sr_core_thread_free(result); ++ return DWARF_CB_ABORT; + } + + struct sr_core_stacktrace * + sr_parse_coredump(const char *core_file, +- const char *exe_file, +- char **error_msg) ++ const char *exe_file, ++ char **error_msg) + { + struct sr_core_stacktrace *stacktrace = NULL; +- short signal = 0; + + /* Initialize error_msg to 'no error'. */ + if (error_msg) +@@ -91,11 +140,9 @@ sr_parse_coredump(const char *core_file, + if (*error_msg) + return NULL; + +- Dwfl_Frame_State *state = dwfl_frame_state_core(ch->dwfl, core_file); +- if (!state) ++ if (dwfl_core_file_attach(ch->dwfl, ch->eh) < 0) + { +- set_error("Failed to initialize frame state from core '%s'", +- core_file); ++ set_error_dwfl("dwfl_core_file_attach"); + goto fail_destroy_handle; + } + +@@ -105,20 +152,30 @@ sr_parse_coredump(const char *core_file, + set_error("Failed to initialize stacktrace memory"); + goto fail_destroy_handle; + } +- struct sr_core_thread *threads_tail = NULL; + +- do ++ struct thread_callback_arg thread_arg = + { +- struct sr_core_thread *t = unwind_thread(ch->dwfl, state, error_msg); +- if (*error_msg) +- { +- goto fail_destroy_trace; +- } +- list_append(stacktrace->threads, threads_tail, t); +- state = dwfl_frame_thread_next(state); +- } while (state); ++ .stacktrace = stacktrace, ++ .error_msg = NULL ++ }; + +- signal = get_signal_number(ch->eh, core_file); ++ int ret = dwfl_getthreads(ch->dwfl, unwind_thread, &thread_arg); ++ if (ret != 0) ++ { ++ if (ret == -1) ++ set_error_dwfl("dwfl_getthreads"); ++ else if (ret == DWARF_CB_ABORT) ++ *error_msg = thread_arg.error_msg; ++ else ++ *error_msg = sr_strdup("Unknown error in dwfl_getthreads"); ++ ++ goto fail_destroy_trace; ++ } ++ ++ stacktrace->executable = sr_strdup(exe_file); ++ stacktrace->signal = get_signal_number(ch->eh, core_file); ++ /* FIXME: is this the best we can do? */ ++ stacktrace->crash_thread = stacktrace->threads; + + fail_destroy_trace: + if (*error_msg) +@@ -128,11 +185,6 @@ fail_destroy_trace: + } + fail_destroy_handle: + core_handle_free(ch); +- +- stacktrace->executable = sr_strdup(exe_file); +- stacktrace->signal = signal; +- /* FIXME: is this the best we can do? */ +- stacktrace->crash_thread = stacktrace->threads; + return stacktrace; + } + +diff --git a/lib/internal_unwind.h b/lib/internal_unwind.h +index d537f86..2c9abb7 100644 +--- a/lib/internal_unwind.h ++++ b/lib/internal_unwind.h +@@ -27,7 +27,7 @@ + #include "config.h" + + /* define macros indicating what unwinder are we using */ +-#if (defined HAVE_LIBELF_H && defined HAVE_GELF_H && defined HAVE_LIBELF && defined HAVE_LIBDW && defined HAVE_ELFUTILS_LIBDWFL_H && defined HAVE_DWFL_FRAME_STATE_CORE) ++#if (defined HAVE_LIBELF_H && defined HAVE_GELF_H && defined HAVE_LIBELF && defined HAVE_LIBDW && defined HAVE_ELFUTILS_LIBDWFL_H && defined HAVE_DWFL_NEXT_THREAD) + # define WITH_LIBDWFL + #endif + +-- +1.8.3.1 + diff --git a/SOURCES/satyr-0.13-fulfill-missing-values-in-core-frames.patch b/SOURCES/satyr-0.13-fulfill-missing-values-in-core-frames.patch new file mode 100644 index 0000000..83ed2b6 --- /dev/null +++ b/SOURCES/satyr-0.13-fulfill-missing-values-in-core-frames.patch @@ -0,0 +1,67 @@ +From 90f464eb0c1ee4f5c23cb34ec6d199ae648625fe Mon Sep 17 00:00:00 2001 +From: Jakub Filak +Date: Fri, 16 May 2014 13:50:22 +0200 +Subject: [PATCH 1/5] Fulfill missing values in core/frames + +File name of __kernel_vsyscall function frame cannot be resolved but we +known that the function comes from kernel. + +The frame often appears in backtraces of sleep. + +Signed-off-by: Jakub Filak +--- + lib/abrt.c | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +diff --git a/lib/abrt.c b/lib/abrt.c +index 39bc45d..ed33800 100644 +--- a/lib/abrt.c ++++ b/lib/abrt.c +@@ -24,6 +24,8 @@ + #include "operating_system.h" + #include "core/unwind.h" + #include "core/stacktrace.h" ++#include "core/thread.h" ++#include "core/frame.h" + #include "core/fingerprint.h" + #include "python/stacktrace.h" + #include "koops/stacktrace.h" +@@ -62,6 +64,26 @@ sr_abrt_print_report_from_dir(const char *directory, + return true; + } + ++static void ++fulfill_missing_values(struct sr_core_stacktrace *core_stacktrace) ++{ ++ struct sr_core_thread *thread = core_stacktrace->threads; ++ while (thread) ++ { ++ struct sr_core_frame *frame = thread->frames; ++ while (frame) ++ { ++ if (!frame->file_name && frame->function_name ++ && strcmp("__kernel_vsyscall", frame->function_name) == 0) ++ { ++ frame->file_name = sr_strdup("kernel"); ++ } ++ frame = frame->next; ++ } ++ thread = thread->next; ++ } ++} ++ + static bool + create_core_stacktrace(const char *directory, const char *gdb_output, + bool hash_fingerprints, char **error_message) +@@ -87,6 +109,8 @@ create_core_stacktrace(const char *directory, const char *gdb_output, + if (!core_stacktrace) + return false; + ++ fulfill_missing_values(core_stacktrace); ++ + #if 0 + sr_core_fingerprint_generate(core_stacktrace, + error_message); +-- +2.4.3 + diff --git a/SOURCES/satyr-0.13-java-suppressed-exceptions.patch b/SOURCES/satyr-0.13-java-suppressed-exceptions.patch new file mode 100644 index 0000000..63824de --- /dev/null +++ b/SOURCES/satyr-0.13-java-suppressed-exceptions.patch @@ -0,0 +1,339 @@ +From 32085a78c063d80cc152f341343a87c8cfecfd2f Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Thu, 2 Oct 2014 16:46:30 +0200 +Subject: [SATYR PATCH] java: ignore suppressed exceptions + +Java exceptions can form a tree - every exception can have reference to +exception that caused it and to a list of exceptions that were +suppressed during handling[1]. We cannot take the suppressed exceptions +into account without changing the uReport format, therefore this commit +makes the java parser ignore them. + +[1] http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html + +Fixes rhbz#1034857. + +Signed-off-by: Martin Milata +--- + include/utils.h | 3 + + lib/java_frame.c | 26 ++++-- + lib/utils.c | 16 ++++ + tests/java_stacktraces/java-04 | 185 +++++++++++++++++++++++++++++++++++++++++ + tests/python/java.py | 12 +++ + 5 files changed, 234 insertions(+), 8 deletions(-) + create mode 100644 tests/java_stacktraces/java-04 + +diff --git a/include/utils.h b/include/utils.h +index 8d0a6ec..1c7984b 100644 +--- a/include/utils.h ++++ b/include/utils.h +@@ -378,6 +378,9 @@ sr_skip_whitespace(const char *s); + char * + sr_skip_non_whitespace(const char *s); + ++bool ++sr_skip_to_next_line_location(const char **s, int *line, int *column); ++ + /** + * Emit a string of hex representation of bytes. + */ +diff --git a/lib/java_frame.c b/lib/java_frame.c +index da9f26b..ee97572 100644 +--- a/lib/java_frame.c ++++ b/lib/java_frame.c +@@ -28,6 +28,7 @@ + #include "stacktrace.h" + #include "internal_utils.h" + #include ++#include + #include + + #define SR_JF_MARK_NATIVE_METHOD "Native Method" +@@ -337,6 +338,10 @@ sr_java_frame_parse_exception(const char **input, + if (strncmp("... ", cursor, strlen("... ")) == 0) + goto current_exception_done; + ++ /* Suppressed exceptions follow after the end of current exception */ ++ if (strncmp("Suppressed: ", cursor, strlen("Suppressed: ")) == 0) ++ goto current_exception_done; ++ + /* The top most exception does not have '...' at its end */ + if (strncmp("Caused by: ", cursor, strlen("Caused by: ")) == 0) + goto parse_inner_exception; +@@ -363,19 +368,24 @@ sr_java_frame_parse_exception(const char **input, + goto exception_parsing_successful; + + current_exception_done: +- sr_location_add(location, 0, sr_skip_char_cspan(&cursor, "\n")); +- +- if (*cursor == '\n') +- { +- ++cursor; +- /* this adds one line */ +- sr_location_add(location, 2, 0); +- } ++ sr_skip_to_next_line_location(&cursor, &location->line, &location->column); + + mark = cursor; + cursor = sr_skip_whitespace(mark); + sr_location_add(location, 0, cursor - mark); + ++ if (strncmp("Suppressed: ", cursor, strlen("Suppressed: ")) == 0) ++ { ++ /* Skip all lines related to the suppressed exception. We can do ++ * this by skipping all lines that begin with a whitespace - the ++ * main exception chain always begins without preceding whitespace. ++ */ ++ sr_skip_to_next_line_location(&cursor, &location->line, &location->column); ++ ++ while (cursor && isspace(*cursor)) ++ sr_skip_to_next_line_location(&cursor, &location->line, &location->column); ++ } ++ + if (strncmp("Caused by: ", cursor, strlen("Caused by: ")) == 0) + { + parse_inner_exception: +diff --git a/lib/utils.c b/lib/utils.c +index 3c036f3..fa3c0a0 100644 +--- a/lib/utils.c ++++ b/lib/utils.c +@@ -656,6 +656,22 @@ sr_skip_non_whitespace(const char *s) + return (char *) s; + } + ++bool ++sr_skip_to_next_line_location(const char **s, int *line, int *column) ++{ ++ *column += sr_skip_char_cspan(s, "\n"); ++ ++ if (*s && **s == '\n') ++ { ++ *column = 0; ++ (*line)++; ++ (*s)++; ++ return true; ++ } ++ ++ return false; ++} ++ + char * + sr_bin2hex(char *dst, const char *str, int count) + { +diff --git a/tests/java_stacktraces/java-04 b/tests/java_stacktraces/java-04 +new file mode 100644 +index 0000000..6968eb3 +--- /dev/null ++++ b/tests/java_stacktraces/java-04 +@@ -0,0 +1,185 @@ ++Exception in thread "main" java.lang.RuntimeException: yes ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.main(WontCatchSuppressedException.java:35) ++Caused by: java.lang.RuntimeException: yes ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 1 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 1 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 3 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 3 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 3 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 5 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 1 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 3 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 3 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 3 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 5 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 4 more ++Caused by: java.lang.RuntimeException: yes ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 2 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 2 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 2 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 4 more ++Caused by: java.lang.RuntimeException: yes ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 3 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 3 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 3 more +diff --git a/tests/python/java.py b/tests/python/java.py +index 3b85c18..1156fb6 100755 +--- a/tests/python/java.py ++++ b/tests/python/java.py +@@ -152,6 +152,18 @@ class TestJavaStacktrace(BindingsTestCase): + def test_hash(self): + self.assertHashable(self.trace) + ++ def test_suppressed(self): ++ contents = load_input_contents('../java_stacktraces/java-04') ++ trace = satyr.JavaStacktrace(contents) ++ ++ names = 4*['java.lang.RuntimeException', 'WontCatchSuppressedException.die', 'WontCatchSuppressedException.die'] ++ names[-1] = 'WontCatchSuppressedException.main' ++ msgs = 4*['yes', None, None] ++ ++ for frame, name, msg in zip(trace.threads[0].frames, names, msgs): ++ self.assertEqual(frame.name, name) ++ self.assertEqual(frame.message, msg) ++ + class TestJavaThread(BindingsTestCase): + def setUp(self): + self.thread = satyr.JavaStacktrace(contents).threads[0] +-- +1.9.3 + diff --git a/SOURCES/satyr-0.13-koops-unreliable-frames.patch b/SOURCES/satyr-0.13-koops-unreliable-frames.patch new file mode 100644 index 0000000..a110e00 --- /dev/null +++ b/SOURCES/satyr-0.13-koops-unreliable-frames.patch @@ -0,0 +1,149 @@ +From fa978bdd2c3a576df357ce7905b11b73810dc100 Mon Sep 17 00:00:00 2001 +From: Jakub Filak +Date: Fri, 20 Jun 2014 20:42:00 +0200 +Subject: [SATYR PATCH 2/6] Fix the compat version of duphash generation + +sr_thread_get_duphash() must return NULL for oopses having no reliable +frame. + +Signed-off-by: Jakub Filak +--- + lib/generic_thread.c | 7 +++- + tests/koops_frame.at | 1 - + tests/koops_stacktrace.at | 83 +++++++++++++++++++++++++++++++++++++++++++++++ + tests/python/koops.py | 1 + + 4 files changed, 90 insertions(+), 2 deletions(-) + +diff --git a/lib/generic_thread.c b/lib/generic_thread.c +index b219f26..59fff1b 100644 +--- a/lib/generic_thread.c ++++ b/lib/generic_thread.c +@@ -262,7 +262,12 @@ sr_thread_get_duphash(struct sr_thread *thread, int nframes, char *prefix, + nframes--; + } + +- if (flags & SR_DUPHASH_NOHASH) ++ if ((flags & SR_DUPHASH_KOOPS_COMPAT) && strbuf->len == 0) ++ { ++ sr_strbuf_free(strbuf); ++ ret = NULL; ++ } ++ else if (flags & SR_DUPHASH_NOHASH) + ret = sr_strbuf_free_nobuf(strbuf); + else + { +diff --git a/tests/koops_frame.at b/tests/koops_frame.at +index be2cb11..4abc32a 100644 +--- a/tests/koops_frame.at ++++ b/tests/koops_frame.at +@@ -288,4 +288,3 @@ main(void) + return 0; + } + ]]) +- +diff --git a/tests/koops_stacktrace.at b/tests/koops_stacktrace.at +index 404d53c..5e17ca7 100644 +--- a/tests/koops_stacktrace.at ++++ b/tests/koops_stacktrace.at +@@ -730,3 +730,86 @@ main(void) + return 0; + } + ]]) ++ ++## --------------------- ## ++## sr_thread_get_duphash ## ++## --------------------- ## ++ ++AT_TESTFUN([sr_thread_get_duphash], ++[[ ++#include "koops/stacktrace.h" ++#include "koops/frame.h" ++#include "thread.h" ++#include "utils.h" ++#include ++#include ++#include ++ ++void generate_and_test(struct sr_thread *thread, int flags, const char *expected) ++{ ++ char *hash = sr_thread_get_duphash(thread, 1, NULL, flags); ++ ++ if (NULL == hash && NULL != expected) ++ { ++ fprintf(stderr, "'%s' != NULL\n", expected); ++ assert(!"NULL was NOT expected"); ++ } ++ ++ if (NULL != hash && NULL == expected) ++ { ++ fprintf(stderr, "NULL != '%s'\n", hash); ++ assert(!"NULL was expected"); ++ } ++ ++ if (NULL == hash && NULL == expected) ++ return; ++ ++ if (strcmp(hash, expected) != 0) ++ { ++ fprintf(stderr, "'%s' != '%s'\n", expected, hash); ++ //assert(!"Expected and hash are not equal"); ++ } ++ ++ free(hash); ++} ++ ++void test(struct sr_thread *thread, const char *expected, const char *expected_compat) ++{ ++ generate_and_test(thread, SR_DUPHASH_NOHASH, expected); ++ fprintf(stderr, "COMPAT\n"); ++ generate_and_test(thread, SR_DUPHASH_NOHASH|SR_DUPHASH_KOOPS_COMPAT, expected_compat); ++} ++ ++int main(void) ++{ ++ struct sr_koops_stacktrace *stacktrace = sr_koops_stacktrace_new(); ++ struct sr_thread *thread = (struct sr_thread *)stacktrace; ++ ++ struct sr_koops_frame *frame = sr_koops_frame_new(); ++ stacktrace->frames = frame; ++ ++ frame->address = 0xDEADBEAF; ++ ++ fprintf(stderr, "Checkpoint 1\n"); ++ frame->reliable = 1; ++ test(thread, "Thread\n0xdeadbeaf\n", "0xdeadbeaf\n"); ++ ++ fprintf(stderr, "Checkpoint 2\n"); ++ frame->reliable = 0; ++ test(thread, "Thread\n0xdeadbeaf\n", NULL); ++ ++ frame->address = 0xDEADBEAF; ++ frame->function_name = sr_strdup("omg_warn_slowpath_common"); ++ stacktrace->frames = frame; ++ ++ fprintf(stderr, "Checkpoint 3\n"); ++ frame->reliable = 1; ++ test(thread, "Thread\nomg_warn_slowpath_common\n", "omg_warn_slowpath_common\n"); ++ ++ fprintf(stderr, "Checkpoint 4\n"); ++ frame->reliable = 0; ++ test(thread, "Thread\nomg_warn_slowpath_common\n", NULL); ++ ++ return 0; ++} ++]]) +diff --git a/tests/python/koops.py b/tests/python/koops.py +index 3f8fe57..0686b70 100755 +--- a/tests/python/koops.py ++++ b/tests/python/koops.py +@@ -75,6 +75,7 @@ class TestKerneloops(BindingsTestCase): + compat_expected_plain) + self.assertEqual(self.koops.get_duphash(flags=satyr.DUPHASH_KOOPS_COMPAT, frames=6), + '5718b3a86c64e7bed5e8ead08ae3084e447ddbee') ++ self.assertRaises(RuntimeError, self.koops.get_duphash, flags=(satyr.DUPHASH_NOHASH|satyr.DUPHASH_KOOPS_COMPAT), frames=-1) + + def test_crash_thread(self): + self.assertTrue(self.koops.crash_thread is self.koops) +-- +1.9.3 + diff --git a/SOURCES/satyr-0.13-limit-stacktrace-depth.patch b/SOURCES/satyr-0.13-limit-stacktrace-depth.patch new file mode 100644 index 0000000..4015dbf --- /dev/null +++ b/SOURCES/satyr-0.13-limit-stacktrace-depth.patch @@ -0,0 +1,66 @@ +From 1c223116114ddf80609e79ec7ada0ec4a5a463f3 Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Wed, 27 Aug 2014 12:51:41 +0200 +Subject: [SATYR PATCH 5/6] unwind: limit the number of frames unwound + +Fixes rhbz#1133907. + +Signed-off-by: Martin Milata +--- + lib/core_unwind_elfutils.c | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +diff --git a/lib/core_unwind_elfutils.c b/lib/core_unwind_elfutils.c +index 6b904c7..43d66be 100644 +--- a/lib/core_unwind_elfutils.c ++++ b/lib/core_unwind_elfutils.c +@@ -29,10 +29,13 @@ + #include + #include + ++#define FRAME_LIMIT 1024 ++ + struct frame_callback_arg + { + struct sr_core_thread *thread; + char *error_msg; ++ unsigned nframes; + }; + + struct thread_callback_arg +@@ -41,7 +44,7 @@ struct thread_callback_arg + char *error_msg; + }; + +-static int CB_STOP_UNWIND = DWARF_CB_ABORT+1; ++static const int CB_STOP_UNWIND = DWARF_CB_ABORT+1; + + static int + frame_callback(Dwfl_Frame *frame, void *data) +@@ -70,6 +73,13 @@ frame_callback(Dwfl_Frame *frame, void *data) + frame_arg->thread->frames = + sr_core_frame_append(frame_arg->thread->frames, result); + ++ /* Avoid huge stacktraces from programs stuck in infinite recursion. */ ++ frame_arg->nframes++; ++ if (frame_arg->nframes >= FRAME_LIMIT) ++ { ++ return CB_STOP_UNWIND; ++ } ++ + return DWARF_CB_OK; + } + +@@ -90,7 +100,8 @@ unwind_thread(Dwfl_Thread *thread, void *data) + struct frame_callback_arg frame_arg = + { + .thread = result, +- .error_msg = NULL ++ .error_msg = NULL, ++ .nframes = 0 + }; + + int ret = dwfl_thread_getframes(thread, frame_callback, &frame_arg); +-- +1.9.3 + diff --git a/SOURCES/satyr-0.13-normalization-actualize-list-of-functions.patch b/SOURCES/satyr-0.13-normalization-actualize-list-of-functions.patch new file mode 100644 index 0000000..6ff08da --- /dev/null +++ b/SOURCES/satyr-0.13-normalization-actualize-list-of-functions.patch @@ -0,0 +1,188 @@ +From 45973b73fd538eb194c0ddc52457eb29b06dd84f Mon Sep 17 00:00:00 2001 +From: Matej Habrnal +Date: Tue, 10 May 2016 15:13:53 +0200 +Subject: [PATCH] normalization: actualize list of functions + +Related to #1332869 + +Signed-off-by: Matej Habrnal +--- + lib/normalize.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 99 insertions(+), 3 deletions(-) + +diff --git a/lib/normalize.c b/lib/normalize.c +index e3a7a13..9d7574e 100644 +--- a/lib/normalize.c ++++ b/lib/normalize.c +@@ -89,6 +89,7 @@ is_removable_dbus(const char *function_name, + call_match(function_name, source_file, "gerror_to_dbus_error_message", "dbus-gobject.c", NULL) || + call_match(function_name, source_file, "dbus_g_method_return_error", "dbus-gobject.c", NULL) || + call_match(function_name, source_file, "message_queue_dispatch", "dbus-gmain.c", NULL) || ++ call_match(function_name, source_file, "_dbus_abort", "dbus-sysdeps.c", "libdbus", NULL) || + call_match(function_name, source_file, "dbus_connection_dispatch", "dbus-connection.c", "libdbus", NULL); + } + +@@ -134,7 +135,17 @@ is_removable_glib(const char *function_name, + call_match(function_name, source_file, "g_thread_pool_thread_proxy", "gthreadpool.c", "libglib", NULL) || + call_match(function_name, source_file, "g_thread_create_proxy", "gthread.c", "libglib", NULL) || + call_match(function_name, source_file, "g_cclosure_marshal_VOID__BOXED", "gmarshal.c", "libgobject", NULL) || +- call_match(function_name, source_file, "g_cclosure_marshal_VOID__VOID", "gclosure.c", "gmarshal.c", "libgobject", NULL); ++ call_match(function_name, source_file, "g_cclosure_marshal_VOID__VOID", "gclosure.c", "gmarshal.c", "libgobject", NULL) || ++ call_match(function_name, source_file, "g_object_notify", "gobject.c", "libgobject", NULL) || ++ call_match(function_name, source_file, "Glib::exception_handlers_invoke()", "libglibmm", NULL) || ++ call_match(function_name, source_file, "g_signal_handlers_destroy", "gsignal.c", "libgobject", NULL) || ++ call_match(function_name, source_file, "g_vasprintf", "gprintf.c", "libglib", NULL) || ++ call_match(function_name, source_file, "g_strdup_vprintf", "libglib", NULL) || ++ call_match(function_name, source_file, "g_strdup_printf", "libglib", NULL) || ++ call_match(function_name, source_file, "g_print", "libglib", NULL) || ++ call_match(function_name, source_file, "invalid_closure_notify", "gsignal.c", "libgobject", NULL) || ++ call_match(function_name, source_file, "smc_tree_abort", "gslice.c", "libglib", NULL) || ++ call_match(function_name, source_file, "g_thread_abort", "libglib", NULL); + } + + static bool +@@ -147,6 +158,7 @@ is_removable_libstdcpp(const char *function_name, + call_match(function_name, source_file, "std::terminate", "eh_terminate.cc", NULL) || + call_match(function_name, source_file, "__cxxabiv1::__cxa_throw", "eh_throw.cc", NULL) || + call_match(function_name, source_file, "__cxxabiv1::__cxa_rethrow", "eh_throw.cc", NULL) || ++ call_match(function_name, source_file, "__verbose_terminate_handler", "vterminate.cc", NULL) || + call_match(function_name, source_file, "__cxxabiv1::__cxa_pure_virtual", "pure.cc", NULL); + } + +@@ -173,6 +185,8 @@ is_removable_xorg(const char *function_name, + call_match(function_name, source_file, "ddxGiveUp", "xf86Init.c", NULL) || + call_match(function_name, source_file, "OsAbort", "utils.c", NULL) || + call_match(function_name, source_file, "handle_error", "xcb_io.c", "libX11", NULL) || ++ call_match(function_name, source_file, "_XIOError", "XlibInt.c", "libX11", NULL) || ++ call_match(function_name, source_file, "_XEventsQueued", "xcb_io.c", "libX11", NULL) || + call_match(function_name, source_file, "handle_response", "xcb_io.c", "libX11", NULL); + } + +@@ -184,10 +198,82 @@ is_removable_glibc(const char *function_name, + call_match(function_name, source_file, "_start", "", NULL) || + call_match(function_name, source_file, "__libc_start_main", "libc", NULL) || + call_match(function_name, source_file, "clone", "clone.S", "libc", NULL) || ++ call_match(function_name, source_file, "poll", "libc", NULL) || ++ call_match(function_name, source_file, "_IO_new_fclose", "iofclose.c", "libc", NULL) || ++ call_match(function_name, source_file, "_IO_vfprintf_internal", "vfprintf.c", "libc", NULL) || ++ call_match(function_name, source_file, "_IO_default_xsputn", "genops.c", "libc", NULL) || ++ call_match(function_name, source_file, "_IO_wdefault_xsputn", "wgenops.c", "libc", NULL) || ++ call_match(function_name, source_file, "__libc_message", "libc_fatal.c", "libc", NULL) || + call_match(function_name, source_file, "start_thread", "pthread_create.c", "libpthread", NULL); + } + + static bool ++is_removable_other(const char *function_name, ++ const char *source_file) ++{ ++ return ++ call_match(function_name, source_file, "assert_cursor", "intel_display.c", NULL) || ++ call_match(function_name, source_file, "assert_device_not_suspended", "intel_uncore.c", NULL) || ++ call_match(function_name, source_file, "assert_pipe", "intel_display.c", NULL) || ++ call_match(function_name, source_file, "assert_plane", "intel_display.c", NULL) || ++ call_match(function_name, source_file, "assert_transcoder_disabled", "intel_display.c", NULL) || ++ call_match(function_name, source_file, "btrfs_assert_delayed_root_empty", "delayed-inode.c", "btrfs", NULL) || ++ call_match(function_name, source_file, "_cogl_set_error", "cogl-error.c", "libcogl", NULL) || ++ call_match(function_name, source_file, "defaultCrashHandler", "kcrash.cpp", "libKF5Crash", NULL) || ++ call_match(function_name, source_file, "_dl_signal_error", "dl-error.c", "ld-linux", NULL) || ++ call_match(function_name, source_file, "error_dialog_response_cb", "", NULL) || ++ call_match(function_name, source_file, "do_warn", "_warnings.c", "libpython",NULL) || ++ call_match(function_name, source_file, "QMessageLogger::fatal(char const*, ...) const", "", NULL) || ++ call_match(function_name, source_file, "nsProfileLock::FatalSignalHandler(int, siginfo_t*, void*)", "", NULL) || ++ call_match(function_name, source_file, "qt_message_output", "qglobal.cpp", "libQtCore", NULL) || ++ call_match(function_name, source_file, "qt_message_output(QtMsgType, char const*)", "", NULL) || ++ call_match(function_name, source_file, "signalHandler(int, siginfo_t*, void*)", "", NULL) || ++ call_match(function_name, source_file, "FatalSignalHandler", "nsProfileLock.cpp", "libxul", NULL) || ++ call_match(function_name, source_file, "Foam::error::abort()", "", NULL) || ++ call_match(function_name, source_file, "JS_AbortIfWrongThread", "libmozjs", NULL) || ++ call_match(function_name, source_file, "Crash::defaultCrashHandler(int)", "libkdeui", "libKF5Crash", NULL) || ++ call_match(function_name, source_file, "Py_FatalError", "pythonrun.c", "libpython", NULL) || ++ call_match(function_name, source_file, "__btrfs_abort_transaction", "btrfs", NULL) || ++ call_match(function_name, source_file, "assert_pch_hdmi_disabled", "", NULL) || ++ call_match(function_name, source_file, "assert_pll", "", NULL) || ++ call_match(function_name, source_file, "core::system::abort()", "", NULL) || ++ call_match(function_name, source_file, "ddd_assert_fail", "assert.C", NULL) || ++ call_match(function_name, source_file, "debug_dma_assert_idle", "", NULL) || ++ call_match(function_name, source_file, "error_handler", "", NULL) || ++ call_match(function_name, source_file, "fatal_error_signal", "", NULL) || ++ call_match(function_name, source_file, "fatal_handler", "signal.c", "libfreerdp", NULL) || ++ call_match(function_name, source_file, "gpf_notice", "", NULL) || ++ call_match(function_name, source_file, "log", "", NULL) || ++ call_match(function_name, source_file, "_log", "", NULL) || ++ call_match(function_name, source_file, "log_assert_failed", "", NULL) || ++ call_match(function_name, source_file, "mozalloc_abort", "mozalloc_abort.cpp", "libmozalloc", NULL) || ++ call_match(function_name, source_file, "mozalloc_abort(char const*)", "libmozalloc", "content-container", "plugin-container", NULL) || ++ call_match(function_name, source_file, "note_interrupt", "spurious.c", "vmlinux", NULL) || ++ call_match(function_name, source_file, "print_bad_pte", "memory.c", "vmlinux", NULL) || ++ call_match(function_name, source_file, "print_oops_end_marker", "panic.c", "vmlinux", NULL) || ++ call_match(function_name, source_file, "printk", "printk.c", "vmlinux", NULL) || ++ call_match(function_name, source_file, "qupzilla_signal_handler", "main.cpp", "qupzilla", NULL) || ++ call_match(function_name, source_file, "rb_bug", "error.c", "libruby", NULL) || ++ call_match(function_name, source_file, "sighandler", "", NULL) || ++ call_match(function_name, source_file, "signalHandler(int)", "", NULL) || ++ call_match(function_name, source_file, "signal_abort", "signal.c", NULL) || ++ call_match(function_name, source_file, "signal_handler", "", NULL) || ++ call_match(function_name, source_file, "sys_abort", "error.c", "libgfortran", NULL) || ++ call_match(function_name, source_file, "terminate_due_to_signal", "emacs.c", "emacs", NULL) || ++ call_match(function_name, source_file, "wl_log", "wayland-util.c", NULL) || ++ call_match(function_name, source_file, "display_protocol_error", "wayland-client.c", NULL) || ++ call_match(function_name, source_file, "display_handle_error", "wayland-client.c", NULL) || ++ call_match(function_name, source_file, "x_io_error", "libmutter", NULL) || ++ call_match(function_name, source_file, "__ioremap_calle ", "ioremap.c", NULL) || ++ call_match(function_name, source_file, "ioremap_nocache", "ioremap.c", NULL) || ++ call_match(function_name, source_file, "wpa_msg", "wpa_debug.c", NULL) || ++ call_match(function_name, source_file, "js::gc::FinalizeArenas(js::FreeOp*, js::gc::ArenaHeader**, js::gc::ArenaList&, js::gc::AllocKind, js::SliceBudget&)\"js::Shape::finalize(js::FreeOp*)", "", NULL) || ++ call_match(function_name, source_file, "WTF::StringImpl::endsWith(char const*, unsigned int, bool) const", "", NULL) || ++ call_match(function_name, source_file, "mozilla::plugins::child::_invokedefault(_NPP*, NPObject*, _NPVariant const*, unsigned int, _NPVariant*)", "", NULL) || ++ call_match(function_name, source_file, "xitk_signal_handler", "xitk.c", "xine", NULL); ++} ++ ++static bool + is_removable_glibc_with_above(const char *function_name, + const char *source_file) + { +@@ -205,6 +291,13 @@ is_removable_glibc_with_above(const char *function_name, + call_match(function_name, source_file, "__snprintf_chk", "", NULL) || + call_match(function_name, source_file, "___snprintf_chk", "", NULL) || + call_match(function_name, source_file, "__vasprintf_chk", "", NULL) || ++ call_match(function_name, source_file, "__vsprintf_chk", "", NULL) || ++ call_match(function_name, source_file, "___sprintf_chk", "", NULL) || ++ call_match(function_name, source_file, "__fwprintf_chk", "", NULL) || ++ call_match(function_name, source_file, "__asprintf_chk", "", NULL) || ++ call_match(function_name, source_file, "___printf_chk", "", NULL) || ++ call_match(function_name, source_file, "___fprintf_chk", "", NULL) || ++ call_match(function_name, source_file, "__vswprintf_chk", "", NULL) || + call_match(function_name, source_file, "malloc_consolidate", "malloc.c", "libc", NULL) || + call_match(function_name, source_file, "malloc_printerr", "malloc.c", "libc", NULL) || + call_match(function_name, source_file, "_int_malloc", "malloc.c", "libc", NULL) || +@@ -295,6 +388,7 @@ sr_gdb_is_exit_frame(struct sr_gdb_frame *frame) + /* Terminates a function in case of buffer overflow. */ + sr_gdb_frame_calls_func(frame, "__chk_fail", "chk_fail.c", "libc.so", NULL) || + sr_gdb_frame_calls_func(frame, "__stack_chk_fail", "stack_chk_fail.c", "libc.so", NULL) || ++ sr_gdb_frame_calls_func(frame, "do_exit", "exit.c", NULL) || + sr_gdb_frame_calls_func(frame, "kill", "syscall-template.S", NULL); + } + +@@ -365,7 +459,8 @@ sr_normalize_gdb_thread(struct sr_gdb_thread *thread) + is_removable_linux(frame->function_name, frame->source_file) || + is_removable_xorg(frame->function_name, frame->source_file) || + is_removable_jvm(frame->function_name, frame->source_file) || +- is_removable_vim(frame->function_name, frame->source_file); ++ is_removable_vim(frame->function_name, frame->source_file) || ++ is_removable_other(frame->function_name, frame->source_file); + + bool removable_with_above = + is_removable_glibc_with_above(frame->function_name, frame->source_file) || +@@ -510,7 +605,8 @@ sr_normalize_core_thread(struct sr_core_thread *thread) + is_removable_linux(frame->function_name, frame->file_name) || + is_removable_xorg(frame->function_name, frame->file_name) || + is_removable_jvm(frame->function_name, frame->file_name) || +- is_removable_vim(frame->function_name, frame->file_name); ++ is_removable_vim(frame->function_name, frame->file_name) || ++ is_removable_other(frame->function_name, frame->file_name); + + bool removable_with_above = + is_removable_glibc_with_above(frame->function_name, frame->file_name) || +-- +1.8.3.1 + diff --git a/SOURCES/satyr-0.13-normalization-add-glibc-__assert_fail_base.patch b/SOURCES/satyr-0.13-normalization-add-glibc-__assert_fail_base.patch new file mode 100644 index 0000000..7c7c505 --- /dev/null +++ b/SOURCES/satyr-0.13-normalization-add-glibc-__assert_fail_base.patch @@ -0,0 +1,24 @@ +From ae3890bb65dfd13c4dd1a52b2f9fb1d1a3515253 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Bry=C5=A1a?= +Date: Wed, 27 May 2015 13:56:12 +0200 +Subject: [PATCH] normalization: add glibc __assert_fail_base + +--- + lib/normalize.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/lib/normalize.c b/lib/normalize.c +index 0e9b497..4b22645 100644 +--- a/lib/normalize.c ++++ b/lib/normalize.c +@@ -193,6 +193,7 @@ is_removable_glibc_with_above(const char *function_name, + { + return + call_match(function_name, source_file, "__assert_fail", "", NULL) || ++ call_match(function_name, source_file, "__assert_fail_base", "", NULL) || + call_match(function_name, source_file, "__chk_fail", "", NULL) || + call_match(function_name, source_file, "__longjmp_chk", "", NULL) || + call_match(function_name, source_file, "__malloc_assert", "", NULL) || +-- +1.8.3.1 + diff --git a/SOURCES/satyr-0.13-normalization-add-glibc-__libc_fatal.patch b/SOURCES/satyr-0.13-normalization-add-glibc-__libc_fatal.patch new file mode 100644 index 0000000..b24b413 --- /dev/null +++ b/SOURCES/satyr-0.13-normalization-add-glibc-__libc_fatal.patch @@ -0,0 +1,26 @@ +From 933bd99829000bcd9bf674f0e2e61c562767a09b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Bry=C5=A1a?= +Date: Wed, 27 May 2015 14:37:15 +0200 +Subject: [PATCH] normalization: add glibc __libc_fatal + +--- + lib/normalize.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/lib/normalize.c b/lib/normalize.c +index 4b22645..bec2ccc 100644 +--- a/lib/normalize.c ++++ b/lib/normalize.c +@@ -216,7 +216,8 @@ is_removable_glibc_with_above(const char *function_name, + call_match(function_name, source_file, "__libc_memalign", "malloc.c", NULL) || + call_match(function_name, source_file, "__libc_realloc", "malloc.c", NULL) || + call_match(function_name, source_file, "__posix_memalign", "malloc.c", NULL) || +- call_match(function_name, source_file, "__libc_calloc", "malloc.c", NULL); ++ call_match(function_name, source_file, "__libc_calloc", "malloc.c", NULL) || ++ call_match(function_name, source_file, "__libc_fatal", "libc", NULL); + } + + static char * +-- +1.8.3.1 + diff --git a/SOURCES/satyr-0.13-normalization-additional-X-GDK-functions.patch b/SOURCES/satyr-0.13-normalization-additional-X-GDK-functions.patch new file mode 100644 index 0000000..74eba7d --- /dev/null +++ b/SOURCES/satyr-0.13-normalization-additional-X-GDK-functions.patch @@ -0,0 +1,40 @@ +From e436cd0b6eb389e6803b8b519e936429e694e453 Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Fri, 28 Nov 2014 17:59:45 +0100 +Subject: [PATCH] normalization: additional X/GDK functions + +Closes #199. + +Signed-off-by: Martin Milata +--- + lib/normalize.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/lib/normalize.c b/lib/normalize.c +index 81be188..0e9b497 100644 +--- a/lib/normalize.c ++++ b/lib/normalize.c +@@ -100,7 +100,8 @@ is_removable_gdk(const char *function_name, + call_match(function_name, source_file, "gdk_x_error", "gdkmain-x11.c", NULL) || + call_match(function_name, source_file, "gdk_threads_dispatch", "gdk.c", NULL) || + call_match(function_name, source_file, "gdk_event_dispatch", "gdkevents-x11.c", "gdkevents.c", NULL) || +- call_match(function_name, source_file, "gdk_event_source_dispatch", "gdkeventsource.c", NULL); ++ call_match(function_name, source_file, "gdk_event_source_dispatch", "gdkeventsource.c", NULL) || ++ call_match(function_name, source_file, "_gdk_x11_display_error_event", "gdkdisplay-x11.c", "libgdk", NULL); + } + + static bool +@@ -170,7 +171,9 @@ is_removable_xorg(const char *function_name, + call_match(function_name, source_file, "AbortServer", "log.c", NULL) || + call_match(function_name, source_file, "AbortDDX", "xf86Init.c", NULL) || + call_match(function_name, source_file, "ddxGiveUp", "xf86Init.c", NULL) || +- call_match(function_name, source_file, "OsAbort", "utils.c", NULL); ++ call_match(function_name, source_file, "OsAbort", "utils.c", NULL) || ++ call_match(function_name, source_file, "handle_error", "xcb_io.c", "libX11", NULL) || ++ call_match(function_name, source_file, "handle_response", "xcb_io.c", "libX11", NULL); + } + + static bool +-- +1.8.3.1 + diff --git a/SOURCES/satyr-0.13-normalization-normalize-out-exit-frames.patch b/SOURCES/satyr-0.13-normalization-normalize-out-exit-frames.patch new file mode 100644 index 0000000..c4d57e3 --- /dev/null +++ b/SOURCES/satyr-0.13-normalization-normalize-out-exit-frames.patch @@ -0,0 +1,144 @@ +From b4120f370475bde243a1ae5bde64f4f0a641c3c3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Bry=C5=A1a?= +Date: Wed, 27 May 2015 18:43:52 +0200 +Subject: [PATCH] normalization: normalize out exit frames + +--- + include/core/thread.h | 3 +++ + lib/core_thread.c | 30 +++++++++++++++++------------- + lib/normalize.c | 36 +++++++++++++++++++++--------------- + 3 files changed, 41 insertions(+), 28 deletions(-) + +diff --git a/include/core/thread.h b/include/core/thread.h +index 0ba71e4..d6125df 100644 +--- a/include/core/thread.h ++++ b/include/core/thread.h +@@ -123,6 +123,9 @@ struct sr_core_thread * + sr_core_thread_append(struct sr_core_thread *dest, + struct sr_core_thread *item); + ++bool ++sr_core_thread_is_exit_frame(struct sr_core_frame *frame); ++ + struct sr_core_frame * + sr_core_thread_find_exit_frame(struct sr_core_thread *thread); + +diff --git a/lib/core_thread.c b/lib/core_thread.c +index 54fb89c..d8aa943 100644 +--- a/lib/core_thread.c ++++ b/lib/core_thread.c +@@ -149,6 +149,22 @@ sr_core_thread_append(struct sr_core_thread *dest, + return dest; + } + ++bool ++sr_core_thread_is_exit_frame(struct sr_core_frame *frame) ++{ ++ return ++ sr_core_frame_calls_func(frame, "__run_exit_handlers", NULL) || ++ sr_core_frame_calls_func(frame, "raise", "libc.so", "libc-", "libpthread.so", NULL) || ++ sr_core_frame_calls_func(frame, "__GI_raise", NULL) || ++ sr_core_frame_calls_func(frame, "exit", NULL) || ++ sr_core_frame_calls_func(frame, "abort", "libc.so", "libc-", NULL) || ++ sr_core_frame_calls_func(frame, "__GI_abort", NULL) || ++ /* Terminates a function in case of buffer overflow. */ ++ sr_core_frame_calls_func(frame, "__chk_fail", "libc.so", NULL) || ++ sr_core_frame_calls_func(frame, "__stack_chk_fail", "libc.so", NULL) || ++ sr_core_frame_calls_func(frame, "kill", NULL); ++} ++ + struct sr_core_frame * + sr_core_thread_find_exit_frame(struct sr_core_thread *thread) + { +@@ -156,19 +172,7 @@ sr_core_thread_find_exit_frame(struct sr_core_thread *thread) + struct sr_core_frame *result = NULL; + while (frame) + { +- bool is_exit_frame = +- sr_core_frame_calls_func(frame, "__run_exit_handlers", NULL) || +- sr_core_frame_calls_func(frame, "raise", "libc.so", "libc-", "libpthread.so", NULL) || +- sr_core_frame_calls_func(frame, "__GI_raise", NULL) || +- sr_core_frame_calls_func(frame, "exit", NULL) || +- sr_core_frame_calls_func(frame, "abort", "libc.so", "libc-", NULL) || +- sr_core_frame_calls_func(frame, "__GI_abort", NULL) || +- /* Terminates a function in case of buffer overflow. */ +- sr_core_frame_calls_func(frame, "__chk_fail", "libc.so", NULL) || +- sr_core_frame_calls_func(frame, "__stack_chk_fail", "libc.so", NULL) || +- sr_core_frame_calls_func(frame, "kill", NULL); +- +- if (is_exit_frame) ++ if (sr_core_thread_is_exit_frame(frame)) + result = frame; + + frame = frame->next; +diff --git a/lib/normalize.c b/lib/normalize.c +index bec2ccc..e3a7a13 100644 +--- a/lib/normalize.c ++++ b/lib/normalize.c +@@ -282,6 +282,22 @@ remove_func_prefix(char *function_name, const char *prefix, int num) + memmove(function_name, function_name + num, func_len - num + 1); + } + ++static bool ++sr_gdb_is_exit_frame(struct sr_gdb_frame *frame) ++{ ++ return ++ sr_gdb_frame_calls_func(frame, "__run_exit_handlers", "exit.c", NULL) || ++ sr_gdb_frame_calls_func(frame, "raise", "pt-raise.c", "libc.so", "libc-", "libpthread.so", NULL) || ++ sr_gdb_frame_calls_func(frame, "__GI_raise", "raise.c", NULL) || ++ sr_gdb_frame_calls_func(frame, "exit", "exit.c", NULL) || ++ sr_gdb_frame_calls_func(frame, "abort", "abort.c", "libc.so", "libc-", NULL) || ++ sr_gdb_frame_calls_func(frame, "__GI_abort", "abort.c", NULL) || ++ /* Terminates a function in case of buffer overflow. */ ++ sr_gdb_frame_calls_func(frame, "__chk_fail", "chk_fail.c", "libc.so", NULL) || ++ sr_gdb_frame_calls_func(frame, "__stack_chk_fail", "stack_chk_fail.c", "libc.so", NULL) || ++ sr_gdb_frame_calls_func(frame, "kill", "syscall-template.S", NULL); ++} ++ + void + sr_normalize_gdb_thread(struct sr_gdb_thread *thread) + { +@@ -352,7 +368,8 @@ sr_normalize_gdb_thread(struct sr_gdb_thread *thread) + is_removable_vim(frame->function_name, frame->source_file); + + bool removable_with_above = +- is_removable_glibc_with_above(frame->function_name, frame->source_file); ++ is_removable_glibc_with_above(frame->function_name, frame->source_file) || ++ sr_gdb_is_exit_frame(frame); + + if (removable_with_above) + { +@@ -496,7 +513,8 @@ sr_normalize_core_thread(struct sr_core_thread *thread) + is_removable_vim(frame->function_name, frame->file_name); + + bool removable_with_above = +- is_removable_glibc_with_above(frame->function_name, frame->file_name); ++ is_removable_glibc_with_above(frame->function_name, frame->file_name) || ++ sr_core_thread_is_exit_frame(frame); + + if (removable_with_above) + { +@@ -689,19 +707,7 @@ sr_glibc_thread_find_exit_frame(struct sr_gdb_thread *thread) + struct sr_gdb_frame *result = NULL; + while (frame) + { +- bool is_exit_frame = +- sr_gdb_frame_calls_func(frame, "__run_exit_handlers", "exit.c", NULL) || +- sr_gdb_frame_calls_func(frame, "raise", "pt-raise.c", "libc.so", "libc-", "libpthread.so", NULL) || +- sr_gdb_frame_calls_func(frame, "__GI_raise", "raise.c", NULL) || +- sr_gdb_frame_calls_func(frame, "exit", "exit.c", NULL) || +- sr_gdb_frame_calls_func(frame, "abort", "abort.c", "libc.so", "libc-", NULL) || +- sr_gdb_frame_calls_func(frame, "__GI_abort", "abort.c", NULL) || +- /* Terminates a function in case of buffer overflow. */ +- sr_gdb_frame_calls_func(frame, "__chk_fail", "chk_fail.c", "libc.so", NULL) || +- sr_gdb_frame_calls_func(frame, "__stack_chk_fail", "stack_chk_fail.c", "libc.so", NULL) || +- sr_gdb_frame_calls_func(frame, "kill", "syscall-template.S", NULL); +- +- if (is_exit_frame) ++ if (sr_gdb_is_exit_frame(frame)) + result = frame; + + frame = frame->next; +-- +1.8.3.1 + diff --git a/SOURCES/satyr-0.13-normalize-extend-xorg-blacklist.patch b/SOURCES/satyr-0.13-normalize-extend-xorg-blacklist.patch new file mode 100644 index 0000000..e747010 --- /dev/null +++ b/SOURCES/satyr-0.13-normalize-extend-xorg-blacklist.patch @@ -0,0 +1,34 @@ +From f3f36ff858f50aed5a6c4df5144ae15aeb5255b9 Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Mon, 24 Feb 2014 12:44:12 +0100 +Subject: [PATCH] normalize: extend xorg blacklist + +Closes rhbz#1068767. + +Signed-off-by: Martin Milata +--- + lib/normalize.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/lib/normalize.c b/lib/normalize.c +index d332882..81be188 100644 +--- a/lib/normalize.c ++++ b/lib/normalize.c +@@ -164,7 +164,13 @@ is_removable_xorg(const char *function_name, + call_match(function_name, source_file, "_XReply", "xcb_io.c", NULL) || + call_match(function_name, source_file, "_XError", "XlibInt.c", NULL) || + call_match(function_name, source_file, "XSync", "Sync.c", NULL) || +- call_match(function_name, source_file, "process_responses", "xcb_io.c", NULL); ++ call_match(function_name, source_file, "process_responses", "xcb_io.c", NULL) || ++ call_match(function_name, source_file, "OsSigHandler", "osinit.c", NULL) || ++ call_match(function_name, source_file, "FatalError", "log.c", NULL) || ++ call_match(function_name, source_file, "AbortServer", "log.c", NULL) || ++ call_match(function_name, source_file, "AbortDDX", "xf86Init.c", NULL) || ++ call_match(function_name, source_file, "ddxGiveUp", "xf86Init.c", NULL) || ++ call_match(function_name, source_file, "OsAbort", "utils.c", NULL); + } + + static bool +-- +1.8.3.1 + diff --git a/SOURCES/satyr-0.13-os-add-support-for-OS-Variant.patch b/SOURCES/satyr-0.13-os-add-support-for-OS-Variant.patch new file mode 100644 index 0000000..1456ada --- /dev/null +++ b/SOURCES/satyr-0.13-os-add-support-for-OS-Variant.patch @@ -0,0 +1,166 @@ +From 20cafc9b23448aaab4fd8f07a5a1defd0cd7038a Mon Sep 17 00:00:00 2001 +From: Jakub Filak +Date: Tue, 1 Sep 2015 13:05:48 +0200 +Subject: [PATCH] os: add support for OS Variant + +VARIANT= and VARIANT_ID= fields are optional. + +http://www.freedesktop.org/software/systemd/man/os-release.html + +Related to abrt/abrt#995 +Resolves: #1342469 + +Signed-off-by: Jakub Filak + +Conflicts: + include/operating_system.h + lib/operating_system.c +--- + include/operating_system.h | 1 + + lib/operating_system.c | 16 ++++++++++++++++ + tests/operating_system.at | 43 +++++++++++++++++++++++++++++++++++++++---- + 3 files changed, 56 insertions(+), 4 deletions(-) + +diff --git a/include/operating_system.h b/include/operating_system.h +index f26e322..84fcce0 100644 +--- a/include/operating_system.h ++++ b/include/operating_system.h +@@ -37,6 +37,7 @@ struct sr_operating_system + char *cpe; + /* Uptime in seconds. */ + uint64_t uptime; ++ char *variant; + }; + + struct sr_operating_system * +diff --git a/lib/operating_system.c b/lib/operating_system.c +index 64f7439..9bebe5b 100644 +--- a/lib/operating_system.c ++++ b/lib/operating_system.c +@@ -43,6 +43,7 @@ sr_operating_system_init(struct sr_operating_system *operating_system) + operating_system->version = NULL; + operating_system->architecture = NULL; + operating_system->cpe = NULL; ++ operating_system->variant = NULL; + operating_system->uptime = 0; + } + +@@ -56,6 +57,7 @@ sr_operating_system_free(struct sr_operating_system *operating_system) + free(operating_system->version); + free(operating_system->architecture); + free(operating_system->cpe); ++ free(operating_system->variant); + free(operating_system); + } + +@@ -92,6 +94,13 @@ sr_operating_system_to_json(struct sr_operating_system *operating_system) + sr_strbuf_append_str(strbuf, "\n"); + } + ++ if (operating_system->variant) ++ { ++ sr_strbuf_append_str(strbuf, ", \"variant\": "); ++ sr_json_append_escaped(strbuf, operating_system->variant); ++ sr_strbuf_append_str(strbuf, "\n"); ++ } ++ + if (operating_system->uptime > 0) + { + sr_strbuf_append_strf(strbuf, +@@ -122,6 +131,9 @@ sr_operating_system_from_json(struct sr_json_value *root, char **error_message) + JSON_READ_STRING(root, "architecture", &result->architecture) && + JSON_READ_UINT64(root, "uptime", &result->uptime); + ++ /* variant is optional - failure is not fatal */ ++ JSON_READ_STRING(root, "variant", &result->variant); ++ + if (!success) + { + sr_operating_system_free(result); +@@ -194,6 +206,10 @@ os_release_callback(char *key, char *value, void *data) + { + operating_system->cpe = value; + } ++ else if (0 == strcmp(key, "VARIANT_ID")) ++ { ++ operating_system->variant = value; ++ } + else + { + free(value); +diff --git a/tests/operating_system.at b/tests/operating_system.at +index 0db2b9d..044d4bd 100644 +--- a/tests/operating_system.at ++++ b/tests/operating_system.at +@@ -46,12 +46,14 @@ AT_TESTFUN([sr_operating_system_parse_etc_os_release], + [[ + #include "operating_system.h" + #include "utils.h" ++#include + #include + #include + + void check(const char *etc_os_release, + const char *expected_name, +- const char *expected_version) ++ const char *expected_version, ++ const char *expected_variant) + { + struct sr_operating_system *os = sr_operating_system_new(); + bool success = sr_operating_system_parse_etc_os_release( +@@ -60,6 +62,20 @@ void check(const char *etc_os_release, + assert(success); + assert(0 == strcmp(os->name, expected_name)); + assert(0 == strcmp(os->version, expected_version)); ++ ++ if ( ( (expected_variant != NULL && os->variant == NULL) ++ || (expected_variant == NULL && os->variant != NULL) ++ ) ++ || ( (expected_variant != NULL && os->variant != NULL) ++ && 0 != strcmp(os->version, expected_version) ++ ) ++ ) ++ { ++ fprintf(stderr, "Expected: '%s'\n", expected_variant); ++ fprintf(stderr, "Got : '%s'\n", os->variant); ++ abort(); ++ } ++ + sr_operating_system_free(os); + } + +@@ -102,9 +118,28 @@ main(void) + "REDHAT_SUPPORT_PRODUCT=\"Red Hat Enterprise Linux\"\n" + "REDHAT_SUPPORT_PRODUCT_VERSION=7.0\n"; + +- check(f19, "fedora", "19"); +- check(raw, "fedora", "rawhide"); +- check(el7, "rhel", "7.0"); ++ char *f23 = ++"NAME=Fedora\n" ++"VERSION=\"23 (Workstation Edition)\"\n" ++"ID=fedora\n" ++"VERSION_ID=23\n" ++"PRETTY_NAME=\"Fedora 23 (Workstation Edition)\"\n" ++"ANSI_COLOR=\"0;34\"\n" ++"CPE_NAME=\"cpe:/o:fedoraproject:fedora:23\"\n" ++"HOME_URL=\"https://fedoraproject.org/\"\n" ++"BUG_REPORT_URL=\"https://bugzilla.redhat.com/\"\n" ++"REDHAT_BUGZILLA_PRODUCT=\"Fedora\"\n" ++"REDHAT_BUGZILLA_PRODUCT_VERSION=23\n" ++"REDHAT_SUPPORT_PRODUCT=\"Fedora\"\n" ++"REDHAT_SUPPORT_PRODUCT_VERSION=23\n" ++"PRIVACY_POLICY_URL=https://fedoraproject.org/wiki/Legal:PrivacyPolicy\n" ++"VARIANT=\"Workstation Edition\"\n" ++"VARIANT_ID=workstation\n"; ++ ++ check(f19, "fedora", "19", NULL); ++ check(raw, "fedora", "rawhide", NULL); ++ check(el7, "rhel", "7.0", NULL); ++ check(f23, "fedora", "23", "workstation"); + + return 0; + } +-- +1.8.3.1 + diff --git a/SOURCES/satyr-0.13-ppc64-backtrace-parsing.patch b/SOURCES/satyr-0.13-ppc64-backtrace-parsing.patch new file mode 100644 index 0000000..fe5ac75 --- /dev/null +++ b/SOURCES/satyr-0.13-ppc64-backtrace-parsing.patch @@ -0,0 +1,153 @@ +From 0fcd63190deeb0afa2ad04826da99e5632f9d517 Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Mon, 14 Jul 2014 16:53:12 +0200 +Subject: [SATYR PATCH 4/6] gdb: fix parsing of ppc64 stacktraces + +Fixes rhbz#1119072. + +Signed-off-by: Martin Milata +--- + lib/gdb_frame.c | 3 +- + tests/gdb_stacktrace.at | 35 ++++++++++++++++++++ + tests/gdb_stacktraces/rhbz-1119072 | 67 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 104 insertions(+), 1 deletion(-) + create mode 100644 tests/gdb_stacktraces/rhbz-1119072 + +diff --git a/lib/gdb_frame.c b/lib/gdb_frame.c +index 57e9479..e449b30 100644 +--- a/lib/gdb_frame.c ++++ b/lib/gdb_frame.c +@@ -559,12 +559,13 @@ sr_gdb_frame_parse_function_name(const char **input, + /* First character: + '~' for destructor + '*' for ???? ++ '.' on ppc64 + '_a-zA-Z' for mangled/nonmangled function name + '(' to start "(anonymous namespace)::" or something + */ + char first; + char *namechunk; +- if (sr_parse_char_limited(&local_input, "~*_" SR_alpha, &first)) ++ if (sr_parse_char_limited(&local_input, "~*._" SR_alnum, &first)) + { + /* If it's a start of 'o'perator, put the 'o' back! */ + if (first == 'o') +diff --git a/tests/gdb_stacktrace.at b/tests/gdb_stacktrace.at +index 8832cb8..f626417 100644 +--- a/tests/gdb_stacktrace.at ++++ b/tests/gdb_stacktrace.at +@@ -247,3 +247,38 @@ main(void) + return 0; + } + ]]) ++ ++## ----------------------------- ## ++## sr_gdb_stacktrace_parse_ppc64 ## ++## ----------------------------- ## ++AT_TESTFUN([sr_gdb_stacktrace_parse_ppc64], ++[[ ++#include "gdb/stacktrace.h" ++#include "gdb/thread.h" ++#include "gdb/frame.h" ++#include "location.h" ++#include "utils.h" ++#include ++#include ++ ++int ++main(void) ++{ ++ /* Check that satyr is able to parse backtrace with missing Thread header */ ++ struct sr_location location; ++ sr_location_init(&location); ++ char *error_message; ++ char *full_input = sr_file_to_string("../../gdb_stacktraces/rhbz-1119072", &error_message); ++ assert(full_input); ++ char *input = full_input; ++ struct sr_gdb_stacktrace *stacktrace = sr_gdb_stacktrace_parse(&input, &location); ++ assert(stacktrace); ++ assert(5 == sr_gdb_stacktrace_get_thread_count(stacktrace)); ++ struct sr_gdb_thread *thread = stacktrace->threads; ++ assert(7 == sr_thread_frame_count(thread)); ++ struct sr_gdb_frame *frame = thread->frames; ++ assert(0 == strcmp(frame->function_name, ".pthread_cond_timedwait")); ++ sr_gdb_stacktrace_free(stacktrace); ++ return 0; ++} ++]]) +diff --git a/tests/gdb_stacktraces/rhbz-1119072 b/tests/gdb_stacktraces/rhbz-1119072 +new file mode 100644 +index 0000000..2aca936 +--- /dev/null ++++ b/tests/gdb_stacktraces/rhbz-1119072 +@@ -0,0 +1,67 @@ ++[New LWP 2271] ++[New LWP 2374] ++[New LWP 2325] ++[New LWP 2360] ++[New LWP 2375] ++[Thread debugging using libthread_db enabled] ++Using host libthread_db library "/lib64/libthread_db.so.1". ++Core was generated by `caja '. ++Program terminated with signal SIGTRAP, Trace/breakpoint trap. ++#0 0x000000803a327a54 in .raise () from /usr/lib64/libpthread.so.0 ++ ++Thread 5 (Thread 0x3fffa0ebeed0 (LWP 2375)): ++#0 0x000000803a321f24 in .pthread_cond_timedwait () from /usr/lib64/libpthread.so.0 ++#1 0x000000803a748054 in .g_cond_wait_until () from /usr/lib64/libglib-2.0.so.0 ++#2 0x000000803a6ac2c8 in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#3 0x000000803a720530 in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#4 0x000000803a71f3f8 in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#5 0x000000803a31c31c in .start_thread () from /usr/lib64/libpthread.so.0 ++#6 0x000000803a20c150 in .__clone () from /usr/lib64/libc.so.6 ++ ++Thread 4 (Thread 0x3fffa208eed0 (LWP 2360)): ++#0 0x000000803a1fd130 in .__GI___poll () from /usr/lib64/libc.so.6 ++#1 0x000000803a6fe0b0 in .g_poll () from /usr/lib64/libglib-2.0.so.0 ++#2 0x000000803a6e9ef4 in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#3 0x000000803a6ea610 in .g_main_loop_run () from /usr/lib64/libglib-2.0.so.0 ++#4 0x000000803ae658c8 in 0000090f.plt_call.g_io_channel_flush+0 () from /usr/lib64/libgio-2.0.so.0 ++#5 0x000000803a71f3f8 in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#6 0x000000803a31c31c in .start_thread () from /usr/lib64/libpthread.so.0 ++#7 0x000000803a20c150 in .__clone () from /usr/lib64/libc.so.6 ++ ++Thread 3 (Thread 0x3fffa288eed0 (LWP 2325)): ++#0 0x000000803a1fd130 in .__GI___poll () from /usr/lib64/libc.so.6 ++#1 0x000000803a6fe0b0 in .g_poll () from /usr/lib64/libglib-2.0.so.0 ++#2 0x000000803a6e9ef4 in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#3 0x000000803a6ea0a4 in .g_main_context_iteration () from /usr/lib64/libglib-2.0.so.0 ++#4 0x00003fffa28ba8dc in 00000010.plt_call.g_hash_table_size+0 () from /usr/lib64/gio/modules/libdconfsettings.so ++#5 0x000000803a71f3f8 in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#6 0x000000803a31c31c in .start_thread () from /usr/lib64/libpthread.so.0 ++#7 0x000000803a20c150 in .__clone () from /usr/lib64/libc.so.6 ++ ++Thread 2 (Thread 0x3fffa16beed0 (LWP 2374)): ++#0 0x000000803a1fd130 in .__GI___poll () from /usr/lib64/libc.so.6 ++#1 0x000000803a6fe0b0 in .g_poll () from /usr/lib64/libglib-2.0.so.0 ++#2 0x000000803a6e9ef4 in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#3 0x000000803a6ea0a4 in .g_main_context_iteration () from /usr/lib64/libglib-2.0.so.0 ++#4 0x000000803a6ea15c in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#5 0x000000803a71f3f8 in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#6 0x000000803a31c31c in .start_thread () from /usr/lib64/libpthread.so.0 ++#7 0x000000803a20c150 in .__clone () from /usr/lib64/libc.so.6 ++ ++Thread 1 (Thread 0x3fffa9210490 (LWP 2271)): ++#0 0x000000803a327a54 in .raise () from /usr/lib64/libpthread.so.0 ++#1 0x000000803a6f3dd8 in .g_logv () from /usr/lib64/libglib-2.0.so.0 ++#2 0x000000803a6f3ef8 in .g_log () from /usr/lib64/libglib-2.0.so.0 ++#3 0x000000803cd0c230 in 000002cd.plt_call.XmbTextExtents+0 () from /usr/lib64/libgdk-x11-2.0.so.0 ++#4 0x000000803a9e0870 in ._XError () from /usr/lib64/libX11.so.6 ++#5 0x000000803a9dc398 in 00000e9e.plt_call._XimXTransGetHostname+0 () from /usr/lib64/libX11.so.6 ++#6 0x000000803a9dc514 in 00000e9e.plt_call._XimXTransGetHostname+0 () from /usr/lib64/libX11.so.6 ++#7 0x000000803a9dd19c in ._XEventsQueued () from /usr/lib64/libX11.so.6 ++#8 0x000000803a9c8d6c in .XPending () from /usr/lib64/libX11.so.6 ++#9 0x000000803ccfbd74 in 000002cd.plt_call.XmbTextExtents+0 () from /usr/lib64/libgdk-x11-2.0.so.0 ++#10 0x000000803ccfbf58 in 000002cd.plt_call.XmbTextExtents+0 () from /usr/lib64/libgdk-x11-2.0.so.0 ++#11 0x000000803a6e9124 in .g_main_context_prepare () from /usr/lib64/libglib-2.0.so.0 ++#12 0x000000803a6e9d7c in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#13 0x000000803a6ea610 in .g_main_loop_run () from /usr/lib64/libglib-2.0.so.0 ++#14 0x000000803c839050 in .gtk_main () from /usr/lib64/libgtk-x11-2.0.so.0 ++#15 0x0000000010051054 in .main () +-- +1.9.3 + diff --git a/SOURCES/satyr-0.13-python-add-Ruby-support.patch b/SOURCES/satyr-0.13-python-add-Ruby-support.patch new file mode 100644 index 0000000..cb2c8a0 --- /dev/null +++ b/SOURCES/satyr-0.13-python-add-Ruby-support.patch @@ -0,0 +1,914 @@ +From b0c2427cdc59be0bb1fa76e928b17d9aa1a0d4eb Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Wed, 28 Jan 2015 17:31:41 +0100 +Subject: [PATCH] python: add Ruby support + +Related: #1334604 + +Signed-off-by: Martin Milata + +Conflicts: + tests/Makefile.am +--- + python/Makefile.am | 4 + + python/py_module.c | 22 +++++ + python/py_report.c | 9 ++ + python/py_ruby_frame.c | 227 ++++++++++++++++++++++++++++++++++++++++++++ + python/py_ruby_frame.h | 69 ++++++++++++++ + python/py_ruby_stacktrace.c | 182 +++++++++++++++++++++++++++++++++++ + python/py_ruby_stacktrace.h | 71 ++++++++++++++ + tests/Makefile.am | 11 ++- + tests/python/ruby.py | 174 +++++++++++++++++++++++++++++++++ + tests/python_bindings.at | 1 + + 10 files changed, 769 insertions(+), 1 deletion(-) + create mode 100644 python/py_ruby_frame.c + create mode 100644 python/py_ruby_frame.h + create mode 100644 python/py_ruby_stacktrace.c + create mode 100644 python/py_ruby_stacktrace.h + create mode 100755 tests/python/ruby.py + +diff --git a/python/Makefile.am b/python/Makefile.am +index 83273a2..9380c12 100644 +--- a/python/Makefile.am ++++ b/python/Makefile.am +@@ -44,6 +44,10 @@ _satyr_la_SOURCES = \ + py_java_thread.c \ + py_java_stacktrace.h \ + py_java_stacktrace.c \ ++ py_ruby_frame.h \ ++ py_ruby_frame.c \ ++ py_ruby_stacktrace.h \ ++ py_ruby_stacktrace.c \ + py_rpm_package.h \ + py_rpm_package.c \ + py_metrics.h \ +diff --git a/python/py_module.c b/python/py_module.c +index 83697ef..144465b 100644 +--- a/python/py_module.c ++++ b/python/py_module.c +@@ -17,6 +17,8 @@ + #include "py_core_frame.h" + #include "py_core_thread.h" + #include "py_core_stacktrace.h" ++#include "py_ruby_frame.h" ++#include "py_ruby_stacktrace.h" + #include "py_rpm_package.h" + #include "py_metrics.h" + #include "py_operating_system.h" +@@ -187,6 +189,18 @@ init_satyr() + return; + } + ++ if (PyType_Ready(&sr_py_ruby_frame_type) < 0) ++ { ++ puts("PyType_Ready(&sr_py_ruby_frame_type) < 0"); ++ return; ++ } ++ ++ if (PyType_Ready(&sr_py_ruby_stacktrace_type) < 0) ++ { ++ puts("PyType_Ready(&sr_py_ruby_stacktrace_type) < 0"); ++ return; ++ } ++ + if (PyType_Ready(&sr_py_operating_system_type) < 0) + { + puts("PyType_Ready(&sr_py_operating_system_type) < 0"); +@@ -301,6 +315,14 @@ init_satyr() + PyModule_AddObject(module, "JavaStacktrace", + (PyObject *)&sr_py_java_stacktrace_type); + ++ Py_INCREF(&sr_py_ruby_frame_type); ++ PyModule_AddObject(module, "RubyFrame", ++ (PyObject *)&sr_py_ruby_frame_type); ++ ++ Py_INCREF(&sr_py_ruby_stacktrace_type); ++ PyModule_AddObject(module, "RubyStacktrace", ++ (PyObject *)&sr_py_ruby_stacktrace_type); ++ + Py_INCREF(&sr_py_core_frame_type); + PyModule_AddObject(module, "CoreFrame", + (PyObject *)&sr_py_core_frame_type); +diff --git a/python/py_report.c b/python/py_report.c +index 8aa35dd..74cd9dd 100644 +--- a/python/py_report.c ++++ b/python/py_report.c +@@ -28,6 +28,7 @@ + #include "py_python_stacktrace.h" + #include "py_koops_stacktrace.h" + #include "py_java_stacktrace.h" ++#include "py_ruby_stacktrace.h" + + #include "report.h" + #include "operating_system.h" +@@ -284,6 +285,10 @@ report_prepare_subobjects(struct sr_py_report *report) + if (stacktrace_prepare(report, &sr_py_koops_stacktrace_type, false) < 0) + return -1; + break; ++ case SR_REPORT_RUBY: ++ if (stacktrace_prepare(report, &sr_py_ruby_stacktrace_type, false) < 0) ++ return -1; ++ break; + default: + report->report->stacktrace = NULL; + break; +@@ -347,6 +352,10 @@ report_to_python_obj(struct sr_report *report) + ro->stacktrace = koops_stacktrace_to_python_obj( + (struct sr_koops_stacktrace *)report->stacktrace); + break; ++ case SR_REPORT_RUBY: ++ ro->stacktrace = ruby_stacktrace_to_python_obj( ++ (struct sr_ruby_stacktrace *)report->stacktrace); ++ break; + default: + Py_INCREF(Py_None); + ro->stacktrace = Py_None; +diff --git a/python/py_ruby_frame.c b/python/py_ruby_frame.c +new file mode 100644 +index 0000000..9ed45ad +--- /dev/null ++++ b/python/py_ruby_frame.c +@@ -0,0 +1,227 @@ ++/* ++ py_ruby_frame.c ++ ++ Copyright (C) 2015 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_ruby_frame.h" ++#include "location.h" ++#include "strbuf.h" ++#include "utils.h" ++#include "ruby/frame.h" ++ ++#define frame_doc "satyr.RubyFrame - class representing a ruby frame\n\n" \ ++ "Usage:\n\n" \ ++ "satyr.RubyFrame() - creates an empty frame\n\n" \ ++ "satyr.RubyFrame(str) - parses str and fills the frame object" ++ ++#define f_dup_doc "Usage: frame.dup()\n\n" \ ++ "Returns: satyr.RubyFrame - a new clone of frame\n\n" \ ++ "Clones the frame object. All new structures are independent " \ ++ "of the original object." ++ ++ ++static PyMethodDef ++frame_methods[] = ++{ ++ /* methods */ ++ { "dup", sr_py_ruby_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_ruby_frame ++#define GSOFF_PY_MEMBER frame ++#define GSOFF_C_STRUCT sr_ruby_frame ++GSOFF_START ++GSOFF_MEMBER(file_name), ++GSOFF_MEMBER(file_line), ++GSOFF_MEMBER(special_function), ++GSOFF_MEMBER(function_name), ++GSOFF_MEMBER(block_level), ++GSOFF_MEMBER(rescue_level) ++GSOFF_END ++ ++static PyGetSetDef ++frame_getset[] = ++{ ++ SR_ATTRIBUTE_STRING(file_name, "Source file name (string)" ), ++ SR_ATTRIBUTE_UINT32(file_line, "Source line number (positive integer)" ), ++ SR_ATTRIBUTE_BOOL (special_function, "True if the frame doesn't belong to an actual function" ), ++ SR_ATTRIBUTE_STRING(function_name, "Function name (string)" ), ++ SR_ATTRIBUTE_UINT32(block_level, "Block nesting level (integer)" ), ++ SR_ATTRIBUTE_UINT32(rescue_level, "Rescue block nesting level (integer)" ), ++ { NULL }, ++}; ++ ++PyTypeObject ++sr_py_ruby_frame_type = ++{ ++ PyObject_HEAD_INIT(NULL) ++ 0, ++ "satyr.RubyFrame", /* tp_name */ ++ sizeof(struct sr_py_ruby_frame), /* tp_basicsize */ ++ 0, /* tp_itemsize */ ++ sr_py_ruby_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_ruby_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_ruby_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_ruby_frame_new(PyTypeObject *object, PyObject *args, PyObject *kwds) ++{ ++ struct sr_py_ruby_frame *fo = (struct sr_py_ruby_frame*) ++ PyObject_New(struct sr_py_ruby_frame, &sr_py_ruby_frame_type); ++ ++ if (!fo) ++ return PyErr_NoMemory(); ++ ++ const char *str = NULL; ++ if (!PyArg_ParseTuple(args, "|s", &str)) ++ return NULL; ++ ++ if (str) ++ { ++ struct sr_location location; ++ sr_location_init(&location); ++ fo->frame = sr_ruby_frame_parse(&str, &location); ++ ++ if (!fo->frame) ++ { ++ PyErr_SetString(PyExc_ValueError, location.message); ++ return NULL; ++ } ++ } ++ else ++ fo->frame = sr_ruby_frame_new(); ++ ++ return (PyObject*)fo; ++} ++ ++/* destructor */ ++void ++sr_py_ruby_frame_free(PyObject *object) ++{ ++ struct sr_py_ruby_frame *this = (struct sr_py_ruby_frame*)object; ++ sr_ruby_frame_free(this->frame); ++ PyObject_Del(object); ++} ++ ++/* str */ ++PyObject * ++sr_py_ruby_frame_str(PyObject *self) ++{ ++ struct sr_py_ruby_frame *this = (struct sr_py_ruby_frame*)self; ++ struct sr_strbuf *buf = sr_strbuf_new(); ++ ++ ++ if (this->frame->file_name) ++ { ++ sr_strbuf_append_str(buf, this->frame->file_name); ++ } ++ ++ if (this->frame->file_line) ++ { ++ sr_strbuf_append_strf(buf, ":%d", this->frame->file_line); ++ } ++ ++ if (this->frame->function_name) ++ { ++ sr_strbuf_append_str(buf, ":in `"); ++ ++ int i; ++ for (i = 0; i < this->frame->rescue_level; i++) ++ { ++ sr_strbuf_append_str(buf, "rescue in "); ++ } ++ ++ if (this->frame->block_level == 1) ++ { ++ sr_strbuf_append_str(buf, "block in "); ++ } ++ else if (this->frame->block_level > 1) ++ { ++ sr_strbuf_append_strf(buf, "block (%d levels) in ", this->frame->block_level); ++ } ++ ++ sr_strbuf_append_strf(buf, "%s%s%s'", ++ (this->frame->special_function ? "<" : ""), ++ this->frame->function_name, ++ (this->frame->special_function ? ">" : "")); ++ } ++ ++ char *str = sr_strbuf_free_nobuf(buf); ++ PyObject *result = Py_BuildValue("s", str); ++ free(str); ++ return result; ++} ++ ++/* methods */ ++PyObject * ++sr_py_ruby_frame_dup(PyObject *self, PyObject *args) ++{ ++ struct sr_py_ruby_frame *this = (struct sr_py_ruby_frame*)self; ++ struct sr_py_ruby_frame *fo = (struct sr_py_ruby_frame*) ++ PyObject_New(struct sr_py_ruby_frame, &sr_py_ruby_frame_type); ++ ++ if (!fo) ++ return PyErr_NoMemory(); ++ ++ fo->frame = sr_ruby_frame_dup(this->frame, false); ++ ++ return (PyObject*)fo; ++} +diff --git a/python/py_ruby_frame.h b/python/py_ruby_frame.h +new file mode 100644 +index 0000000..7f460ff +--- /dev/null ++++ b/python/py_ruby_frame.h +@@ -0,0 +1,69 @@ ++/* ++ py_ruby_frame.h ++ ++ Copyright (C) 2015 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_RUBY_FRAME_H ++#define SATYR_PY_RUBY_FRAME_H ++ ++/** ++ * @file ++ * @brief Python bindings for RUBY frame. ++ */ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include ++#include ++ ++PyTypeObject sr_py_ruby_frame_type; ++ ++/* The beginning of this structure has to have the same layout as ++ * sr_py_base_frame. ++ */ ++struct sr_py_ruby_frame ++{ ++ PyObject_HEAD ++ struct sr_ruby_frame *frame; ++}; ++ ++/** ++ * Constructor. ++ */ ++PyObject *sr_py_ruby_frame_new(PyTypeObject *object, ++ PyObject *args, PyObject *kwds); ++ ++/** ++ * Destructor. ++ */ ++void sr_py_ruby_frame_free(PyObject *object); ++ ++/** ++ * str ++ */ ++PyObject *sr_py_ruby_frame_str(PyObject *self); ++ ++/* methods */ ++PyObject *sr_py_ruby_frame_dup(PyObject *self, PyObject *args); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/python/py_ruby_stacktrace.c b/python/py_ruby_stacktrace.c +new file mode 100644 +index 0000000..97fbb44 +--- /dev/null ++++ b/python/py_ruby_stacktrace.c +@@ -0,0 +1,182 @@ ++#include "py_common.h" ++#include "py_ruby_frame.h" ++#include "py_ruby_stacktrace.h" ++#include "py_base_stacktrace.h" ++#include "utils.h" ++#include "strbuf.h" ++#include "ruby/frame.h" ++#include "ruby/stacktrace.h" ++#include "location.h" ++#include "stacktrace.h" ++ ++#define stacktrace_doc "satyr.RubyStacktrace - class representing a ruby stacktrace\n\n" \ ++ "Usage:\n\n" \ ++ "satyr.RubyStacktrace() - creates an empty ruby stacktrace\n\n" \ ++ "satyr.RubyStacktrace(str) - parses str and fills the ruby stacktrace object" ++ ++#define f_dup_doc "Usage: stacktrace.dup()\n\n" \ ++ "Returns: satyr.RubyStacktrace - a new clone of ruby stacktrace\n\n" \ ++ "Clones the RubyStacktrace object. All new structures are independent " \ ++ "of the original object." ++ ++ ++static PyMethodDef ++ruby_stacktrace_methods[] = ++{ ++ /* methods */ ++ { "dup", sr_py_ruby_stacktrace_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_ruby_stacktrace ++#define GSOFF_PY_MEMBER stacktrace ++#define GSOFF_C_STRUCT sr_ruby_stacktrace ++GSOFF_START ++GSOFF_MEMBER(exception_name) ++GSOFF_END ++ ++static PyGetSetDef ++ruby_stacktrace_getset[] = ++{ ++ SR_ATTRIBUTE_STRING(exception_name, "Exception type (string)" ), ++ { NULL }, ++}; ++ ++PyTypeObject sr_py_ruby_stacktrace_type = { ++ PyObject_HEAD_INIT(NULL) ++ 0, ++ "satyr.RubyStacktrace", /* tp_name */ ++ sizeof(struct sr_py_ruby_stacktrace), /* tp_basicsize */ ++ 0, /* tp_itemsize */ ++ sr_py_ruby_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_ruby_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 */ ++ ruby_stacktrace_methods, /* tp_methods */ ++ NULL, /* tp_members */ ++ ruby_stacktrace_getset, /* tp_getset */ ++ &sr_py_single_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_ruby_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 */ ++}; ++ ++PyObject * ++ruby_stacktrace_to_python_obj(struct sr_ruby_stacktrace *stacktrace) ++{ ++ struct sr_py_ruby_stacktrace *bo = PyObject_New(struct sr_py_ruby_stacktrace, ++ &sr_py_ruby_stacktrace_type); ++ if (!bo) ++ return PyErr_NoMemory(); ++ ++ bo->frame_type = &sr_py_ruby_frame_type; ++ ++ bo->stacktrace = stacktrace; ++ bo->frames = frames_to_python_list((struct sr_thread *)bo->stacktrace, ++ bo->frame_type); ++ if (!bo->frames) ++ return NULL; ++ ++ return (PyObject *)bo; ++} ++ ++/* constructor */ ++PyObject * ++sr_py_ruby_stacktrace_new(PyTypeObject *object, ++ PyObject *args, ++ PyObject *kwds) ++{ ++ const char *str = NULL; ++ if (!PyArg_ParseTuple(args, "|s", &str)) ++ return NULL; ++ ++ struct sr_ruby_stacktrace *stacktrace; ++ ++ if (str) ++ { ++ struct sr_location location; ++ sr_location_init(&location); ++ stacktrace = sr_ruby_stacktrace_parse(&str, &location); ++ if (!stacktrace) ++ { ++ PyErr_SetString(PyExc_ValueError, location.message); ++ return NULL; ++ } ++ } ++ else ++ stacktrace = sr_ruby_stacktrace_new(); ++ ++ return ruby_stacktrace_to_python_obj(stacktrace); ++} ++ ++/* destructor */ ++void ++sr_py_ruby_stacktrace_free(PyObject *object) ++{ ++ struct sr_py_ruby_stacktrace *this = (struct sr_py_ruby_stacktrace*)object; ++ /* the list will decref all of its elements */ ++ Py_DECREF(this->frames); ++ this->stacktrace->frames = NULL; ++ sr_ruby_stacktrace_free(this->stacktrace); ++ PyObject_Del(object); ++} ++ ++/* str */ ++PyObject * ++sr_py_ruby_stacktrace_str(PyObject *self) ++{ ++ struct sr_py_ruby_stacktrace *this = (struct sr_py_ruby_stacktrace *)self; ++ struct sr_strbuf *buf = sr_strbuf_new(); ++ sr_strbuf_append_strf(buf, "Ruby stacktrace 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_ruby_stacktrace_dup(PyObject *self, PyObject *args) ++{ ++ struct sr_py_ruby_stacktrace *this = (struct sr_py_ruby_stacktrace*)self; ++ if (frames_prepare_linked_list((struct sr_py_base_thread *)this) < 0) ++ return NULL; ++ ++ struct sr_ruby_stacktrace *stacktrace = sr_ruby_stacktrace_dup(this->stacktrace); ++ if (!stacktrace) ++ return NULL; ++ ++ return ruby_stacktrace_to_python_obj(stacktrace); ++} +diff --git a/python/py_ruby_stacktrace.h b/python/py_ruby_stacktrace.h +new file mode 100644 +index 0000000..f4eebce +--- /dev/null ++++ b/python/py_ruby_stacktrace.h +@@ -0,0 +1,71 @@ ++/* ++ py_ruby_stacktrace.h ++ ++ Copyright (C) 2015 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_RUBY_STACKTRACE_H ++#define SATYR_PY_RUBY_STACKTRACE_H ++ ++/** ++ * @file ++ * @brief Python bindings for Ruby stack trace. ++ */ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include ++#include ++ ++PyTypeObject sr_py_ruby_stacktrace_type; ++ ++/* The beginning of this structure has to have the same layout as ++ * sr_py_base_thread. ++ */ ++struct sr_py_ruby_stacktrace ++{ ++ PyObject_HEAD ++ struct sr_ruby_stacktrace *stacktrace; ++ PyObject *frames; ++ PyTypeObject *frame_type; ++}; ++ ++/* helpers */ ++PyObject *ruby_stacktrace_to_python_obj(struct sr_ruby_stacktrace *stacktrace); ++ ++/* constructor */ ++PyObject *sr_py_ruby_stacktrace_new(PyTypeObject *object, ++ PyObject *args, ++ PyObject *kwds); ++ ++/* destructor */ ++void sr_py_ruby_stacktrace_free(PyObject *object); ++ ++/* str */ ++PyObject *sr_py_ruby_stacktrace_str(PyObject *self); ++ ++/* methods */ ++PyObject *sr_py_ruby_stacktrace_dup(PyObject *self, PyObject *args); ++PyObject *sr_py_ruby_stacktrace_normalize(PyObject *self, PyObject *args); ++ ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/tests/Makefile.am b/tests/Makefile.am +index 0eb33a5..b261880 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -4,7 +4,16 @@ EXTRA_DIST = gdb_stacktraces \ + json_files \ + kerneloopses \ + programs \ +- python \ ++ python/core.py \ ++ python/gdb.py \ ++ python/java.py \ ++ python/koops.py \ ++ python/metrics.py \ ++ python/misc.py \ ++ python/python.py \ ++ python/report.py \ ++ python/ruby.py \ ++ python/test_helpers.py \ + python_stacktraces + + ## ------------ ## +diff --git a/tests/python/ruby.py b/tests/python/ruby.py +new file mode 100755 +index 0000000..8a20593 +--- /dev/null ++++ b/tests/python/ruby.py +@@ -0,0 +1,174 @@ ++#!/usr/bin/env python ++ ++import unittest ++from test_helpers import * ++ ++path = '../ruby_stacktraces/ruby-01' ++contents = load_input_contents(path) ++frames_expected = 21 ++expected_short_text = '''#1 rescue in block (2 levels) in func in /usr/share/ruby/vendor_ruby/will_crash.rb:13 ++#2 block (2 levels) in func in /usr/share/ruby/vendor_ruby/will_crash.rb:10 ++#3 times in /usr/share/ruby/vendor_ruby/will_crash.rb:9 ++#4 block in func in /usr/share/ruby/vendor_ruby/will_crash.rb:9 ++#5 times in /usr/share/ruby/vendor_ruby/will_crash.rb:8 ++#6 func in /usr/share/ruby/vendor_ruby/will_crash.rb:8 ++''' ++ ++class TestRubyStacktrace(BindingsTestCase): ++ def setUp(self): ++ self.trace = satyr.RubyStacktrace(contents) ++ ++ 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.frames), id(self.trace.frames)) ++ self.assertTrue(all(map(lambda t1, t2: t1.equals(t2), dup.frames, self.trace.frames))) ++ ++ dup.frames = dup.frames[:5] ++ dup2 = dup.dup() ++ self.assertEqual(len(dup.frames), len(dup2.frames)) ++ self.assertNotEqual(id(dup.frames), id(dup2.frames)) ++ ++ def test_prepare_linked_list(self): ++ dup = self.trace.dup() ++ dup.frames = dup.frames[:5] ++ dup2 = dup.dup() ++ self.assertTrue(len(dup2.frames) <= 5) ++ ++ def test_str(self): ++ out = str(self.trace) ++ self.assertTrue(('Ruby stacktrace with %d frames' % frames_expected) in out) ++ ++ def test_getset(self): ++ self.assertGetSetCorrect(self.trace, 'exception_name', 'Wrap::MyException', 'WhateverException') ++ ++ def test_special_functions(self): ++ trace = load_input_contents('../ruby_stacktraces/ruby-01') ++ trace = satyr.RubyStacktrace(trace) ++ ++ f = trace.frames[0] ++ self.assertEqual(f.file_name, '/usr/share/ruby/vendor_ruby/will_crash.rb') ++ self.assertEqual(f.function_name, 'func') ++ self.assertFalse(f.special_function) ++ self.assertEqual(f.block_level, 2) ++ self.assertEqual(f.rescue_level, 1) ++ ++ f = trace.frames[-1] ++ self.assertEqual(f.file_name, '/usr/bin/will_ruby_raise') ++ self.assertEqual(f.function_name, 'main') ++ self.assertTrue(f.special_function) ++ self.assertEqual(f.block_level, 0) ++ self.assertEqual(f.rescue_level, 0) ++ ++ def test_to_short_text(self): ++ self.assertEqual(self.trace.to_short_text(6), expected_short_text) ++ ++ def test_bthash(self): ++ self.assertEqual(self.trace.get_bthash(), '6124e03542a0381b14ebaf2c5b5e9f467ebba33b') ++ ++ def test_duphash(self): ++ expected_plain = '''Thread ++/usr/share/ruby/vendor_ruby/will_crash.rb:13 ++/usr/share/ruby/vendor_ruby/will_crash.rb:10 ++/usr/share/ruby/vendor_ruby/will_crash.rb:9 ++''' ++ self.assertEqual(self.trace.get_duphash(flags=satyr.DUPHASH_NOHASH, frames=3), expected_plain) ++ self.assertEqual(self.trace.get_duphash(), 'c877cda04fbce8d51c4d9b1d628b0f618677607e') ++ ++ def test_crash_thread(self): ++ self.assertTrue(self.trace.crash_thread is self.trace) ++ ++ def test_from_json(self): ++ trace = satyr.RubyStacktrace.from_json('{}') ++ self.assertEqual(trace.frames, []) ++ ++ json_text = ''' ++ { ++ "exception_name": "NotImplementedError", ++ "stacktrace": [ ++ { ++ "file_name": "/usr/share/foobar/mod/file.rb", ++ "function_name": "do_nothing", ++ "file_line": 42, ++ }, ++ { ++ "special_function": "top (required)", ++ "file_line": 451 ++ }, ++ { ++ "unknown_key": 19876543, ++ "block_level": 42, ++ "rescue_level": 6 ++ } ++ ] ++ } ++''' ++ trace = satyr.RubyStacktrace.from_json(json_text) ++ self.assertEqual(trace.exception_name, 'NotImplementedError') ++ self.assertEqual(len(trace.frames), 3) ++ ++ self.assertEqual(trace.frames[0].file_name, '/usr/share/foobar/mod/file.rb') ++ self.assertEqual(trace.frames[0].function_name, 'do_nothing') ++ self.assertEqual(trace.frames[0].file_line, 42) ++ self.assertFalse(trace.frames[0].special_function) ++ self.assertEqual(trace.frames[0].block_level, 0) ++ self.assertEqual(trace.frames[0].rescue_level, 0) ++ ++ self.assertEqual(trace.frames[1].file_name, None) ++ self.assertEqual(trace.frames[1].function_name, 'top (required)') ++ self.assertEqual(trace.frames[1].file_line, 451) ++ self.assertTrue(trace.frames[1].special_function) ++ self.assertEqual(trace.frames[1].block_level, 0) ++ self.assertEqual(trace.frames[1].rescue_level, 0) ++ ++ self.assertEqual(trace.frames[2].file_name, None) ++ self.assertEqual(trace.frames[2].function_name, None) ++ self.assertEqual(trace.frames[2].file_line, 0) ++ self.assertFalse(trace.frames[2].special_function) ++ self.assertEqual(trace.frames[2].block_level, 42) ++ self.assertEqual(trace.frames[2].rescue_level, 6) ++ ++ def test_hash(self): ++ self.assertHashable(self.trace) ++ ++ ++class TestRubyFrame(BindingsTestCase): ++ def setUp(self): ++ self.frame = satyr.RubyStacktrace(contents).frames[1] ++ ++ def test_str(self): ++ out = str(self.frame) ++ self.assertEqual(out, "/usr/share/ruby/vendor_ruby/will_crash.rb:10:in `block (2 levels) in func'") ++ ++ def test_dup(self): ++ dup = self.frame.dup() ++ self.assertEqual(dup.function_name, ++ self.frame.function_name) ++ ++ dup.function_name = 'other' ++ self.assertNotEqual(dup.function_name, ++ self.frame.function_name) ++ ++ def test_cmp(self): ++ dup = self.frame.dup() ++ self.assertTrue(dup.equals(dup)) ++ self.assertTrue(dup.equals(self.frame)) ++ self.assertNotEqual(id(dup), id(self.frame)) ++ dup.function_name = 'another' ++ self.assertFalse(dup.equals(self.frame)) ++ ++ def test_getset(self): ++ self.assertGetSetCorrect(self.frame, 'file_name', '/usr/share/ruby/vendor_ruby/will_crash.rb', 'java.rs') ++ self.assertGetSetCorrect(self.frame, 'file_line', 10, 6667) ++ self.assertGetSetCorrect(self.frame, 'special_function', False, True) ++ self.assertGetSetCorrect(self.frame, 'function_name', 'func', 'iiiiii') ++ self.assertGetSetCorrect(self.frame, 'block_level', 2, 666) ++ self.assertGetSetCorrect(self.frame, 'rescue_level', 0, 9000) ++ ++ def test_hash(self): ++ self.assertHashable(self.frame) ++ ++if __name__ == '__main__': ++ unittest.main() +diff --git a/tests/python_bindings.at b/tests/python_bindings.at +index a5e19c3..906f561 100644 +--- a/tests/python_bindings.at ++++ b/tests/python_bindings.at +@@ -12,5 +12,6 @@ AT_TEST_PYTHON([koops]) + AT_TEST_PYTHON([python]) + AT_TEST_PYTHON([java]) + AT_TEST_PYTHON([core]) ++AT_TEST_PYTHON([ruby]) + AT_TEST_PYTHON([metrics]) + AT_TEST_PYTHON([report]) +-- +1.8.3.1 + diff --git a/SOURCES/satyr-0.13-python-exceptions.patch b/SOURCES/satyr-0.13-python-exceptions.patch new file mode 100644 index 0000000..f028fea --- /dev/null +++ b/SOURCES/satyr-0.13-python-exceptions.patch @@ -0,0 +1,273 @@ +From 7fa846eea1f9f1a541ec061f272d4f82d4aee428 Mon Sep 17 00:00:00 2001 +From: Jakub Filak +Date: Wed, 25 Jun 2014 17:28:00 +0200 +Subject: [SATYR PATCH 3/6] Fix parsing of invalid syntax Python exceptions + +Closes #168 + +Signed-off-by: Jakub Filak + +mmilata: commit msg typo +--- + lib/python_frame.c | 56 +++++++++++++++++++------------ + lib/python_stacktrace.c | 69 ++++++++++++++++++++++++++++++++++---- + tests/python/python.py | 38 +++++++++++++++++++++ + tests/python_stacktraces/python-04 | 6 ++++ + tests/python_stacktraces/python-05 | 16 +++++++++ + 5 files changed, 157 insertions(+), 28 deletions(-) + create mode 100644 tests/python_stacktraces/python-04 + create mode 100644 tests/python_stacktraces/python-05 + +diff --git a/lib/python_frame.c b/lib/python_frame.c +index b62628f..b0540fe 100644 +--- a/lib/python_frame.c ++++ b/lib/python_frame.c +@@ -260,33 +260,45 @@ sr_python_frame_parse(const char **input, + + if (0 == sr_skip_string(&local_input, ", in ")) + { +- location->message = sr_asprintf("Function name separator not found."); +- return NULL; +- } +- +- location->column += strlen(", in "); ++ if (local_input[0] != '\n') ++ { ++ location->message = sr_asprintf("Function name separator not found."); ++ return NULL; ++ } + +- /* Parse function name */ +- if (!sr_parse_char_cspan(&local_input, "\n", &frame->function_name)) ++ /* The last frame of SyntaxError stack trace does not have ++ * function name on its line. For the sake of simplicity, we will ++ * believe that we are dealing with such a frame now. ++ */ ++ frame->function_name = sr_strdup("syntax"); ++ frame->special_function = true; ++ } ++ else + { +- sr_python_frame_free(frame); +- location->message = sr_asprintf("Unable to find the newline character " +- "identifying the end of function name."); ++ location->column += strlen(", in "); + +- return NULL; +- } ++ /* Parse function name */ ++ if (!sr_parse_char_cspan(&local_input, "\n", &frame->function_name)) ++ { ++ sr_python_frame_free(frame); ++ location->message = sr_asprintf("Unable to find the newline character " ++ "identifying the end of function name."); + +- location->column += strlen(frame->function_name); ++ return NULL; ++ } + +- if (strlen(frame->function_name) > 0 && +- frame->function_name[0] == '<' && +- frame->function_name[strlen(frame->function_name)-1] == '>') +- { +- frame->special_function = true; +- frame->function_name[strlen(frame->function_name)-1] = '\0'; +- char *inside = sr_strdup(frame->function_name + 1); +- free(frame->function_name); +- frame->function_name = inside; ++ location->column += strlen(frame->function_name); ++ ++ if (strlen(frame->function_name) > 0 && ++ frame->function_name[0] == '<' && ++ frame->function_name[strlen(frame->function_name)-1] == '>') ++ { ++ frame->special_function = true; ++ frame->function_name[strlen(frame->function_name)-1] = '\0'; ++ char *inside = sr_strdup(frame->function_name + 1); ++ free(frame->function_name); ++ frame->function_name = inside; ++ } + } + + sr_skip_char(&local_input, '\n'); +diff --git a/lib/python_stacktrace.c b/lib/python_stacktrace.c +index 3dc0a75..99aa52c 100644 +--- a/lib/python_stacktrace.c ++++ b/lib/python_stacktrace.c +@@ -29,6 +29,7 @@ + #include "generic_thread.h" + #include "internal_utils.h" + #include ++#include + #include + #include + +@@ -137,13 +138,48 @@ sr_python_stacktrace_parse(const char **input, + + if (!local_input) + { +- location->message = "Traceback header not found."; +- return NULL; +- } ++ /* SyntaxError stack trace of an exception thrown in the executed file ++ * conforms to the following template: ++ * invalid syntax ($file, line $number) ++ * ++ * File "$file", line $number ++ * $code ++ * ^ ++ * SyntaxError: invalid syntax ++ * ++ * for exceptions thrown from imported files, the stack trace has the ++ * regular form, except the last frame has no function name and is ++ * followed by the pointer line (^). ++ */ ++ HEADER = "invalid syntax (", ++ local_input = sr_strstr_location(*input, ++ HEADER, ++ &location->line, ++ &location->column); + +- local_input += strlen(HEADER); +- location->line += 2; +- location->column = 0; ++ if (!local_input) ++ { ++ location->message = "Traceback header not found."; ++ return NULL; ++ } ++ ++ local_input = sr_strstr_location(local_input, ++ " File \"", ++ &location->line, ++ &location->column); ++ ++ if (!local_input) ++ { ++ location->message = "Frame with invalid line not found."; ++ return NULL; ++ } ++ } ++ else ++ { ++ local_input += strlen(HEADER); ++ location->line += 2; ++ location->column = 0; ++ } + + struct sr_python_stacktrace *stacktrace = sr_python_stacktrace_new(); + +@@ -173,6 +209,27 @@ sr_python_stacktrace_parse(const char **input, + return NULL; + } + ++ bool invalid_syntax_pointer = true; ++ const char *tmp_input = local_input; ++ while (*tmp_input != '\n' && *tmp_input != '\0') ++ { ++ if (*tmp_input != ' ' && *tmp_input != '^') ++ { ++ invalid_syntax_pointer = false; ++ break; ++ } ++ ++tmp_input; ++ } ++ ++ if (invalid_syntax_pointer) ++ { ++ /* Skip line " ^" pointing to the invalid code */ ++ sr_skip_char_cspan(&local_input, "\n"); ++ ++local_input; ++ ++location->line; ++ location->column = 1; ++ } ++ + /* Parse exception name. */ + sr_parse_char_cspan(&local_input, + ":\n", +diff --git a/tests/python/python.py b/tests/python/python.py +index 146ca01..9044200 100755 +--- a/tests/python/python.py ++++ b/tests/python/python.py +@@ -143,6 +143,44 @@ class TestPythonStacktrace(BindingsTestCase): + def test_hash(self): + self.assertHashable(self.trace) + ++ def test_invalid_syntax_current_file(self): ++ trace = load_input_contents('../python_stacktraces/python-04') ++ trace = satyr.PythonStacktrace(trace) ++ ++ self.assertEqual(len(trace.frames), 1) ++ self.assertEqual(trace.exception_name, 'SyntaxError') ++ ++ f = trace.frames[0] ++ self.assertEqual(f.file_name, '/usr/bin/python3-mako-render') ++ self.assertEqual(f.function_name, "syntax") ++ self.assertEqual(f.file_line, 43) ++ self.assertEqual(f.line_contents, 'print render(data, kw)') ++ self.assertTrue(f.special_function) ++ self.assertFalse(f.special_file) ++ ++ def test_invalid_syntax_imported_file(self): ++ trace = load_input_contents('../python_stacktraces/python-05') ++ trace = satyr.PythonStacktrace(trace) ++ ++ self.assertEqual(len(trace.frames), 2) ++ self.assertEqual(trace.exception_name, 'SyntaxError') ++ ++ f = trace.frames[1] ++ self.assertEqual(f.file_name, '/usr/bin/will_python_raise') ++ self.assertEqual(f.function_name, "module") ++ self.assertEqual(f.file_line, 2) ++ self.assertEqual(f.line_contents, 'import report') ++ self.assertTrue(f.special_function) ++ self.assertFalse(f.special_file) ++ ++ f = trace.frames[0] ++ self.assertEqual(f.file_name, '/usr/lib64/python2.7/site-packages/report/__init__.py') ++ self.assertEqual(f.function_name, "syntax") ++ self.assertEqual(f.file_line, 15) ++ self.assertEqual(f.line_contents, 'def foo(:') ++ self.assertTrue(f.special_function) ++ self.assertFalse(f.special_file) ++ + class TestPythonFrame(BindingsTestCase): + def setUp(self): + self.frame = satyr.PythonStacktrace(contents).frames[-1] +diff --git a/tests/python_stacktraces/python-04 b/tests/python_stacktraces/python-04 +new file mode 100644 +index 0000000..91c3a3d +--- /dev/null ++++ b/tests/python_stacktraces/python-04 +@@ -0,0 +1,6 @@ ++invalid syntax (python3-mako-render, line 43) ++ ++ File "/usr/bin/python3-mako-render", line 43 ++ print render(data, kw) ++ ^ ++SyntaxError: invalid syntax +diff --git a/tests/python_stacktraces/python-05 b/tests/python_stacktraces/python-05 +new file mode 100644 +index 0000000..79d1b8d +--- /dev/null ++++ b/tests/python_stacktraces/python-05 +@@ -0,0 +1,16 @@ ++will_python_raise:2:: File "/usr/lib64/python2.7/site-packages/report/__init__.py", line 15 ++ ++Traceback (most recent call last): ++ File "/usr/bin/will_python_raise", line 2, in ++ import report ++ File "/usr/lib64/python2.7/site-packages/report/__init__.py", line 15 ++ def foo(: ++ ^ ++SyntaxError: invalid syntax ++ ++Local variables in innermost frame: ++__builtins__: ++__name__: '__main__' ++__file__: '/usr/bin/will_python_raise' ++__doc__: None ++__package__: None +-- +1.9.3 + diff --git a/SOURCES/satyr-0.13-static-analyzer-bugs.patch b/SOURCES/satyr-0.13-static-analyzer-bugs.patch new file mode 100644 index 0000000..af1dd34 --- /dev/null +++ b/SOURCES/satyr-0.13-static-analyzer-bugs.patch @@ -0,0 +1,137 @@ +From 5aab1d42916b93e593db632479a800b1de05d169 Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Mon, 17 Feb 2014 12:28:04 +0100 +Subject: [SATYR PATCH 1/6] Fix minor issues found by static analyzers + +Avoid possible NULL dereferences of error_msg + +Fixes #155. + +Signed-off-by: Martin Milata +Signed-off-by: Jakub Filak + +Fix insecure string formatting + +core_unwind_elfutils.c: In function 'sr_parse_coredump': +core_unwind_elfutils.c:169:13: error: format not a string literal and no format arguments [-Werror=format-security] + set_error(thread_arg.error_msg); + ^ + +Signed-off-by: Martin Milata +--- + lib/Makefile.am | 2 +- + lib/core_unwind.c | 5 +++-- + lib/core_unwind_elfutils.c | 27 +++++++++++++-------------- + lib/core_unwind_libunwind.c | 2 +- + 4 files changed, 18 insertions(+), 18 deletions(-) + +diff --git a/lib/Makefile.am b/lib/Makefile.am +index 73fffe2..f798347 100644 +--- a/lib/Makefile.am ++++ b/lib/Makefile.am +@@ -62,7 +62,7 @@ libsatyr_conv_la_SOURCES = \ + unstrip.c \ + utils.c + +-libsatyr_conv_la_CFLAGS = -Wall -std=gnu99 -D_GNU_SOURCE -I$(top_srcdir)/include $(GLIB_CFLAGS) ++libsatyr_conv_la_CFLAGS = -Wall -Wformat=2 -std=gnu99 -D_GNU_SOURCE -I$(top_srcdir)/include $(GLIB_CFLAGS) + libsatyr_conv_la_LDFLAGS = $(GLIB_LIBS) + + if HAVE_LIBOPCODES +diff --git a/lib/core_unwind.c b/lib/core_unwind.c +index 7910254..8b7cc22 100644 +--- a/lib/core_unwind.c ++++ b/lib/core_unwind.c +@@ -227,9 +227,10 @@ open_coredump(const char *elf_file, const char *exe_file, char **error_msg) + } + ch->segments = head; + +- if (!*error_msg && !head) ++ if (!head) + { +- set_error("No segments found in coredump '%s'", elf_file); ++ if (error_msg && !*error_msg) ++ set_error("No segments found in coredump '%s'", elf_file); + goto fail_dwfl; + } + +diff --git a/lib/core_unwind_elfutils.c b/lib/core_unwind_elfutils.c +index a8d8b3f..6b904c7 100644 +--- a/lib/core_unwind_elfutils.c ++++ b/lib/core_unwind_elfutils.c +@@ -137,20 +137,20 @@ sr_parse_coredump(const char *core_file, + *error_msg = NULL; + + struct core_handle *ch = open_coredump(core_file, exe_file, error_msg); +- if (*error_msg) +- return NULL; ++ if (!ch) ++ goto fail; + + if (dwfl_core_file_attach(ch->dwfl, ch->eh) < 0) + { + set_error_dwfl("dwfl_core_file_attach"); +- goto fail_destroy_handle; ++ goto fail; + } + + stacktrace = sr_core_stacktrace_new(); + if (!stacktrace) + { + set_error("Failed to initialize stacktrace memory"); +- goto fail_destroy_handle; ++ goto fail; + } + + struct thread_callback_arg thread_arg = +@@ -165,11 +165,16 @@ sr_parse_coredump(const char *core_file, + if (ret == -1) + set_error_dwfl("dwfl_getthreads"); + else if (ret == DWARF_CB_ABORT) +- *error_msg = thread_arg.error_msg; ++ { ++ set_error("%s", thread_arg.error_msg); ++ free(thread_arg.error_msg); ++ } + else +- *error_msg = sr_strdup("Unknown error in dwfl_getthreads"); ++ set_error("Unknown error in dwfl_getthreads"); + +- goto fail_destroy_trace; ++ sr_core_stacktrace_free(stacktrace); ++ stacktrace = NULL; ++ goto fail; + } + + stacktrace->executable = sr_strdup(exe_file); +@@ -177,13 +182,7 @@ sr_parse_coredump(const char *core_file, + /* FIXME: is this the best we can do? */ + stacktrace->crash_thread = stacktrace->threads; + +-fail_destroy_trace: +- if (*error_msg) +- { +- sr_core_stacktrace_free(stacktrace); +- stacktrace = NULL; +- } +-fail_destroy_handle: ++fail: + core_handle_free(ch); + return stacktrace; + } +diff --git a/lib/core_unwind_libunwind.c b/lib/core_unwind_libunwind.c +index 966a5b9..b45e2ad 100644 +--- a/lib/core_unwind_libunwind.c ++++ b/lib/core_unwind_libunwind.c +@@ -99,7 +99,7 @@ unwind_thread(struct UCD_info *ui, + } + } + +- if (!error_msg && !trace) ++ if (error_msg && !*error_msg && !trace) + { + set_error("No frames found for thread %d", thread_no); + } +-- +1.9.3 + diff --git a/SOURCES/satyr-0.13-support-unwinding-from-core-hook.patch b/SOURCES/satyr-0.13-support-unwinding-from-core-hook.patch new file mode 100644 index 0000000..56a3060 --- /dev/null +++ b/SOURCES/satyr-0.13-support-unwinding-from-core-hook.patch @@ -0,0 +1,308 @@ +From 91cdbeb45d11cfd1dd62afe50f80a7c5d4342f5b Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Mon, 24 Nov 2014 14:36:14 +0100 +Subject: [SATYR PATCH] unwind: support unwinding from core hook + +In-memory unwinding of dying processes, related to abrt/abrt#gh829. The +implementation mostly mirrors code in src/stack.c of elfutils repo +(jankratochvil/corepattern branch). + +Related to abrt/abrt#829. + +Signed-off-by: Martin Milata +--- + include/abrt.h | 8 +++ + include/core/unwind.h | 18 ++++++ + lib/abrt.c | 35 ++++++++++++ + lib/core_unwind.c | 14 +++++ + lib/core_unwind_elfutils.c | 136 +++++++++++++++++++++++++++++++++++++++++++++ + 5 files changed, 211 insertions(+) + +diff --git a/include/abrt.h b/include/abrt.h +index 0a8f5ac..5116446 100644 +--- a/include/abrt.h ++++ b/include/abrt.h +@@ -26,6 +26,7 @@ extern "C" { + + #include "report_type.h" + #include ++#include + + bool + sr_abrt_print_report_from_dir(const char *directory, +@@ -43,6 +44,13 @@ sr_abrt_create_core_stacktrace_from_gdb(const char *directory, + bool hash_fingerprints, + char **error_message); + ++bool ++sr_abrt_create_core_stacktrace_from_core_hook(const char *directory, ++ pid_t thread_id, ++ const char *executable, ++ int signum, ++ char **error_message); ++ + struct sr_rpm_package * + sr_abrt_parse_dso_list(const char *text); + +diff --git a/include/core/unwind.h b/include/core/unwind.h +index 8ab9d52..b128c76 100644 +--- a/include/core/unwind.h ++++ b/include/core/unwind.h +@@ -24,6 +24,8 @@ + extern "C" { + #endif + ++#include ++ + struct sr_core_stacktrace; + struct sr_gdb_stacktrace; + +@@ -38,6 +40,22 @@ sr_core_stacktrace_from_gdb(const char *gdb_output, + const char *executable_filename, + char **error_message); + ++/* This function can be used to unwind stack of live ("dying") process, invoked ++ * from the core dump hook (/proc/sys/kernel/core_pattern). ++ * ++ * Beware: ++ * ++ * - It can only unwind one thread of the process, the thread that caused the ++ * terminating signal to be sent. You must supply that thread's tid. ++ * - The function calls close() on stdin, meaning that in the core handler you ++ * cannot access the core image after calling this function. ++ */ ++struct sr_core_stacktrace * ++sr_core_stacktrace_from_core_hook(pid_t thread_id, ++ const char *executable_filename, ++ int signum, ++ char **error_message); ++ + #ifdef __cplusplus + } + #endif +diff --git a/lib/abrt.c b/lib/abrt.c +index 39bc45d..585fdfb 100644 +--- a/lib/abrt.c ++++ b/lib/abrt.c +@@ -138,6 +138,41 @@ sr_abrt_create_core_stacktrace(const char *directory, + error_message); + } + ++bool ++sr_abrt_create_core_stacktrace_from_core_hook(const char *directory, ++ pid_t thread_id, ++ const char *executable, ++ int signum, ++ char **error_message) ++{ ++ ++ struct sr_core_stacktrace *core_stacktrace; ++ core_stacktrace = sr_core_stacktrace_from_core_hook(thread_id, executable, ++ signum, error_message); ++ ++ if (!core_stacktrace) ++ return false; ++ ++ fulfill_missing_values(core_stacktrace); ++ ++ char *json = sr_core_stacktrace_to_json(core_stacktrace); ++ ++ // Add newline to the end of core stacktrace file to make text ++ // editors happy. ++ json = sr_realloc(json, strlen(json) + 2); ++ strcat(json, "\n"); ++ ++ char *core_backtrace_filename = sr_build_path(directory, "core_backtrace", NULL); ++ bool success = sr_string_to_file(core_backtrace_filename, ++ json, ++ error_message); ++ ++ free(core_backtrace_filename); ++ free(json); ++ sr_core_stacktrace_free(core_stacktrace); ++ return success; ++} ++ + struct sr_rpm_package * + sr_abrt_parse_dso_list(const char *text) + { +diff --git a/lib/core_unwind.c b/lib/core_unwind.c +index 8b7cc22..7ea66da 100644 +--- a/lib/core_unwind.c ++++ b/lib/core_unwind.c +@@ -52,6 +52,20 @@ sr_parse_coredump(const char *coredump_filename, + + #endif /* !defined WITH_LIBDWFL && !defined WITH_LIBUNWIND */ + ++#if !defined WITH_LIBDWFL ++ ++struct sr_core_stacktrace * ++sr_core_stacktrace_from_core_hook(pid_t thread_id, ++ const char *executable_filename, ++ int signum, ++ char **error_message); ++{ ++ *error_message = sr_asprintf("satyr is built without live process unwind support"); ++ return NULL; ++} ++ ++#endif /* !defined WITH_LIBDWFL */ ++ + /* FIXME: is there another way to pass the executable name to the find_elf + * callback? */ + const char *executable_file = NULL; +diff --git a/lib/core_unwind_elfutils.c b/lib/core_unwind_elfutils.c +index bbd4813..bea9b5f 100644 +--- a/lib/core_unwind_elfutils.c ++++ b/lib/core_unwind_elfutils.c +@@ -28,6 +28,10 @@ + + #include + #include ++#include ++#include ++#include ++#include + + #define FRAME_LIMIT 256 + +@@ -202,4 +206,136 @@ fail: + return stacktrace; + } + ++struct sr_core_stacktrace * ++sr_core_stacktrace_from_core_hook(pid_t tid, ++ const char *executable, ++ int signum, ++ char **error_msg) ++{ ++ struct sr_core_stacktrace *stacktrace = NULL; ++ ++ /* Initialize error_msg to 'no error'. */ ++ if (error_msg) ++ *error_msg = NULL; ++ ++ const Dwfl_Callbacks proc_cb = ++ { ++ .find_elf = dwfl_linux_proc_find_elf, ++ .find_debuginfo = find_debuginfo_none, ++ }; ++ ++ Dwfl *dwfl = dwfl_begin(&proc_cb); ++ ++ if (dwfl_linux_proc_report(dwfl, tid) != 0) ++ { ++ set_error_dwfl("dwfl_linux_proc_report"); ++ goto fail; ++ } ++ ++ if (dwfl_report_end(dwfl, NULL, NULL) != 0) ++ { ++ set_error_dwfl("dwfl_report_end"); ++ goto fail; ++ } ++ ++ if (ptrace(PTRACE_SEIZE, tid, NULL, (void *)(uintptr_t)PTRACE_O_TRACEEXIT) != 0) ++ { ++ set_error("PTRACE_SEIZE (tid %u) failed: %s", (unsigned)tid, strerror(errno)); ++ goto fail; ++ } ++ ++ if (close(STDIN_FILENO) != 0) ++ { ++ set_error("Failed to close stdin: %s", strerror(errno)); ++ goto fail; ++ } ++ ++ int status; ++ pid_t got = waitpid(tid, &status, 0); ++ if (got == -1) ++ { ++ set_error("waitpid failed: %s", strerror(errno)); ++ goto fail; ++ } ++ ++ if (got != tid) ++ { ++ set_error("waitpid returned %u but %u was expected", (unsigned)got, (unsigned)tid); ++ goto fail; ++ } ++ ++ if (!WIFSTOPPED(status)) ++ { ++ set_error("waitpid returned 0x%x but WIFSTOPPED was expected", status); ++ goto fail; ++ } ++ ++ if ((status >> 8) != (SIGTRAP | (PTRACE_EVENT_EXIT << 8))) ++ { ++ set_error("waitpid returned 0x%x but (status >> 8) == " ++ "(SIGTRAP | (PTRACE_EVENT_EXIT << 8)) was expected", status); ++ goto fail; ++ } ++ ++ if (dwfl_linux_proc_attach(dwfl, tid, true) != 0) ++ { ++ set_error_dwfl("dwfl_linux_proc_attach"); ++ goto fail; ++ } ++ ++ stacktrace = sr_core_stacktrace_new(); ++ if (!stacktrace) ++ { ++ set_error("Failed to initialize stacktrace memory"); ++ goto fail; ++ } ++ ++ stacktrace->threads = sr_core_thread_new(); ++ if (!stacktrace->threads) ++ { ++ set_error("Failed to initialize thread memory"); ++ sr_core_stacktrace_free(stacktrace); ++ stacktrace = 0; ++ goto fail; ++ } ++ stacktrace->threads->id = tid; ++ ++ struct frame_callback_arg frame_arg = ++ { ++ .frames_tail = &(stacktrace->threads->frames), ++ .error_msg = NULL, ++ .nframes = 0 ++ }; ++ ++ int ret = dwfl_getthread_frames(dwfl, tid, frame_callback, &frame_arg); ++ if (ret != 0 && ret != CB_STOP_UNWIND) ++ { ++ if (ret == -1) ++ set_error_dwfl("dwfl_getthread_frames"); ++ else if (ret == DWARF_CB_ABORT) ++ { ++ set_error("%s", frame_arg.error_msg); ++ free(frame_arg.error_msg); ++ } ++ else ++ set_error("Unknown error in dwfl_getthreads"); ++ ++ sr_core_stacktrace_free(stacktrace); ++ stacktrace = NULL; ++ goto fail; ++ } ++ ++ truncate_long_thread(stacktrace->threads, &frame_arg); ++ ++ if (executable) ++ stacktrace->executable = sr_strdup(executable); ++ if (signum > 0) ++ stacktrace->signal = (uint16_t)signum; ++ stacktrace->crash_thread = stacktrace->threads; ++ ++fail: ++ dwfl_end(dwfl); ++ return stacktrace; ++} ++ + #endif /* WITH_LIBDWFL */ +-- +2.4.3 + diff --git a/SOURCES/satyr-0.13-tests-fix-failure-on-gcc5-on-x86_64.patch b/SOURCES/satyr-0.13-tests-fix-failure-on-gcc5-on-x86_64.patch new file mode 100644 index 0000000..3dd268c --- /dev/null +++ b/SOURCES/satyr-0.13-tests-fix-failure-on-gcc5-on-x86_64.patch @@ -0,0 +1,44 @@ +From c1e7562809a1ea9955d43bc21fa2b2ac88d95ee5 Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Fri, 20 Feb 2015 15:50:32 +0100 +Subject: [PATCH] tests: fix failure on gcc5 on x86_64 + +Resolves #1334604 + +Signed-off-by: Martin Milata +--- + tests/ruby_stacktrace.at | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/tests/ruby_stacktrace.at b/tests/ruby_stacktrace.at +index 5a2d901..09d5638 100644 +--- a/tests/ruby_stacktrace.at ++++ b/tests/ruby_stacktrace.at +@@ -261,6 +261,7 @@ main(void) + + AT_TESTFUN([sr_ruby_stacktrace_from_json], + [[ ++#include "stacktrace.h" + #include "ruby/stacktrace.h" + #include "ruby/frame.h" + #include "utils.h" +@@ -268,13 +269,14 @@ AT_TESTFUN([sr_ruby_stacktrace_from_json], + #include + #include + #include ++#include + + void + check(char *filename) + { + char *error_message = NULL; +- const char *file_contents = sr_file_to_string(filename, &error_message); +- const char *input = file_contents; ++ char *file_contents = sr_file_to_string(filename, &error_message); ++ char *input = file_contents; + struct sr_location location; + sr_location_init(&location); + +-- +1.8.3.1 + diff --git a/SOURCES/satyr-0.13-testsuite-add-test-for-limit-frame-number-in-GDB-cor.patch b/SOURCES/satyr-0.13-testsuite-add-test-for-limit-frame-number-in-GDB-cor.patch new file mode 100644 index 0000000..0cfcc61 --- /dev/null +++ b/SOURCES/satyr-0.13-testsuite-add-test-for-limit-frame-number-in-GDB-cor.patch @@ -0,0 +1,182 @@ +From 77243901ca72e4f49ed650eecfe501edfb2344df Mon Sep 17 00:00:00 2001 +From: Matej Habrnal +Date: Tue, 30 Aug 2016 11:09:50 +0200 +Subject: [PATCH] testsuite: add test for limit frame number in GDB core + unwinder + +Related to #1260074 + +Signed-off-by: Matej Habrnal +--- + tests/core_stacktrace.at | 158 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 158 insertions(+) + +diff --git a/tests/core_stacktrace.at b/tests/core_stacktrace.at +index f45e126..313d8b8 100644 +--- a/tests/core_stacktrace.at ++++ b/tests/core_stacktrace.at +@@ -197,3 +197,161 @@ main(void) + return 0; + } + ]]) ++ ++## --------------------------------- ## ++## sr_core_stacktrace_from_gdb_limit ## ++## --------------------------------- ## ++ ++AT_TESTFUN([sr_core_stacktrace_from_gdb_limit], ++[[ ++#define _GNU_SOURCE ++#include "stacktrace.h" ++#include "core/stacktrace.h" ++#include "core/thread.h" ++#include "core/frame.h" ++#include "core/unwind.h" ++#include "strbuf.h" ++#include "utils.h" ++#include "report_type.h" ++#include ++#include ++#include "internal_unwind.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++char *generate_coredump(int start) ++{ ++ ++ if (start) ++ { ++ return generate_coredump(--start); ++ } ++ ++ const char* coredump_pattern = "/tmp/satyr.core"; ++ ++ char *mypid = NULL; ++ asprintf(&mypid, "%d", getpid()); ++ if (mypid == NULL) ++ err(1, "asprintf"); ++ ++ pid_t pid = fork(); ++ if (pid < 0) ++ err(1, "fork"); ++ if (pid == 0) ++ { ++ char *args[] = { "gcore", "-o", (char *)coredump_pattern, mypid, NULL }; ++ execv("/usr/bin/gcore", args); ++ } ++ ++ int status; ++ int r; ++ while ((r = waitpid(pid, &status, 0)) < 0) ++ { ++ if (errno != EAGAIN) ++ err(1, "waitpid"); ++ } ++ ++ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) ++ errx(1, "gcore failed"); ++ ++ return sr_asprintf("%s.%s", coredump_pattern, mypid); ++} ++struct sr_strbuf * ++get_backtrace(const char *core_file, const char *executable) ++{ ++ unsigned i = 0; ++ char *args[25]; ++ args[i++] = (char*)"/usr/bin/gdb"; ++ args[i++] = (char*)"-batch"; ++ args[i++] = (char*)"-iex"; ++ args[i++] = (char*)"set debug-file-directory /"; ++ args[i++] = (char*)"-ex"; ++ args[i++] = sr_asprintf("file %s", executable); ++ args[i++] = (char*)"-ex"; ++ args[i++] = sr_asprintf("core-file %s", core_file); ++ args[i++] = (char*)"-ex"; ++ args[i++] = (char*)"thread apply all backtrace 1024 full"; ++ args[i++] = (char*)"-ex"; ++ args[i++] = (char*)"info sharedlib"; ++ args[i++] = (char*)"-ex"; ++ args[i++] = (char*)"print (char*)__abort_msg"; ++ args[i++] = (char*)"-ex"; ++ args[i++] = (char*)"print (char*)__glib_assert_msg"; ++ args[i++] = (char*)"-ex"; ++ args[i++] = (char*)"info all-registers"; ++ args[i++] = (char*)"-ex"; ++ args[i++] = (char*)"disassemble"; ++ args[i++] = NULL; ++ ++ int p[2]; ++ int pipe_return = pipe(p); ++ if (pipe_return != 0) ++ err(1, "pipe"); ++ ++ pid_t pid = fork(); ++ if (pid < 0) ++ err(1, "fork"); ++ if (pid == 0) /* child */ ++ { ++ close(p[0]); ++ dup2(p[1], 1); // send stdout to the pipe ++ dup2(p[1], 2); // send stderr to the pipe ++ ++ execv(args[0], args); ++ } ++ close(p[1]); ++ ++ struct sr_strbuf *gdb_buf = sr_strbuf_new(); ++ sr_strbuf_init(gdb_buf); ++ char buf[256]; ++ ssize_t size; ++ while((size = read(p[0], buf, sizeof(buf)-1)) > 0 && errno != EINTR) ++ { ++ buf[size] = '\0'; ++ sr_strbuf_append_str(gdb_buf, (const char*) buf); ++ } ++ ++ int status, r; ++ while ((r = waitpid(pid, &status, 0)) < 0) ++ { ++ if (errno != EAGAIN) ++ err(1, "waitpid"); ++ } ++ ++ return gdb_buf; ++} ++ ++int ++main(int argc, char* argv[]) ++{ ++ char *coredump_path = generate_coredump(257); ++ ++ struct sr_strbuf *gdb_output = get_backtrace(coredump_path, argv[0]); ++ assert(gdb_output); ++ assert(gdb_output->buf); ++ ++ char *error_msg = NULL; ++ struct sr_core_stacktrace *core_stacktrace = sr_core_stacktrace_from_gdb(gdb_output->buf, coredump_path, argv[0], &error_msg); ++ assert(!error_msg); ++ ++ struct sr_core_thread *threads = core_stacktrace->threads; ++ struct sr_core_frame *frames = threads->frames; ++ unsigned nframes = 0; ++ for(; frames; ++nframes, frames = frames->next) ++ ; ++ ++ /* max num of frames is CORE_STACKTRACE_FRAME_LIMIT */ ++ assert(nframes == CORE_STACKTRACE_FRAME_LIMIT); ++ ++ free(coredump_path); ++ sr_strbuf_free(gdb_output); ++ ++ return 0; ++} ++]]) +-- +2.17.1 + diff --git a/SOURCES/satyr-0.13-unwind-minor-refactoring.patch b/SOURCES/satyr-0.13-unwind-minor-refactoring.patch new file mode 100644 index 0000000..3c392cb --- /dev/null +++ b/SOURCES/satyr-0.13-unwind-minor-refactoring.patch @@ -0,0 +1,94 @@ +From f8be518c5d8534f8724c752c6ad95b3e61460a7b Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Mon, 24 Nov 2014 14:34:09 +0100 +Subject: [PATCH 2/5] unwind: minor refactoring + +Related to abrt/abrt#829. + +Signed-off-by: Martin Milata +--- + lib/core_unwind.c | 2 +- + lib/core_unwind_elfutils.c | 24 +++++++++++++++--------- + lib/internal_unwind.h | 6 ++++++ + 3 files changed, 22 insertions(+), 10 deletions(-) + +diff --git a/lib/core_unwind.c b/lib/core_unwind.c +index 8b7cc22..48d5ca5 100644 +--- a/lib/core_unwind.c ++++ b/lib/core_unwind.c +@@ -127,7 +127,7 @@ find_elf_core (Dwfl_Module *mod, void **userdata, const char *modname, + } + + /* Do not use debuginfo files at all. */ +-static int ++int + find_debuginfo_none (Dwfl_Module *mod, void **userdata, const char *modname, + GElf_Addr base, const char *file_name, + const char *debuglink_file, GElf_Word debuglink_crc, +diff --git a/lib/core_unwind_elfutils.c b/lib/core_unwind_elfutils.c +index bbd4813..8351ebf 100644 +--- a/lib/core_unwind_elfutils.c ++++ b/lib/core_unwind_elfutils.c +@@ -77,6 +77,19 @@ frame_callback(Dwfl_Frame *frame, void *data) + return DWARF_CB_OK; + } + ++static void ++truncate_long_thread(struct sr_core_thread *thread, struct frame_callback_arg *frame_arg) ++{ ++ /* Truncate the stacktrace to FRAME_LIMIT least recent frames. */ ++ while (thread->frames && frame_arg->nframes > FRAME_LIMIT) ++ { ++ struct sr_core_frame *old_frame = thread->frames; ++ thread->frames = old_frame->next; ++ sr_core_frame_free(old_frame); ++ frame_arg->nframes--; ++ } ++} ++ + static int + unwind_thread(Dwfl_Thread *thread, void *data) + { +@@ -86,7 +99,7 @@ unwind_thread(Dwfl_Thread *thread, void *data) + struct sr_core_thread *result = sr_core_thread_new(); + if (!result) + { +- set_error("Failed to initialize stacktrace memory"); ++ set_error("Failed to initialize thread memory"); + return DWARF_CB_ABORT; + } + result->id = (int64_t)dwfl_thread_tid(thread); +@@ -121,14 +134,7 @@ unwind_thread(Dwfl_Thread *thread, void *data) + goto abort; + } + +- /* Truncate the stacktrace to FRAME_LIMIT least recent frames. */ +- while (result->frames && frame_arg.nframes > FRAME_LIMIT) +- { +- struct sr_core_frame *old_frame = result->frames; +- result->frames = old_frame->next; +- sr_core_frame_free(old_frame); +- frame_arg.nframes--; +- } ++ truncate_long_thread(result, &frame_arg); + + *thread_arg->threads_tail = result; + thread_arg->threads_tail = &result->next; +diff --git a/lib/internal_unwind.h b/lib/internal_unwind.h +index 2c9abb7..84261c6 100644 +--- a/lib/internal_unwind.h ++++ b/lib/internal_unwind.h +@@ -96,4 +96,10 @@ resolve_frame(Dwfl *dwfl, Dwarf_Addr ip, bool minus_one); + short + get_signal_number(Elf *e, const char *elf_file); + ++int ++find_debuginfo_none (Dwfl_Module *mod, void **userdata, const char *modname, ++ GElf_Addr base, const char *file_name, ++ const char *debuglink_file, GElf_Word debuglink_crc, ++ char **debuginfo_file_name); ++ + #endif /* SATYR_INTERNAL_UNWIND_H */ +-- +2.4.3 + diff --git a/SOURCES/satyr-0.13-unwinder-refresh-config-h.patch b/SOURCES/satyr-0.13-unwinder-refresh-config-h.patch new file mode 100644 index 0000000..735c3ca --- /dev/null +++ b/SOURCES/satyr-0.13-unwinder-refresh-config-h.patch @@ -0,0 +1,46 @@ +From 7351e5b5ad04bd840e75fafb8ab19d4e0c90d717 Mon Sep 17 00:00:00 2001 +From: Matej Habrnal +Date: Fri, 13 May 2016 14:42:23 +0200 +Subject: [PATCH] unwinder refresh config h + +--- + configure | 6 +++--- + lib/config.h.in | 4 ++-- + 2 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/configure b/configure +index 2418533..f79cfeb 100755 +--- a/configure ++++ b/configure +@@ -13261,10 +13261,10 @@ _ACEOF + + fi + +-ac_fn_c_check_func "$LINENO" "dwfl_frame_state_core" "ac_cv_func_dwfl_frame_state_core" +-if test "x$ac_cv_func_dwfl_frame_state_core" = xyes; then : ++ac_fn_c_check_func "$LINENO" "dwfl_getthreads" "ac_cv_func_dwfl_getthreads" ++if test "x$ac_cv_func_dwfl_getthreads" = xyes; then : + +-$as_echo "#define HAVE_DWFL_FRAME_STATE_CORE /**/" >>confdefs.h ++$as_echo "#define HAVE_DWFL_NEXT_THREAD /**/" >>confdefs.h + + fi + +diff --git a/lib/config.h.in b/lib/config.h.in +index b5c34c6..b7b6255 100644 +--- a/lib/config.h.in ++++ b/lib/config.h.in +@@ -6,8 +6,8 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_DWARF_H + +-/* Have function dwfl_frame_state_core for coredump unwinding */ +-#undef HAVE_DWFL_FRAME_STATE_CORE ++/* Have function dwfl_getthreads for coredump unwinding */ ++#undef HAVE_DWFL_NEXT_THREAD + + /* Define to 1 if you have the header file. */ + #undef HAVE_ELFUTILS_LIBDWFL_H +-- +1.8.3.1 + diff --git a/SOURCES/satyr-0.13-ureport-auth-support.patch b/SOURCES/satyr-0.13-ureport-auth-support.patch new file mode 100644 index 0000000..220ac73 --- /dev/null +++ b/SOURCES/satyr-0.13-ureport-auth-support.patch @@ -0,0 +1,565 @@ +From 3e11b3c67bc31662810f72571311d7d7580e3f0a Mon Sep 17 00:00:00 2001 +From: Jakub Filak +Date: Mon, 18 Aug 2014 20:39:34 +0200 +Subject: [SATYR PATCH 6/6] Add authentication data support in uReport + +Support authentication data in uReport + +We need this for 'machine id' element which should be present only if +users wishes to it. + +The authentication entries are string based, key value pairs (JSON +string elements). + +The entries are enclosed in an envelope object called 'auth'. + +Example: + +"auth" : { "machineid" : "deadbeaf" + } + +Fixes #171 + +Signed-off-by: Jakub Filak +Signed-off-by: Martin Milata + +mmilata: s/custom/auth/g, python bindings + +tests: add a test for uReport auth data + +Related to #171 + +Signed-off-by: Jakub Filak +Signed-off-by: Martin Milata + +mmilata: python test + +ureport: fix indentation of 'auth' key in JSON + +Fixes a bug in commit 189f7adbba9c38bd434c30e7d0c07b546c373312 + +Related to #171 + +Signed-off-by: Jakub Filak +--- + include/report.h | 17 ++++++ + lib/internal_utils.h | 12 +++++ + lib/json.c | 26 ++++++++++ + lib/report.c | 82 +++++++++++++++++++++++++++++ + python/py_report.c | 30 +++++++++++ + python/py_report.h | 2 + + tests/json_files/ureport-1-auth | 112 ++++++++++++++++++++++++++++++++++++++++ + tests/python/report.py | 6 +++ + tests/report.at | 81 +++++++++++++++++++++++++++++ + 9 files changed, 368 insertions(+) + create mode 100644 tests/json_files/ureport-1-auth + +diff --git a/include/report.h b/include/report.h +index 424b730..025d8f8 100644 +--- a/include/report.h ++++ b/include/report.h +@@ -31,6 +31,13 @@ extern "C" { + struct sr_json_value; + struct sr_stacktrace; + ++struct sr_report_custom_entry ++{ ++ char *key; ++ char *value; ++ struct sr_report_custom_entry *next; ++}; ++ + struct sr_report + { + uint32_t report_version; +@@ -49,6 +56,8 @@ struct sr_report + struct sr_rpm_package *rpm_packages; + + struct sr_stacktrace *stacktrace; ++ ++ struct sr_report_custom_entry *auth_entries; + }; + + struct sr_report * +@@ -60,6 +69,14 @@ sr_report_init(struct sr_report *report); + void + sr_report_free(struct sr_report *report); + ++ ++/* @brief Adds a new entry to 'auth' object ++ * ++ * The implementation is LIFO. The resulting list is in reversed. ++ */ ++void ++sr_report_add_auth(struct sr_report *report, const char *key, const char *value); ++ + char * + sr_report_to_json(struct sr_report *report); + +diff --git a/lib/internal_utils.h b/lib/internal_utils.h +index 20f65de..3a10520 100644 +--- a/lib/internal_utils.h ++++ b/lib/internal_utils.h +@@ -130,4 +130,16 @@ json_read_bool(struct sr_json_value *object, const char *key_name, bool *dest, + abort(); \ + } + ++/* returns the number of object's children */ ++unsigned ++json_object_children_count(struct sr_json_value *object); ++ ++/* returns the child_noth child and passes its name in child_name arg */ ++struct sr_json_value * ++json_object_get_child(struct sr_json_value *object, unsigned child_no, const char **child_name); ++ ++/* returns string's value */ ++const char * ++json_string_get_value(struct sr_json_value *object); ++ + #endif +diff --git a/lib/json.c b/lib/json.c +index 35b7e6b..d777973 100644 +--- a/lib/json.c ++++ b/lib/json.c +@@ -787,6 +787,32 @@ json_element(struct sr_json_value *object, const char *key_name) + return NULL; + } + ++unsigned ++json_object_children_count(struct sr_json_value *object) ++{ ++ assert(object->type == SR_JSON_OBJECT); ++ ++ return object->u.object.length; ++} ++ ++struct sr_json_value * ++json_object_get_child(struct sr_json_value *object, unsigned child_no, const char **child_name) ++{ ++ assert(object->type == SR_JSON_OBJECT); ++ assert(child_no < object->u.object.length); ++ ++ *child_name = object->u.object.values[child_no].name; ++ return object->u.object.values[child_no].value; ++} ++ ++const char * ++json_string_get_value(struct sr_json_value *object) ++{ ++ assert(object->type == SR_JSON_STRING); ++ ++ return object->u.string.ptr; ++} ++ + #define DEFINE_JSON_READ(name, c_type, json_type, json_member, conversion) \ + bool \ + name(struct sr_json_value *object, const char *key_name, c_type *dest, char **error_message) \ +diff --git a/lib/report.c b/lib/report.c +index a6ecae4..411df64 100644 +--- a/lib/report.c ++++ b/lib/report.c +@@ -66,6 +66,7 @@ sr_report_init(struct sr_report *report) + report->component_name = NULL; + report->rpm_packages = NULL; + report->stacktrace = NULL; ++ report->auth_entries = NULL; + } + + void +@@ -75,9 +76,36 @@ sr_report_free(struct sr_report *report) + sr_operating_system_free(report->operating_system); + sr_rpm_package_free(report->rpm_packages, true); + sr_stacktrace_free(report->stacktrace); ++ ++ struct sr_report_custom_entry *iter = report->auth_entries; ++ while (iter) ++ { ++ struct sr_report_custom_entry *tmp = iter->next; ++ ++ free(iter->value); ++ free(iter->key); ++ free(iter); ++ ++ iter = tmp; ++ } ++ + free(report); + } + ++void ++sr_report_add_auth(struct sr_report *report, const char *key, const char *value) ++{ ++ struct sr_report_custom_entry *new_entry = sr_malloc(sizeof(*new_entry)); ++ new_entry->key = sr_strdup(key); ++ new_entry->value = sr_strdup(value); ++ ++ /* prepend the new value ++ * it is much faster and easier ++ * we can do it because we do not to preserve the order(?) */ ++ new_entry->next = report->auth_entries; ++ report->auth_entries = new_entry; ++} ++ + /* The object has to be non-empty, i.e. contain at least one key-value pair. */ + static void + dismantle_object(char *obj) +@@ -221,6 +249,34 @@ sr_report_to_json(struct sr_report *report) + free(rpms_str_indented); + } + ++ /* Custom entries. ++ * "auth" : { "foo": "blah" ++ * , "one": "two" ++ * } ++ */ ++ struct sr_report_custom_entry *iter = report->auth_entries; ++ if (iter) ++ { ++ sr_strbuf_append_strf(strbuf, ", \"auth\": { "); ++ sr_json_append_escaped(strbuf, iter->key); ++ sr_strbuf_append_str(strbuf, ": "); ++ sr_json_append_escaped(strbuf, iter->value); ++ sr_strbuf_append_str(strbuf, "\n"); ++ ++ /* the first entry is prefix with '{', see lines above */ ++ iter = iter->next; ++ while (iter) ++ { ++ sr_strbuf_append_strf(strbuf, " , "); ++ sr_json_append_escaped(strbuf, iter->key); ++ sr_strbuf_append_str(strbuf, ": "); ++ sr_json_append_escaped(strbuf, iter->value); ++ sr_strbuf_append_str(strbuf, "\n"); ++ iter = iter->next; ++ } ++ sr_strbuf_append_str(strbuf, " } "); ++ } ++ + sr_strbuf_append_str(strbuf, "}"); + return sr_strbuf_free_nobuf(strbuf); + } +@@ -336,6 +392,32 @@ sr_report_from_json(struct sr_json_value *root, char **error_message) + + } + ++ /* Authentication entries. */ ++ struct sr_json_value *extra = json_element(root, "auth"); ++ if (extra) ++ { ++ if (!JSON_CHECK_TYPE(extra, SR_JSON_OBJECT, "auth")) ++ goto fail; ++ ++ const unsigned children = json_object_children_count(extra); ++ ++ /* from the last children down to the first for easier testing :) ++ * keep it as it is as long as sr_report_add_auth() does LIFO */ ++ for (unsigned i = 1; i <= children; ++i) ++ { ++ const char *child_name = NULL; ++ struct sr_json_value *child_object = json_object_get_child(extra, ++ children - i, ++ &child_name); ++ ++ if (!JSON_CHECK_TYPE(child_object, SR_JSON_STRING, child_name)) ++ continue; ++ ++ const char *child_value = json_string_get_value(child_object); ++ sr_report_add_auth(report, child_name, child_value); ++ } ++ } ++ + return report; + + fail: +diff --git a/python/py_report.c b/python/py_report.c +index d314027..8aa35dd 100644 +--- a/python/py_report.c ++++ b/python/py_report.c +@@ -42,6 +42,8 @@ + #define to_json_doc "Usage: report.to_json()\n\n" \ + "Returns: string - the report serialized as JSON" + ++#define auth_doc "Dictinary of key/value pairs used for authentication" ++ + /* See python/py_common.h and python/py_gdb_frame.c for generic getters/setters documentation. */ + #define GSOFF_PY_STRUCT sr_py_report + #define GSOFF_PY_MEMBER report +@@ -64,6 +66,7 @@ report_getset[] = + SR_ATTRIBUTE_STRING(component_name, "Name of the software component this report pertains to (string)" ), + { (char*)"report_version", sr_py_report_get_version, sr_py_setter_readonly, (char*)"Version of the report (int)", NULL }, + { (char*)"report_type", sr_py_report_get_type, sr_py_report_set_type, (char*)"Report type (string)", NULL }, ++ { (char*)"auth", sr_py_report_get_auth, sr_py_report_set_auth, (char*)auth_doc, NULL }, + { NULL }, + }; + +@@ -492,3 +495,30 @@ sr_py_report_to_json(PyObject *self, PyObject *args) + free(json); + return result; + } ++ ++PyObject * ++sr_py_report_get_auth(PyObject *self, void *data) ++{ ++ struct sr_report *report = ((struct sr_py_report *)self)->report; ++ struct sr_report_custom_entry *ae = report->auth_entries; ++ ++ PyObject *auth = PyDict_New(); ++ for (ae = report->auth_entries; ae; ae = ae->next) ++ { ++ PyObject *val = PyString_FromString(ae->value); ++ if (!val) ++ return NULL; ++ ++ if (PyDict_SetItemString(auth, ae->key, val) == -1) ++ return NULL; ++ } ++ ++ return auth; ++} ++ ++int ++sr_py_report_set_auth(PyObject *self, PyObject *rhs, void *data) ++{ ++ PyErr_SetString(PyExc_NotImplementedError, "Setting auth data is not implemented."); ++ return -1; ++} +diff --git a/python/py_report.h b/python/py_report.h +index a451dbc..88ec30f 100644 +--- a/python/py_report.h ++++ b/python/py_report.h +@@ -65,6 +65,8 @@ PyObject *sr_py_report_str(PyObject *self); + PyObject *sr_py_report_get_version(PyObject *self, void *data); + PyObject *sr_py_report_get_type(PyObject *self, void *data); + int sr_py_report_set_type(PyObject *self, PyObject *rhs, void *data); ++PyObject *sr_py_report_get_auth(PyObject *self, void *data); ++int sr_py_report_set_auth(PyObject *self, PyObject *rhs, void *data); + + /** + * Methods. +diff --git a/tests/json_files/ureport-1-auth b/tests/json_files/ureport-1-auth +new file mode 100644 +index 0000000..a8db2df +--- /dev/null ++++ b/tests/json_files/ureport-1-auth +@@ -0,0 +1,112 @@ ++{ ++ "ureport_version": 2, ++ ++ "reason": "Program /usr/bin/sleep was terminated by signal 11", ++ ++ "os": { ++ "name": "fedora", ++ "version": "18", ++ "architecture": "x86_64" ++ }, ++ ++ "problem": { ++ "type": "core", ++ ++ "executable": "/usr/bin/sleep", ++ ++ "signal": 11, ++ ++ "component": "coreutils", ++ ++ "user": { ++ "local": true, ++ "root": false ++ }, ++ ++ "stacktrace": [ ++ { ++ "crash_thread": true, ++ ++ "frames": [ ++ { ++ "build_id": "5f6632d75fd027f5b7b410787f3f06c6bf73eee6", ++ "build_id_offset": 767024, ++ "file_name": "/lib64/libc.so.6", ++ "address": 251315074096, ++ "fingerprint": "6c1eb9626919a2a5f6a4fc4c2edc9b21b33b7354", ++ "function_name": "__nanosleep" ++ }, ++ { ++ "build_id": "cd379d3bb5d07c96d491712e41c34bcd06b2ce32", ++ "build_id_offset": 16567, ++ "file_name": "/usr/bin/sleep", ++ "address": 4210871, ++ "fingerprint": "d24433b82a2c751fc580f47154823e0bed641a54", ++ "function_name": "close_stdout" ++ }, ++ { ++ "build_id": "cd379d3bb5d07c96d491712e41c34bcd06b2ce32", ++ "build_id_offset": 16202, ++ "file_name": "/usr/bin/sleep", ++ "address": 4210506, ++ "fingerprint": "562719fb960d1c4dbf30c04b3cff37c82acc3d2d", ++ "function_name": "close_stdout" ++ }, ++ { ++ "build_id": "cd379d3bb5d07c96d491712e41c34bcd06b2ce32", ++ "build_id_offset": 6404, ++ "fingerprint": "2e8fb95adafe21d035b9bcb9993810fecf4be657", ++ "file_name": "/usr/bin/sleep", ++ "address": 4200708 ++ }, ++ { ++ "build_id": "5f6632d75fd027f5b7b410787f3f06c6bf73eee6", ++ "build_id_offset": 137733, ++ "file_name": "/lib64/libc.so.6", ++ "address": 251314444805, ++ "fingerprint": "075acda5d3230e115cf7c88597eaba416bdaa6bb", ++ "function_name": "__libc_start_main" ++ } ++ ] ++ } ++ ] ++ }, ++ ++ "packages": [ ++ { ++ "name": "coreutils", ++ "epoch": 0, ++ "version": "8.17", ++ "architecture": "x86_64", ++ "package_role": "affected", ++ "release": "8.fc18", ++ "install_time": 1371464601 ++ }, ++ { ++ "name": "glibc", ++ "epoch": 0, ++ "version": "2.16", ++ "architecture": "x86_64", ++ "release": "31.fc18", ++ "install_time": 1371464176 ++ }, ++ { ++ "name": "glibc-common", ++ "epoch": 0, ++ "version": "2.16", ++ "architecture": "x86_64", ++ "release": "31.fc18", ++ "install_time": 1371464184 ++ } ++ ], ++ ++ "reporter": { ++ "version": "0.3", ++ "name": "satyr" ++ }, ++ ++ "auth": { ++ "hostname": "localhost", ++ "machine_id": "0000" ++ } ++} +diff --git a/tests/python/report.py b/tests/python/report.py +index 83fa76b..5f731b5 100755 +--- a/tests/python/report.py ++++ b/tests/python/report.py +@@ -91,6 +91,12 @@ class TestReport(BindingsTestCase): + def test_hash(self): + self.assertHashable(self.report) + ++ def test_auth(self): ++ self.assertFalse(self.report.auth) ++ self.assertRaises(NotImplementedError, self.report.__setattr__, 'auth', {'hostname': 'darkstar'}) ++ ++ report_with_auth = satyr.Report(load_input_contents('../json_files/ureport-1-auth')) ++ self.assertEqual(report_with_auth.auth, {'hostname': 'localhost', 'machine_id': '0000'}) + + if __name__ == '__main__': + unittest.main() +diff --git a/tests/report.at b/tests/report.at +index 8736137..1e773d2 100644 +--- a/tests/report.at ++++ b/tests/report.at +@@ -57,3 +57,84 @@ int main(void) + return 0; + } + ]]) ++ ++## ------------------ ## ++## sr_report_add_auth ## ++## ------------------ ## ++ ++AT_TESTFUN([sr_report_add_auth], ++[[ ++#include ++#include ++#include ++#include ++#include "report.h" ++ ++void check_struct(struct sr_report *report, const char **expected) ++{ ++ const char **exp_iter = expected; ++ struct sr_report_custom_entry *cust_iter = report->auth_entries; ++ ++ while(cust_iter && *exp_iter) ++ { ++ fprintf(stdout, "Expected('%s':'%s') vs. Current('%s':'%s')\n", ++ exp_iter[0], exp_iter[1], cust_iter->key, cust_iter->value); ++ ++ assert(strcmp(cust_iter->key, exp_iter[0]) == 0 && ++ strcmp(cust_iter->value, exp_iter[1]) == 0); ++ ++ cust_iter = cust_iter->next; ++ exp_iter += 2; ++ } ++ ++ assert(cust_iter == NULL); ++ assert(*exp_iter == NULL); ++} ++ ++void check_json(const char *json, const char **expected) ++{ ++ const char **exp_iter = expected; ++ while (*exp_iter) ++ { ++ char *entry = NULL; ++ asprintf(&entry, "\"%s\": \"%s\"", exp_iter[0], exp_iter[1]); ++ ++ fprintf(stdout, "Checking: '%s'\n", entry); ++ ++ if (strstr(json, entry) == NULL) ++ { ++ fprintf(stderr, "JSON:\n%s\n", json); ++ abort(); ++ } ++ ++ exp_iter += 2; ++ } ++} ++ ++ ++int main(void) ++{ ++ struct sr_report *report = sr_report_new(); ++ ++ sr_report_add_auth(report, "foo", "blah"); ++ sr_report_add_auth(report, "abrt", "awesome"); ++ sr_report_add_auth(report, "satyr", "wonderful"); ++ ++ const char *expected[] = { "satyr", "wonderful", "abrt", "awesome", "foo", "blah", NULL }; ++ ++ check_struct(report, expected); ++ ++ sr_report_to_json(report); ++ ++ char *json = sr_report_to_json(report); ++ ++ check_json(json, expected); ++ ++ char *error = NULL; ++ struct sr_report *copy = sr_report_from_json_text(json, &error); ++ ++ check_struct(copy, expected); ++ ++ return 0; ++} ++]]) +-- +1.9.3 + diff --git a/SPECS/satyr.spec b/SPECS/satyr.spec new file mode 100644 index 0000000..ee2824a --- /dev/null +++ b/SPECS/satyr.spec @@ -0,0 +1,331 @@ +%{!?python_sitearch: %global python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")} + +# rhel6's python-sphinx cannot build manual pages +%if 0%{?rhel} && 0%{?rhel} <= 6 + %define enable_python_manpage 0 +%else + %define enable_python_manpage 1 +%endif + +%if 0%{?suse_version} + %define python2_devel python-devel + %define libdw_devel libdw-devel + %define libelf_devel libelf-devel +%else + %define python2_devel python2-devel + %define libdw_devel elfutils-devel + %define libelf_devel elfutils-libelf-devel +%endif + +Name: satyr +Version: 0.13 +Release: 15%{?dist} +Summary: Tools to create anonymous, machine-friendly problem reports +Group: System Environment/Libraries +License: GPLv2+ +URL: https://github.com/abrt/satyr +Source0: https://fedorahosted.org/released/abrt/satyr-%{version}.tar.xz +BuildRequires: %{python2_devel} +BuildRequires: %{libdw_devel} +BuildRequires: %{libelf_devel} +BuildRequires: binutils-devel +BuildRequires: rpm-devel +BuildRequires: libtool +BuildRequires: pkgconfig +BuildRequires: automake +BuildRequires: gcc-c++ +%if %{?enable_python_manpage} +BuildRequires: python-sphinx +%endif + +# git is need for '%%autosetup -S git' which automatically applies all the +# patches above. Please, be aware that the patches must be generated +# by 'git format-patch' +BuildRequires: git + +Patch0: satyr-0.13-elfutils-0.158.patch +Patch1: satyr-0.13-elfutils-unwinder.patch +Patch2: satyr-0.13-disable-fingerprints.patch +Patch3: satyr-0.13-unwinder-refresh-config-h.patch + +# 1142856, minor bugs found by static analyzer +Patch4: satyr-0.13-static-analyzer-bugs.patch + +# 1123262, empty duphash of unreliable koops +Patch5: satyr-0.13-koops-unreliable-frames.patch + +# 1142339, python exception parsing +Patch6: satyr-0.13-python-exceptions.patch + +# 1142338, ppc64 backtrace parsing +Patch7: satyr-0.13-ppc64-backtrace-parsing.patch + +# 1142346, limit stacktrace depth +Patch8: satyr-0.13-limit-stacktrace-depth.patch + +# 1139555, ureport auth support +Patch9: satyr-0.13-ureport-auth-support.patch + +# 1034857, ignore java suppressed exceptions +Patch10: satyr-0.13-java-suppressed-exceptions.patch + +# 1147952, don't free gdb stacktrace on method failure +Patch11: satyr-0.13-dont-free-gdb-stacktrace.patch + +# 1142346, better handling of infinite recursion +Patch12: satyr-0.13-better-inf-recursion-handling.patch + +# 1210599, add functionality to generate a backtrace without saving a coredump +Patch13: satyr-0.13-fulfill-missing-values-in-core-frames.patch +Patch14: satyr-0.13-unwind-minor-refactoring.patch +Patch15: satyr-0.13-support-unwinding-from-core-hook.patch +Patch16: satyr-0.13-debug-unwinding-from-core-hook-using-satyr-binary.patch +Patch17: satyr-0.13-disable-hook-unwind-on-kernels-w-o-PTRACE_SEIZE.patch +Patch18: satyr-0.13-abrt-refactorize-unwinding-from-core-hook.patch +Patch19: satyr-0.13-core_unwind-fix-the-missing-frame-build_id-and-file.patch + +# 1334604, add support for Ruby +Patch20: satyr-0.13-Add-support-for-Ruby-report-type.patch +Patch21: satyr-0.13-python-add-Ruby-support.patch + +# 1332869, actualize list of normalization function in satyr +Patch22: satyr-0.13-normalize-extend-xorg-blacklist.patch +Patch23: satyr-0.13-normalization-additional-X-GDK-functions.patch +Patch24: satyr-0.13-normalization-add-glibc-__assert_fail_base.patch +Patch25: satyr-0.13-normalization-add-glibc-__libc_fatal.patch +Patch26: satyr-0.13-normalization-normalize-out-exit-frames.patch +Patch27: satyr-0.13-normalization-actualize-list-of-functions.patch + +# 1334604, add support for Ruby testsuite fix +Patch28: satyr-0.13-tests-fix-failure-on-gcc5-on-x86_64.patch + +# 1336390, fix defects found by coverity +Patch29: satyr-0.13-Fix-defects-found-by-coverity.patch +Patch30: satyr-0.13-Check-the-return-value-of-sr_parse_char_cspan.patch + +# 1342469, support for VARIANT and VARIANT_ID +Patch31: satyr-0.13-os-add-support-for-OS-Variant.patch + +# 1260074, Incorrectly unwinding core_backtrace for stack overflow (aarch64) +Patch32: satyr-0.13-Honor-frame-number-limit-in-GDB-core-unwinder.patch +Patch33: satyr-0.13-testsuite-add-test-for-limit-frame-number-in-GDB-cor.patch + +%description +Satyr is a library that can be used to create and process microreports. +Microreports consist of structured data suitable to be analyzed in a fully +automated manner, though they do not necessarily contain sufficient information +to fix the underlying problem. The reports are designed not to contain any +potentially sensitive data to eliminate the need for review before submission. +Included is a tool that can create microreports and perform some basic +operations on them. + +%package devel +Summary: Development libraries for %{name} +Group: Development/Libraries +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description devel +Development libraries and headers for %{name}. + +%package python +Summary: Python bindings for %{name} +Group: Development/Libraries +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description python +Python bindings for %{name}. + +%prep +# http://www.rpm.org/wiki/PackagerDocs/Autosetup +# Default '__scm_apply_git' is 'git apply && git commit' but this workflow +# doesn't allow us to create a new file within a patch, so we have to use +# 'git am' (see /usr/lib/rpm/macros for more details) +%define __scm_apply_git(qp:m:) %{__git} am +%autosetup -S git + +%build +autoreconf + +%configure \ +%if ! %{?enable_python_manpage} + --disable-python-manpage \ +%endif + --disable-static + +make %{?_smp_mflags} + +%install +make install DESTDIR=%{buildroot} + +# Remove all libtool archives (*.la) from modules directory. +find %{buildroot} -name "*.la" | xargs rm -- + +%check +make check || { + # find and print the logs of failed test + # do not cat tests/testsuite.log because it contains a lot of bloat + find tests -name "testsuite.log" -print -exec cat '{}' \; + exit 1 +} + + +%post -p /sbin/ldconfig +%postun -p /sbin/ldconfig + +%files +%doc README NEWS COPYING +%{_bindir}/satyr +%{_mandir}/man1/%{name}.1* +%{_libdir}/lib*.so.* + +%files devel +%{_includedir}/* +%{_libdir}/lib*.so +%{_libdir}/pkgconfig/* + +%files python +%dir %{python_sitearch}/%{name} +%{python_sitearch}/%{name}/* + +%if %{?enable_python_manpage} +%{_mandir}/man3/satyr-python.3* +%endif + +%changelog +* Tue Jun 19 2018 Matej Marusak - 0.13-15 +- Honor frame number limit in GDB core unwinder + - Related: #1260074 + +* Mon Jun 06 2016 Matej Habrnal - 0.13-14 +- add support for OS Variant + - Related: #1342469 + +* Thu May 12 2016 Matej Habrnal - 0.13-13 +- add support for Ruby + - Related: #1334604 +- actualize list of normalization function in satyr + - Related: #1332869 +- fix defects found by coverity + - Related: #1336390 + +* Wed Sep 9 2015 Richard Marko - 0.13-12 +- apply last patch + - Related: #1210599 + +* Wed Sep 9 2015 Richard Marko - 0.13-11 +- core unwind: fix the missing frame build_id and file_name + - Related: #1210599 + +* Fri Jul 17 2015 Richard Marko - 0.13-10 +- leave saving of core backtrace to abrt hook + - Related: #1210599 + +* Tue Jun 23 2015 Richard Marko - 0.13-9 +- Add functionality to generate a backtrace without saving a coredump + - Resolves: #1210599 + +* Wed Nov 19 2014 Martin Milata - 0.13-8 +- Better handling of stacktraces with infinite recursion + - Resolves: #1142346 + +* Fri Oct 03 2014 Martin Milata - 0.13-7 +- Don't free GDB stacktrace on error + - Resolves: #1147952 + +* Fri Oct 03 2014 Martin Milata - 0.13-6 +- Ignore suppressed exceptions in the Java exception parser + - Resolves: #1034857 + +* Thu Sep 18 2014 Martin Milata - 0.13-5 +- Fix minor bugs found by static analyzers + - Resolves: #1142856 +- Return empty duphash for koopses with no reliable frames + - Resolves: #1123262 +- Fix parsing of python SyntaxError exceptions + - Resolves: #1142339 +- Fix parsing of ppc64 gdb stacktraces + - Resolves: #1142338 +- Limit the depth of generated stacktrace to avoid huge reports + - Resolves: #1142346 +- Add authentication support to uReport, needed for reporting to customer portal + - Resolves: #1139555 + +* Fri Jan 24 2014 Daniel Mach - 0.13-4 +- Mass rebuild 2014-01-24 + +* Wed Jan 22 2014 Martin Milata 0.13-3 +- Fix build with elfutils unwinder + - Resolves: #1051569 + +* Tue Jan 14 2014 Martin Milata 0.13-2 +- Use elfutils unwinder + - Resolves: #1051569 +- Disable function fingerprinting + - Resolves: #1052402 + +* Tue Jan 07 2014 Martin Milata 0.13-1 +- Rebase to satyr-0.13 + - Resolves: #1040900 +- Includes patch to build against elfutils-0.158 + +* Fri Dec 27 2013 Daniel Mach - 0.9-3 +- Mass rebuild 2013-12-27 + +* Wed Dec 04 2013 Martin Milata 0.9-2 +- Fix malformed JSON for some Java and koops reports + - Resolves: #1035317 + - Resolves: #1036790 +- Fix memory leak in RPM handling + - Resolves: #1016780 +- Check for unsigned overflows + - Resolves: #1034869 + +* Wed Sep 11 2013 Jakub Filak 0.9-1 +- New upstream version + - Enrich koops uReport data with koops text and kernel version. + - Improve koops modules handling. + +* Wed Aug 28 2013 Richard Marko 0.8-1 +- New upstream version + - Added support for json de/serialization of reports and stacktraces. + - Library version number increased, as the interface changed since the last release + +* Mon Aug 26 2013 Martin Milata 0.7-1 +- New upstream version + - Fix couple of crashes (#997076, #994747) + +* Mon Jul 29 2013 Martin Milata 0.6-1 +- New upstream version + - Do not export internal function symbols. + +* Thu Jul 25 2013 Martin Milata 0.5-2 +- Remove libunwind dependency altogether, always use GDB for unwinding. + +* Thu Jul 25 2013 Jakub Filak 0.5-1 +- Added function that creates core stacktrace from GDB output. Several bugfixes. + +* Tue Jul 09 2013 Martin Milata 0.4-2 +- Fix failing tests (failure manifests only on s390x) + +* Mon Jul 08 2013 Martin Milata 0.4-1 +- New upstream version + - Added features needed by ABRT + - Support for uReport2 + - Major C and Python API changes +- Patch for python-2.6 compatibility + +* Tue Apr 02 2013 Dan HorĂ¡k 0.3-2 +- libunwind exists only on selected arches + +* Mon Mar 25 2013 Martin Milata 0.3-1 +- New upstream version + - Bug fixes + - Build fixes for older systems +- Do not require libunwind on rhel + +* Mon Mar 18 2013 Martin Milata 0.2-1 +- Documentation and spec cleanup +- Build fixes (build against RPM) + +* Mon Aug 30 2010 Karel Klic 0.1-1 +- Upstream package spec file