The JSON format is used to make it more easy to parse the
trace results by other program. It is enabled by -j option.

* Makefile.am: Add json.c to compile list.
* strace.1: Add documents for -j option.
* strace.c(init, usage, tprintf, tprints, tvprintf): Add support
for the '-j' option. Modify tprintf() to use tvprintf(new function)
to use the hook framework, Modify tprints() to use the hook framework.
* defs.h: Add declarations for the hook framework of JSON output.
* json.c: New file. Implementation of the hook framework.
---
 Makefile.am |   1 +
 defs.h      |  99 +++++++++++++++++++++++++
 json.c      | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 strace.1    |   3 +
 strace.c    |  66 +++++++++++++----
 5 files changed, 391 insertions(+), 13 deletions(-)
 create mode 100644 json.c

diff --git a/Makefile.am b/Makefile.am
index be05946..4ce8d44 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -29,6 +29,7 @@ strace_SOURCES =      \
        ioctl.c         \
        ioprio.c        \
        ipc.c           \
+       json.c          \
        kexec.c         \
        keyctl.c        \
        ldt.c           \
diff --git a/defs.h b/defs.h
index 1a3b483..dc94cb4 100644
--- a/defs.h
+++ b/defs.h
@@ -829,3 +829,102 @@ 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 {
+       EVENT_NONE = 0,
+
+       EVENT_SEPARATE,
+       EVENT_NEWLINE,
+
+       EVENT_CALL_BEGIN,
+       EVENT_NAME,
+       EVENT_CALL_END,
+
+       EVENT_ARGS_BEGIN,
+       EVENT_ARG,
+       EVENT_ARGS_END,
+
+       EVENT_RET,
+       EVENT_DESC,
+       EVENT_DESC_LONG,
+       EVENT_AUXSTR,
+       EVENT_ERRNO,
+       EVENT_ERROR,
+
+       EVENT_SIGNAL_BEGIN,
+       EVENT_SIGNAL_END,
+       EVENT_SIGCODE,
+
+       BEF_SEPRATOR = 0x10000,
+       SEPARATOR    = 0x20000,
+       NEWLINE      = 0x40000,
+} json_type_event;
+
+/*
+ * Control the behaviour of tprints() and tprintf()
+ */
+typedef enum {
+       SKIP,          // skip the output, do nothing
+       NORMAL,        // output directly, the same to the normal 
tprints()/tprintf()
+       META_ALL,      // extract all outputs of a snippet code to a single 
meta buffer
+       META_SINGLE,   // extract the meta parameter based on the format string 
of a tprintf() call
+} json_type_output;
+
+/*
+ * JSON_META_SIZE is largest number of metas
+ * we could handle in one json_event() call.
+ *
+ * So We must make sure the first argument of json_event() is <= 10 !
+ */
+#define JSON_META_SIZE (10)
+typedef struct {
+       int top;
+       size_t size[JSON_META_SIZE];
+       FILE *hook[JSON_META_SIZE];
+       char *data[JSON_META_SIZE];
+} json_type_meta;
+
+
+// defined in strace.c
+extern int jflag;
+
+// defined in json.c
+extern json_type_output json_output_control;
+extern json_type_meta  json_hook_metas;
+
+#define JSON_META_BEGIN                                 \
+        do {                                            \
+        json_type_output __json_last_output;            \
+        if (jflag == 1) {                               \
+            __json_last_output = json_output_control;   \
+            json_output_control = META_ALL;             \
+        }                                               \
+        do {
+
+#define JSON_META_END                                   \
+        } while(0);                                     \
+        if (jflag == 1)                                 \
+            json_output_control = __json_last_output;   \
+            json_hook_metas.top++;                      \
+    } while(0)
+
+#define JSON_META(wrapped_call)  \
+        JSON_META_BEGIN          \
+        wrapped_call ;           \
+        JSON_META_END
+
+#define JSON_BEGIN_META_MODE do { json_hook_metas.top = 0; json_output_control 
= META_SINGLE; } while(0)
+
+
+void json_meta_vprintf(const char *fmt, va_list args);
+void json_event(int counts, ...);
+int json_init(void);
+
+/*
+ * 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..c7c908b
--- /dev/null
+++ b/json.c
@@ -0,0 +1,235 @@
+#include "defs.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+
+/*
+ * We also modified tprintf()/tprints() to implement this hook framework
+*/
+
+json_type_meta json_hook_metas;
+json_type_output json_output_control = SKIP;
+
+static void flush_buffer(void)
+{
+       int i;
+       for (i = 0; i < json_hook_metas.top; i++) {
+               fflush(json_hook_metas.hook[i]);
+       }
+       return;
+}
+static void clear_buffer(void)
+{
+       int i;
+       for (i = 0; i < json_hook_metas.top; i++) {
+               rewind(json_hook_metas.hook[i]);
+               fflush(json_hook_metas.hook[i]);
+       }
+       json_hook_metas.top = 0;
+       return;
+}
+
+/*
+ * 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++;
+       }
+}
+
+/*
+ * The main event handler for JSON output
+ */
+#define JSON_META(i) (json_hook_metas.data[i])
+static int json_event_handler(int counts, va_list ap)
+{
+       int i = 0;
+       int meta_i = 0;
+
+       while(i++ < counts) {
+               json_type_event event = va_arg(ap, json_type_event);
+
+               const char *param1 = NULL;
+               const char *key = NULL;
+
+               char has_seperator = 0, has_newline = 0;
+               if (event & SEPARATOR) has_seperator = 1;
+               if (event & NEWLINE) has_newline = 1;
+
+               if (event & BEF_SEPRATOR) tprints(", ");
+
+               event &= 0x0000FFFF;
+               switch (event) {
+                       case EVENT_NONE:
+                               break;
+
+                       case EVENT_ARG:
+                               param1 = JSON_META(meta_i++);
+                               if (param1 == NULL) continue;
+
+                               tprints("\"");
+                               escape_output(param1);
+                               tprints("\"");
+                               break;
+
+                       case EVENT_SEPARATE:
+                               tprints(", ");
+                               break;
+                       case EVENT_NEWLINE:
+                               tprints("\n");
+                               break;
+
+                       case EVENT_CALL_BEGIN:
+                               tprints("{ \"type\" : \"syscall\"");
+                               break;
+                       case EVENT_CALL_END:
+                               tprints(" }");
+                               break;
+
+                       case EVENT_ARGS_BEGIN:
+                               tprints("\"args\" : [");
+                               break;
+                       case EVENT_ARGS_END:
+                               tprints("]");
+                               break;
+
+                       case EVENT_SIGNAL_BEGIN:
+                               tprints("{ \"type\" : \"+++\"");
+                               break;
+                       case EVENT_SIGNAL_END:
+                               tprints("}");
+                               break;
+
+                       case EVENT_NAME:
+                               key = "name";
+                               break;
+                       case EVENT_RET:
+                               key = "ret";
+                               break;
+                       case EVENT_ERROR:
+                               key = "error";
+                               break;
+                       case EVENT_DESC:
+                               key = "desc";
+                               break;
+                       case EVENT_DESC_LONG:
+                               key = "desc_long";
+                               break;
+                       case EVENT_ERRNO:
+                               key = "errno";
+                               break;
+                       case EVENT_AUXSTR:
+                               key = "auxstr";
+                               break;
+                       case EVENT_SIGCODE:
+                               key = "sigcode";
+                               break;
+
+                       default:
+                               break;
+               }
+               if (key) {
+                       param1 = JSON_META(meta_i++);
+                       tprintf("\"%s\" : \"%s\"", key, param1);
+               }
+               if (has_seperator == 1)
+                       tprints(", ");
+               if (has_newline == 1)
+                       tprints("\n");
+       }
+       return 0;
+}
+
+/*
+ * User call this function to trigger the correspond event for output
+ * later maybe changed to support multiple event handler
+ */
+void json_event(int counts, ...)
+{
+       if (jflag == 0) return;
+
+       flush_buffer();
+       json_output_control = NORMAL;
+
+       va_list ap;
+       va_start(ap, counts);
+       json_event_handler(counts, ap);
+       va_end(ap);
+
+       clear_buffer();
+       json_output_control = SKIP;
+       return;
+}
+
+/*
+ * Extract the % specifier in a format string
+ * buf should be long enough to contain the whole specifier string
+ *
+ * NOTE: this function could not handle all the situation in a format string.
+ * For example: The "%.*s" which consumes 2 arguments and we always use only 
one
+ * argument a time when handling each event.
+ * But in strace it seems enough now.
+ *
+ * Return the next start position or NULL(when reach the '\0' of a string)
+*/
+static const char* json_meta_build_fmt(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;
+}
+
+void json_meta_vprintf(const char *fmt, va_list args)
+{
+       char fmtbuf[101];
+       while ((fmt = json_meta_build_fmt(fmt, fmtbuf)) != NULL) {
+               FILE *meta_file = json_hook_metas.hook[json_hook_metas.top++];
+               vfprintf(meta_file, fmtbuf, args);
+       }
+       return;
+}
+
+/*
+ * Init the internal buffer
+ * Return 1 when success, return -1 when failed.
+ */
+int json_init(void)
+{
+       json_hook_metas.top = 0;
+       int i;
+       for (i = 0; i < JSON_META_SIZE; i++) {
+               json_hook_metas.hook[i] = 
open_memstream(&json_hook_metas.data[i], &json_hook_metas.size[i]);
+               if (json_hook_metas.hook[i] == NULL) {
+                       return -1;
+               }
+       }
+       return 1;
+}
\ No newline at end of file
diff --git a/strace.1 b/strace.1
index c310d65..901712e 100644
--- a/strace.1
+++ b/strace.1
@@ -267,6 +267,9 @@ Print the help summary.
 .B \-i
 Print the instruction pointer at the time of the system call.
 .TP
+.B \-j
+Print the trace results in JSON format.
+.TP
 .B \-k
 Print the execution stack trace of the traced processes after each system call.
 .TP
diff --git a/strace.c b/strace.c
index 46c9d63..800f229 100644
--- a/strace.c
+++ b/strace.c
@@ -89,6 +89,8 @@ static unsigned int syscall_trap_sig = SIGTRAP;
 static unsigned int tflag = 0;
 static bool rflag = 0;
 static bool print_pid_pfx = 0;
+/* Support option '-j' to enable JSON output */
+int jflag = 0;
 
 /* -I n */
 enum {
@@ -210,6 +212,7 @@ usage: strace [-CdffhiqrtttTvVxxy] [-I n] [-e expr]...\n\
 -D -- run tracer process as a detached grandchild, not as parent\n\
 -f -- follow forks, -ff -- with output into separate files\n\
 -i -- print instruction pointer at time of syscall\n\
+-j -- print the trace results in JSON format\n\
 -q -- suppress messages about attaching, detaching, etc.\n\
 -r -- print relative timestamp, -t -- absolute timestamp, -tt -- with usecs\n\
 -T -- print time spent in each syscall\n\
@@ -534,12 +537,9 @@ strace_popen(const char *command)
        return fp;
 }
 
-void
-tprintf(const char *fmt, ...)
+static void
+tvprintf(const char *fmt, va_list args)
 {
-       va_list args;
-
-       va_start(args, fmt);
        if (current_tcp) {
                int n = strace_vfprintf(current_tcp->outf, fmt, args);
                if (n < 0) {
@@ -548,6 +548,32 @@ tprintf(const char *fmt, ...)
                } else
                        current_tcp->curcol += n;
        }
+}
+
+void
+tprintf(const char *fmt, ...)
+{
+       va_list args;
+       va_start(args, fmt);
+
+       if (jflag == 0)
+               tvprintf(fmt, args);
+       else {
+               switch (json_output_control) {
+                       case META_ALL:
+                               
vfprintf(json_hook_metas.hook[json_hook_metas.top], fmt, args);
+                               break;
+                       case META_SINGLE:
+                               json_meta_vprintf(fmt, args);
+                               break;
+                       case NORMAL:
+                               tvprintf(fmt, args);
+                               break;
+                       case SKIP:
+                       default:
+                               break;
+               }
+       }
        va_end(args);
 }
 
@@ -558,15 +584,23 @@ tprintf(const char *fmt, ...)
 void
 tprints(const char *str)
 {
-       if (current_tcp) {
-               int n = fputs_unlocked(str, current_tcp->outf);
-               if (n >= 0) {
-                       current_tcp->curcol += strlen(str);
-                       return;
+       if (jflag == 0 || (jflag == 1 && json_output_control == NORMAL)) {
+               if (current_tcp) {
+                       int n = fputs_unlocked(str, current_tcp->outf);
+                       if (n >= 0) {
+                               current_tcp->curcol += strlen(str);
+                               return;
+                       }
+                       if (current_tcp->outf != stderr)
+                               perror_msg("%s", outfname);
                }
-               if (current_tcp->outf != stderr)
-                       perror_msg("%s", outfname);
        }
+       else if (jflag == 1 && json_output_control == META_ALL) {
+               fprintf(json_hook_metas.hook[json_hook_metas.top], "%s", str);
+               return;
+       }
+       else if (jflag == 1 && (json_output_control == SKIP || 
json_output_control == META_SINGLE))
+               return;
 }
 
 void
@@ -1672,7 +1706,7 @@ init(int argc, char *argv[])
 #endif
        qualify("signal=all");
        while ((c = getopt(argc, argv,
-               "+b:cCdfFhiqrtTvVwxyz"
+               "+b:cCdfFhijqrtTvVwxyz"
 #ifdef USE_LIBUNWIND
                "k"
 #endif
@@ -1715,6 +1749,12 @@ init(int argc, char *argv[])
                case 'i':
                        iflag = 1;
                        break;
+               case 'j':
+                       if (json_init() == -1)
+                               fprintf(stderr, "json_init() failed, back to 
normal output.\n");
+                       else
+                               jflag = 1;
+                       break;
                case 'q':
                        qflag++;
                        break;
-- 
1.9.1


------------------------------------------------------------------------------
HPCC Systems Open Source Big Data Platform from LexisNexis Risk Solutions
Find What Matters Most in Your Big Data with HPCC Systems
Open Source. Fast. Scalable. Simple. Ideal for Dirty Data.
Leverages Graph Analysis for Fast Processing & Easy Data Exploration
http://p.sf.net/sfu/hpccsystems
_______________________________________________
Strace-devel mailing list
Strace-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/strace-devel

Reply via email to