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-elfutils-0.158.patch b/SOURCES/satyr-0.13-elfutils-0.158.patch index 715b329..fafd6a1 100644 --- a/SOURCES/satyr-0.13-elfutils-0.158.patch +++ b/SOURCES/satyr-0.13-elfutils-0.158.patch @@ -1,3 +1,13 @@ +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 @@ -11,3 +21,6 @@ index d0c7aec..7910254 100644 { set_error_dwfl("dwfl_core_file_report"); goto fail_dwfl; +-- +1.8.3.1 + 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-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-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-unwinder-refresh-config-h.patch b/SOURCES/satyr-0.13-unwinder-refresh-config-h.patch index 9a1abac..735c3ca 100644 --- a/SOURCES/satyr-0.13-unwinder-refresh-config-h.patch +++ b/SOURCES/satyr-0.13-unwinder-refresh-config-h.patch @@ -1,7 +1,18 @@ -diff -ur satyr-0.13/configure satyr-0.13-patched/configure ---- satyr-0.13/configure 2014-01-22 18:44:45.710649231 +0100 -+++ satyr-0.13-patched/configure 2014-01-22 18:40:12.606671169 +0100 -@@ -13261,10 +13255,10 @@ +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 @@ -15,9 +26,10 @@ diff -ur satyr-0.13/configure satyr-0.13-patched/configure fi -diff -ur satyr-0.13/lib/config.h.in satyr-0.13-patched/lib/config.h.in ---- satyr-0.13/lib/config.h.in 2014-01-22 18:44:45.796649204 +0100 -+++ satyr-0.13-patched/lib/config.h.in 2014-01-22 18:40:13.001671173 +0100 +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 @@ -29,3 +41,6 @@ diff -ur satyr-0.13/lib/config.h.in satyr-0.13-patched/lib/config.h.in /* Define to 1 if you have the header file. */ #undef HAVE_ELFUTILS_LIBDWFL_H +-- +1.8.3.1 + diff --git a/SPECS/satyr.spec b/SPECS/satyr.spec index 41b18e5..f0cf641 100644 --- a/SPECS/satyr.spec +++ b/SPECS/satyr.spec @@ -19,7 +19,7 @@ Name: satyr Version: 0.13 -Release: 12%{?dist} +Release: 14%{?dist} Summary: Tools to create anonymous, machine-friendly problem reports Group: System Environment/Libraries License: GPLv2+ @@ -38,6 +38,11 @@ BuildRequires: gcc-c++ 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 @@ -79,6 +84,28 @@ 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 + %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 @@ -105,29 +132,16 @@ Requires: %{name}%{?_isa} = %{version}-%{release} Python bindings for %{name}. %prep -%setup -q -%patch0 -p1 -%patch1 -p1 -%patch2 -p1 -%patch3 -p1 -%patch4 -p1 -%patch5 -p1 -%patch6 -p1 -%patch7 -p1 -%patch8 -p1 -%patch9 -p1 -%patch10 -p1 -%patch11 -p1 -%patch12 -p1 -%patch13 -p1 -%patch14 -p1 -%patch15 -p1 -%patch16 -p1 -%patch17 -p1 -%patch18 -p1 -%patch19 -p1 +# 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 \ @@ -143,7 +157,13 @@ make install DESTDIR=%{buildroot} find %{buildroot} -name "*.la" | xargs rm -- %check -make 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 @@ -168,6 +188,18 @@ make check %endif %changelog +* 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