[GSOC 2014][PATCH 1/7] JSON: Import the basic JSON output framework

Zhu YangMin zym0017d at gmail.com
Mon Jul 21 14:35:23 UTC 2014


The JSON output format is used to make it easier for other program
to parse strace's results. We need to modify the existing code to
use this basic framework to support the JSON output.

* defs.h: Add the interface of the JSON output framework.
* json.c(newfile): The Implmentation of JSON output framework.
---
 defs.h |  75 ++++++++++++
 json.c | 401 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 476 insertions(+)
 create mode 100644 json.c

diff --git a/defs.h b/defs.h
index 1a3b483..b943f0c 100644
--- a/defs.h
+++ b/defs.h
@@ -766,7 +766,9 @@ extern struct tcb *printing_tcp;
 extern void printleader(struct tcb *);
 extern void line_ended(void);
 extern void tabto(void);
+extern void _tvprintf(const char *fmt, va_list args);
 extern void tprintf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+extern void _tprints(const char *str);
 extern void tprints(const char *str);
 
 #if SUPPORTED_PERSONALITIES > 1
@@ -829,3 +831,76 @@ extern unsigned num_quals;
 /* Only ensures that sysent[scno] isn't out of range */
 #define SCNO_IN_RANGE(scno) \
 	((unsigned long)(scno) < nsyscalls)
+
+
+/*
+ * Support JSON output enabled by option '-j'
+ */
+
+typedef enum {
+	JSON_LAST = 0,
+
+	JSON_SEPA,
+	JSON_NEWLINE,
+
+	JSON_NULL,
+	JSON_DIRECT,
+	JSON_DIRECT_ESCAPE,
+	JSON_STRING,
+	JSON_STRING_QUOTE,
+
+	JSON_CALL_BEGIN,
+	JSON_NAME,
+	JSON_CALL_END,
+
+	JSON_ARGS_BEGIN,
+	JSON_ARG,
+	JSON_ARGS_END,
+
+	JSON_ARRAY_BEGIN,
+	JSON_ARRAY_END,
+
+	JSON_RET,
+	JSON_DESC,
+	JSON_DESC_LONG,
+	JSON_AUXSTR,
+	JSON_ERRNO,
+	JSON_ERROR,
+
+	JSON_SIGNAL_BEGIN,
+	JSON_SIGNAL_END,
+	JSON_SIGCODE,
+} json_event_t;
+
+/*
+ * Control the behaviour of tprints() and tprintf()
+ */
+
+// defined in strace.c, default to 0.
+extern int jflag;
+
+typedef enum {
+	JSON_HOOK_SKIP,     // skip the output, do nothing
+	JSON_HOOK_NONE,     // output directly, the same to the normal tprints()/tprintf()
+	JSON_HOOK_ALL,      // extract all outputs of a snippet code to a single meta buffer
+	JSON_HOOK_SINGLE,   // extract the meta parameter based on the format string of a tprintf() call
+	JSON_HOOK_MERGE,
+} json_hook_t;
+
+void json_hook_push(json_hook_t mode);
+void json_hook_pop(void);
+
+// the variable in ... must be json_event_t
+void json_output_event(json_hook_t mode, ...);
+int json_init(void);
+
+int json_merge_from(void);
+void json_merge_all(int from);
+
+
+#define json_event(...) json_output_event(JSON_HOOK_NONE, __VA_ARGS__ , JSON_LAST)
+#define json_merge(...) json_output_event(JSON_HOOK_MERGE, __VA_ARGS__, JSON_LAST)
+
+/*
+ * End of Support JSON output enabled by option '-j'
+ */
\ No newline at end of file
diff --git a/json.c b/json.c
new file mode 100644
index 0000000..20397a3
--- /dev/null
+++ b/json.c
@@ -0,0 +1,401 @@
+#include "defs.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <assert.h>
+
+/*
+ * We also modified tprintf()/tprints() to implement this hook framework
+*/
+
+#define HOOK_DATA_SIZE (30)
+static struct json_hook_type{
+	int top;
+	size_t size[HOOK_DATA_SIZE];
+	FILE *hook[HOOK_DATA_SIZE];
+	char *data[HOOK_DATA_SIZE];
+} hook_data;
+
+static void clear_buffer(FILE *file)
+{
+	assert(file != NULL);
+	rewind(file);
+	fflush(file);
+	return;
+}
+static void json_index_sub(void)
+{
+	hook_data.top = (hook_data.top - 1 + HOOK_DATA_SIZE) % HOOK_DATA_SIZE;
+}
+static void json_index_add(void)
+{
+	hook_data.top = (hook_data.top + 1) % HOOK_DATA_SIZE;
+}
+static FILE* json_file_get(int off)
+{
+	while (off < 0)
+		off += HOOK_DATA_SIZE;
+	return hook_data.hook[(hook_data.top + off) % HOOK_DATA_SIZE];
+}
+static const char* json_buf_get(int off)
+{
+	while (off < 0)
+		off += HOOK_DATA_SIZE;
+	int index = (hook_data.top + off) % HOOK_DATA_SIZE;
+	fflush(hook_data.hook[index]);
+	return hook_data.data[index];
+}
+
+
+#define HOOK_MODE_SIZE (100)
+static struct {
+	json_hook_t mode[HOOK_MODE_SIZE];
+	int top;
+} hook_mode;
+void json_hook_push(json_hook_t mode)
+{
+	if (jflag == 0) return;
+	assert(hook_mode.top >= 0 && hook_mode.top < HOOK_MODE_SIZE);
+	hook_mode.mode[hook_mode.top++] = mode;
+	return;
+}
+void json_hook_pop(void)
+{
+	if (jflag == 0) return;
+	assert(hook_mode.top > 0 && hook_mode.top <= HOOK_MODE_SIZE);
+
+	if (hook_mode.mode[hook_mode.top - 1] == JSON_HOOK_ALL)
+		json_index_add();
+
+	--hook_mode.top;
+	return;
+}
+static json_hook_t json_hook_top(void)
+{
+	assert(hook_mode.top >= 1 && hook_mode.top < 100);
+	return hook_mode.mode[hook_mode.top - 1];
+}
+
+
+/*
+ * We need to escape the illegal characters in JSON string
+ */
+static void escape_output(const char *str)
+{
+	if (str == NULL) return;
+	while (*str != '\0') {
+		switch (*str) {
+			case '\"':
+				tprints("\\\"");
+				break;
+			case '\\':
+				tprints("\\\\");
+				break;
+
+			default:
+				tprintf("%c", *str);
+		}
+		str++;
+	}
+}
+
+
+/*
+ * Extract the % specifier in a format string
+ * buf should be long enough to contain the whole specifier string
+ *
+ * Return the next start position or NULL(when reach the '\0' of a string)
+*/
+static const char* extract_format_string(const char *fmt, char *buf)
+{
+	while (*fmt != '%' && *fmt != '\0') ++fmt;
+	if (*fmt == '\0') return NULL;
+
+	const char *end = fmt + 1;
+	end += strcspn(end, "diuoxXfFeEgGaAcspn%");
+
+	if (*end == '\0')
+		return NULL;
+	else if (*end == '%')
+		return fmt + 1; // two %% don't consume any parameter
+
+	++end;
+	while (fmt != end) *buf++ = *fmt++;
+	*buf = '\0';
+	return fmt;
+}
+
+static void json_meta_vprintf(const char *fmt, va_list args)
+{
+	char fmtbuf[101];
+	while ((fmt = extract_format_string(fmt, fmtbuf)) != NULL) {
+		FILE *meta_file = json_file_get(0);
+		json_index_add();
+		clear_buffer(meta_file);
+		vfprintf(meta_file, fmtbuf, args);
+	}
+	return;
+}
+
+
+void json_tprintf(const char *fmt, va_list args)
+{
+	switch (json_hook_top()) {
+		case JSON_HOOK_NONE:
+			_tvprintf(fmt, args);
+			break;
+		case JSON_HOOK_SKIP:
+			break;
+		case JSON_HOOK_SINGLE:
+			json_meta_vprintf(fmt, args);
+			break;
+		case JSON_HOOK_ALL:
+		case JSON_HOOK_MERGE:
+			vfprintf(json_file_get(0), fmt, args);
+			break;
+
+		default:
+			assert(0);
+	}
+}
+
+void json_tprints(const char *str)
+{
+	switch (json_hook_top()) {
+		case JSON_HOOK_NONE:
+			_tprints(str);
+			break;
+		case JSON_HOOK_SKIP:
+		case JSON_HOOK_SINGLE:
+			break;
+		case JSON_HOOK_ALL:
+		case JSON_HOOK_MERGE:
+			fprintf(json_file_get(0), "%s", str);
+			break;
+		default:
+			assert(0);
+	}
+}
+
+
+/*
+ * The main event handler for JSON output
+ */
+static int event_handler(json_event_t event[], int count)
+{
+	int i;
+	int index = 0;
+	for (i = 0; i < count; i++) {
+		switch (event[i]) {
+			case JSON_DIRECT:
+			case JSON_DIRECT_ESCAPE:
+			case JSON_STRING:
+			case JSON_ARG:
+			case JSON_NAME:
+			case JSON_RET:
+			case JSON_ERROR:
+			case JSON_DESC:
+			case JSON_DESC_LONG:
+			case JSON_ERRNO:
+			case JSON_AUXSTR:
+			case JSON_SIGCODE:
+				index -= 1;
+				break;
+
+			default:
+				break;
+		}
+	}
+	int orig_index = index;
+	if (json_hook_top() == JSON_HOOK_MERGE)
+		clear_buffer(json_file_get(0));
+
+	for(i = 0; i < count; i++) {
+		const char *param1 = NULL;
+		const char *key = NULL;
+		int direct = 0;
+
+		switch (event[i]) {
+			case JSON_LAST:
+				break;
+
+			case JSON_DIRECT_ESCAPE:
+				param1 = json_buf_get(index);
+				if (param1 == NULL) continue;
+				escape_output(param1);
+				break;
+			case JSON_DIRECT:
+				direct = 1;
+			case JSON_STRING:
+			case JSON_ARG:
+				param1 = json_buf_get(index);
+				if (param1 == NULL) continue;
+
+				if (direct == 1) {
+					tprints(param1);
+				} else {
+					tprints("\"");
+					escape_output(param1);
+					tprints("\"");
+				}
+				break;
+
+			case JSON_STRING_QUOTE:
+				tprints("\"");
+				break;
+
+			case JSON_SEPA:
+				tprints(", ");
+				break;
+			case JSON_NEWLINE:
+				tprints("\n");
+				break;
+
+			case JSON_NULL:
+				tprints("null");
+				break;
+
+			case JSON_CALL_BEGIN:
+				tprints("{ \"type\" : \"syscall\"");
+				break;
+			case JSON_CALL_END:
+				tprints(" }");
+				break;
+
+			case JSON_ARGS_BEGIN:
+				tprints("\"args\" : [");
+				break;
+			case JSON_ARGS_END:
+				tprints("]");
+				break;
+
+			case JSON_ARRAY_BEGIN:
+				tprints("[");
+				break;
+			case JSON_ARRAY_END:
+				tprints("]");
+				break;
+
+			case JSON_SIGNAL_BEGIN:
+				tprints("{ \"type\" : \"+++\"");
+				break;
+			case JSON_SIGNAL_END:
+				tprints("}");
+				break;
+
+			case JSON_NAME:
+				key = "name";
+				break;
+			case JSON_RET:
+				key = "ret";
+				break;
+			case JSON_ERROR:
+				key = "error";
+				break;
+			case JSON_DESC:
+				key = "desc";
+				break;
+			case JSON_DESC_LONG:
+				key = "desc_long";
+				break;
+			case JSON_ERRNO:
+				key = "errno";
+				break;
+			case JSON_AUXSTR:
+				key = "auxstr";
+				break;
+			case JSON_SIGCODE:
+				key = "sigcode";
+				break;
+
+			default:
+				break;
+		}
+		if (key) {
+			param1 = json_buf_get(index);
+			tprintf("\"%s\" : \"%s\"", key, param1);
+		}
+
+		if (param1 != NULL) {
+			clear_buffer(json_file_get(index));
+			++index;
+		}
+
+	}
+
+	if (json_hook_top() == JSON_HOOK_MERGE && orig_index != 0) {
+		fprintf(json_file_get(orig_index), "%s", json_buf_get(0));
+		clear_buffer(json_file_get(0));
+		while(++orig_index != 0) json_index_sub();
+	} else if (orig_index == 0) {
+		json_index_add();
+	}
+	return 0;
+}
+
+int json_merge_from(void)
+{
+	return hook_data.top;
+}
+void json_merge_all(int from)
+{
+	if (jflag == 0) return;
+	int i = 0;
+	if (from > hook_data.top) from -= HOOK_DATA_SIZE;
+
+	json_event_t event_array[HOOK_DATA_SIZE];
+	while (from++ != hook_data.top) {
+		assert(i < HOOK_DATA_SIZE);
+		event_array[i++] = JSON_DIRECT;
+	}
+	json_hook_push(JSON_HOOK_MERGE);
+	event_handler(event_array, i);
+	json_hook_pop();
+}
+
+/*
+ * User call this function to trigger the correspond event for output
+ * later maybe changed to support multiple event handler
+ */
+void json_output_event(json_hook_t mode, ...)
+{
+	if (jflag == 0) return;
+
+	va_list ap;
+	va_start(ap, mode);
+
+	int count = 0;
+	json_event_t event_array[HOOK_DATA_SIZE];
+	json_event_t event;
+	while ((event = va_arg(ap, json_event_t)) != JSON_LAST) {
+		assert(count < HOOK_DATA_SIZE);
+		event_array[count++] = event;
+	}
+
+	json_hook_push(mode);
+	event_handler(event_array, count);
+	json_hook_pop();
+
+	va_end(ap);
+	return;
+}
+
+/*
+ * called when find '-j' option
+ */
+int json_init(void)
+{
+	hook_data.top = 0;
+	int i;
+	for (i = 0; i < HOOK_DATA_SIZE; i++) {
+		hook_data.hook[i] = open_memstream(&hook_data.data[i], &hook_data.size[i]);
+		if (hook_data.hook[i] == NULL)
+			return -1;
+	}
+
+	hook_mode.top = 0;
+	// we never pop this mode, it's used as the default mode
+	json_hook_push(JSON_HOOK_SINGLE);
+	return 1;
+}
-- 
1.9.1





More information about the Strace-devel mailing list