[SATYR PATCH 8/9] Implement duplication hashes

Martin Milata mmilata at redhat.com
Wed Jun 26 20:14:12 UTC 2013


Related to #66.

Signed-off-by: Martin Milata <mmilata at redhat.com>
---
 include/thread.h     | 34 ++++++++++++++++++++++++++++++
 lib/core_frame.c     | 31 ++++++++++++++++++++++++++++
 lib/gdb_frame.c      | 25 ++++++++++++++++++++++
 lib/generic_frame.c  |  9 ++++++++
 lib/generic_frame.h  |  8 ++++++++
 lib/generic_thread.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 lib/java_frame.c     | 19 +++++++++++++++++
 lib/koops_frame.c    | 16 +++++++++++++++
 lib/python_frame.c   | 16 +++++++++++++++
 9 files changed, 214 insertions(+), 2 deletions(-)

diff --git a/include/thread.h b/include/thread.h
index c62c08f..3e1130b 100644
--- a/include/thread.h
+++ b/include/thread.h
@@ -47,6 +47,24 @@ struct sr_thread
 };
 
 /**
+ * Flags that influence how the duphash is computed.
+ */
+enum sr_duphash_flags
+{
+    /* Default hashing process.
+     */
+    SR_DUPHASH_NORMAL = 1 << 0,
+
+    /* Return the plaintext that would be hashed. Useful mainly for debugging.
+     */
+    SR_DUPHASH_NOHASH = 1 << 1,
+
+    /* Do not perform stacktrace normalization.
+     */
+    SR_DUPHASH_NONORMALIZE = 1 << 2,
+};
+
+/**
  * Returns pointer to the first frame in thread.
  */
 struct sr_frame *
@@ -125,6 +143,22 @@ sr_thread_dup(struct sr_thread *thread);
 void
 sr_thread_normalize(struct sr_thread *thread);
 
+/**
+ * Returns the duplication hash. Two threads resulting from the same crash
+ * should have the same duphash. The returned string is allocated by malloc().
+ *
+ * @param thread The thread to hash.
+ * @param frames Number of frames to include in the hash. If the value is 0,
+ *               all frames are used.
+ * @param prefix String that should be prefixed before the text from which the
+ *               hash is computed. ABRT/FAF used the crash component here. Can
+ *               be NULL.
+ * @param flags Bitwise OR of sr_duphash_flags.
+ */
+char *
+sr_thread_get_duphash(struct sr_thread *thread, int frames, char *prefix,
+                      enum sr_duphash_flags flags);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/core_frame.c b/lib/core_frame.c
index 4ae8e8c..a75f859 100644
--- a/lib/core_frame.c
+++ b/lib/core_frame.c
@@ -22,6 +22,7 @@
 #include "strbuf.h"
 #include "json.h"
 #include "generic_frame.h"
+#include "thread.h"
 #include "stacktrace.h"
 #include "internal_utils.h"
 #include <string.h>
@@ -31,6 +32,9 @@
 static void
 core_append_bthash_text(struct sr_core_frame *frame, enum sr_bthash_flags flags,
                         struct sr_strbuf *strbuf);
+static void
+core_append_duphash_text(struct sr_core_frame *frame, enum sr_duphash_flags flags,
+                         struct sr_strbuf *strbuf);
 
 DEFINE_NEXT_FUNC(core_next, struct sr_frame, struct sr_core_frame)
 DEFINE_SET_NEXT_FUNC(core_set_next, struct sr_frame, struct sr_core_frame)
@@ -44,6 +48,8 @@ struct frame_methods core_frame_methods =
     .cmp_distance = (frame_cmp_fn_t) sr_core_frame_cmp_distance,
     .frame_append_bthash_text =
         (frame_append_bthash_text_fn_t) core_append_bthash_text,
+    .frame_append_duphash_text =
+        (frame_append_duphash_text_fn_t) core_append_duphash_text,
     .frame_free = (frame_free_fn_t) sr_core_frame_free,
 };
 
@@ -428,3 +434,28 @@ core_append_bthash_text(struct sr_core_frame *frame, enum sr_bthash_flags flags,
                           OR_UNKNOWN(frame->file_name),
                           OR_UNKNOWN(frame->fingerprint));
 }
+
+static void
+core_append_duphash_text(struct sr_core_frame *frame, enum sr_duphash_flags flags,
+                         struct sr_strbuf *strbuf)
+{
+    /* Build id should be the preferred deduplication mechanism. */
+    if (frame->build_id)
+        sr_strbuf_append_strf(strbuf, "%s+0x%"PRIx64"\n",
+                              frame->build_id,
+                              frame->build_id_offset);
+
+    /* If we don't have it, try the function name. */
+    else if (frame->function_name)
+        sr_strbuf_append_strf(strbuf, "  %s\n", frame->function_name);
+
+    /* Function fingerprint? */
+    else if (frame->fingerprint)
+        sr_strbuf_append_strf(strbuf, "%s+0x%"PRIx64"\n",
+                              frame->fingerprint,
+                              frame->build_id_offset);
+
+    /* Address should be better than nothing. */
+    else
+        sr_strbuf_append_strf(strbuf, "0x%"PRIx64"\n", frame->address);
+}
diff --git a/lib/gdb_frame.c b/lib/gdb_frame.c
index 220e358..e3985c4 100644
--- a/lib/gdb_frame.c
+++ b/lib/gdb_frame.c
@@ -22,6 +22,7 @@
 #include "strbuf.h"
 #include "location.h"
 #include "generic_frame.h"
+#include "thread.h"
 #include "stacktrace.h"
 #include "internal_utils.h"
 #include <stdlib.h>
@@ -35,6 +36,9 @@
 static void
 gdb_append_bthash_text(struct sr_gdb_frame *frame, enum sr_bthash_flags flags,
                        struct sr_strbuf *strbuf);
+static void
+gdb_append_duphash_text(struct sr_gdb_frame *frame, enum sr_duphash_flags flags,
+                        struct sr_strbuf *strbuf);
 
 DEFINE_NEXT_FUNC(gdb_next, struct sr_frame, struct sr_gdb_frame)
 DEFINE_SET_NEXT_FUNC(gdb_set_next, struct sr_frame, struct sr_gdb_frame)
@@ -54,6 +58,8 @@ struct frame_methods gdb_frame_methods =
     .cmp_distance = (frame_cmp_fn_t) sr_gdb_frame_cmp_distance,
     .frame_append_bthash_text =
         (frame_append_bthash_text_fn_t) gdb_append_bthash_text,
+    .frame_append_duphash_text =
+        (frame_append_duphash_text_fn_t) gdb_append_duphash_text,
     .frame_free = (frame_free_fn_t) sr_gdb_frame_free,
 };
 
@@ -1116,3 +1122,22 @@ gdb_append_bthash_text(struct sr_gdb_frame *frame, enum sr_bthash_flags flags,
                           frame->address,
                           OR_UNKNOWN(frame->library_name));
 }
+
+static void
+gdb_append_duphash_text(struct sr_gdb_frame *frame, enum sr_duphash_flags flags,
+                        struct sr_strbuf *strbuf)
+{
+    /* Taken from btparser. */
+    sr_strbuf_append_str(strbuf, " ");
+
+    if (frame->function_type)
+        sr_strbuf_append_strf(strbuf, " %s", frame->function_type);
+
+    if (frame->function_name)
+        sr_strbuf_append_strf(strbuf, " %s", frame->function_name);
+
+    if (frame->signal_handler_called)
+        sr_strbuf_append_str(strbuf, " <signal handler called>");
+
+    sr_strbuf_append_str(strbuf, "\n");
+}
diff --git a/lib/generic_frame.c b/lib/generic_frame.c
index b3a5f3c..eb644b5 100644
--- a/lib/generic_frame.c
+++ b/lib/generic_frame.c
@@ -23,6 +23,7 @@
 #include "report_type.h"
 #include "internal_utils.h"
 #include "generic_frame.h"
+#include "generic_thread.h"
 #include "stacktrace.h"
 
 /* Initialize dispatch table. */
@@ -80,6 +81,14 @@ frame_append_bthash_text(struct sr_frame *frame, enum sr_bthash_flags flags,
             (frame, flags, strbuf);
 }
 
+void
+frame_append_duphash_text(struct sr_frame *frame, enum sr_duphash_flags flags,
+                          struct sr_strbuf *strbuf)
+{
+    DISPATCH(dtable, frame->type, frame_append_duphash_text)
+            (frame, flags, strbuf);
+}
+
 void sr_frame_free(struct sr_frame *frame)
 {
     if (!frame)
diff --git a/lib/generic_frame.h b/lib/generic_frame.h
index 7a02bbe..a61626d 100644
--- a/lib/generic_frame.h
+++ b/lib/generic_frame.h
@@ -23,6 +23,7 @@
 #include "frame.h"
 
 enum sr_bthash_flags;
+enum sr_duphash_flags;
 
 typedef void (*append_to_str_fn_t)(struct sr_frame *, struct sr_strbuf *);
 typedef struct sr_frame* (*next_frame_fn_t)(struct sr_frame *);
@@ -30,6 +31,8 @@ typedef void (*set_next_frame_fn_t)(struct sr_frame *, struct sr_frame *);
 typedef int (*frame_cmp_fn_t)(struct sr_frame *, struct sr_frame *);
 typedef void (*frame_append_bthash_text_fn_t)(struct sr_frame*, enum sr_bthash_flags,
                                               struct sr_strbuf*);
+typedef void (*frame_append_duphash_text_fn_t)(struct sr_frame*, enum sr_duphash_flags,
+                                               struct sr_strbuf*);
 typedef void (*frame_free_fn_t)(struct sr_frame*);
 
 struct frame_methods
@@ -40,6 +43,7 @@ struct frame_methods
     frame_cmp_fn_t cmp;
     frame_cmp_fn_t cmp_distance;
     frame_append_bthash_text_fn_t frame_append_bthash_text;
+    frame_append_duphash_text_fn_t frame_append_duphash_text;
     frame_free_fn_t frame_free;
 };
 
@@ -50,4 +54,8 @@ void
 frame_append_bthash_text(struct sr_frame *frame, enum sr_bthash_flags flags,
                          struct sr_strbuf *strbuf);
 
+void
+frame_append_duphash_text(struct sr_frame *frame, enum sr_duphash_flags flags,
+                          struct sr_strbuf *strbuf);
+
 #endif
diff --git a/lib/generic_thread.c b/lib/generic_thread.c
index 4b17a63..9370159 100644
--- a/lib/generic_thread.c
+++ b/lib/generic_thread.c
@@ -20,12 +20,15 @@
 
 #include "report_type.h"
 #include "internal_utils.h"
-#include "frame.h"
-#include "stacktrace.h"
+#include "generic_frame.h"
 #include "generic_thread.h"
+#include "stacktrace.h"
+#include "sha1.h"
+#include "strbuf.h"
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <limits.h>
 
 //XXX
 /* Note that python and koops do not have multiple threads, thus the functions
@@ -213,3 +216,54 @@ sr_thread_normalize(struct sr_thread *thread)
 {
     DISPATCH(dtable, thread->type, normalize)(thread);
 }
+
+char *
+sr_thread_get_duphash(struct sr_thread *thread, int nframes, char *prefix,
+                      enum sr_duphash_flags flags)
+{
+    char *ret;
+    struct sr_strbuf *strbuf = sr_strbuf_new();
+
+    /* Normalization is destructive, we need to make a copy. */
+    thread = sr_thread_dup(thread);
+
+    if (!(flags & SR_DUPHASH_NONORMALIZE))
+        sr_thread_normalize(thread);
+
+    /* User supplied hash text prefix. */
+    if (prefix)
+        sr_strbuf_append_str(strbuf, prefix);
+
+    /* Here would be the place to append thread-specific information. However,
+     * no current problem type has any for duphash. So we just append
+     * "Thread\n" here because that is what btparser does, in order to produce
+     * compatible hashes at least for gdb problems. */
+    /*
+    DISPATCH(dtable, thread->type, thread_append_duphash_text)
+            (thread, flags, strbuf);
+    */
+    sr_strbuf_append_str(strbuf, "Thread\n");
+
+    /* Number of nframes, (almost) not limited if nframes = 0. */
+    if (nframes == 0)
+        nframes = INT_MAX;
+
+    for (struct sr_frame *frame = sr_thread_frames(thread);
+         frame && nframes > 0;
+         frame = sr_frame_next(frame), nframes--)
+    {
+        frame_append_duphash_text(frame, flags, strbuf);
+    }
+
+    if (flags & SR_DUPHASH_NOHASH)
+        ret = sr_strbuf_free_nobuf(strbuf);
+    else
+    {
+        ret = sr_sha1_hash_string(strbuf->buf);
+        sr_strbuf_free(strbuf);
+    }
+
+    sr_thread_free(thread);
+
+    return ret;
+}
diff --git a/lib/java_frame.c b/lib/java_frame.c
index 4e61332..2bea373 100644
--- a/lib/java_frame.c
+++ b/lib/java_frame.c
@@ -24,6 +24,7 @@
 #include "utils.h"
 #include "json.h"
 #include "generic_frame.h"
+#include "thread.h"
 #include "stacktrace.h"
 #include "internal_utils.h"
 #include <string.h>
@@ -37,6 +38,9 @@
 static void
 java_append_bthash_text(struct sr_java_frame *frame, enum sr_bthash_flags flags,
                         struct sr_strbuf *strbuf);
+static void
+java_append_duphash_text(struct sr_java_frame *frame, enum sr_duphash_flags flags,
+                         struct sr_strbuf *strbuf);
 
 DEFINE_NEXT_FUNC(java_next, struct sr_frame, struct sr_java_frame)
 DEFINE_SET_NEXT_FUNC(java_set_next, struct sr_frame, struct sr_java_frame)
@@ -50,6 +54,8 @@ struct frame_methods java_frame_methods =
     .cmp_distance = (frame_cmp_fn_t) sr_java_frame_cmp_distance,
     .frame_append_bthash_text =
         (frame_append_bthash_text_fn_t) java_append_bthash_text,
+    .frame_append_duphash_text =
+        (frame_append_duphash_text_fn_t) java_append_duphash_text,
     .frame_free = (frame_free_fn_t) sr_java_frame_free,
 };
 
@@ -594,3 +600,16 @@ java_append_bthash_text(struct sr_java_frame *frame, enum sr_bthash_flags flags,
                           frame->is_exception,
                           OR_UNKNOWN(frame->message));
 }
+
+static void
+java_append_duphash_text(struct sr_java_frame *frame, enum sr_duphash_flags flags,
+                         struct sr_strbuf *strbuf)
+{
+    if (frame->name)
+        sr_strbuf_append_strf(strbuf, "%s\n", frame->name);
+    else
+        sr_strbuf_append_strf(strbuf, "%s/%s:%"PRIu32"\n",
+                              OR_UNKNOWN(frame->class_path),
+                              OR_UNKNOWN(frame->file_name),
+                              frame->file_line);
+}
diff --git a/lib/koops_frame.c b/lib/koops_frame.c
index aecd591..1e00a1f 100644
--- a/lib/koops_frame.c
+++ b/lib/koops_frame.c
@@ -22,6 +22,7 @@
 #include "strbuf.h"
 #include "json.h"
 #include "generic_frame.h"
+#include "thread.h"
 #include "stacktrace.h"
 #include "internal_utils.h"
 #include <stdlib.h>
@@ -34,6 +35,9 @@
 static void
 koops_append_bthash_text(struct sr_koops_frame *frame, enum sr_bthash_flags flags,
                          struct sr_strbuf *strbuf);
+static void
+koops_append_duphash_text(struct sr_koops_frame *frame, enum sr_duphash_flags flags,
+                          struct sr_strbuf *strbuf);
 
 DEFINE_NEXT_FUNC(koops_next, struct sr_frame, struct sr_koops_frame)
 DEFINE_SET_NEXT_FUNC(koops_set_next, struct sr_frame, struct sr_koops_frame)
@@ -47,6 +51,8 @@ struct frame_methods koops_frame_methods =
     .cmp_distance = (frame_cmp_fn_t) sr_koops_frame_cmp_distance,
     .frame_append_bthash_text =
         (frame_append_bthash_text_fn_t) koops_append_bthash_text,
+    .frame_append_duphash_text =
+        (frame_append_duphash_text_fn_t) koops_append_duphash_text,
     .frame_free = (frame_free_fn_t) sr_koops_frame_free,
 };
 
@@ -541,3 +547,13 @@ koops_append_bthash_text(struct sr_koops_frame *frame, enum sr_bthash_flags flag
                           frame->from_function_length,
                           OR_UNKNOWN(frame->from_module_name));
 }
+
+static void
+koops_append_duphash_text(struct sr_koops_frame *frame, enum sr_duphash_flags flags,
+                         struct sr_strbuf *strbuf)
+{
+    if (frame->function_name)
+        sr_strbuf_append_strf(strbuf, "%s\n", frame->function_name);
+    else
+        sr_strbuf_append_strf(strbuf, "0x%"PRIx64"\n", frame->address);
+}
diff --git a/lib/python_frame.c b/lib/python_frame.c
index 4a6a209..07502a1 100644
--- a/lib/python_frame.c
+++ b/lib/python_frame.c
@@ -23,6 +23,7 @@
 #include "strbuf.h"
 #include "json.h"
 #include "generic_frame.h"
+#include "thread.h"
 #include "stacktrace.h"
 #include "internal_utils.h"
 #include <string.h>
@@ -33,6 +34,9 @@
 static void
 python_append_bthash_text(struct sr_python_frame *frame, enum sr_bthash_flags flags,
                           struct sr_strbuf *strbuf);
+static void
+python_append_duphash_text(struct sr_python_frame *frame, enum sr_duphash_flags flags,
+                           struct sr_strbuf *strbuf);
 
 DEFINE_NEXT_FUNC(python_next, struct sr_frame, struct sr_python_frame)
 DEFINE_SET_NEXT_FUNC(python_set_next, struct sr_frame, struct sr_python_frame)
@@ -46,6 +50,8 @@ struct frame_methods python_frame_methods =
     .cmp_distance = (frame_cmp_fn_t) sr_python_frame_cmp_distance,
     .frame_append_bthash_text =
         (frame_append_bthash_text_fn_t) python_append_bthash_text,
+    .frame_append_duphash_text =
+        (frame_append_duphash_text_fn_t) python_append_duphash_text,
     .frame_free = (frame_free_fn_t) sr_python_frame_free,
 };
 
@@ -366,3 +372,13 @@ python_append_bthash_text(struct sr_python_frame *frame, enum sr_bthash_flags fl
                           frame->special_function,
                           OR_UNKNOWN(frame->line_contents));
 }
+
+static void
+python_append_duphash_text(struct sr_python_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);
+}
-- 
1.7.11.7



More information about the Crash-catcher mailing list