[SATYR PATCH 3/7] Implement JSON deserialization for reports

Martin Milata mmilata at redhat.com
Wed Aug 14 15:36:35 UTC 2013


Most of the changeset implements deserialization for all stacktrace
types (except GDB). The core stacktrace deserialization was rewritten to
use the same functions as other types (making it somewhat shorter).

Signed-off-by: Martin Milata <mmilata at redhat.com>
---
 include/core/frame.h        |  17 ++++--
 include/core/thread.h       |   9 +++
 include/java/frame.h        |  13 +++++
 include/java/stacktrace.h   |  13 +++++
 include/java/thread.h       |  13 +++++
 include/koops/frame.h       |  13 +++++
 include/koops/stacktrace.h  |  13 +++++
 include/operating_system.h  |   5 ++
 include/python/frame.h      |  13 +++++
 include/python/stacktrace.h |  15 ++++-
 include/rpm.h               |   6 ++
 lib/core_frame.c            | 131 ++++----------------------------------------
 lib/core_stacktrace.c       |  92 ++++++++-----------------------
 lib/core_thread.c           |  38 +++++--------
 lib/java_frame.c            |  26 +++++++++
 lib/java_stacktrace.c       |  34 ++++++++++++
 lib/java_thread.c           |  38 +++++++++++++
 lib/koops_frame.c           |  30 ++++++++++
 lib/koops_stacktrace.c      |  94 +++++++++++++++++++++++++++++++
 lib/operating_system.c      |  24 ++++++++
 lib/python_frame.c          |  59 ++++++++++++++++++++
 lib/python_stacktrace.c     |  39 +++++++++++++
 lib/report.c                | 121 ++++++++++++++++++++++++++++++++++++++++
 lib/rpm.c                   |  79 ++++++++++++++++++++++++++
 24 files changed, 718 insertions(+), 217 deletions(-)

diff --git a/include/core/frame.h b/include/core/frame.h
index cc6c462..7cf32ed 100644
--- a/include/core/frame.h
+++ b/include/core/frame.h
@@ -186,10 +186,6 @@ struct sr_core_frame *
 sr_core_frame_append(struct sr_core_frame *dest,
                      struct sr_core_frame *item);
 
-struct sr_core_frame *
-sr_core_frame_from_json(struct sr_json_value *root,
-                        char **error_message);
-
 /**
  * Returns a textual representation of the frame.
  * @param frame
@@ -200,6 +196,19 @@ char *
 sr_core_frame_to_json(struct sr_core_frame *frame);
 
 /**
+ * Deserializes frame structure from JSON representation.
+ * @param root
+ * JSON value to be deserialized.
+ * @param error_message
+ * On error, *error_message will contain the description of the error.
+ * @returns
+ * Resulting frame, or NULL on error.
+ */
+struct sr_core_frame *
+sr_core_frame_from_json(struct sr_json_value *root,
+                        char **error_message);
+
+/**
  * Appends textual representation of the frame to the string buffer dest.
  */
 void
diff --git a/include/core/thread.h b/include/core/thread.h
index 42617e5..0ba71e4 100644
--- a/include/core/thread.h
+++ b/include/core/thread.h
@@ -126,6 +126,15 @@ sr_core_thread_append(struct sr_core_thread *dest,
 struct sr_core_frame *
 sr_core_thread_find_exit_frame(struct sr_core_thread *thread);
 
+/**
+ * Deserializes thread from JSON representation.
+ * @param root
+ * JSON value to be deserialized.
+ * @param error_message
+ * On error, *error_message will contain the description of the error.
+ * @returns
+ * Resulting thread, or NULL on error.
+ */
 struct sr_core_thread *
 sr_core_thread_from_json(struct sr_json_value *root,
                          char **error_message);
diff --git a/include/java/frame.h b/include/java/frame.h
index 903fde2..7b3f066 100644
--- a/include/java/frame.h
+++ b/include/java/frame.h
@@ -36,6 +36,7 @@ extern "C" {
 
 struct sr_strbuf;
 struct sr_location;
+struct sr_json_value;
 
 struct sr_java_frame
 {
@@ -265,6 +266,18 @@ sr_java_frame_parse(const char **input,
 char *
 sr_java_frame_to_json(struct sr_java_frame *frame);
 
+/**
+ * Deserializes frame structure from JSON representation.
+ * @param root
+ * JSON value to be deserialized.
+ * @param error_message
+ * On error, *error_message will contain the description of the error.
+ * @returns
+ * Resulting frame, or NULL on error.
+ */
+struct sr_java_frame *
+sr_java_frame_from_json(struct sr_json_value *root, char **error_message);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/java/stacktrace.h b/include/java/stacktrace.h
index e58c08c..b35e56a 100644
--- a/include/java/stacktrace.h
+++ b/include/java/stacktrace.h
@@ -32,6 +32,7 @@ extern "C" {
 
 struct sr_java_thread;
 struct sr_location;
+struct sr_json_value;
 
 #include "../report_type.h"
 #include <stdint.h>
@@ -136,6 +137,18 @@ sr_java_stacktrace_get_reason(struct sr_java_stacktrace *stacktrace);
 char *
 sr_java_stacktrace_to_json(struct sr_java_stacktrace *stacktrace);
 
+/**
+ * Deserializes stacktrace from JSON representation.
+ * @param root
+ * JSON value to be deserialized.
+ * @param error_message
+ * On error, *error_message will contain the description of the error.
+ * @returns
+ * Resulting stacktrace, or NULL on error.
+ */
+struct sr_java_stacktrace *
+sr_java_stacktrace_from_json(struct sr_json_value *root, char **error_message);
+
 struct sr_java_thread *
 sr_java_find_crash_thread(struct sr_java_stacktrace *stacktrace);
 
diff --git a/include/java/thread.h b/include/java/thread.h
index cf10a6d..f18c043 100644
--- a/include/java/thread.h
+++ b/include/java/thread.h
@@ -37,6 +37,7 @@ extern "C" {
 struct sr_java_frame;
 struct sr_strbuf;
 struct sr_location;
+struct sr_json_value;
 
 /**
  * @brief A thread of execution of a JAVA-produced stack trace.
@@ -224,6 +225,18 @@ sr_java_thread_format_funs(struct sr_java_thread *thread);
 char *
 sr_java_thread_to_json(struct sr_java_thread *thread);
 
+/**
+ * Deserializes thread from JSON representation.
+ * @param root
+ * JSON value to be deserialized.
+ * @param error_message
+ * On error, *error_message will contain the description of the error.
+ * @returns
+ * Resulting thread, or NULL on error.
+ */
+struct sr_java_thread *
+sr_java_thread_from_json(struct sr_json_value *root, char **error_message);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/koops/frame.h b/include/koops/frame.h
index cc69904..b13336e 100644
--- a/include/koops/frame.h
+++ b/include/koops/frame.h
@@ -35,6 +35,7 @@ extern "C" {
 #include <stdint.h>
 
 struct sr_strbuf;
+struct sr_json_value;
 
 /**
  * @brief Kernel oops stack frame.
@@ -220,6 +221,18 @@ char *
 sr_koops_frame_to_json(struct sr_koops_frame *frame);
 
 /**
+ * Deserializes frame structure from JSON representation.
+ * @param root
+ * JSON value to be deserialized.
+ * @param error_message
+ * On error, *error_message will contain the description of the error.
+ * @returns
+ * Resulting frame, or NULL on error.
+ */
+struct sr_koops_frame *
+sr_koops_frame_from_json(struct sr_json_value *root, char **error_message);
+
+/**
  * Appends textual representation of the frame to the string buffer dest.
  */
 void
diff --git a/include/koops/stacktrace.h b/include/koops/stacktrace.h
index 85bc755..ce6fc7b 100644
--- a/include/koops/stacktrace.h
+++ b/include/koops/stacktrace.h
@@ -36,6 +36,7 @@ extern "C" {
 #include <stddef.h>
 
 struct sr_location;
+struct sr_json_value;
 
 struct sr_koops_stacktrace
 {
@@ -159,6 +160,18 @@ sr_koops_stacktrace_get_reason(struct sr_koops_stacktrace *stacktrace);
 char *
 sr_koops_stacktrace_to_json(struct sr_koops_stacktrace *stacktrace);
 
+/**
+ * Deserializes stacktrace from JSON representation.
+ * @param root
+ * JSON value to be deserialized.
+ * @param error_message
+ * On error, *error_message will contain the description of the error.
+ * @returns
+ * Resulting stacktrace, or NULL on error.
+ */
+struct sr_koops_stacktrace *
+sr_koops_stacktrace_from_json(struct sr_json_value *root, char **error_message);
+
 void
 sr_normalize_koops_stacktrace(struct sr_koops_stacktrace *stacktrace);
 
diff --git a/include/operating_system.h b/include/operating_system.h
index a2f5e2c..a67ca93 100644
--- a/include/operating_system.h
+++ b/include/operating_system.h
@@ -27,6 +27,8 @@ extern "C" {
 #include <inttypes.h>
 #include <stdbool.h>
 
+struct sr_json_value;
+
 struct sr_operating_system
 {
     char *name;
@@ -48,6 +50,9 @@ sr_operating_system_free(struct sr_operating_system *operating_system);
 char *
 sr_operating_system_to_json(struct sr_operating_system *operating_system);
 
+struct sr_operating_system *
+sr_operating_system_from_json(struct sr_json_value *root, char **error_message);
+
 bool
 sr_operating_system_parse_etc_system_release(const char *etc_system_release,
                                              char **name,
diff --git a/include/python/frame.h b/include/python/frame.h
index 362b402..ed3f2b7 100644
--- a/include/python/frame.h
+++ b/include/python/frame.h
@@ -36,6 +36,7 @@ extern "C" {
 
 struct sr_location;
 struct sr_strbuf;
+struct sr_json_value;
 
 struct sr_python_frame
 {
@@ -175,6 +176,18 @@ char *
 sr_python_frame_to_json(struct sr_python_frame *frame);
 
 /**
+ * Deserializes frame structure from JSON representation.
+ * @param root
+ * JSON value to be deserialized.
+ * @param error_message
+ * On error, *error_message will contain the description of the error.
+ * @returns
+ * Resulting frame, or NULL on error.
+ */
+struct sr_python_frame *
+sr_python_frame_from_json(struct sr_json_value *root, char **error_message);
+
+/**
  * Appends textual representation of the frame to the string buffer dest.
  */
 void
diff --git a/include/python/stacktrace.h b/include/python/stacktrace.h
index 6223dc1..3abfbe4 100644
--- a/include/python/stacktrace.h
+++ b/include/python/stacktrace.h
@@ -35,6 +35,7 @@ extern "C" {
 
 struct sr_python_frame;
 struct sr_location;
+struct sr_json_value;
 
 struct sr_python_stacktrace
 {
@@ -104,7 +105,7 @@ sr_python_stacktrace_get_reason(struct sr_python_stacktrace *stacktrace);
 
 /**
  * Serializes stacktrace to string.
- * @returnes
+ * @returns
  * Newly allocated memory containing the textual representation of the
  * provided stacktrace.  Caller should free the memory when it's no
  * longer needed.
@@ -112,6 +113,18 @@ sr_python_stacktrace_get_reason(struct sr_python_stacktrace *stacktrace);
 char *
 sr_python_stacktrace_to_json(struct sr_python_stacktrace *stacktrace);
 
+/**
+ * Deserializes stacktrace from JSON representation.
+ * @param root
+ * JSON value to be deserialized.
+ * @param error_message
+ * On error, *error_message will contain the description of the error.
+ * @returns
+ * Resulting stacktrace, or NULL on error.
+ */
+struct sr_python_stacktrace *
+sr_python_stacktrace_from_json(struct sr_json_value *root, char **error_message);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/rpm.h b/include/rpm.h
index 41f81e7..59081b0 100644
--- a/include/rpm.h
+++ b/include/rpm.h
@@ -32,6 +32,8 @@ extern "C" {
 #include <stdbool.h>
 #include <inttypes.h>
 
+struct sr_json_value;
+
 /* XXX: Should be moved to separated header once we support more package types.
  */
 enum sr_package_role
@@ -171,6 +173,10 @@ char *
 sr_rpm_package_to_json(struct sr_rpm_package *package,
                        bool recursive);
 
+struct sr_rpm_package *
+sr_rpm_package_from_json(struct sr_json_value *list, bool recursive,
+                         char **error_message);
+
 bool
 sr_rpm_package_parse_nvr(const char *text,
                          char **name,
diff --git a/lib/core_frame.c b/lib/core_frame.c
index f8c201d..40cbc74 100644
--- a/lib/core_frame.c
+++ b/lib/core_frame.c
@@ -242,131 +242,24 @@ struct sr_core_frame *
 sr_core_frame_from_json(struct sr_json_value *root,
                         char **error_message)
 {
-    if (root->type != SR_JSON_OBJECT)
-    {
-        *error_message = sr_strdup("Invalid type of root value; object expected.");
+    if (!JSON_CHECK_TYPE(root, SR_JSON_OBJECT, "frame"))
         return NULL;
-    }
 
     struct sr_core_frame *result = sr_core_frame_new();
 
-    /* Read address. */
-    for (unsigned i = 0; i < root->u.object.length; ++i)
-    {
-        if (0 != strcmp("address", root->u.object.values[i].name))
-            continue;
-
-        if (root->u.object.values[i].value->type != SR_JSON_INTEGER)
-        {
-            *error_message = sr_strdup("Invalid type of \"address\"; integer expected.");
-            sr_core_frame_free(result);
-            return NULL;
-        }
-
-        result->address = root->u.object.values[i].value->u.integer;
-        break;
-    }
-
-    /* Read build id. */
-    for (unsigned i = 0; i < root->u.object.length; ++i)
-    {
-        if (0 != strcmp("build_id", root->u.object.values[i].name))
-            continue;
-
-        if (root->u.object.values[i].value->type != SR_JSON_STRING)
-        {
-            *error_message = sr_strdup("Invalid type of \"build_id\"; string expected.");
-            sr_core_frame_free(result);
-            return NULL;
-        }
-
-        result->build_id = sr_strdup(root->u.object.values[i].value->u.string.ptr);
-        break;
-    }
-
-    /* Read build id offset. */
-    for (unsigned i = 0; i < root->u.object.length; ++i)
-    {
-        if (0 != strcmp("build_id_offset", root->u.object.values[i].name))
-            continue;
-
-        if (root->u.object.values[i].value->type != SR_JSON_INTEGER)
-        {
-            *error_message = sr_strdup("Invalid type of \"build_id_offset\"; integer expected.");
-            sr_core_frame_free(result);
-            return NULL;
-        }
-
-        result->build_id_offset = root->u.object.values[i].value->u.integer;
-        break;
-    }
-
-    /* Read function name. */
-    for (unsigned i = 0; i < root->u.object.length; ++i)
-    {
-        if (0 != strcmp("function_name", root->u.object.values[i].name))
-            continue;
-
-        if (root->u.object.values[i].value->type != SR_JSON_STRING)
-        {
-            *error_message = sr_strdup("Invalid type of \"function_name\"; string expected.");
-            sr_core_frame_free(result);
-            return NULL;
-        }
-
-        result->function_name = sr_strdup(root->u.object.values[i].value->u.string.ptr);
-        break;
-    }
-
-    /* Read file name. */
-    for (unsigned i = 0; i < root->u.object.length; ++i)
-    {
-        if (0 != strcmp("file_name", root->u.object.values[i].name))
-            continue;
-
-        if (root->u.object.values[i].value->type != SR_JSON_STRING)
-        {
-            *error_message = sr_strdup("Invalid type of \"file_name\"; string expected.");
-            sr_core_frame_free(result);
-            return NULL;
-        }
-
-        result->file_name = sr_strdup(root->u.object.values[i].value->u.string.ptr);
-        break;
-    }
-
-    /* Read fingerprint. */
-    for (unsigned i = 0; i < root->u.object.length; ++i)
-    {
-        if (0 != strcmp("fingerprint", root->u.object.values[i].name))
-            continue;
-
-        if (root->u.object.values[i].value->type != SR_JSON_STRING)
-        {
-            *error_message = sr_strdup("Invalid type of \"fingerprint\"; string expected.");
-            sr_core_frame_free(result);
-            return NULL;
-        }
-
-        result->fingerprint = sr_strdup(root->u.object.values[i].value->u.string.ptr);
-        break;
-    }
+    bool success =
+        JSON_READ_UINT64(root, "address", &result->address) &&
+        JSON_READ_STRING(root, "build_id", &result->build_id) &&
+        JSON_READ_UINT64(root, "build_id_offset", &result->build_id_offset) &&
+        JSON_READ_STRING(root, "function_name", &result->function_name) &&
+        JSON_READ_STRING(root, "file_name", &result->file_name) &&
+        JSON_READ_STRING(root, "fingerprint", &result->fingerprint) &&
+        JSON_READ_BOOL(root, "fingerprint_hashed", &result->fingerprint_hashed);
 
-    /* Read fingerprint_hashed. */
-    for (unsigned i = 0; i < root->u.object.length; ++i)
+    if (!success)
     {
-        if (0 != strcmp("fingerprint_hashed", root->u.object.values[i].name))
-            continue;
-
-        if (root->u.object.values[i].value->type != SR_JSON_BOOLEAN)
-        {
-            *error_message = sr_strdup("Invalid type of \"fingerprint_hashed\"; bool expected.");
-            sr_core_frame_free(result);
-            return NULL;
-        }
-
-        result->fingerprint_hashed = root->u.object.values[i].value->u.boolean;
-        break;
+        sr_core_frame_free(result);
+        return NULL;
     }
 
     return result;
diff --git a/lib/core_stacktrace.c b/lib/core_stacktrace.c
index e7cba4e..c8a899e 100644
--- a/lib/core_stacktrace.c
+++ b/lib/core_stacktrace.c
@@ -155,99 +155,53 @@ struct sr_core_stacktrace *
 sr_core_stacktrace_from_json(struct sr_json_value *root,
                              char **error_message)
 {
-    if (root->type != SR_JSON_OBJECT)
-    {
-        *error_message = sr_strdup("Invalid type of root value; object expected.");
+    if (!JSON_CHECK_TYPE(root, SR_JSON_OBJECT, "stacktrace"))
         return NULL;
-    }
 
     struct sr_core_stacktrace *result = sr_core_stacktrace_new();
 
-    /* Read signal. */
-    for (unsigned i = 0; i < root->u.object.length; ++i)
-    {
-        if (0 != strcmp("signal", root->u.object.values[i].name))
-            continue;
-
-        if (root->u.object.values[i].value->type != SR_JSON_INTEGER)
-        {
-            *error_message = sr_strdup("Invalid type of \"signal\"; integer expected.");
-            sr_core_stacktrace_free(result);
-            return NULL;
-        }
-
-        result->signal = root->u.object.values[i].value->u.integer;
-        break;
-    }
-
-    /* Read executable. */
-    for (unsigned i = 0; i < root->u.object.length; ++i)
-    {
-        if (0 != strcmp("executable", root->u.object.values[i].name))
-            continue;
+    bool success =
+        JSON_READ_UINT16(root, "signal", &result->signal) &&
+        JSON_READ_STRING(root, "executable", &result->executable);
 
-        if (root->u.object.values[i].value->type != SR_JSON_STRING)
-        {
-            *error_message = sr_strdup("Invalid type of \"executable\"; string expected.");
-            sr_core_stacktrace_free(result);
-            return NULL;
-        }
-
-        result->executable = sr_strdup(root->u.object.values[i].value->u.string.ptr);
-        break;
-    }
+    if (!success)
+        goto fail;
 
     /* Read threads. */
-    for (unsigned i = 0; i < root->u.object.length; ++i)
+    struct sr_json_value *stacktrace = json_element(root, "stacktrace");
+    if (stacktrace)
     {
-        if (0 != strcmp("stacktrace", root->u.object.values[i].name))
-            continue;
-
-        if (root->u.object.values[i].value->type != SR_JSON_ARRAY)
-        {
-            *error_message = sr_strdup("Invalid type of \"stacktrace\"; array expected.");
-            sr_core_stacktrace_free(result);
-            return NULL;
-        }
+        if (!JSON_CHECK_TYPE(stacktrace, SR_JSON_ARRAY, "stacktrace"))
+            goto fail;
 
-        for (unsigned j = 0; j < root->u.object.values[i].value->u.array.length; ++j)
+        struct sr_json_value *json_thread;
+        FOR_JSON_ARRAY(stacktrace, json_thread)
         {
-            struct sr_json_value *json_thread
-                = root->u.object.values[i].value->u.array.values[j];
-
             struct sr_core_thread *thread = sr_core_thread_from_json(json_thread,
                     error_message);
 
             if (!thread)
-            {
-                sr_core_stacktrace_free(result);
-                return NULL;
-            }
+                goto fail;
 
-            for (unsigned k = 0; k < json_thread->u.object.length; ++k)
+            struct sr_json_value *crash_thread = json_element(json_thread, "crash_thread");
+            if (crash_thread)
             {
-                if (0 != strcmp("crash_thread", json_thread->u.object.values[k].name))
-                    continue;
-
-                if (json_thread->u.object.values[k].value->type != SR_JSON_BOOLEAN)
-                {
-                    *error_message = sr_strdup(
-                            "Invalid type of \"crash_thread\"; boolean expected.");
-                    sr_core_stacktrace_free(result);
-                    return NULL;
-                }
-
-                if (json_thread->u.object.values[k].value->u.boolean)
+                if (!JSON_CHECK_TYPE(crash_thread, SR_JSON_BOOLEAN, "crash_thread"))
+                    goto fail;
+
+                if (crash_thread->u.boolean)
                     result->crash_thread = thread;
             }
 
             result->threads = sr_core_thread_append(result->threads, thread);
         }
-
-        break;
     }
 
     return result;
+
+fail:
+    sr_core_stacktrace_free(result);
+    return NULL;
 }
 
 struct sr_core_stacktrace *
diff --git a/lib/core_thread.c b/lib/core_thread.c
index fc5f9e7..54fb89c 100644
--- a/lib/core_thread.c
+++ b/lib/core_thread.c
@@ -181,46 +181,36 @@ struct sr_core_thread *
 sr_core_thread_from_json(struct sr_json_value *root,
                          char **error_message)
 {
-    if (root->type != SR_JSON_OBJECT)
-    {
-        *error_message = sr_strdup("Invalid type of root value; object expected.");
+    if (!JSON_CHECK_TYPE(root, SR_JSON_OBJECT, "thread"))
         return NULL;
-    }
 
     struct sr_core_thread *result = sr_core_thread_new();
 
     /* Read frames. */
-    for (unsigned i = 0; i < root->u.object.length; ++i)
+    struct sr_json_value *frames = json_element(root, "frames");
+    if (frames)
     {
-        if (0 != strcmp("frames", root->u.object.values[i].name))
-            continue;
+        if (!JSON_CHECK_TYPE(frames, SR_JSON_ARRAY, "frames"))
+            goto fail;
 
-        if (root->u.object.values[i].value->type != SR_JSON_ARRAY)
+        struct sr_json_value *frame_json;
+        FOR_JSON_ARRAY(frames, frame_json)
         {
-            *error_message = sr_strdup("Invalid type of \"frames\"; array expected.");
-            sr_core_thread_free(result);
-            return NULL;
-        }
-
-        for (unsigned j = 0; j < root->u.object.values[i].value->u.array.length; ++j)
-        {
-            struct sr_core_frame *frame = sr_core_frame_from_json(
-                root->u.object.values[i].value->u.array.values[j],
-                error_message);
+            struct sr_core_frame *frame = sr_core_frame_from_json(frame_json,
+                    error_message);
 
             if (!frame)
-            {
-                sr_core_thread_free(result);
-                return NULL;
-            }
+                goto fail;
 
             result->frames = sr_core_frame_append(result->frames, frame);
         }
-
-        break;
     }
 
     return result;
+
+fail:
+    sr_core_thread_free(result);
+    return NULL;
 }
 
 char *
diff --git a/lib/java_frame.c b/lib/java_frame.c
index a9f906c..da9f26b 100644
--- a/lib/java_frame.c
+++ b/lib/java_frame.c
@@ -601,6 +601,32 @@ sr_java_frame_to_json(struct sr_java_frame *frame)
     return sr_strbuf_free_nobuf(strbuf);
 }
 
+struct sr_java_frame *
+sr_java_frame_from_json(struct sr_json_value *root, char **error_message)
+{
+    if (!JSON_CHECK_TYPE(root, SR_JSON_OBJECT, "frame"))
+        return NULL;
+
+    struct sr_java_frame *result = sr_java_frame_new();
+
+    bool success =
+        JSON_READ_STRING(root, "name", &result->name) &&
+        JSON_READ_STRING(root, "file_name", &result->file_name) &&
+        JSON_READ_UINT32(root, "file_line", &result->file_line) &&
+        JSON_READ_STRING(root, "class_path", &result->class_path) &&
+        JSON_READ_BOOL(root, "is_native", &result->is_native) &&
+        JSON_READ_BOOL(root, "is_exception", &result->is_exception) &&
+        JSON_READ_STRING(root, "message", &result->message);
+
+    if (!success)
+    {
+        sr_java_frame_free(result);
+        return NULL;
+    }
+
+    return result;
+}
+
 static void
 java_append_bthash_text(struct sr_java_frame *frame, enum sr_bthash_flags flags,
                         struct sr_strbuf *strbuf)
diff --git a/lib/java_stacktrace.c b/lib/java_stacktrace.c
index f8ef874..f2ddce3 100644
--- a/lib/java_stacktrace.c
+++ b/lib/java_stacktrace.c
@@ -23,6 +23,7 @@
 #include "java/frame.h"
 #include "utils.h"
 #include "strbuf.h"
+#include "json.h"
 #include "generic_stacktrace.h"
 #include "internal_utils.h"
 #include <stdio.h>
@@ -191,6 +192,39 @@ sr_java_stacktrace_to_json(struct sr_java_stacktrace *stacktrace)
     return sr_strbuf_free_nobuf(strbuf);
 }
 
+struct sr_java_stacktrace *
+sr_java_stacktrace_from_json(struct sr_json_value *root, char **error_message)
+{
+    if (!JSON_CHECK_TYPE(root, SR_JSON_OBJECT, "stacktrace"))
+        return NULL;
+
+    struct sr_java_stacktrace *result = sr_java_stacktrace_new();
+
+    struct sr_json_value *threads = json_element(root, "threads");
+    if (threads)
+    {
+        if (!JSON_CHECK_TYPE(threads, SR_JSON_ARRAY, "threads"))
+            goto fail;
+
+        struct sr_json_value *thread_json;
+        FOR_JSON_ARRAY(threads, thread_json)
+        {
+            struct sr_java_thread *thread = sr_java_thread_from_json(thread_json, error_message);
+
+            if (!thread)
+                goto fail;
+
+            result->threads = sr_java_thread_append(result->threads, thread);
+        }
+    }
+
+    return result;
+
+fail:
+    sr_java_stacktrace_free(result);
+    return NULL;
+}
+
 char *
 sr_java_stacktrace_get_reason(struct sr_java_stacktrace *stacktrace)
 {
diff --git a/lib/java_thread.c b/lib/java_thread.c
index 9f6145d..6d2d3c8 100644
--- a/lib/java_thread.c
+++ b/lib/java_thread.c
@@ -408,6 +408,44 @@ sr_java_thread_to_json(struct sr_java_thread *thread)
     return sr_strbuf_free_nobuf(strbuf);
 }
 
+struct sr_java_thread *
+sr_java_thread_from_json(struct sr_json_value *root, char **error_message)
+{
+    if (!JSON_CHECK_TYPE(root, SR_JSON_OBJECT, "thread"))
+        return NULL;
+
+    struct sr_java_thread *result = sr_java_thread_new();
+
+    if (!JSON_READ_STRING(root, "name", &result->name))
+        goto fail;
+
+    /* Read frames. */
+    struct sr_json_value *frames = json_element(root, "frames");
+    if (frames)
+    {
+        if (!JSON_CHECK_TYPE(frames, SR_JSON_ARRAY, "frames"))
+            goto fail;
+
+        struct sr_json_value *frame_json;
+        FOR_JSON_ARRAY(frames, frame_json)
+        {
+            struct sr_java_frame *frame = sr_java_frame_from_json(frame_json,
+                    error_message);
+
+            if (!frame)
+                goto fail;
+
+            result->frames = sr_java_frame_append(result->frames, frame);
+        }
+    }
+
+    return result;
+
+fail:
+    sr_java_thread_free(result);
+    return NULL;
+}
+
 static void
 java_append_bthash_text(struct sr_java_thread *thread, enum sr_bthash_flags flags,
                         struct sr_strbuf *strbuf)
diff --git a/lib/koops_frame.c b/lib/koops_frame.c
index 1e00a1f..01eea4a 100644
--- a/lib/koops_frame.c
+++ b/lib/koops_frame.c
@@ -516,6 +516,36 @@ sr_koops_frame_to_json(struct sr_koops_frame *frame)
     return sr_strbuf_free_nobuf(strbuf);
 }
 
+struct sr_koops_frame *
+sr_koops_frame_from_json(struct sr_json_value *root, char **error_message)
+{
+    if (!JSON_CHECK_TYPE(root, SR_JSON_OBJECT, "frame"))
+        return NULL;
+
+    struct sr_koops_frame *result = sr_koops_frame_new();
+
+    bool success =
+        JSON_READ_UINT64(root, "address", &result->address) &&
+        JSON_READ_BOOL(root, "reliable", &result->reliable) &&
+        JSON_READ_STRING(root, "function_name", &result->function_name) &&
+        JSON_READ_UINT64(root, "function_offset", &result->function_offset) &&
+        JSON_READ_UINT64(root, "function_length", &result->function_length) &&
+        JSON_READ_STRING(root, "module_name", &result->module_name) &&
+        JSON_READ_UINT64(root, "from_address", &result->from_address) &&
+        JSON_READ_STRING(root, "from_function_name", &result->from_function_name) &&
+        JSON_READ_UINT64(root, "from_function_offset", &result->from_function_offset) &&
+        JSON_READ_UINT64(root, "from_function_length", &result->from_function_length) &&
+        JSON_READ_STRING(root, "from_module_name", &result->from_module_name);
+
+    if (!success)
+    {
+        sr_koops_frame_free(result);
+        return NULL;
+    }
+
+    return result;
+}
+
 void
 sr_koops_frame_append_to_str(struct sr_koops_frame *frame,
                              struct sr_strbuf *str)
diff --git a/lib/koops_stacktrace.c b/lib/koops_stacktrace.c
index 4ba74db..9bc181c 100644
--- a/lib/koops_stacktrace.c
+++ b/lib/koops_stacktrace.c
@@ -514,6 +514,100 @@ sr_koops_stacktrace_to_json(struct sr_koops_stacktrace *stacktrace)
     return sr_strbuf_free_nobuf(strbuf);
 }
 
+struct sr_koops_stacktrace *
+sr_koops_stacktrace_from_json(struct sr_json_value *root, char **error_message)
+{
+    if (!JSON_CHECK_TYPE(root, SR_JSON_OBJECT, "stacktrace"))
+        return NULL;
+
+    struct sr_koops_stacktrace *result = sr_koops_stacktrace_new();
+
+    /* Kernel version. */
+    if (!JSON_READ_STRING(root, "version", &result->version))
+        goto fail;
+
+    /* Kernel taint flags. */
+    struct sr_json_value *taint_flags = json_element(root, "taint_flags");
+    if (taint_flags)
+    {
+        if (!JSON_CHECK_TYPE(taint_flags, SR_JSON_ARRAY, "taint_flags"))
+            goto fail;
+
+        struct sr_json_value *flag_json;
+        FOR_JSON_ARRAY(taint_flags, flag_json)
+        {
+            if (!JSON_CHECK_TYPE(flag_json, SR_JSON_STRING, "taint flag"))
+                goto fail;
+
+            for (struct sr_taint_flag *f = sr_flags; f->name; f++)
+            {
+                if (0 == strcmp(f->name, flag_json->u.string.ptr))
+                {
+                    *(bool *)((void *)result + f->member_offset) = true;
+                    break;
+                }
+            }
+            /* XXX should we do something if nothing is set? */
+        }
+    }
+
+    /* Modules. */
+    struct sr_json_value *modules = json_element(root, "modules");
+    if (modules)
+    {
+        if (!JSON_CHECK_TYPE(modules, SR_JSON_ARRAY, "modules"))
+            goto fail;
+
+        unsigned i = 0;
+        size_t allocated = 128;
+        result->modules = sr_malloc(sizeof(char*) * allocated);
+
+        struct sr_json_value *mod_json;
+        FOR_JSON_ARRAY(modules, mod_json)
+        {
+            if (!JSON_CHECK_TYPE(mod_json, SR_JSON_STRING, "module"))
+                goto fail;
+
+            /* need to keep the last element for NULL terminator */
+            if (i+1 == allocated)
+            {
+                allocated *= 2;
+                result->modules = sr_realloc(result->modules, allocated);
+            }
+            result->modules[i] = sr_strdup(mod_json->u.string.ptr);
+            i++;
+        }
+
+        result->modules[i] = NULL;
+    }
+
+    /* Frames. */
+    struct sr_json_value *frames = json_element(root, "frames");
+    if (frames)
+    {
+        if (!JSON_CHECK_TYPE(frames, SR_JSON_ARRAY, "frames"))
+            goto fail;
+
+        struct sr_json_value *frame_json;
+        FOR_JSON_ARRAY(frames, frame_json)
+        {
+            struct sr_koops_frame *frame = sr_koops_frame_from_json(frame_json,
+                error_message);
+
+            if (!frame)
+                goto fail;
+
+            result->frames = sr_koops_frame_append(result->frames, frame);
+        }
+    }
+
+    return result;
+
+fail:
+    sr_koops_stacktrace_free(result);
+    return NULL;
+}
+
 char *
 sr_koops_stacktrace_get_reason(struct sr_koops_stacktrace *stacktrace)
 {
diff --git a/lib/operating_system.c b/lib/operating_system.c
index cfb33bc..ed5b39e 100644
--- a/lib/operating_system.c
+++ b/lib/operating_system.c
@@ -22,6 +22,7 @@
 #include "utils.h"
 #include "json.h"
 #include "strbuf.h"
+#include "internal_utils.h"
 #include <string.h>
 #include <ctype.h>
 #include <stddef.h>
@@ -98,6 +99,29 @@ sr_operating_system_to_json(struct sr_operating_system *operating_system)
     return sr_strbuf_free_nobuf(strbuf);
 }
 
+struct sr_operating_system *
+sr_operating_system_from_json(struct sr_json_value *root, char **error_message)
+{
+    if (!JSON_CHECK_TYPE(root, SR_JSON_OBJECT, "operating system"))
+        return NULL;
+
+    struct sr_operating_system *result = sr_operating_system_new();
+
+    bool success =
+        JSON_READ_STRING(root, "name", &result->name) &&
+        JSON_READ_STRING(root, "version", &result->version) &&
+        JSON_READ_STRING(root, "architecture", &result->architecture) &&
+        JSON_READ_UINT64(root, "uptime", &result->uptime);
+
+    if (!success)
+    {
+        sr_operating_system_free(result);
+        return NULL;
+    }
+
+    return result;
+}
+
 bool
 sr_operating_system_parse_etc_system_release(const char *etc_system_release,
                                              char **name,
diff --git a/lib/python_frame.c b/lib/python_frame.c
index 07502a1..c5bbfd2 100644
--- a/lib/python_frame.c
+++ b/lib/python_frame.c
@@ -336,6 +336,65 @@ sr_python_frame_to_json(struct sr_python_frame *frame)
     return sr_strbuf_free_nobuf(strbuf);
 }
 
+struct sr_python_frame *
+sr_python_frame_from_json(struct sr_json_value *root, char **error_message)
+{
+    if (!JSON_CHECK_TYPE(root, SR_JSON_OBJECT, "frame"))
+        return NULL;
+
+    struct sr_python_frame *result = sr_python_frame_new();
+    struct sr_json_value *val;
+
+    /* Source file name / special file */
+    if ((val = json_element(root, "file_name")))
+    {
+        if (!JSON_CHECK_TYPE(val, SR_JSON_STRING, "file_name"))
+            goto fail;
+
+        result->special_file = false;
+        result->file_name = sr_strdup(val->u.string.ptr);
+    }
+    else if ((val = json_element(root, "special_file")))
+    {
+        if (!JSON_CHECK_TYPE(val, SR_JSON_STRING, "special_file"))
+            goto fail;
+
+        result->special_file = true;
+        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_STRING(root, "line_contents", &result->line_contents) &&
+        JSON_READ_UINT32(root, "file_line", &result->file_line);
+
+    if (!success)
+        goto fail;
+
+    return result;
+
+fail:
+    sr_python_frame_free(result);
+    return NULL;
+}
+
 void
 sr_python_frame_append_to_str(struct sr_python_frame *frame,
                               struct sr_strbuf *dest)
diff --git a/lib/python_stacktrace.c b/lib/python_stacktrace.c
index b7c69cf..7cc3ed2 100644
--- a/lib/python_stacktrace.c
+++ b/lib/python_stacktrace.c
@@ -228,6 +228,45 @@ sr_python_stacktrace_to_json(struct sr_python_stacktrace *stacktrace)
     return sr_strbuf_free_nobuf(strbuf);
 }
 
+struct sr_python_stacktrace *
+sr_python_stacktrace_from_json(struct sr_json_value *root, char **error_message)
+{
+    if (!JSON_CHECK_TYPE(root, SR_JSON_OBJECT, "stacktrace"))
+        return NULL;
+
+    struct sr_python_stacktrace *result = sr_python_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_python_frame *frame = sr_python_frame_from_json(frame_json,
+                error_message);
+
+            if (!frame)
+                goto fail;
+
+            result->frames = sr_python_frame_append(result->frames, frame);
+        }
+    }
+
+    return result;
+
+fail:
+    sr_python_stacktrace_free(result);
+    return NULL;
+}
+
 char *
 sr_python_stacktrace_get_reason(struct sr_python_stacktrace *stacktrace)
 {
diff --git a/lib/report.c b/lib/report.c
index b2c4d29..0b278b4 100644
--- a/lib/report.c
+++ b/lib/report.c
@@ -28,6 +28,7 @@
 #include "java/stacktrace.h"
 #include "operating_system.h"
 #include "rpm.h"
+#include "internal_utils.h"
 #include "strbuf.h"
 #include <string.h>
 #include <assert.h>
@@ -254,3 +255,123 @@ sr_report_to_json(struct sr_report *report)
     sr_strbuf_append_str(strbuf, "}");
     return sr_strbuf_free_nobuf(strbuf);
 }
+
+static enum sr_report_type
+report_type_from_string(const char *report_type_str)
+{
+    assert(report_type_str);
+
+    if (0 == strcmp("core", report_type_str))
+        return SR_REPORT_CORE;
+    else if (0 == strcmp("python", report_type_str))
+        return SR_REPORT_PYTHON;
+    else if (0 == strcmp("kerneloops", report_type_str))
+        return SR_REPORT_KERNELOOPS;
+    else if (0 == strcmp("java", report_type_str))
+        return SR_REPORT_JAVA;
+    else
+        return SR_REPORT_INVALID;
+}
+
+struct sr_report *
+sr_report_from_json(struct sr_json_value *root, char **error_message)
+{
+    if (!JSON_CHECK_TYPE(root, SR_JSON_OBJECT, "root value"))
+        return NULL;
+
+    bool success;
+    struct sr_report *report = sr_report_new();
+
+    /* Report version. */
+    if (!JSON_READ_UINT32(root, "ureport_version", &report->report_version))
+        goto fail;
+
+    /* Reporter name and version. */
+    struct sr_json_value *reporter = json_element(root, "reporter");
+    if (reporter)
+    {
+        success =
+            JSON_CHECK_TYPE(reporter, SR_JSON_OBJECT, "reporter") &&
+            JSON_READ_STRING(reporter, "name", &report->reporter_name) &&
+            JSON_READ_STRING(reporter, "version", &report->reporter_version);
+
+        if (!success)
+            goto fail;
+    }
+
+    /* Operating system. */
+    struct sr_json_value *os = json_element(root, "os");
+    if (os)
+    {
+        report->operating_system = sr_operating_system_from_json(os, error_message);
+        if (!report->operating_system)
+            goto fail;
+    }
+
+    /* Packages. */
+    struct sr_json_value *packages = json_element(root, "packages");
+    if (packages)
+    {
+        /* In the future, we'll choose the parsing function according to OS here. */
+        report->rpm_packages = sr_rpm_package_from_json(packages, true, error_message);
+        if (!report->rpm_packages)
+            goto fail;
+    }
+
+    /* Problem. */
+    struct sr_json_value *problem = json_element(root, "problem");
+    if (problem)
+    {
+        char *report_type;
+
+        success =
+            JSON_CHECK_TYPE(problem, SR_JSON_OBJECT, "problem") &&
+            JSON_READ_STRING(problem, "type", &report_type) &&
+            JSON_READ_STRING(problem, "component", &report->component_name);
+
+        if (!success)
+            goto fail;
+
+        report->report_type = report_type_from_string(report_type);
+
+        /* User. */
+        struct sr_json_value *user = json_element(root, "user");
+        if (user)
+        {
+            success =
+                JSON_CHECK_TYPE(user, SR_JSON_OBJECT, "user") &&
+                JSON_READ_BOOL(user, "root", &report->user_root) &&
+                JSON_READ_BOOL(user, "local", &report->user_local);
+
+            if (!success)
+                goto fail;
+        }
+
+        /* Stacktrace. */
+        switch (report->report_type)
+        {
+        case SR_REPORT_CORE:
+            report->core_stacktrace = sr_core_stacktrace_from_json(problem, error_message);
+            break;
+        case SR_REPORT_PYTHON:
+            report->python_stacktrace = sr_python_stacktrace_from_json(problem, error_message);
+            break;
+        case SR_REPORT_KERNELOOPS:
+            report->koops_stacktrace = sr_koops_stacktrace_from_json(problem, error_message);
+            break;
+        case SR_REPORT_JAVA:
+            report->java_stacktrace = sr_java_stacktrace_from_json(problem, error_message);
+            break;
+        default:
+            /* Invalid report type -> no stacktrace. */
+            break;
+        }
+
+    }
+
+    return report;
+
+fail:
+    sr_report_free(report);
+    return NULL;
+}
diff --git a/lib/rpm.c b/lib/rpm.c
index eb18b61..a420433 100644
--- a/lib/rpm.c
+++ b/lib/rpm.c
@@ -22,6 +22,7 @@
 #include "json.h"
 #include "strbuf.h"
 #include "config.h"
+#include "internal_utils.h"
 #include <errno.h>
 #ifdef HAVE_LIBRPM
 #include <rpm/rpmlib.h>
@@ -568,6 +569,84 @@ sr_rpm_package_to_json(struct sr_rpm_package *package,
     return sr_strbuf_free_nobuf(strbuf);
 }
 
+static struct sr_rpm_package *
+single_rpm_pacakge_from_json(struct sr_json_value *root, char **error_message)
+{
+    if (!JSON_CHECK_TYPE(root, SR_JSON_OBJECT, "package"))
+        return NULL;
+
+    struct sr_rpm_package *package = sr_rpm_package_new();
+
+    bool success =
+        JSON_READ_STRING(root, "name", &package->name) &&
+        JSON_READ_STRING(root, "version", &package->version) &&
+        JSON_READ_STRING(root, "release", &package->release) &&
+        JSON_READ_STRING(root, "architecture", &package->architecture) &&
+        JSON_READ_UINT32(root, "epoch", &package->epoch) &&
+        JSON_READ_UINT64(root, "install_time", &package->install_time);
+
+    if (!success)
+        goto fail;
+
+    struct sr_json_value *role_json = json_element(root, "package_role");
+    if (role_json)
+    {
+        if (!JSON_CHECK_TYPE(role_json, SR_JSON_STRING, "package_role"))
+            goto fail;
+
+        /* We only know "affected" so far. */
+        char *role = role_json->u.string.ptr;
+        if (0 != strcmp(role, "affected"))
+        {
+            if (error_message)
+                *error_message = sr_asprintf("Invalid package role %s", role);
+            return NULL;
+        }
+
+        package->role = SR_ROLE_AFFECTED;
+    }
+
+    return package;
+
+fail:
+    sr_rpm_package_free(package, true);
+    return NULL;
+}
+
+struct sr_rpm_package *
+sr_rpm_package_from_json(struct sr_json_value *json, bool recursive, char **error_message)
+{
+    if (!recursive)
+    {
+        return single_rpm_pacakge_from_json(json, error_message);
+    }
+    else
+    {
+        if (!JSON_CHECK_TYPE(json, SR_JSON_ARRAY, "package list"))
+            return NULL;
+
+        struct sr_rpm_package *result = NULL;
+
+        struct sr_json_value *pkg_json;
+        FOR_JSON_ARRAY(json, pkg_json)
+        {
+            struct sr_rpm_package *pkg = single_rpm_pacakge_from_json(pkg_json, error_message);
+            if (!pkg)
+                goto fail;
+
+            result = sr_rpm_package_append(result, pkg);
+        }
+
+        if (result == NULL && error_message)
+            *error_message = sr_strdup("No RPM packages present");
+        return result;
+
+fail:
+        sr_rpm_package_free(result, true);
+        return NULL;
+    }
+}
+
 bool
 sr_rpm_package_parse_nvr(const char *text,
                          char **name,
-- 
1.8.3.1



More information about the Crash-catcher mailing list