diff --git a/SOURCES/satyr-0.13-better-inf-recursion-handling.patch b/SOURCES/satyr-0.13-better-inf-recursion-handling.patch new file mode 100644 index 0000000..ba945f4 --- /dev/null +++ b/SOURCES/satyr-0.13-better-inf-recursion-handling.patch @@ -0,0 +1,109 @@ +From 9639948f3fdda4afcf25e7080aa87d8ad2fbeb34 Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Wed, 27 Aug 2014 13:55:50 +0200 +Subject: [SATYR PATCH] Better handling of infinite recursion + +unwind: Append threads/frames in O(1) +unwind: throw away the most recent frames +unwind: lower stacktrace length limit to 256 + +Fixes #179. + +Signed-off-by: Martin Milata +--- + lib/core_unwind_elfutils.c | 36 ++++++++++++++++++++---------------- + 1 file changed, 20 insertions(+), 16 deletions(-) + +diff --git a/lib/core_unwind_elfutils.c b/lib/core_unwind_elfutils.c +index 43d66be..bbd4813 100644 +--- a/lib/core_unwind_elfutils.c ++++ b/lib/core_unwind_elfutils.c +@@ -29,18 +29,18 @@ + #include + #include + +-#define FRAME_LIMIT 1024 ++#define FRAME_LIMIT 256 + + struct frame_callback_arg + { +- struct sr_core_thread *thread; ++ struct sr_core_frame **frames_tail; + char *error_msg; + unsigned nframes; + }; + + struct thread_callback_arg + { +- struct sr_core_stacktrace *stacktrace; ++ struct sr_core_thread **threads_tail; + char *error_msg; + }; + +@@ -70,15 +70,9 @@ frame_callback(Dwfl_Frame *frame, void *data) + return CB_STOP_UNWIND; + } + +- frame_arg->thread->frames = +- sr_core_frame_append(frame_arg->thread->frames, result); +- +- /* Avoid huge stacktraces from programs stuck in infinite recursion. */ ++ *frame_arg->frames_tail = result; ++ frame_arg->frames_tail = &result->next; + frame_arg->nframes++; +- if (frame_arg->nframes >= FRAME_LIMIT) +- { +- return CB_STOP_UNWIND; +- } + + return DWARF_CB_OK; + } +@@ -99,7 +93,7 @@ unwind_thread(Dwfl_Thread *thread, void *data) + + struct frame_callback_arg frame_arg = + { +- .thread = result, ++ .frames_tail = &(result->frames), + .error_msg = NULL, + .nframes = 0 + }; +@@ -121,14 +115,24 @@ unwind_thread(Dwfl_Thread *thread, void *data) + goto abort; + } + +- if (!error_msg && !frame_arg.thread->frames) ++ if (!error_msg && !result->frames) + { + set_error("No frames found for thread id %d", (int)result->id); + goto abort; + } + +- thread_arg->stacktrace->threads = +- sr_core_thread_append(thread_arg->stacktrace->threads, result); ++ /* Truncate the stacktrace to FRAME_LIMIT least recent frames. */ ++ while (result->frames && frame_arg.nframes > FRAME_LIMIT) ++ { ++ struct sr_core_frame *old_frame = result->frames; ++ result->frames = old_frame->next; ++ sr_core_frame_free(old_frame); ++ frame_arg.nframes--; ++ } ++ ++ *thread_arg->threads_tail = result; ++ thread_arg->threads_tail = &result->next; ++ + return DWARF_CB_OK; + + abort: +@@ -166,7 +170,7 @@ sr_parse_coredump(const char *core_file, + + struct thread_callback_arg thread_arg = + { +- .stacktrace = stacktrace, ++ .threads_tail = &(stacktrace->threads), + .error_msg = NULL + }; + +-- +1.9.3 + diff --git a/SOURCES/satyr-0.13-dont-free-gdb-stacktrace.patch b/SOURCES/satyr-0.13-dont-free-gdb-stacktrace.patch new file mode 100644 index 0000000..163ffb9 --- /dev/null +++ b/SOURCES/satyr-0.13-dont-free-gdb-stacktrace.patch @@ -0,0 +1,30 @@ +From 969aedadec7f1f693f53ddaf8dcb1f1d59d5ac72 Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Tue, 23 Sep 2014 18:22:27 +0200 +Subject: [SATYR PATCH] Don't free gdb stacktrace if no crash thread found + +Possibly related to rhbz#1145697. + +Signed-off-by: Martin Milata +--- + lib/gdb_stacktrace.c | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/lib/gdb_stacktrace.c b/lib/gdb_stacktrace.c +index 8e73e21..3b2465a 100644 +--- a/lib/gdb_stacktrace.c ++++ b/lib/gdb_stacktrace.c +@@ -558,10 +558,7 @@ sr_gdb_stacktrace_to_short_text(struct sr_gdb_stacktrace *stacktrace, + crash_thread = sr_gdb_stacktrace_find_crash_thread(stacktrace); + + if (!crash_thread) +- { +- sr_gdb_stacktrace_free(stacktrace); + return NULL; +- } + + struct sr_gdb_thread *optimized_thread + = sr_gdb_thread_get_optimized(crash_thread, stacktrace->libs, +-- +1.9.3 + diff --git a/SOURCES/satyr-0.13-java-suppressed-exceptions.patch b/SOURCES/satyr-0.13-java-suppressed-exceptions.patch new file mode 100644 index 0000000..63824de --- /dev/null +++ b/SOURCES/satyr-0.13-java-suppressed-exceptions.patch @@ -0,0 +1,339 @@ +From 32085a78c063d80cc152f341343a87c8cfecfd2f Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Thu, 2 Oct 2014 16:46:30 +0200 +Subject: [SATYR PATCH] java: ignore suppressed exceptions + +Java exceptions can form a tree - every exception can have reference to +exception that caused it and to a list of exceptions that were +suppressed during handling[1]. We cannot take the suppressed exceptions +into account without changing the uReport format, therefore this commit +makes the java parser ignore them. + +[1] http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html + +Fixes rhbz#1034857. + +Signed-off-by: Martin Milata +--- + include/utils.h | 3 + + lib/java_frame.c | 26 ++++-- + lib/utils.c | 16 ++++ + tests/java_stacktraces/java-04 | 185 +++++++++++++++++++++++++++++++++++++++++ + tests/python/java.py | 12 +++ + 5 files changed, 234 insertions(+), 8 deletions(-) + create mode 100644 tests/java_stacktraces/java-04 + +diff --git a/include/utils.h b/include/utils.h +index 8d0a6ec..1c7984b 100644 +--- a/include/utils.h ++++ b/include/utils.h +@@ -378,6 +378,9 @@ sr_skip_whitespace(const char *s); + char * + sr_skip_non_whitespace(const char *s); + ++bool ++sr_skip_to_next_line_location(const char **s, int *line, int *column); ++ + /** + * Emit a string of hex representation of bytes. + */ +diff --git a/lib/java_frame.c b/lib/java_frame.c +index da9f26b..ee97572 100644 +--- a/lib/java_frame.c ++++ b/lib/java_frame.c +@@ -28,6 +28,7 @@ + #include "stacktrace.h" + #include "internal_utils.h" + #include ++#include + #include + + #define SR_JF_MARK_NATIVE_METHOD "Native Method" +@@ -337,6 +338,10 @@ sr_java_frame_parse_exception(const char **input, + if (strncmp("... ", cursor, strlen("... ")) == 0) + goto current_exception_done; + ++ /* Suppressed exceptions follow after the end of current exception */ ++ if (strncmp("Suppressed: ", cursor, strlen("Suppressed: ")) == 0) ++ goto current_exception_done; ++ + /* The top most exception does not have '...' at its end */ + if (strncmp("Caused by: ", cursor, strlen("Caused by: ")) == 0) + goto parse_inner_exception; +@@ -363,19 +368,24 @@ sr_java_frame_parse_exception(const char **input, + goto exception_parsing_successful; + + current_exception_done: +- sr_location_add(location, 0, sr_skip_char_cspan(&cursor, "\n")); +- +- if (*cursor == '\n') +- { +- ++cursor; +- /* this adds one line */ +- sr_location_add(location, 2, 0); +- } ++ sr_skip_to_next_line_location(&cursor, &location->line, &location->column); + + mark = cursor; + cursor = sr_skip_whitespace(mark); + sr_location_add(location, 0, cursor - mark); + ++ if (strncmp("Suppressed: ", cursor, strlen("Suppressed: ")) == 0) ++ { ++ /* Skip all lines related to the suppressed exception. We can do ++ * this by skipping all lines that begin with a whitespace - the ++ * main exception chain always begins without preceding whitespace. ++ */ ++ sr_skip_to_next_line_location(&cursor, &location->line, &location->column); ++ ++ while (cursor && isspace(*cursor)) ++ sr_skip_to_next_line_location(&cursor, &location->line, &location->column); ++ } ++ + if (strncmp("Caused by: ", cursor, strlen("Caused by: ")) == 0) + { + parse_inner_exception: +diff --git a/lib/utils.c b/lib/utils.c +index 3c036f3..fa3c0a0 100644 +--- a/lib/utils.c ++++ b/lib/utils.c +@@ -656,6 +656,22 @@ sr_skip_non_whitespace(const char *s) + return (char *) s; + } + ++bool ++sr_skip_to_next_line_location(const char **s, int *line, int *column) ++{ ++ *column += sr_skip_char_cspan(s, "\n"); ++ ++ if (*s && **s == '\n') ++ { ++ *column = 0; ++ (*line)++; ++ (*s)++; ++ return true; ++ } ++ ++ return false; ++} ++ + char * + sr_bin2hex(char *dst, const char *str, int count) + { +diff --git a/tests/java_stacktraces/java-04 b/tests/java_stacktraces/java-04 +new file mode 100644 +index 0000000..6968eb3 +--- /dev/null ++++ b/tests/java_stacktraces/java-04 +@@ -0,0 +1,185 @@ ++Exception in thread "main" java.lang.RuntimeException: yes ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.main(WontCatchSuppressedException.java:35) ++Caused by: java.lang.RuntimeException: yes ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 1 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 1 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 3 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 3 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 3 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 5 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 1 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 3 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 3 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 3 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 5 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 5 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 4 more ++Caused by: java.lang.RuntimeException: yes ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 2 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 2 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:29) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 2 more ++ Caused by: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 4 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 4 more ++Caused by: java.lang.RuntimeException: yes ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:27) ++ ... 3 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 3 more ++ Suppressed: java.lang.RuntimeException: no ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:23) ++ at WontCatchSuppressedException.close(WontCatchSuppressedException.java:16) ++ at WontCatchSuppressedException.die(WontCatchSuppressedException.java:28) ++ ... 3 more +diff --git a/tests/python/java.py b/tests/python/java.py +index 3b85c18..1156fb6 100755 +--- a/tests/python/java.py ++++ b/tests/python/java.py +@@ -152,6 +152,18 @@ class TestJavaStacktrace(BindingsTestCase): + def test_hash(self): + self.assertHashable(self.trace) + ++ def test_suppressed(self): ++ contents = load_input_contents('../java_stacktraces/java-04') ++ trace = satyr.JavaStacktrace(contents) ++ ++ names = 4*['java.lang.RuntimeException', 'WontCatchSuppressedException.die', 'WontCatchSuppressedException.die'] ++ names[-1] = 'WontCatchSuppressedException.main' ++ msgs = 4*['yes', None, None] ++ ++ for frame, name, msg in zip(trace.threads[0].frames, names, msgs): ++ self.assertEqual(frame.name, name) ++ self.assertEqual(frame.message, msg) ++ + class TestJavaThread(BindingsTestCase): + def setUp(self): + self.thread = satyr.JavaStacktrace(contents).threads[0] +-- +1.9.3 + diff --git a/SOURCES/satyr-0.13-koops-unreliable-frames.patch b/SOURCES/satyr-0.13-koops-unreliable-frames.patch new file mode 100644 index 0000000..a110e00 --- /dev/null +++ b/SOURCES/satyr-0.13-koops-unreliable-frames.patch @@ -0,0 +1,149 @@ +From fa978bdd2c3a576df357ce7905b11b73810dc100 Mon Sep 17 00:00:00 2001 +From: Jakub Filak +Date: Fri, 20 Jun 2014 20:42:00 +0200 +Subject: [SATYR PATCH 2/6] Fix the compat version of duphash generation + +sr_thread_get_duphash() must return NULL for oopses having no reliable +frame. + +Signed-off-by: Jakub Filak +--- + lib/generic_thread.c | 7 +++- + tests/koops_frame.at | 1 - + tests/koops_stacktrace.at | 83 +++++++++++++++++++++++++++++++++++++++++++++++ + tests/python/koops.py | 1 + + 4 files changed, 90 insertions(+), 2 deletions(-) + +diff --git a/lib/generic_thread.c b/lib/generic_thread.c +index b219f26..59fff1b 100644 +--- a/lib/generic_thread.c ++++ b/lib/generic_thread.c +@@ -262,7 +262,12 @@ sr_thread_get_duphash(struct sr_thread *thread, int nframes, char *prefix, + nframes--; + } + +- if (flags & SR_DUPHASH_NOHASH) ++ if ((flags & SR_DUPHASH_KOOPS_COMPAT) && strbuf->len == 0) ++ { ++ sr_strbuf_free(strbuf); ++ ret = NULL; ++ } ++ else if (flags & SR_DUPHASH_NOHASH) + ret = sr_strbuf_free_nobuf(strbuf); + else + { +diff --git a/tests/koops_frame.at b/tests/koops_frame.at +index be2cb11..4abc32a 100644 +--- a/tests/koops_frame.at ++++ b/tests/koops_frame.at +@@ -288,4 +288,3 @@ main(void) + return 0; + } + ]]) +- +diff --git a/tests/koops_stacktrace.at b/tests/koops_stacktrace.at +index 404d53c..5e17ca7 100644 +--- a/tests/koops_stacktrace.at ++++ b/tests/koops_stacktrace.at +@@ -730,3 +730,86 @@ main(void) + return 0; + } + ]]) ++ ++## --------------------- ## ++## sr_thread_get_duphash ## ++## --------------------- ## ++ ++AT_TESTFUN([sr_thread_get_duphash], ++[[ ++#include "koops/stacktrace.h" ++#include "koops/frame.h" ++#include "thread.h" ++#include "utils.h" ++#include ++#include ++#include ++ ++void generate_and_test(struct sr_thread *thread, int flags, const char *expected) ++{ ++ char *hash = sr_thread_get_duphash(thread, 1, NULL, flags); ++ ++ if (NULL == hash && NULL != expected) ++ { ++ fprintf(stderr, "'%s' != NULL\n", expected); ++ assert(!"NULL was NOT expected"); ++ } ++ ++ if (NULL != hash && NULL == expected) ++ { ++ fprintf(stderr, "NULL != '%s'\n", hash); ++ assert(!"NULL was expected"); ++ } ++ ++ if (NULL == hash && NULL == expected) ++ return; ++ ++ if (strcmp(hash, expected) != 0) ++ { ++ fprintf(stderr, "'%s' != '%s'\n", expected, hash); ++ //assert(!"Expected and hash are not equal"); ++ } ++ ++ free(hash); ++} ++ ++void test(struct sr_thread *thread, const char *expected, const char *expected_compat) ++{ ++ generate_and_test(thread, SR_DUPHASH_NOHASH, expected); ++ fprintf(stderr, "COMPAT\n"); ++ generate_and_test(thread, SR_DUPHASH_NOHASH|SR_DUPHASH_KOOPS_COMPAT, expected_compat); ++} ++ ++int main(void) ++{ ++ struct sr_koops_stacktrace *stacktrace = sr_koops_stacktrace_new(); ++ struct sr_thread *thread = (struct sr_thread *)stacktrace; ++ ++ struct sr_koops_frame *frame = sr_koops_frame_new(); ++ stacktrace->frames = frame; ++ ++ frame->address = 0xDEADBEAF; ++ ++ fprintf(stderr, "Checkpoint 1\n"); ++ frame->reliable = 1; ++ test(thread, "Thread\n0xdeadbeaf\n", "0xdeadbeaf\n"); ++ ++ fprintf(stderr, "Checkpoint 2\n"); ++ frame->reliable = 0; ++ test(thread, "Thread\n0xdeadbeaf\n", NULL); ++ ++ frame->address = 0xDEADBEAF; ++ frame->function_name = sr_strdup("omg_warn_slowpath_common"); ++ stacktrace->frames = frame; ++ ++ fprintf(stderr, "Checkpoint 3\n"); ++ frame->reliable = 1; ++ test(thread, "Thread\nomg_warn_slowpath_common\n", "omg_warn_slowpath_common\n"); ++ ++ fprintf(stderr, "Checkpoint 4\n"); ++ frame->reliable = 0; ++ test(thread, "Thread\nomg_warn_slowpath_common\n", NULL); ++ ++ return 0; ++} ++]]) +diff --git a/tests/python/koops.py b/tests/python/koops.py +index 3f8fe57..0686b70 100755 +--- a/tests/python/koops.py ++++ b/tests/python/koops.py +@@ -75,6 +75,7 @@ class TestKerneloops(BindingsTestCase): + compat_expected_plain) + self.assertEqual(self.koops.get_duphash(flags=satyr.DUPHASH_KOOPS_COMPAT, frames=6), + '5718b3a86c64e7bed5e8ead08ae3084e447ddbee') ++ self.assertRaises(RuntimeError, self.koops.get_duphash, flags=(satyr.DUPHASH_NOHASH|satyr.DUPHASH_KOOPS_COMPAT), frames=-1) + + def test_crash_thread(self): + self.assertTrue(self.koops.crash_thread is self.koops) +-- +1.9.3 + diff --git a/SOURCES/satyr-0.13-limit-stacktrace-depth.patch b/SOURCES/satyr-0.13-limit-stacktrace-depth.patch new file mode 100644 index 0000000..4015dbf --- /dev/null +++ b/SOURCES/satyr-0.13-limit-stacktrace-depth.patch @@ -0,0 +1,66 @@ +From 1c223116114ddf80609e79ec7ada0ec4a5a463f3 Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Wed, 27 Aug 2014 12:51:41 +0200 +Subject: [SATYR PATCH 5/6] unwind: limit the number of frames unwound + +Fixes rhbz#1133907. + +Signed-off-by: Martin Milata +--- + lib/core_unwind_elfutils.c | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +diff --git a/lib/core_unwind_elfutils.c b/lib/core_unwind_elfutils.c +index 6b904c7..43d66be 100644 +--- a/lib/core_unwind_elfutils.c ++++ b/lib/core_unwind_elfutils.c +@@ -29,10 +29,13 @@ + #include + #include + ++#define FRAME_LIMIT 1024 ++ + struct frame_callback_arg + { + struct sr_core_thread *thread; + char *error_msg; ++ unsigned nframes; + }; + + struct thread_callback_arg +@@ -41,7 +44,7 @@ struct thread_callback_arg + char *error_msg; + }; + +-static int CB_STOP_UNWIND = DWARF_CB_ABORT+1; ++static const int CB_STOP_UNWIND = DWARF_CB_ABORT+1; + + static int + frame_callback(Dwfl_Frame *frame, void *data) +@@ -70,6 +73,13 @@ frame_callback(Dwfl_Frame *frame, void *data) + frame_arg->thread->frames = + sr_core_frame_append(frame_arg->thread->frames, result); + ++ /* Avoid huge stacktraces from programs stuck in infinite recursion. */ ++ frame_arg->nframes++; ++ if (frame_arg->nframes >= FRAME_LIMIT) ++ { ++ return CB_STOP_UNWIND; ++ } ++ + return DWARF_CB_OK; + } + +@@ -90,7 +100,8 @@ unwind_thread(Dwfl_Thread *thread, void *data) + struct frame_callback_arg frame_arg = + { + .thread = result, +- .error_msg = NULL ++ .error_msg = NULL, ++ .nframes = 0 + }; + + int ret = dwfl_thread_getframes(thread, frame_callback, &frame_arg); +-- +1.9.3 + diff --git a/SOURCES/satyr-0.13-ppc64-backtrace-parsing.patch b/SOURCES/satyr-0.13-ppc64-backtrace-parsing.patch new file mode 100644 index 0000000..fe5ac75 --- /dev/null +++ b/SOURCES/satyr-0.13-ppc64-backtrace-parsing.patch @@ -0,0 +1,153 @@ +From 0fcd63190deeb0afa2ad04826da99e5632f9d517 Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Mon, 14 Jul 2014 16:53:12 +0200 +Subject: [SATYR PATCH 4/6] gdb: fix parsing of ppc64 stacktraces + +Fixes rhbz#1119072. + +Signed-off-by: Martin Milata +--- + lib/gdb_frame.c | 3 +- + tests/gdb_stacktrace.at | 35 ++++++++++++++++++++ + tests/gdb_stacktraces/rhbz-1119072 | 67 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 104 insertions(+), 1 deletion(-) + create mode 100644 tests/gdb_stacktraces/rhbz-1119072 + +diff --git a/lib/gdb_frame.c b/lib/gdb_frame.c +index 57e9479..e449b30 100644 +--- a/lib/gdb_frame.c ++++ b/lib/gdb_frame.c +@@ -559,12 +559,13 @@ sr_gdb_frame_parse_function_name(const char **input, + /* First character: + '~' for destructor + '*' for ???? ++ '.' on ppc64 + '_a-zA-Z' for mangled/nonmangled function name + '(' to start "(anonymous namespace)::" or something + */ + char first; + char *namechunk; +- if (sr_parse_char_limited(&local_input, "~*_" SR_alpha, &first)) ++ if (sr_parse_char_limited(&local_input, "~*._" SR_alnum, &first)) + { + /* If it's a start of 'o'perator, put the 'o' back! */ + if (first == 'o') +diff --git a/tests/gdb_stacktrace.at b/tests/gdb_stacktrace.at +index 8832cb8..f626417 100644 +--- a/tests/gdb_stacktrace.at ++++ b/tests/gdb_stacktrace.at +@@ -247,3 +247,38 @@ main(void) + return 0; + } + ]]) ++ ++## ----------------------------- ## ++## sr_gdb_stacktrace_parse_ppc64 ## ++## ----------------------------- ## ++AT_TESTFUN([sr_gdb_stacktrace_parse_ppc64], ++[[ ++#include "gdb/stacktrace.h" ++#include "gdb/thread.h" ++#include "gdb/frame.h" ++#include "location.h" ++#include "utils.h" ++#include ++#include ++ ++int ++main(void) ++{ ++ /* Check that satyr is able to parse backtrace with missing Thread header */ ++ struct sr_location location; ++ sr_location_init(&location); ++ char *error_message; ++ char *full_input = sr_file_to_string("../../gdb_stacktraces/rhbz-1119072", &error_message); ++ assert(full_input); ++ char *input = full_input; ++ struct sr_gdb_stacktrace *stacktrace = sr_gdb_stacktrace_parse(&input, &location); ++ assert(stacktrace); ++ assert(5 == sr_gdb_stacktrace_get_thread_count(stacktrace)); ++ struct sr_gdb_thread *thread = stacktrace->threads; ++ assert(7 == sr_thread_frame_count(thread)); ++ struct sr_gdb_frame *frame = thread->frames; ++ assert(0 == strcmp(frame->function_name, ".pthread_cond_timedwait")); ++ sr_gdb_stacktrace_free(stacktrace); ++ return 0; ++} ++]]) +diff --git a/tests/gdb_stacktraces/rhbz-1119072 b/tests/gdb_stacktraces/rhbz-1119072 +new file mode 100644 +index 0000000..2aca936 +--- /dev/null ++++ b/tests/gdb_stacktraces/rhbz-1119072 +@@ -0,0 +1,67 @@ ++[New LWP 2271] ++[New LWP 2374] ++[New LWP 2325] ++[New LWP 2360] ++[New LWP 2375] ++[Thread debugging using libthread_db enabled] ++Using host libthread_db library "/lib64/libthread_db.so.1". ++Core was generated by `caja '. ++Program terminated with signal SIGTRAP, Trace/breakpoint trap. ++#0 0x000000803a327a54 in .raise () from /usr/lib64/libpthread.so.0 ++ ++Thread 5 (Thread 0x3fffa0ebeed0 (LWP 2375)): ++#0 0x000000803a321f24 in .pthread_cond_timedwait () from /usr/lib64/libpthread.so.0 ++#1 0x000000803a748054 in .g_cond_wait_until () from /usr/lib64/libglib-2.0.so.0 ++#2 0x000000803a6ac2c8 in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#3 0x000000803a720530 in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#4 0x000000803a71f3f8 in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#5 0x000000803a31c31c in .start_thread () from /usr/lib64/libpthread.so.0 ++#6 0x000000803a20c150 in .__clone () from /usr/lib64/libc.so.6 ++ ++Thread 4 (Thread 0x3fffa208eed0 (LWP 2360)): ++#0 0x000000803a1fd130 in .__GI___poll () from /usr/lib64/libc.so.6 ++#1 0x000000803a6fe0b0 in .g_poll () from /usr/lib64/libglib-2.0.so.0 ++#2 0x000000803a6e9ef4 in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#3 0x000000803a6ea610 in .g_main_loop_run () from /usr/lib64/libglib-2.0.so.0 ++#4 0x000000803ae658c8 in 0000090f.plt_call.g_io_channel_flush+0 () from /usr/lib64/libgio-2.0.so.0 ++#5 0x000000803a71f3f8 in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#6 0x000000803a31c31c in .start_thread () from /usr/lib64/libpthread.so.0 ++#7 0x000000803a20c150 in .__clone () from /usr/lib64/libc.so.6 ++ ++Thread 3 (Thread 0x3fffa288eed0 (LWP 2325)): ++#0 0x000000803a1fd130 in .__GI___poll () from /usr/lib64/libc.so.6 ++#1 0x000000803a6fe0b0 in .g_poll () from /usr/lib64/libglib-2.0.so.0 ++#2 0x000000803a6e9ef4 in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#3 0x000000803a6ea0a4 in .g_main_context_iteration () from /usr/lib64/libglib-2.0.so.0 ++#4 0x00003fffa28ba8dc in 00000010.plt_call.g_hash_table_size+0 () from /usr/lib64/gio/modules/libdconfsettings.so ++#5 0x000000803a71f3f8 in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#6 0x000000803a31c31c in .start_thread () from /usr/lib64/libpthread.so.0 ++#7 0x000000803a20c150 in .__clone () from /usr/lib64/libc.so.6 ++ ++Thread 2 (Thread 0x3fffa16beed0 (LWP 2374)): ++#0 0x000000803a1fd130 in .__GI___poll () from /usr/lib64/libc.so.6 ++#1 0x000000803a6fe0b0 in .g_poll () from /usr/lib64/libglib-2.0.so.0 ++#2 0x000000803a6e9ef4 in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#3 0x000000803a6ea0a4 in .g_main_context_iteration () from /usr/lib64/libglib-2.0.so.0 ++#4 0x000000803a6ea15c in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#5 0x000000803a71f3f8 in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#6 0x000000803a31c31c in .start_thread () from /usr/lib64/libpthread.so.0 ++#7 0x000000803a20c150 in .__clone () from /usr/lib64/libc.so.6 ++ ++Thread 1 (Thread 0x3fffa9210490 (LWP 2271)): ++#0 0x000000803a327a54 in .raise () from /usr/lib64/libpthread.so.0 ++#1 0x000000803a6f3dd8 in .g_logv () from /usr/lib64/libglib-2.0.so.0 ++#2 0x000000803a6f3ef8 in .g_log () from /usr/lib64/libglib-2.0.so.0 ++#3 0x000000803cd0c230 in 000002cd.plt_call.XmbTextExtents+0 () from /usr/lib64/libgdk-x11-2.0.so.0 ++#4 0x000000803a9e0870 in ._XError () from /usr/lib64/libX11.so.6 ++#5 0x000000803a9dc398 in 00000e9e.plt_call._XimXTransGetHostname+0 () from /usr/lib64/libX11.so.6 ++#6 0x000000803a9dc514 in 00000e9e.plt_call._XimXTransGetHostname+0 () from /usr/lib64/libX11.so.6 ++#7 0x000000803a9dd19c in ._XEventsQueued () from /usr/lib64/libX11.so.6 ++#8 0x000000803a9c8d6c in .XPending () from /usr/lib64/libX11.so.6 ++#9 0x000000803ccfbd74 in 000002cd.plt_call.XmbTextExtents+0 () from /usr/lib64/libgdk-x11-2.0.so.0 ++#10 0x000000803ccfbf58 in 000002cd.plt_call.XmbTextExtents+0 () from /usr/lib64/libgdk-x11-2.0.so.0 ++#11 0x000000803a6e9124 in .g_main_context_prepare () from /usr/lib64/libglib-2.0.so.0 ++#12 0x000000803a6e9d7c in 000000ca.plt_call.strncasecmp@@GLIBC_2.3+0 () from /usr/lib64/libglib-2.0.so.0 ++#13 0x000000803a6ea610 in .g_main_loop_run () from /usr/lib64/libglib-2.0.so.0 ++#14 0x000000803c839050 in .gtk_main () from /usr/lib64/libgtk-x11-2.0.so.0 ++#15 0x0000000010051054 in .main () +-- +1.9.3 + diff --git a/SOURCES/satyr-0.13-python-exceptions.patch b/SOURCES/satyr-0.13-python-exceptions.patch new file mode 100644 index 0000000..f028fea --- /dev/null +++ b/SOURCES/satyr-0.13-python-exceptions.patch @@ -0,0 +1,273 @@ +From 7fa846eea1f9f1a541ec061f272d4f82d4aee428 Mon Sep 17 00:00:00 2001 +From: Jakub Filak +Date: Wed, 25 Jun 2014 17:28:00 +0200 +Subject: [SATYR PATCH 3/6] Fix parsing of invalid syntax Python exceptions + +Closes #168 + +Signed-off-by: Jakub Filak + +mmilata: commit msg typo +--- + lib/python_frame.c | 56 +++++++++++++++++++------------ + lib/python_stacktrace.c | 69 ++++++++++++++++++++++++++++++++++---- + tests/python/python.py | 38 +++++++++++++++++++++ + tests/python_stacktraces/python-04 | 6 ++++ + tests/python_stacktraces/python-05 | 16 +++++++++ + 5 files changed, 157 insertions(+), 28 deletions(-) + create mode 100644 tests/python_stacktraces/python-04 + create mode 100644 tests/python_stacktraces/python-05 + +diff --git a/lib/python_frame.c b/lib/python_frame.c +index b62628f..b0540fe 100644 +--- a/lib/python_frame.c ++++ b/lib/python_frame.c +@@ -260,33 +260,45 @@ sr_python_frame_parse(const char **input, + + if (0 == sr_skip_string(&local_input, ", in ")) + { +- location->message = sr_asprintf("Function name separator not found."); +- return NULL; +- } +- +- location->column += strlen(", in "); ++ if (local_input[0] != '\n') ++ { ++ location->message = sr_asprintf("Function name separator not found."); ++ return NULL; ++ } + +- /* Parse function name */ +- if (!sr_parse_char_cspan(&local_input, "\n", &frame->function_name)) ++ /* The last frame of SyntaxError stack trace does not have ++ * function name on its line. For the sake of simplicity, we will ++ * believe that we are dealing with such a frame now. ++ */ ++ frame->function_name = sr_strdup("syntax"); ++ frame->special_function = true; ++ } ++ else + { +- sr_python_frame_free(frame); +- location->message = sr_asprintf("Unable to find the newline character " +- "identifying the end of function name."); ++ location->column += strlen(", in "); + +- return NULL; +- } ++ /* Parse function name */ ++ if (!sr_parse_char_cspan(&local_input, "\n", &frame->function_name)) ++ { ++ sr_python_frame_free(frame); ++ location->message = sr_asprintf("Unable to find the newline character " ++ "identifying the end of function name."); + +- location->column += strlen(frame->function_name); ++ return NULL; ++ } + +- if (strlen(frame->function_name) > 0 && +- frame->function_name[0] == '<' && +- frame->function_name[strlen(frame->function_name)-1] == '>') +- { +- frame->special_function = true; +- frame->function_name[strlen(frame->function_name)-1] = '\0'; +- char *inside = sr_strdup(frame->function_name + 1); +- free(frame->function_name); +- frame->function_name = inside; ++ location->column += strlen(frame->function_name); ++ ++ if (strlen(frame->function_name) > 0 && ++ frame->function_name[0] == '<' && ++ frame->function_name[strlen(frame->function_name)-1] == '>') ++ { ++ frame->special_function = true; ++ frame->function_name[strlen(frame->function_name)-1] = '\0'; ++ char *inside = sr_strdup(frame->function_name + 1); ++ free(frame->function_name); ++ frame->function_name = inside; ++ } + } + + sr_skip_char(&local_input, '\n'); +diff --git a/lib/python_stacktrace.c b/lib/python_stacktrace.c +index 3dc0a75..99aa52c 100644 +--- a/lib/python_stacktrace.c ++++ b/lib/python_stacktrace.c +@@ -29,6 +29,7 @@ + #include "generic_thread.h" + #include "internal_utils.h" + #include ++#include + #include + #include + +@@ -137,13 +138,48 @@ sr_python_stacktrace_parse(const char **input, + + if (!local_input) + { +- location->message = "Traceback header not found."; +- return NULL; +- } ++ /* SyntaxError stack trace of an exception thrown in the executed file ++ * conforms to the following template: ++ * invalid syntax ($file, line $number) ++ * ++ * File "$file", line $number ++ * $code ++ * ^ ++ * SyntaxError: invalid syntax ++ * ++ * for exceptions thrown from imported files, the stack trace has the ++ * regular form, except the last frame has no function name and is ++ * followed by the pointer line (^). ++ */ ++ HEADER = "invalid syntax (", ++ local_input = sr_strstr_location(*input, ++ HEADER, ++ &location->line, ++ &location->column); + +- local_input += strlen(HEADER); +- location->line += 2; +- location->column = 0; ++ if (!local_input) ++ { ++ location->message = "Traceback header not found."; ++ return NULL; ++ } ++ ++ local_input = sr_strstr_location(local_input, ++ " File \"", ++ &location->line, ++ &location->column); ++ ++ if (!local_input) ++ { ++ location->message = "Frame with invalid line not found."; ++ return NULL; ++ } ++ } ++ else ++ { ++ local_input += strlen(HEADER); ++ location->line += 2; ++ location->column = 0; ++ } + + struct sr_python_stacktrace *stacktrace = sr_python_stacktrace_new(); + +@@ -173,6 +209,27 @@ sr_python_stacktrace_parse(const char **input, + return NULL; + } + ++ bool invalid_syntax_pointer = true; ++ const char *tmp_input = local_input; ++ while (*tmp_input != '\n' && *tmp_input != '\0') ++ { ++ if (*tmp_input != ' ' && *tmp_input != '^') ++ { ++ invalid_syntax_pointer = false; ++ break; ++ } ++ ++tmp_input; ++ } ++ ++ if (invalid_syntax_pointer) ++ { ++ /* Skip line " ^" pointing to the invalid code */ ++ sr_skip_char_cspan(&local_input, "\n"); ++ ++local_input; ++ ++location->line; ++ location->column = 1; ++ } ++ + /* Parse exception name. */ + sr_parse_char_cspan(&local_input, + ":\n", +diff --git a/tests/python/python.py b/tests/python/python.py +index 146ca01..9044200 100755 +--- a/tests/python/python.py ++++ b/tests/python/python.py +@@ -143,6 +143,44 @@ class TestPythonStacktrace(BindingsTestCase): + def test_hash(self): + self.assertHashable(self.trace) + ++ def test_invalid_syntax_current_file(self): ++ trace = load_input_contents('../python_stacktraces/python-04') ++ trace = satyr.PythonStacktrace(trace) ++ ++ self.assertEqual(len(trace.frames), 1) ++ self.assertEqual(trace.exception_name, 'SyntaxError') ++ ++ f = trace.frames[0] ++ self.assertEqual(f.file_name, '/usr/bin/python3-mako-render') ++ self.assertEqual(f.function_name, "syntax") ++ self.assertEqual(f.file_line, 43) ++ self.assertEqual(f.line_contents, 'print render(data, kw)') ++ self.assertTrue(f.special_function) ++ self.assertFalse(f.special_file) ++ ++ def test_invalid_syntax_imported_file(self): ++ trace = load_input_contents('../python_stacktraces/python-05') ++ trace = satyr.PythonStacktrace(trace) ++ ++ self.assertEqual(len(trace.frames), 2) ++ self.assertEqual(trace.exception_name, 'SyntaxError') ++ ++ f = trace.frames[1] ++ self.assertEqual(f.file_name, '/usr/bin/will_python_raise') ++ self.assertEqual(f.function_name, "module") ++ self.assertEqual(f.file_line, 2) ++ self.assertEqual(f.line_contents, 'import report') ++ self.assertTrue(f.special_function) ++ self.assertFalse(f.special_file) ++ ++ f = trace.frames[0] ++ self.assertEqual(f.file_name, '/usr/lib64/python2.7/site-packages/report/__init__.py') ++ self.assertEqual(f.function_name, "syntax") ++ self.assertEqual(f.file_line, 15) ++ self.assertEqual(f.line_contents, 'def foo(:') ++ self.assertTrue(f.special_function) ++ self.assertFalse(f.special_file) ++ + class TestPythonFrame(BindingsTestCase): + def setUp(self): + self.frame = satyr.PythonStacktrace(contents).frames[-1] +diff --git a/tests/python_stacktraces/python-04 b/tests/python_stacktraces/python-04 +new file mode 100644 +index 0000000..91c3a3d +--- /dev/null ++++ b/tests/python_stacktraces/python-04 +@@ -0,0 +1,6 @@ ++invalid syntax (python3-mako-render, line 43) ++ ++ File "/usr/bin/python3-mako-render", line 43 ++ print render(data, kw) ++ ^ ++SyntaxError: invalid syntax +diff --git a/tests/python_stacktraces/python-05 b/tests/python_stacktraces/python-05 +new file mode 100644 +index 0000000..79d1b8d +--- /dev/null ++++ b/tests/python_stacktraces/python-05 +@@ -0,0 +1,16 @@ ++will_python_raise:2:: File "/usr/lib64/python2.7/site-packages/report/__init__.py", line 15 ++ ++Traceback (most recent call last): ++ File "/usr/bin/will_python_raise", line 2, in ++ import report ++ File "/usr/lib64/python2.7/site-packages/report/__init__.py", line 15 ++ def foo(: ++ ^ ++SyntaxError: invalid syntax ++ ++Local variables in innermost frame: ++__builtins__: ++__name__: '__main__' ++__file__: '/usr/bin/will_python_raise' ++__doc__: None ++__package__: None +-- +1.9.3 + diff --git a/SOURCES/satyr-0.13-static-analyzer-bugs.patch b/SOURCES/satyr-0.13-static-analyzer-bugs.patch new file mode 100644 index 0000000..af1dd34 --- /dev/null +++ b/SOURCES/satyr-0.13-static-analyzer-bugs.patch @@ -0,0 +1,137 @@ +From 5aab1d42916b93e593db632479a800b1de05d169 Mon Sep 17 00:00:00 2001 +From: Martin Milata +Date: Mon, 17 Feb 2014 12:28:04 +0100 +Subject: [SATYR PATCH 1/6] Fix minor issues found by static analyzers + +Avoid possible NULL dereferences of error_msg + +Fixes #155. + +Signed-off-by: Martin Milata +Signed-off-by: Jakub Filak + +Fix insecure string formatting + +core_unwind_elfutils.c: In function 'sr_parse_coredump': +core_unwind_elfutils.c:169:13: error: format not a string literal and no format arguments [-Werror=format-security] + set_error(thread_arg.error_msg); + ^ + +Signed-off-by: Martin Milata +--- + lib/Makefile.am | 2 +- + lib/core_unwind.c | 5 +++-- + lib/core_unwind_elfutils.c | 27 +++++++++++++-------------- + lib/core_unwind_libunwind.c | 2 +- + 4 files changed, 18 insertions(+), 18 deletions(-) + +diff --git a/lib/Makefile.am b/lib/Makefile.am +index 73fffe2..f798347 100644 +--- a/lib/Makefile.am ++++ b/lib/Makefile.am +@@ -62,7 +62,7 @@ libsatyr_conv_la_SOURCES = \ + unstrip.c \ + utils.c + +-libsatyr_conv_la_CFLAGS = -Wall -std=gnu99 -D_GNU_SOURCE -I$(top_srcdir)/include $(GLIB_CFLAGS) ++libsatyr_conv_la_CFLAGS = -Wall -Wformat=2 -std=gnu99 -D_GNU_SOURCE -I$(top_srcdir)/include $(GLIB_CFLAGS) + libsatyr_conv_la_LDFLAGS = $(GLIB_LIBS) + + if HAVE_LIBOPCODES +diff --git a/lib/core_unwind.c b/lib/core_unwind.c +index 7910254..8b7cc22 100644 +--- a/lib/core_unwind.c ++++ b/lib/core_unwind.c +@@ -227,9 +227,10 @@ open_coredump(const char *elf_file, const char *exe_file, char **error_msg) + } + ch->segments = head; + +- if (!*error_msg && !head) ++ if (!head) + { +- set_error("No segments found in coredump '%s'", elf_file); ++ if (error_msg && !*error_msg) ++ set_error("No segments found in coredump '%s'", elf_file); + goto fail_dwfl; + } + +diff --git a/lib/core_unwind_elfutils.c b/lib/core_unwind_elfutils.c +index a8d8b3f..6b904c7 100644 +--- a/lib/core_unwind_elfutils.c ++++ b/lib/core_unwind_elfutils.c +@@ -137,20 +137,20 @@ sr_parse_coredump(const char *core_file, + *error_msg = NULL; + + struct core_handle *ch = open_coredump(core_file, exe_file, error_msg); +- if (*error_msg) +- return NULL; ++ if (!ch) ++ goto fail; + + if (dwfl_core_file_attach(ch->dwfl, ch->eh) < 0) + { + set_error_dwfl("dwfl_core_file_attach"); +- goto fail_destroy_handle; ++ goto fail; + } + + stacktrace = sr_core_stacktrace_new(); + if (!stacktrace) + { + set_error("Failed to initialize stacktrace memory"); +- goto fail_destroy_handle; ++ goto fail; + } + + struct thread_callback_arg thread_arg = +@@ -165,11 +165,16 @@ sr_parse_coredump(const char *core_file, + if (ret == -1) + set_error_dwfl("dwfl_getthreads"); + else if (ret == DWARF_CB_ABORT) +- *error_msg = thread_arg.error_msg; ++ { ++ set_error("%s", thread_arg.error_msg); ++ free(thread_arg.error_msg); ++ } + else +- *error_msg = sr_strdup("Unknown error in dwfl_getthreads"); ++ set_error("Unknown error in dwfl_getthreads"); + +- goto fail_destroy_trace; ++ sr_core_stacktrace_free(stacktrace); ++ stacktrace = NULL; ++ goto fail; + } + + stacktrace->executable = sr_strdup(exe_file); +@@ -177,13 +182,7 @@ sr_parse_coredump(const char *core_file, + /* FIXME: is this the best we can do? */ + stacktrace->crash_thread = stacktrace->threads; + +-fail_destroy_trace: +- if (*error_msg) +- { +- sr_core_stacktrace_free(stacktrace); +- stacktrace = NULL; +- } +-fail_destroy_handle: ++fail: + core_handle_free(ch); + return stacktrace; + } +diff --git a/lib/core_unwind_libunwind.c b/lib/core_unwind_libunwind.c +index 966a5b9..b45e2ad 100644 +--- a/lib/core_unwind_libunwind.c ++++ b/lib/core_unwind_libunwind.c +@@ -99,7 +99,7 @@ unwind_thread(struct UCD_info *ui, + } + } + +- if (!error_msg && !trace) ++ if (error_msg && !*error_msg && !trace) + { + set_error("No frames found for thread %d", thread_no); + } +-- +1.9.3 + diff --git a/SOURCES/satyr-0.13-ureport-auth-support.patch b/SOURCES/satyr-0.13-ureport-auth-support.patch new file mode 100644 index 0000000..220ac73 --- /dev/null +++ b/SOURCES/satyr-0.13-ureport-auth-support.patch @@ -0,0 +1,565 @@ +From 3e11b3c67bc31662810f72571311d7d7580e3f0a Mon Sep 17 00:00:00 2001 +From: Jakub Filak +Date: Mon, 18 Aug 2014 20:39:34 +0200 +Subject: [SATYR PATCH 6/6] Add authentication data support in uReport + +Support authentication data in uReport + +We need this for 'machine id' element which should be present only if +users wishes to it. + +The authentication entries are string based, key value pairs (JSON +string elements). + +The entries are enclosed in an envelope object called 'auth'. + +Example: + +"auth" : { "machineid" : "deadbeaf" + } + +Fixes #171 + +Signed-off-by: Jakub Filak +Signed-off-by: Martin Milata + +mmilata: s/custom/auth/g, python bindings + +tests: add a test for uReport auth data + +Related to #171 + +Signed-off-by: Jakub Filak +Signed-off-by: Martin Milata + +mmilata: python test + +ureport: fix indentation of 'auth' key in JSON + +Fixes a bug in commit 189f7adbba9c38bd434c30e7d0c07b546c373312 + +Related to #171 + +Signed-off-by: Jakub Filak +--- + include/report.h | 17 ++++++ + lib/internal_utils.h | 12 +++++ + lib/json.c | 26 ++++++++++ + lib/report.c | 82 +++++++++++++++++++++++++++++ + python/py_report.c | 30 +++++++++++ + python/py_report.h | 2 + + tests/json_files/ureport-1-auth | 112 ++++++++++++++++++++++++++++++++++++++++ + tests/python/report.py | 6 +++ + tests/report.at | 81 +++++++++++++++++++++++++++++ + 9 files changed, 368 insertions(+) + create mode 100644 tests/json_files/ureport-1-auth + +diff --git a/include/report.h b/include/report.h +index 424b730..025d8f8 100644 +--- a/include/report.h ++++ b/include/report.h +@@ -31,6 +31,13 @@ extern "C" { + struct sr_json_value; + struct sr_stacktrace; + ++struct sr_report_custom_entry ++{ ++ char *key; ++ char *value; ++ struct sr_report_custom_entry *next; ++}; ++ + struct sr_report + { + uint32_t report_version; +@@ -49,6 +56,8 @@ struct sr_report + struct sr_rpm_package *rpm_packages; + + struct sr_stacktrace *stacktrace; ++ ++ struct sr_report_custom_entry *auth_entries; + }; + + struct sr_report * +@@ -60,6 +69,14 @@ sr_report_init(struct sr_report *report); + void + sr_report_free(struct sr_report *report); + ++ ++/* @brief Adds a new entry to 'auth' object ++ * ++ * The implementation is LIFO. The resulting list is in reversed. ++ */ ++void ++sr_report_add_auth(struct sr_report *report, const char *key, const char *value); ++ + char * + sr_report_to_json(struct sr_report *report); + +diff --git a/lib/internal_utils.h b/lib/internal_utils.h +index 20f65de..3a10520 100644 +--- a/lib/internal_utils.h ++++ b/lib/internal_utils.h +@@ -130,4 +130,16 @@ json_read_bool(struct sr_json_value *object, const char *key_name, bool *dest, + abort(); \ + } + ++/* returns the number of object's children */ ++unsigned ++json_object_children_count(struct sr_json_value *object); ++ ++/* returns the child_noth child and passes its name in child_name arg */ ++struct sr_json_value * ++json_object_get_child(struct sr_json_value *object, unsigned child_no, const char **child_name); ++ ++/* returns string's value */ ++const char * ++json_string_get_value(struct sr_json_value *object); ++ + #endif +diff --git a/lib/json.c b/lib/json.c +index 35b7e6b..d777973 100644 +--- a/lib/json.c ++++ b/lib/json.c +@@ -787,6 +787,32 @@ json_element(struct sr_json_value *object, const char *key_name) + return NULL; + } + ++unsigned ++json_object_children_count(struct sr_json_value *object) ++{ ++ assert(object->type == SR_JSON_OBJECT); ++ ++ return object->u.object.length; ++} ++ ++struct sr_json_value * ++json_object_get_child(struct sr_json_value *object, unsigned child_no, const char **child_name) ++{ ++ assert(object->type == SR_JSON_OBJECT); ++ assert(child_no < object->u.object.length); ++ ++ *child_name = object->u.object.values[child_no].name; ++ return object->u.object.values[child_no].value; ++} ++ ++const char * ++json_string_get_value(struct sr_json_value *object) ++{ ++ assert(object->type == SR_JSON_STRING); ++ ++ return object->u.string.ptr; ++} ++ + #define DEFINE_JSON_READ(name, c_type, json_type, json_member, conversion) \ + bool \ + name(struct sr_json_value *object, const char *key_name, c_type *dest, char **error_message) \ +diff --git a/lib/report.c b/lib/report.c +index a6ecae4..411df64 100644 +--- a/lib/report.c ++++ b/lib/report.c +@@ -66,6 +66,7 @@ sr_report_init(struct sr_report *report) + report->component_name = NULL; + report->rpm_packages = NULL; + report->stacktrace = NULL; ++ report->auth_entries = NULL; + } + + void +@@ -75,9 +76,36 @@ sr_report_free(struct sr_report *report) + sr_operating_system_free(report->operating_system); + sr_rpm_package_free(report->rpm_packages, true); + sr_stacktrace_free(report->stacktrace); ++ ++ struct sr_report_custom_entry *iter = report->auth_entries; ++ while (iter) ++ { ++ struct sr_report_custom_entry *tmp = iter->next; ++ ++ free(iter->value); ++ free(iter->key); ++ free(iter); ++ ++ iter = tmp; ++ } ++ + free(report); + } + ++void ++sr_report_add_auth(struct sr_report *report, const char *key, const char *value) ++{ ++ struct sr_report_custom_entry *new_entry = sr_malloc(sizeof(*new_entry)); ++ new_entry->key = sr_strdup(key); ++ new_entry->value = sr_strdup(value); ++ ++ /* prepend the new value ++ * it is much faster and easier ++ * we can do it because we do not to preserve the order(?) */ ++ new_entry->next = report->auth_entries; ++ report->auth_entries = new_entry; ++} ++ + /* The object has to be non-empty, i.e. contain at least one key-value pair. */ + static void + dismantle_object(char *obj) +@@ -221,6 +249,34 @@ sr_report_to_json(struct sr_report *report) + free(rpms_str_indented); + } + ++ /* Custom entries. ++ * "auth" : { "foo": "blah" ++ * , "one": "two" ++ * } ++ */ ++ struct sr_report_custom_entry *iter = report->auth_entries; ++ if (iter) ++ { ++ sr_strbuf_append_strf(strbuf, ", \"auth\": { "); ++ sr_json_append_escaped(strbuf, iter->key); ++ sr_strbuf_append_str(strbuf, ": "); ++ sr_json_append_escaped(strbuf, iter->value); ++ sr_strbuf_append_str(strbuf, "\n"); ++ ++ /* the first entry is prefix with '{', see lines above */ ++ iter = iter->next; ++ while (iter) ++ { ++ sr_strbuf_append_strf(strbuf, " , "); ++ sr_json_append_escaped(strbuf, iter->key); ++ sr_strbuf_append_str(strbuf, ": "); ++ sr_json_append_escaped(strbuf, iter->value); ++ sr_strbuf_append_str(strbuf, "\n"); ++ iter = iter->next; ++ } ++ sr_strbuf_append_str(strbuf, " } "); ++ } ++ + sr_strbuf_append_str(strbuf, "}"); + return sr_strbuf_free_nobuf(strbuf); + } +@@ -336,6 +392,32 @@ sr_report_from_json(struct sr_json_value *root, char **error_message) + + } + ++ /* Authentication entries. */ ++ struct sr_json_value *extra = json_element(root, "auth"); ++ if (extra) ++ { ++ if (!JSON_CHECK_TYPE(extra, SR_JSON_OBJECT, "auth")) ++ goto fail; ++ ++ const unsigned children = json_object_children_count(extra); ++ ++ /* from the last children down to the first for easier testing :) ++ * keep it as it is as long as sr_report_add_auth() does LIFO */ ++ for (unsigned i = 1; i <= children; ++i) ++ { ++ const char *child_name = NULL; ++ struct sr_json_value *child_object = json_object_get_child(extra, ++ children - i, ++ &child_name); ++ ++ if (!JSON_CHECK_TYPE(child_object, SR_JSON_STRING, child_name)) ++ continue; ++ ++ const char *child_value = json_string_get_value(child_object); ++ sr_report_add_auth(report, child_name, child_value); ++ } ++ } ++ + return report; + + fail: +diff --git a/python/py_report.c b/python/py_report.c +index d314027..8aa35dd 100644 +--- a/python/py_report.c ++++ b/python/py_report.c +@@ -42,6 +42,8 @@ + #define to_json_doc "Usage: report.to_json()\n\n" \ + "Returns: string - the report serialized as JSON" + ++#define auth_doc "Dictinary of key/value pairs used for authentication" ++ + /* See python/py_common.h and python/py_gdb_frame.c for generic getters/setters documentation. */ + #define GSOFF_PY_STRUCT sr_py_report + #define GSOFF_PY_MEMBER report +@@ -64,6 +66,7 @@ report_getset[] = + SR_ATTRIBUTE_STRING(component_name, "Name of the software component this report pertains to (string)" ), + { (char*)"report_version", sr_py_report_get_version, sr_py_setter_readonly, (char*)"Version of the report (int)", NULL }, + { (char*)"report_type", sr_py_report_get_type, sr_py_report_set_type, (char*)"Report type (string)", NULL }, ++ { (char*)"auth", sr_py_report_get_auth, sr_py_report_set_auth, (char*)auth_doc, NULL }, + { NULL }, + }; + +@@ -492,3 +495,30 @@ sr_py_report_to_json(PyObject *self, PyObject *args) + free(json); + return result; + } ++ ++PyObject * ++sr_py_report_get_auth(PyObject *self, void *data) ++{ ++ struct sr_report *report = ((struct sr_py_report *)self)->report; ++ struct sr_report_custom_entry *ae = report->auth_entries; ++ ++ PyObject *auth = PyDict_New(); ++ for (ae = report->auth_entries; ae; ae = ae->next) ++ { ++ PyObject *val = PyString_FromString(ae->value); ++ if (!val) ++ return NULL; ++ ++ if (PyDict_SetItemString(auth, ae->key, val) == -1) ++ return NULL; ++ } ++ ++ return auth; ++} ++ ++int ++sr_py_report_set_auth(PyObject *self, PyObject *rhs, void *data) ++{ ++ PyErr_SetString(PyExc_NotImplementedError, "Setting auth data is not implemented."); ++ return -1; ++} +diff --git a/python/py_report.h b/python/py_report.h +index a451dbc..88ec30f 100644 +--- a/python/py_report.h ++++ b/python/py_report.h +@@ -65,6 +65,8 @@ PyObject *sr_py_report_str(PyObject *self); + PyObject *sr_py_report_get_version(PyObject *self, void *data); + PyObject *sr_py_report_get_type(PyObject *self, void *data); + int sr_py_report_set_type(PyObject *self, PyObject *rhs, void *data); ++PyObject *sr_py_report_get_auth(PyObject *self, void *data); ++int sr_py_report_set_auth(PyObject *self, PyObject *rhs, void *data); + + /** + * Methods. +diff --git a/tests/json_files/ureport-1-auth b/tests/json_files/ureport-1-auth +new file mode 100644 +index 0000000..a8db2df +--- /dev/null ++++ b/tests/json_files/ureport-1-auth +@@ -0,0 +1,112 @@ ++{ ++ "ureport_version": 2, ++ ++ "reason": "Program /usr/bin/sleep was terminated by signal 11", ++ ++ "os": { ++ "name": "fedora", ++ "version": "18", ++ "architecture": "x86_64" ++ }, ++ ++ "problem": { ++ "type": "core", ++ ++ "executable": "/usr/bin/sleep", ++ ++ "signal": 11, ++ ++ "component": "coreutils", ++ ++ "user": { ++ "local": true, ++ "root": false ++ }, ++ ++ "stacktrace": [ ++ { ++ "crash_thread": true, ++ ++ "frames": [ ++ { ++ "build_id": "5f6632d75fd027f5b7b410787f3f06c6bf73eee6", ++ "build_id_offset": 767024, ++ "file_name": "/lib64/libc.so.6", ++ "address": 251315074096, ++ "fingerprint": "6c1eb9626919a2a5f6a4fc4c2edc9b21b33b7354", ++ "function_name": "__nanosleep" ++ }, ++ { ++ "build_id": "cd379d3bb5d07c96d491712e41c34bcd06b2ce32", ++ "build_id_offset": 16567, ++ "file_name": "/usr/bin/sleep", ++ "address": 4210871, ++ "fingerprint": "d24433b82a2c751fc580f47154823e0bed641a54", ++ "function_name": "close_stdout" ++ }, ++ { ++ "build_id": "cd379d3bb5d07c96d491712e41c34bcd06b2ce32", ++ "build_id_offset": 16202, ++ "file_name": "/usr/bin/sleep", ++ "address": 4210506, ++ "fingerprint": "562719fb960d1c4dbf30c04b3cff37c82acc3d2d", ++ "function_name": "close_stdout" ++ }, ++ { ++ "build_id": "cd379d3bb5d07c96d491712e41c34bcd06b2ce32", ++ "build_id_offset": 6404, ++ "fingerprint": "2e8fb95adafe21d035b9bcb9993810fecf4be657", ++ "file_name": "/usr/bin/sleep", ++ "address": 4200708 ++ }, ++ { ++ "build_id": "5f6632d75fd027f5b7b410787f3f06c6bf73eee6", ++ "build_id_offset": 137733, ++ "file_name": "/lib64/libc.so.6", ++ "address": 251314444805, ++ "fingerprint": "075acda5d3230e115cf7c88597eaba416bdaa6bb", ++ "function_name": "__libc_start_main" ++ } ++ ] ++ } ++ ] ++ }, ++ ++ "packages": [ ++ { ++ "name": "coreutils", ++ "epoch": 0, ++ "version": "8.17", ++ "architecture": "x86_64", ++ "package_role": "affected", ++ "release": "8.fc18", ++ "install_time": 1371464601 ++ }, ++ { ++ "name": "glibc", ++ "epoch": 0, ++ "version": "2.16", ++ "architecture": "x86_64", ++ "release": "31.fc18", ++ "install_time": 1371464176 ++ }, ++ { ++ "name": "glibc-common", ++ "epoch": 0, ++ "version": "2.16", ++ "architecture": "x86_64", ++ "release": "31.fc18", ++ "install_time": 1371464184 ++ } ++ ], ++ ++ "reporter": { ++ "version": "0.3", ++ "name": "satyr" ++ }, ++ ++ "auth": { ++ "hostname": "localhost", ++ "machine_id": "0000" ++ } ++} +diff --git a/tests/python/report.py b/tests/python/report.py +index 83fa76b..5f731b5 100755 +--- a/tests/python/report.py ++++ b/tests/python/report.py +@@ -91,6 +91,12 @@ class TestReport(BindingsTestCase): + def test_hash(self): + self.assertHashable(self.report) + ++ def test_auth(self): ++ self.assertFalse(self.report.auth) ++ self.assertRaises(NotImplementedError, self.report.__setattr__, 'auth', {'hostname': 'darkstar'}) ++ ++ report_with_auth = satyr.Report(load_input_contents('../json_files/ureport-1-auth')) ++ self.assertEqual(report_with_auth.auth, {'hostname': 'localhost', 'machine_id': '0000'}) + + if __name__ == '__main__': + unittest.main() +diff --git a/tests/report.at b/tests/report.at +index 8736137..1e773d2 100644 +--- a/tests/report.at ++++ b/tests/report.at +@@ -57,3 +57,84 @@ int main(void) + return 0; + } + ]]) ++ ++## ------------------ ## ++## sr_report_add_auth ## ++## ------------------ ## ++ ++AT_TESTFUN([sr_report_add_auth], ++[[ ++#include ++#include ++#include ++#include ++#include "report.h" ++ ++void check_struct(struct sr_report *report, const char **expected) ++{ ++ const char **exp_iter = expected; ++ struct sr_report_custom_entry *cust_iter = report->auth_entries; ++ ++ while(cust_iter && *exp_iter) ++ { ++ fprintf(stdout, "Expected('%s':'%s') vs. Current('%s':'%s')\n", ++ exp_iter[0], exp_iter[1], cust_iter->key, cust_iter->value); ++ ++ assert(strcmp(cust_iter->key, exp_iter[0]) == 0 && ++ strcmp(cust_iter->value, exp_iter[1]) == 0); ++ ++ cust_iter = cust_iter->next; ++ exp_iter += 2; ++ } ++ ++ assert(cust_iter == NULL); ++ assert(*exp_iter == NULL); ++} ++ ++void check_json(const char *json, const char **expected) ++{ ++ const char **exp_iter = expected; ++ while (*exp_iter) ++ { ++ char *entry = NULL; ++ asprintf(&entry, "\"%s\": \"%s\"", exp_iter[0], exp_iter[1]); ++ ++ fprintf(stdout, "Checking: '%s'\n", entry); ++ ++ if (strstr(json, entry) == NULL) ++ { ++ fprintf(stderr, "JSON:\n%s\n", json); ++ abort(); ++ } ++ ++ exp_iter += 2; ++ } ++} ++ ++ ++int main(void) ++{ ++ struct sr_report *report = sr_report_new(); ++ ++ sr_report_add_auth(report, "foo", "blah"); ++ sr_report_add_auth(report, "abrt", "awesome"); ++ sr_report_add_auth(report, "satyr", "wonderful"); ++ ++ const char *expected[] = { "satyr", "wonderful", "abrt", "awesome", "foo", "blah", NULL }; ++ ++ check_struct(report, expected); ++ ++ sr_report_to_json(report); ++ ++ char *json = sr_report_to_json(report); ++ ++ check_json(json, expected); ++ ++ char *error = NULL; ++ struct sr_report *copy = sr_report_from_json_text(json, &error); ++ ++ check_struct(copy, expected); ++ ++ return 0; ++} ++]]) +-- +1.9.3 + diff --git a/SPECS/satyr.spec b/SPECS/satyr.spec index 3753ec1..b4ad206 100644 --- a/SPECS/satyr.spec +++ b/SPECS/satyr.spec @@ -19,7 +19,7 @@ Name: satyr Version: 0.13 -Release: 4%{?dist} +Release: 8%{?dist} Summary: Tools to create anonymous, machine-friendly problem reports Group: System Environment/Libraries License: GPLv2+ @@ -43,6 +43,33 @@ Patch1: satyr-0.13-elfutils-unwinder.patch Patch2: satyr-0.13-disable-fingerprints.patch Patch3: satyr-0.13-unwinder-refresh-config-h.patch +# 1142856, minor bugs found by static analyzer +Patch4: satyr-0.13-static-analyzer-bugs.patch + +# 1123262, empty duphash of unreliable koops +Patch5: satyr-0.13-koops-unreliable-frames.patch + +# 1142339, python exception parsing +Patch6: satyr-0.13-python-exceptions.patch + +# 1142338, ppc64 backtrace parsing +Patch7: satyr-0.13-ppc64-backtrace-parsing.patch + +# 1142346, limit stacktrace depth +Patch8: satyr-0.13-limit-stacktrace-depth.patch + +# 1139555, ureport auth support +Patch9: satyr-0.13-ureport-auth-support.patch + +# 1034857, ignore java suppressed exceptions +Patch10: satyr-0.13-java-suppressed-exceptions.patch + +# 1147952, don't free gdb stacktrace on method failure +Patch11: satyr-0.13-dont-free-gdb-stacktrace.patch + +# 1142346, better handling of infinite recursion +Patch12: satyr-0.13-better-inf-recursion-handling.patch + %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 @@ -74,6 +101,15 @@ Python bindings for %{name}. %patch1 -p1 %patch2 -p1 %patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 +%patch12 -p1 %build %configure \ @@ -116,6 +152,32 @@ make check %endif %changelog +* Wed Nov 19 2014 Martin Milata - 0.13-8 +- Better handling of stacktraces with infinite recursion + - Resolves: #1142346 + +* Fri Oct 03 2014 Martin Milata - 0.13-7 +- Don't free GDB stacktrace on error + - Resolves: #1147952 + +* Fri Oct 03 2014 Martin Milata - 0.13-6 +- Ignore suppressed exceptions in the Java exception parser + - Resolves: #1034857 + +* Thu Sep 18 2014 Martin Milata - 0.13-5 +- Fix minor bugs found by static analyzers + - Resolves: #1142856 +- Return empty duphash for koopses with no reliable frames + - Resolves: #1123262 +- Fix parsing of python SyntaxError exceptions + - Resolves: #1142339 +- Fix parsing of ppc64 gdb stacktraces + - Resolves: #1142338 +- Limit the depth of generated stacktrace to avoid huge reports + - Resolves: #1142346 +- Add authentication support to uReport, needed for reporting to customer portal + - Resolves: #1139555 + * Fri Jan 24 2014 Daniel Mach - 0.13-4 - Mass rebuild 2014-01-24