Discussion:
[SATYR PATCHv3 2/7] Helper functions for json deserialization
Martin Milata
2013-08-26 14:47:42 UTC
Permalink
Written in a way to repeat as little code as possible when used:
- when a key is not present in an JSON object, just silently go on,
validation of required items will be done separately.
- the functions set error_message variable to the description of the
error when they fail; macros are provided that pass the error_message
argument implicitly

Signed-off-by: Martin Milata <mmilata-H+wXaHxf7aLQT0dZR+***@public.gmane.org>
---
lib/internal_utils.h | 61 ++++++++++++++++++++++++++++++++++++++++++++++++
lib/json.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 126 insertions(+)

diff --git a/lib/internal_utils.h b/lib/internal_utils.h
index e69fb97..132905a 100644
--- a/lib/internal_utils.h
+++ b/lib/internal_utils.h
@@ -23,6 +23,11 @@
#include "utils.h"
#include <stddef.h>
#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+struct sr_json_value;
+enum sr_json_type;

void
warn(const char *fmt, ...) __sr_printf(1, 2);
@@ -60,4 +65,60 @@ struct sr_taint_flag
};
extern struct sr_taint_flag sr_flags[];

+/* Checks whether json value is of the right type and sets the error_message if
+ * not. The "name" argument is used in the error message. */
+bool
+json_check_type(struct sr_json_value *value, enum sr_json_type type,
+ const char *name, char **error_message);
+
+/* same as above with implicit error_message argument */
+#define JSON_CHECK_TYPE(value, type, name) json_check_type(value, type, name, error_message)
+
+struct sr_json_value *
+json_element(struct sr_json_value *object, const char *name);
+
+/* These functions check whether JSON object "object" has key "key_name". If it
+ * does not, they don't do anything and return true.
+ *
+ * If it does, they check whether the JSON type of the element corresponds to
+ * the C type of "dest" -- if it does not, they set the error message and
+ * return false.
+ *
+ * Finally, if the type is correct, they write the value to "dest" and return true.
+ */
+bool
+json_read_uint64(struct sr_json_value *object, const char *key_name, uint64_t *dest,
+ char **error_message);
+bool
+json_read_uint32(struct sr_json_value *object, const char *key_name, uint32_t *dest,
+ char **error_message);
+bool
+json_read_uint16(struct sr_json_value *object, const char *key_name, uint16_t *dest,
+ char **error_message);
+bool
+json_read_string(struct sr_json_value *object, const char *key_name, char **dest,
+ char **error_message);
+bool
+json_read_bool(struct sr_json_value *object, const char *key_name, bool *dest,
+ char **error_message);
+
+/* same as above with implicit error_message argument */
+#define JSON_READ_UINT64(object, key_name, dest) \
+ json_read_uint64(object, key_name, dest, error_message)
+#define JSON_READ_UINT32(object, key_name, dest) \
+ json_read_uint32(object, key_name, dest, error_message)
+#define JSON_READ_UINT16(object, key_name, dest) \
+ json_read_uint16(object, key_name, dest, error_message)
+#define JSON_READ_STRING(object, key_name, dest) \
+ json_read_string(object, key_name, dest, error_message)
+#define JSON_READ_BOOL(object, key_name, dest) \
+ json_read_bool(object, key_name, dest, error_message)
+
+/* iterates over json array "json", assigning the current element to "elem_var" */
+#define FOR_JSON_ARRAY(json, elem_var) \
+ assert(json->type == SR_JSON_ARRAY); \
+ for(unsigned _j = 0; \
+ _j < json->u.array.length && (elem_var = json->u.array.values[_j]); \
+ ++_j)
+
#endif
diff --git a/lib/json.c b/lib/json.c
index 1e6ab79..aa60c0a 100644
--- a/lib/json.c
+++ b/lib/json.c
@@ -37,6 +37,7 @@
#include <stdio.h>
#include <string.h>
#include <ctype.h>
+#include <assert.h>

typedef unsigned short json_uchar;

@@ -740,3 +741,67 @@ sr_json_append_escaped(struct sr_strbuf *strbuf, const char *str)

return strbuf;
}
+
+static char *type_names[] = {
+ [SR_JSON_NONE] = "none",
+ [SR_JSON_OBJECT] = "object",
+ [SR_JSON_ARRAY] = "array",
+ [SR_JSON_INTEGER] = "integer",
+ [SR_JSON_DOUBLE] = "double",
+ [SR_JSON_STRING] = "string",
+ [SR_JSON_BOOLEAN] = "boolean",
+ [SR_JSON_NULL] = "null",
+};
+bool
+json_check_type(struct sr_json_value *value, enum sr_json_type type,
+ const char *name, char **error_message)
+{
+ if (value->type == type)
+ return true;
+
+ assert(type >= SR_JSON_NONE && type <= SR_JSON_NULL);
+ char *type_str = type_names[type];
+
+ if (error_message)
+ *error_message = sr_asprintf("Invalid type of %s; %s expected", name, type_str);
+ return false;
+}
+
+struct sr_json_value *
+json_element(struct sr_json_value *object, const char *key_name)
+{
+ assert(object->type == SR_JSON_OBJECT);
+
+ for (unsigned i = 0; i < object->u.object.length; ++i)
+ {
+ if (0 == strcmp(key_name, object->u.object.values[i].name))
+ return object->u.object.values[i].value;
+ }
+
+ return NULL;
+}
+
+#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) \
+ { \
+ struct sr_json_value *val = json_element(object, key_name); \
+ \
+ if (!val) \
+ return true; \
+ \
+ if (!json_check_type(val, json_type, key_name, error_message)) \
+ return false; \
+ \
+ *dest = conversion(val->json_member); \
+ \
+ return true; \
+ }
+
+#define NOOP
+
+DEFINE_JSON_READ(json_read_uint64, uint64_t, SR_JSON_INTEGER, u.integer, NOOP)
+DEFINE_JSON_READ(json_read_uint32, uint32_t, SR_JSON_INTEGER, u.integer, NOOP)
+DEFINE_JSON_READ(json_read_uint16, uint16_t, SR_JSON_INTEGER, u.integer, NOOP)
+DEFINE_JSON_READ(json_read_string, char *, SR_JSON_STRING, u.string.ptr, sr_strdup)
+DEFINE_JSON_READ(json_read_bool, bool, SR_JSON_BOOLEAN, u.boolean, NOOP)
--
1.8.3.1
Martin Milata
2013-08-26 14:47:41 UTC
Permalink
Signed-off-by: Martin Milata <mmilata-H+wXaHxf7aLQT0dZR+***@public.gmane.org>
---
include/java/frame.h | 9 +++++++++
lib/java_frame.c | 15 +++++++++++++++
2 files changed, 24 insertions(+)

diff --git a/include/java/frame.h b/include/java/frame.h
index 57eb96e..903fde2 100644
--- a/include/java/frame.h
+++ b/include/java/frame.h
@@ -135,6 +135,15 @@ void
sr_java_frame_free_full(struct sr_java_frame *frame);

/**
+ * Appends 'item' at the end of the list 'dest'.
+ * @returns
+ * This function returns the 'dest' frame.
+ */
+struct sr_java_frame *
+sr_java_frame_append(struct sr_java_frame *dest,
+ struct sr_java_frame *item);
+
+/**
* Gets a number of frame in list.
* @param frame
* If the frame is NULL, no operation is performed.
diff --git a/lib/java_frame.c b/lib/java_frame.c
index 2bea373..a9f906c 100644
--- a/lib/java_frame.c
+++ b/lib/java_frame.c
@@ -194,6 +194,21 @@ sr_java_frame_cmp_distance(struct sr_java_frame *frame1,
return 0;
}

+struct sr_java_frame *
+sr_java_frame_append(struct sr_java_frame *dest,
+ struct sr_java_frame *item)
+{
+ if (!dest)
+ return item;
+
+ struct sr_java_frame *dest_loop = dest;
+ while (dest_loop->next)
+ dest_loop = dest_loop->next;
+
+ dest_loop->next = item;
+ return dest;
+}
+
void
sr_java_frame_append_to_str(struct sr_java_frame *frame,
struct sr_strbuf *dest)
--
1.8.3.1
Martin Milata
2013-08-26 14:47:44 UTC
Permalink
Signed-off-by: Martin Milata <mmilata-H+wXaHxf7aLQT0dZR+***@public.gmane.org>
---
include/stacktrace.h | 14 ++++++++++++++
lib/core_stacktrace.c | 1 +
lib/gdb_stacktrace.c | 9 +++++++++
lib/generic_stacktrace.c | 25 +++++++++++++++++++++++++
lib/generic_stacktrace.h | 6 +++++-
lib/java_stacktrace.c | 1 +
lib/koops_stacktrace.c | 1 +
lib/python_stacktrace.c | 1 +
8 files changed, 57 insertions(+), 1 deletion(-)

diff --git a/include/stacktrace.h b/include/stacktrace.h
index 13152a0..8009fc1 100644
--- a/include/stacktrace.h
+++ b/include/stacktrace.h
@@ -37,6 +37,8 @@ extern "C" {

#include "report_type.h"

+struct sr_json_value;
+
struct sr_stacktrace
{
enum sr_report_type type;
@@ -94,6 +96,18 @@ char *
sr_stacktrace_to_json(struct sr_stacktrace *stacktrace);

/**
+ * Deserialize stacktrace from its json representation.
+ */
+struct sr_stacktrace*
+sr_stacktrace_from_json(enum sr_report_type, struct sr_json_value *root, char **error_message);
+
+/**
+ * Deserialize stacktrace from its json representation.
+ */
+struct sr_stacktrace*
+sr_stacktrace_from_json_text(enum sr_report_type, const char *input, char **error_message);
+
+/**
* Returns brief, human-readable explanation of the stacktrace.
*/
char *
diff --git a/lib/core_stacktrace.c b/lib/core_stacktrace.c
index de40085..0e1bca1 100644
--- a/lib/core_stacktrace.c
+++ b/lib/core_stacktrace.c
@@ -54,6 +54,7 @@ struct stacktrace_methods core_stacktrace_methods =
.parse_location = (parse_location_fn_t) NULL,
.to_short_text = (to_short_text_fn_t) stacktrace_to_short_text,
.to_json = (to_json_fn_t) sr_core_stacktrace_to_json,
+ .from_json = (from_json_fn_t) sr_core_stacktrace_from_json,
.get_reason = (get_reason_fn_t) sr_core_stacktrace_get_reason,
.find_crash_thread =
(find_crash_thread_fn_t) sr_core_stacktrace_find_crash_thread,
diff --git a/lib/gdb_stacktrace.c b/lib/gdb_stacktrace.c
index 2147e18..a2ec2bc 100644
--- a/lib/gdb_stacktrace.c
+++ b/lib/gdb_stacktrace.c
@@ -27,6 +27,7 @@
#include "normalize.h"
#include "generic_stacktrace.h"
#include "internal_utils.h"
+#include "json.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -45,6 +46,13 @@ gdb_return_null(struct sr_stacktrace *stacktrace)
return NULL;
}

+static struct sr_gdb_stacktrace *
+gdb_from_json(struct sr_json_value *root, char **error_message)
+{
+ *error_message = sr_strdup("Not implemented");
+ return NULL;
+}
+
DEFINE_THREADS_FUNC(gdb_threads, struct sr_gdb_stacktrace)
DEFINE_SET_THREADS_FUNC(gdb_set_threads, struct sr_gdb_stacktrace)
DEFINE_PARSE_WRAPPER_FUNC(gdb_parse, SR_REPORT_GDB)
@@ -55,6 +63,7 @@ struct stacktrace_methods gdb_stacktrace_methods =
.parse_location = (parse_location_fn_t) sr_gdb_stacktrace_parse,
.to_short_text = (to_short_text_fn_t) sr_gdb_stacktrace_to_short_text,
.to_json = (to_json_fn_t) gdb_return_null,
+ .from_json = (from_json_fn_t) gdb_from_json,
.get_reason = (get_reason_fn_t) gdb_return_null,
.find_crash_thread =
(find_crash_thread_fn_t) sr_gdb_stacktrace_find_crash_thread,
diff --git a/lib/generic_stacktrace.c b/lib/generic_stacktrace.c
index 8d6678c..70d876f 100644
--- a/lib/generic_stacktrace.c
+++ b/lib/generic_stacktrace.c
@@ -24,6 +24,7 @@
#include "strbuf.h"
#include "location.h"
#include "sha1.h"
+#include "json.h"

#include "frame.h"
#include "thread.h"
@@ -98,6 +99,30 @@ sr_stacktrace_parse(enum sr_report_type type, const char *input, char **error_me
return DISPATCH(dtable, type, parse)(input, error_message);
}

+struct sr_stacktrace *
+sr_stacktrace_from_json(enum sr_report_type type, struct sr_json_value *root, char **error_message)
+{
+ return DISPATCH(dtable, type, from_json)(root, error_message);
+}
+
+struct sr_stacktrace *
+sr_stacktrace_from_json_text(enum sr_report_type type, const char *input, char **error_message)
+{
+ struct sr_json_settings settings;
+ memset(&settings, 0, sizeof(struct sr_json_settings));
+ struct sr_location location;
+ sr_location_init(&location);
+ struct sr_json_value *json_root = sr_json_parse_ex(&settings, input, &location);
+
+ if (!json_root)
+ {
+ *error_message = sr_location_to_string(&location);
+ return NULL;
+ }
+
+ return sr_stacktrace_from_json(type, json_root, error_message);
+}
+
char *
sr_stacktrace_to_short_text(struct sr_stacktrace *stacktrace, int max_frames)
{
diff --git a/lib/generic_stacktrace.h b/lib/generic_stacktrace.h
index 4112d70..8e3d629 100644
--- a/lib/generic_stacktrace.h
+++ b/lib/generic_stacktrace.h
@@ -22,12 +22,15 @@

#include "stacktrace.h"
#include "thread.h"
-#include "strbuf.h"
+
+struct sr_json_value;
+struct sr_strbuf;

typedef struct sr_stacktrace* (*parse_fn_t)(const char *, char **);
typedef struct sr_stacktrace* (*parse_location_fn_t)(const char **, struct sr_location *);
typedef char* (*to_short_text_fn_t)(struct sr_stacktrace*, int);
typedef char* (*to_json_fn_t)(struct sr_stacktrace *);
+typedef struct sr_stacktrace* (*from_json_fn_t)(struct sr_json_value *, char **);
typedef char* (*get_reason_fn_t)(struct sr_stacktrace *);
typedef struct sr_thread* (*find_crash_thread_fn_t)(struct sr_stacktrace *);
typedef struct sr_thread* (*threads_fn_t)(struct sr_stacktrace *);
@@ -42,6 +45,7 @@ struct stacktrace_methods
parse_location_fn_t parse_location;
to_short_text_fn_t to_short_text;
to_json_fn_t to_json;
+ from_json_fn_t from_json;
get_reason_fn_t get_reason;
find_crash_thread_fn_t find_crash_thread;
threads_fn_t threads;
diff --git a/lib/java_stacktrace.c b/lib/java_stacktrace.c
index f2ddce3..4914b7d 100644
--- a/lib/java_stacktrace.c
+++ b/lib/java_stacktrace.c
@@ -49,6 +49,7 @@ struct stacktrace_methods java_stacktrace_methods =
.parse_location = (parse_location_fn_t) sr_java_stacktrace_parse,
.to_short_text = (to_short_text_fn_t) stacktrace_to_short_text,
.to_json = (to_json_fn_t) sr_java_stacktrace_to_json,
+ .from_json = (from_json_fn_t) sr_java_stacktrace_from_json,
.get_reason = (get_reason_fn_t) sr_java_stacktrace_get_reason,
.find_crash_thread =
(find_crash_thread_fn_t) sr_java_find_crash_thread,
diff --git a/lib/koops_stacktrace.c b/lib/koops_stacktrace.c
index 9bc181c..6b72d54 100644
--- a/lib/koops_stacktrace.c
+++ b/lib/koops_stacktrace.c
@@ -87,6 +87,7 @@ struct stacktrace_methods koops_stacktrace_methods =
.parse_location = (parse_location_fn_t) sr_koops_stacktrace_parse,
.to_short_text = (to_short_text_fn_t) stacktrace_to_short_text,
.to_json = (to_json_fn_t) sr_koops_stacktrace_to_json,
+ .from_json = (from_json_fn_t) sr_koops_stacktrace_from_json,
.get_reason = (get_reason_fn_t) sr_koops_stacktrace_get_reason,
.find_crash_thread = (find_crash_thread_fn_t) stacktrace_one_thread_only,
.threads = (threads_fn_t) stacktrace_one_thread_only,
diff --git a/lib/python_stacktrace.c b/lib/python_stacktrace.c
index 7cc3ed2..3dc0a75 100644
--- a/lib/python_stacktrace.c
+++ b/lib/python_stacktrace.c
@@ -66,6 +66,7 @@ struct stacktrace_methods python_stacktrace_methods =
.parse_location = (parse_location_fn_t) sr_python_stacktrace_parse,
.to_short_text = (to_short_text_fn_t) stacktrace_to_short_text,
.to_json = (to_json_fn_t) sr_python_stacktrace_to_json,
+ .from_json = (from_json_fn_t) sr_python_stacktrace_from_json,
.get_reason = (get_reason_fn_t) sr_python_stacktrace_get_reason,
.find_crash_thread = (find_crash_thread_fn_t) stacktrace_one_thread_only,
.threads = (threads_fn_t) stacktrace_one_thread_only,
--
1.8.3.1
Martin Milata
2013-08-26 14:47:45 UTC
Permalink
Signed-off-by: Martin Milata <mmilata-H+wXaHxf7aLQT0dZR+***@public.gmane.org>
---
python/py_koops_stacktrace.c | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/python/py_koops_stacktrace.c b/python/py_koops_stacktrace.c
index 0057cb1..80bc104 100644
--- a/python/py_koops_stacktrace.c
+++ b/python/py_koops_stacktrace.c
@@ -42,9 +42,18 @@ koops_stacktrace_methods[] =
{ NULL },
};

+/* See python/py_common.h and python/py_gdb_frame.c for generic getters/setters documentation. */
+#define GSOFF_PY_STRUCT sr_py_koops_stacktrace
+#define GSOFF_PY_MEMBER stacktrace
+#define GSOFF_C_STRUCT sr_koops_stacktrace
+GSOFF_START
+GSOFF_MEMBER(version)
+GSOFF_END
+
static PyGetSetDef
koops_stacktrace_getset[] =
{
+ SR_ATTRIBUTE_STRING(version, "Kernel version (string)"),
{ (char*)"modules", sr_py_koops_stacktrace_get_modules, sr_py_koops_stacktrace_set_modules, (char*)b_modules_doc, NULL },
{ (char*)"taint_flags", sr_py_koops_stacktrace_get_taint_flags, sr_py_koops_stacktrace_set_taint_flags, (char*)b_taint_flags_doc, NULL },
{ NULL },
--
1.8.3.1
Martin Milata
2013-08-26 14:47:47 UTC
Permalink
Signed-off-by: Martin Milata <mmilata-H+wXaHxf7aLQT0dZR+***@public.gmane.org>
---
tests/python/core.py | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++
tests/python/java.py | 80 ++++++++++++++++++++++++++++++++++++++++++++++++
tests/python/koops.py | 80 ++++++++++++++++++++++++++++++++++++++++++++++++
tests/python/python.py | 50 ++++++++++++++++++++++++++++++
4 files changed, 292 insertions(+)

diff --git a/tests/python/core.py b/tests/python/core.py
index cf09ba0..e4593b4 100755
--- a/tests/python/core.py
+++ b/tests/python/core.py
@@ -55,6 +55,88 @@ class TestCoreStacktrace(BindingsTestCase):
def test_crash_thread(self):
self.assertTrue(self.trace.crash_thread is self.trace.threads[1])

+ def test_from_json(self):
+ trace = satyr.CoreStacktrace.from_json('{}')
+ self.assertEqual(trace.threads, [])
+
+ json_text = '''
+ {
+ "signal": 9,
+ "executable": "C:\\\\porn.bat",
+ "stacktrace": [
+ {
+ "frames": [
+ {
+ "address": 60100,
+ "build_id": "aaaaaaaaaaaaaaaaaaaaaaaaAAAAAAAAAAA",
+ "build_id_offset": 5,
+ "function_name": "destroy_universe",
+ "file_name": "del.exe",
+ "fingerprint": "1234123412341234123412341234",
+ "fingerprint_hashed": true,
+ },
+ {},
+ {
+ "address": 2,
+ "fingerprint": "a b c d",
+ "fingerprint_hashed": false,
+ }
+ ]
+ },
+ {
+ "crash_thread": true,
+ "bogus": "value",
+ "frames": [
+ {
+ "address": 1,
+ "build_id": "b",
+ "build_id_offset": 5,
+ }
+ ]
+ }
+ ]
+ }
+'''
+ trace = satyr.CoreStacktrace.from_json(json_text)
+ self.assertEqual(trace.signal, 9)
+ self.assertEqual(trace.executable, 'C:\\porn.bat')
+
+ self.assertEqual(len(trace.threads), 2)
+ self.assertEqual(len(trace.threads[0].frames), 3)
+ self.assertEqual(len(trace.threads[1].frames), 1)
+
+ self.assertEqual(trace.threads[0].frames[0].address, 60100)
+ self.assertEqual(trace.threads[0].frames[0].build_id, 'aaaaaaaaaaaaaaaaaaaaaaaaAAAAAAAAAAA')
+ self.assertEqual(trace.threads[0].frames[0].build_id_offset, 5)
+ self.assertEqual(trace.threads[0].frames[0].function_name, 'destroy_universe')
+ self.assertEqual(trace.threads[0].frames[0].file_name, 'del.exe')
+ self.assertEqual(trace.threads[0].frames[0].fingerprint, '1234123412341234123412341234')
+ self.assertTrue(trace.threads[0].frames[0].fingerprint_hashed)
+
+ self.assertEqual(trace.threads[0].frames[1].address, 18446744073709551615L) # !!! FIXME
+ self.assertEqual(trace.threads[0].frames[1].build_id, None)
+ self.assertEqual(trace.threads[0].frames[1].build_id_offset, 18446744073709551615L) # !!!
+ self.assertEqual(trace.threads[0].frames[1].function_name, None)
+ self.assertEqual(trace.threads[0].frames[1].file_name, None)
+ self.assertEqual(trace.threads[0].frames[1].fingerprint, None)
+ self.assertTrue(trace.threads[0].frames[1].fingerprint_hashed)
+
+ self.assertEqual(trace.threads[0].frames[2].address, 2)
+ self.assertEqual(trace.threads[0].frames[2].build_id, None)
+ self.assertEqual(trace.threads[0].frames[2].build_id_offset, 18446744073709551615L) # !!!
+ self.assertEqual(trace.threads[0].frames[2].function_name, None)
+ self.assertEqual(trace.threads[0].frames[2].file_name, None)
+ self.assertEqual(trace.threads[0].frames[2].fingerprint, 'a b c d')
+ self.assertFalse(trace.threads[0].frames[2].fingerprint_hashed)
+
+ self.assertEqual(trace.threads[1].frames[0].address, 1)
+ self.assertEqual(trace.threads[1].frames[0].build_id, 'b')
+ self.assertEqual(trace.threads[1].frames[0].build_id_offset, 5)
+ self.assertEqual(trace.threads[1].frames[0].function_name, None)
+ self.assertEqual(trace.threads[1].frames[0].file_name, None)
+ self.assertEqual(trace.threads[1].frames[0].fingerprint, None)
+ self.assertTrue(trace.threads[1].frames[0].fingerprint_hashed)
+
class TestCoreThread(BindingsTestCase):
def setUp(self):
self.thread = satyr.CoreStacktrace(contents).threads[0]
diff --git a/tests/python/java.py b/tests/python/java.py
index 6a9a227..175273a 100755
--- a/tests/python/java.py
+++ b/tests/python/java.py
@@ -52,6 +52,86 @@ class TestJavaStacktrace(BindingsTestCase):
def test_bthash(self):
self.assertEqual(self.trace.get_bthash(), '184370433901aa9f14bfc85c29576e24ebca2c2e')

+ def test_from_json(self):
+ trace = satyr.JavaStacktrace.from_json('{}')
+ self.assertEqual(trace.threads, [])
+
+ json_text = '''
+ {
+ "threads": [
+ {
+ "name": "ProbablySomeThreadOrSomethingImpl",
+ "frames": [
+ {
+ "name": "julia",
+ "file_name": "/u/b/f",
+ "file_line": 9000,
+ "class_path": "i don't know",
+ "is_native": false,
+ "is_exception": false,
+ "message": "Hi!"
+ },
+ {},
+ {
+ "name": "helen",
+ "is_exception": true
+ }
+ ],
+ "what is": "this",
+ },
+ {
+ "name": "TheOtherThread",
+ "frames": [
+ {
+ "name": "tiffany",
+ "file_name": "blahz",
+ "is_native": true,
+ }
+ ]
+ }
+ ]
+ }
+'''
+ trace = satyr.JavaStacktrace.from_json(json_text)
+ self.assertEqual(len(trace.threads), 2)
+ self.assertEqual(len(trace.threads[0].frames), 3)
+ self.assertEqual(len(trace.threads[1].frames), 1)
+
+ self.assertEqual(trace.threads[0].name, 'ProbablySomeThreadOrSomethingImpl')
+ self.assertEqual(trace.threads[1].name, 'TheOtherThread')
+
+ self.assertEqual(trace.threads[0].frames[0].name, 'julia')
+ self.assertEqual(trace.threads[0].frames[0].file_name, '/u/b/f')
+ self.assertEqual(trace.threads[0].frames[0].file_line, 9000)
+ self.assertEqual(trace.threads[0].frames[0].class_path, 'i don\'t know')
+ self.assertFalse(trace.threads[0].frames[0].is_native)
+ self.assertFalse(trace.threads[0].frames[0].is_exception)
+ self.assertEqual(trace.threads[0].frames[0].message, 'Hi!')
+
+ self.assertEqual(trace.threads[0].frames[1].name, None)
+ self.assertEqual(trace.threads[0].frames[1].file_name, None)
+ self.assertEqual(trace.threads[0].frames[1].file_line, 0)
+ self.assertEqual(trace.threads[0].frames[1].class_path, None)
+ self.assertFalse(trace.threads[0].frames[1].is_native)
+ self.assertFalse(trace.threads[0].frames[1].is_exception)
+ self.assertEqual(trace.threads[0].frames[1].message, None)
+
+ self.assertEqual(trace.threads[0].frames[2].name, 'helen')
+ self.assertEqual(trace.threads[0].frames[2].file_name, None)
+ self.assertEqual(trace.threads[0].frames[2].file_line, 0)
+ self.assertEqual(trace.threads[0].frames[2].class_path, None)
+ self.assertFalse(trace.threads[0].frames[2].is_native)
+ self.assertTrue(trace.threads[0].frames[2].is_exception)
+ self.assertEqual(trace.threads[0].frames[2].message, None)
+
+ self.assertEqual(trace.threads[1].frames[0].name, 'tiffany')
+ self.assertEqual(trace.threads[1].frames[0].file_name, 'blahz')
+ self.assertEqual(trace.threads[1].frames[0].file_line, 0)
+ self.assertEqual(trace.threads[1].frames[0].class_path, None)
+ self.assertTrue(trace.threads[1].frames[0].is_native)
+ self.assertFalse(trace.threads[1].frames[0].is_exception)
+ self.assertEqual(trace.threads[1].frames[0].message, None)
+
class TestJavaThread(BindingsTestCase):
def setUp(self):
self.thread = satyr.JavaStacktrace(contents).threads[0]
diff --git a/tests/python/koops.py b/tests/python/koops.py
index 260cb6e..7609a98 100755
--- a/tests/python/koops.py
+++ b/tests/python/koops.py
@@ -73,6 +73,86 @@ class TestKerneloops(BindingsTestCase):
def test_crash_thread(self):
self.assertTrue(self.koops.crash_thread is self.koops)

+ def test_from_json(self):
+ trace = satyr.Kerneloops.from_json('{}')
+ self.assertEqual(trace.frames, [])
+
+ json_text = '''
+ {
+ "version": "3.11.666",
+ "taint_flags": ["warning", "died_recently"],
+ "modules": ["not", "much", "modules", "loaded"],
+ "frames": [
+ {
+ "address": 28,
+ "reliable": true,
+ "function_name": "f",
+ "function_offset": 5,
+ "function_length": 6,
+ "module_name": "modularmodule",
+ "from_address": 27,
+ "from_function_name": "h",
+ "from_function_offset": 3,
+ "from_function_length": 9,
+ "from_module_name": "nonmodularmodule",
+ "byl": "jsem tady"
+ },
+ {
+ },
+ {
+ "address": 23,
+ "reliable": false,
+ "function_name": "g",
+ "module_name": "much",
+ }
+ ]
+ }
+'''
+ trace = satyr.Kerneloops.from_json(json_text)
+ print dir(trace)
+ self.assertEqual(trace.version, '3.11.666')
+ self.assertTrue(trace.taint_flags['warning'])
+ self.assertTrue(trace.taint_flags['died_recently'])
+ self.assertFalse(trace.taint_flags['module_out_of_tree'])
+ self.assertEqual(trace.modules, ['not', 'much', 'modules', 'loaded'])
+ self.assertEqual(len(trace.frames), 3)
+
+ self.assertEqual(trace.frames[0].address, 28)
+ self.assertTrue(trace.frames[0].reliable)
+ self.assertEqual(trace.frames[0].function_name, 'f')
+ self.assertEqual(trace.frames[0].function_offset, 5)
+ self.assertEqual(trace.frames[0].function_length, 6)
+ self.assertEqual(trace.frames[0].module_name, 'modularmodule')
+ self.assertEqual(trace.frames[0].from_address, 27)
+ self.assertEqual(trace.frames[0].from_function_name, 'h')
+ self.assertEqual(trace.frames[0].from_function_offset, 3)
+ self.assertEqual(trace.frames[0].from_function_length, 9)
+ self.assertEqual(trace.frames[0].from_module_name, 'nonmodularmodule')
+
+ self.assertEqual(trace.frames[1].address, 0)
+ self.assertFalse(trace.frames[1].reliable)
+ self.assertEqual(trace.frames[1].function_name, None)
+ self.assertEqual(trace.frames[1].function_offset, 0)
+ self.assertEqual(trace.frames[1].function_length, 0)
+ self.assertEqual(trace.frames[1].module_name, None)
+ self.assertEqual(trace.frames[1].from_address, 0)
+ self.assertEqual(trace.frames[1].from_function_name, None)
+ self.assertEqual(trace.frames[1].from_function_offset, 0)
+ self.assertEqual(trace.frames[1].from_function_length, 0)
+ self.assertEqual(trace.frames[1].from_module_name, None)
+
+ self.assertEqual(trace.frames[2].address, 23)
+ self.assertFalse(trace.frames[2].reliable)
+ self.assertEqual(trace.frames[2].function_name, 'g')
+ self.assertEqual(trace.frames[2].function_offset, 0)
+ self.assertEqual(trace.frames[2].function_length, 0)
+ self.assertEqual(trace.frames[2].module_name, 'much')
+ self.assertEqual(trace.frames[2].from_address, 0)
+ self.assertEqual(trace.frames[2].from_function_name, None)
+ self.assertEqual(trace.frames[2].from_function_offset, 0)
+ self.assertEqual(trace.frames[2].from_function_length, 0)
+ self.assertEqual(trace.frames[2].from_module_name, None)
+
class TestKoopsFrame(BindingsTestCase):
def setUp(self):
self.frame = satyr.Kerneloops(contents).frames[0]
diff --git a/tests/python/python.py b/tests/python/python.py
index 13c3bd7..a20d3b3 100755
--- a/tests/python/python.py
+++ b/tests/python/python.py
@@ -90,6 +90,56 @@ class TestPythonStacktrace(BindingsTestCase):
def test_crash_thread(self):
self.assertTrue(self.trace.crash_thread is self.trace)

+ def test_from_json(self):
+ trace = satyr.PythonStacktrace.from_json('{}')
+ self.assertEqual(trace.frames, [])
+
+ json_text = '''
+ {
+ "exception_name": "NotImplementedError",
+ "stacktrace": [
+ {
+ "file_name": "/usr/share/foobar/mod/file.py",
+ "function_name": "do_nothing",
+ "file_line": 42,
+ "line_contents": "#this does nothing"
+ },
+ {
+ "special_file": "stdin",
+ "special_function": "module",
+ "file_line": 451
+ },
+ {
+ "unknown_key": 19876543
+ }
+ ]
+ }
+'''
+ trace = satyr.PythonStacktrace.from_json(json_text)
+ self.assertEqual(trace.exception_name, 'NotImplementedError')
+ self.assertEqual(len(trace.frames), 3)
+
+ self.assertEqual(trace.frames[0].file_name, '/usr/share/foobar/mod/file.py')
+ self.assertEqual(trace.frames[0].function_name, 'do_nothing')
+ self.assertEqual(trace.frames[0].file_line, 42)
+ self.assertEqual(trace.frames[0].line_contents, '#this does nothing')
+ self.assertFalse(trace.frames[0].special_file)
+ self.assertFalse(trace.frames[0].special_function)
+
+ self.assertEqual(trace.frames[1].file_name, 'stdin')
+ self.assertEqual(trace.frames[1].function_name, 'module')
+ self.assertEqual(trace.frames[1].file_line, 451)
+ self.assertEqual(trace.frames[1].line_contents, None)
+ self.assertTrue(trace.frames[1].special_file)
+ self.assertTrue(trace.frames[1].special_function)
+
+ self.assertEqual(trace.frames[2].file_name, None)
+ self.assertEqual(trace.frames[2].function_name, None)
+ self.assertEqual(trace.frames[2].file_line, 0)
+ self.assertEqual(trace.frames[2].line_contents, None)
+ self.assertFalse(trace.frames[2].special_file)
+ self.assertFalse(trace.frames[2].special_function)
+
class TestPythonFrame(BindingsTestCase):
def setUp(self):
self.frame = satyr.PythonStacktrace(contents).frames[-1]
--
1.8.3.1
Martin Milata
2013-08-26 14:47:46 UTC
Permalink
Class method from_json is provided to provide additional means of
creating stacktrace objects.

Related to #92, it still remains to implement #86 in order to close it.

Signed-off-by: Martin Milata <mmilata-H+wXaHxf7aLQT0dZR+***@public.gmane.org>
---
python/py_base_stacktrace.c | 80 ++++++++++++++++++++++++++++++++++++++++++---
python/py_base_stacktrace.h | 2 ++
2 files changed, 78 insertions(+), 4 deletions(-)

diff --git a/python/py_base_stacktrace.c b/python/py_base_stacktrace.c
index 399acd9..e9bf484 100644
--- a/python/py_base_stacktrace.c
+++ b/python/py_base_stacktrace.c
@@ -35,6 +35,10 @@
"Returns: string - hash of the stacktrace\n" \
"flags: integer - bitwise sum of flags (BTHASH_NORMAL, BTHASH_NOHASH)"

+#define from_json_doc "Usage: SomeStacktrace.from_json(json_string) (class method)\n" \
+ "Returns: stacktrace (of SomeStacktrace class) deserialized from json_string\n" \
+ "json_string: string - json input"
+
#define crash_thread_doc "Reference to the thread that caused the crash, if known"

#define threads_doc "A list containing the objects representing threads in the stacktrace."
@@ -42,8 +46,9 @@
static PyMethodDef
single_methods[] =
{
- { "to_short_text", sr_py_single_stacktrace_to_short_text, METH_VARARGS, to_short_text_doc },
- { "get_bthash", sr_py_single_stacktrace_get_bthash, METH_VARARGS, get_bthash_doc },
+ { "to_short_text", sr_py_single_stacktrace_to_short_text, METH_VARARGS, to_short_text_doc },
+ { "get_bthash", sr_py_single_stacktrace_get_bthash, METH_VARARGS, get_bthash_doc },
+ { "from_json", sr_py_single_stacktrace_from_json, METH_VARARGS|METH_CLASS, from_json_doc },
{ NULL },
};

@@ -113,8 +118,9 @@ multi_members[] =
static PyMethodDef
multi_methods[] =
{
- { "to_short_text", sr_py_multi_stacktrace_to_short_text, METH_VARARGS, to_short_text_doc },
- { "get_bthash", sr_py_multi_stacktrace_get_bthash, METH_VARARGS, get_bthash_doc },
+ { "to_short_text", sr_py_multi_stacktrace_to_short_text, METH_VARARGS, to_short_text_doc },
+ { "get_bthash", sr_py_multi_stacktrace_get_bthash, METH_VARARGS, get_bthash_doc },
+ { "from_json", sr_py_multi_stacktrace_from_json, METH_VARARGS|METH_CLASS, from_json_doc },
{ NULL },
};

@@ -407,6 +413,72 @@ sr_py_multi_stacktrace_get_crash(PyObject *self, void *unused)
Py_RETURN_NONE;
}

+PyObject *
+sr_py_single_stacktrace_from_json(PyObject *class, PyObject *args)
+{
+ char *json_str, *error_message;
+ if (!PyArg_ParseTuple(args, "s", &json_str))
+ return NULL;
+
+ /* Create new stacktrace */
+ PyObject *noargs = PyTuple_New(0);
+ PyObject *result = PyObject_CallObject(class, noargs);
+ Py_DECREF(noargs);
+
+ /* Free the C structure and thread list */
+ struct sr_py_base_thread *this = (struct sr_py_base_thread*)result;
+ enum sr_report_type type = this->thread->type;
+ /* the list will decref all of its elements */
+ Py_DECREF(this->frames);
+ sr_thread_set_frames(this->thread, NULL);
+ sr_thread_free(this->thread);
+
+ /* Parse json */
+ this->thread = (struct sr_thread*)
+ sr_stacktrace_from_json_text(type, json_str, &error_message);
+
+ if (!this->thread)
+ {
+ PyErr_SetString(PyExc_ValueError, error_message);
+ return NULL;
+ }
+ this->frames = frames_to_python_list((struct sr_thread *)this->thread, this->frame_type);
+
+ return result;
+}
+
+PyObject *
+sr_py_multi_stacktrace_from_json(PyObject *class, PyObject *args)
+{
+ char *json_str, *error_message;
+ if (!PyArg_ParseTuple(args, "s", &json_str))
+ return NULL;
+
+ /* Create new stacktrace */
+ PyObject *noargs = PyTuple_New(0);
+ PyObject *result = PyObject_CallObject(class, noargs);
+ Py_DECREF(noargs);
+
+ /* Free the C structure and thread list */
+ struct sr_py_multi_stacktrace *this = (struct sr_py_multi_stacktrace*)result;
+ enum sr_report_type type = this->stacktrace->type;
+ /* the list will decref all of its elements */
+ Py_DECREF(this->threads);
+ sr_stacktrace_set_threads(this->stacktrace, NULL);
+ sr_stacktrace_free(this->stacktrace);
+
+ /* Parse json */
+ this->stacktrace = sr_stacktrace_from_json_text(type, json_str, &error_message);
+ if (!this->stacktrace)
+ {
+ PyErr_SetString(PyExc_ValueError, error_message);
+ return NULL;
+ }
+ this->threads = threads_to_python_list(this->stacktrace, this->thread_type, this->frame_type);
+
+ return result;
+}
+
int
sr_py_multi_stacktrace_set_crash(PyObject *self, PyObject *value, void *unused)
{
diff --git a/python/py_base_stacktrace.h b/python/py_base_stacktrace.h
index c7945f0..3be2a91 100644
--- a/python/py_base_stacktrace.h
+++ b/python/py_base_stacktrace.h
@@ -57,6 +57,8 @@ PyObject *sr_py_single_stacktrace_to_short_text(PyObject *self, PyObject *args);
PyObject *sr_py_multi_stacktrace_to_short_text(PyObject *self, PyObject *args);
PyObject *sr_py_single_stacktrace_get_bthash(PyObject *self, PyObject *args);
PyObject *sr_py_multi_stacktrace_get_bthash(PyObject *self, PyObject *args);
+PyObject *sr_py_single_stacktrace_from_json(PyObject *cls, PyObject *args);
+PyObject *sr_py_multi_stacktrace_from_json(PyObject *cls, PyObject *args);

PyObject *sr_py_single_stacktrace_get_crash(PyObject *self, void *unused);
int sr_py_single_stacktrace_set_crash(PyObject *self, PyObject *value, void *unused);
--
1.8.3.1
Martin Milata
2013-08-26 14:47:43 UTC
Permalink
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-H+wXaHxf7aLQT0dZR+***@public.gmane.org>
---
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 | 23 ++++++++
lib/python_frame.c | 59 ++++++++++++++++++++
lib/python_stacktrace.c | 39 +++++++++++++
lib/report.c | 121 ++++++++++++++++++++++++++++++++++++++++
lib/rpm.c | 79 ++++++++++++++++++++++++++
24 files changed, 717 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 bfc2a55..f26e322 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;
@@ -49,6 +51,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 2b94d58..de40085 100644
--- a/lib/core_stacktrace.c
+++ b/lib/core_stacktrace.c
@@ -159,99 +159,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 a54ee0a..64f7439 100644
--- a/lib/operating_system.c
+++ b/lib/operating_system.c
@@ -108,6 +108,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
Richard Marko
2013-08-28 09:45:01 UTC
Permalink
Pushed all of them. Thanks!
--
Richard Marko
Loading...