From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753526AbbIKQAo (ORCPT ); Fri, 11 Sep 2015 12:00:44 -0400 Received: from mga11.intel.com ([192.55.52.93]:56661 "EHLO mga11.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753477AbbIKQAl (ORCPT ); Fri, 11 Sep 2015 12:00:41 -0400 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.17,511,1437462000"; d="scan'208";a="803191920" From: Alexander Shishkin To: Peter Zijlstra , Ingo Molnar Cc: linux-kernel@vger.kernel.org, vince@deater.net, eranian@google.com, johannes@sipsolutions.net, Arnaldo Carvalho de Melo , Alexander Shishkin Subject: [PATCH RFC v3 4/6] perf tools: Add a simple JSON parser Date: Fri, 11 Sep 2015 19:00:03 +0300 Message-Id: <1441987205-4021-5-git-send-email-alexander.shishkin@linux.intel.com> X-Mailer: git-send-email 2.5.1 In-Reply-To: <1441987205-4021-1-git-send-email-alexander.shishkin@linux.intel.com> References: <1441987205-4021-1-git-send-email-alexander.shishkin@linux.intel.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org In order to process kernel's extended syscall error reports, we need a JSON parser. This one I wrote myself, it should be very simple and straightforward and extensible when/if somebody needs more features from it. Signed-off-by: Alexander Shishkin --- tools/include/tools/json.h | 40 ++++++++ tools/lib/util/json.c | 250 +++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/Build | 5 + 3 files changed, 295 insertions(+) create mode 100644 tools/include/tools/json.h create mode 100644 tools/lib/util/json.c diff --git a/tools/include/tools/json.h b/tools/include/tools/json.h new file mode 100644 index 0000000000..a1684574c2 --- /dev/null +++ b/tools/include/tools/json.h @@ -0,0 +1,40 @@ +/* + * A simple JSON parser + * Copyright (c) 2015, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef _TOOLS_JSON_H +#define _TOOLS_JSON_H + +#define JSON_DEPTH 8 + +struct json_member { + const char *key; + char *value; + unsigned int size; +}; + +struct json_parser { + const char *buffer; + const char *end; + const char *cursor; + unsigned int state; + unsigned int level; + unsigned char stack[JSON_DEPTH]; + struct json_member *schema; + unsigned int schema_strict; + int key; +}; + +int parse_json(struct json_parser *p); + +#endif /* _TOOLS_JSON_H */ diff --git a/tools/lib/util/json.c b/tools/lib/util/json.c new file mode 100644 index 0000000000..029c244d0e --- /dev/null +++ b/tools/lib/util/json.c @@ -0,0 +1,250 @@ +/* + * A simple JSON parser + * Copyright (c) 2015, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include + +#include "tools/json.h" + +enum { + JS_VALUE = 0, + JS_SEPARATOR, + JS_KEY, + JS_END, +}; + +enum { + OBJECT = 0, + ARRAY, + LITERAL, +}; + +static int got_key(struct json_parser *p, unsigned int back) +{ + const char *key = p->cursor - back; + int i; + + for (i = 0; p->schema[i].key; i++) { + struct json_member *m = &p->schema[i]; + + if (!strncmp(key, m->key, strlen(m->key))) { + p->key = i; + return 0; + } + } + + p->key = -1; + return p->schema_strict ? -EINVAL : 0; +} + +static int got_value(struct json_parser *p, unsigned int back) +{ + unsigned int len = back; + + if (p->key < 0) + return p->schema_strict ? -EINVAL : 0; + + if (len > p->schema[p->key].size) + len = p->schema[p->key].size; + + memcpy(p->schema[p->key].value, p->cursor - back, len); + p->schema[p->key].value[len] = 0; + p->key = -1; + return 0; +} + +static int consume_string(struct json_parser *p) +{ + int ret = 0; + + for (p->cursor++; p->cursor < p->end; + p->cursor++, ret++) { + switch(*p->cursor) { + case '"': + goto done; + case '\\': + p->cursor++; + ret++; + default: + continue; + } + } + + return -EINVAL; + +done: + if (p->state == JS_KEY) + ret = got_key(p, ret); + else if (p->state == JS_VALUE) + ret = got_value(p, ret); + + return ret; +} + +static int consume_number(struct json_parser *p) +{ + unsigned int hex = 0; + int ret = 0; + + for (; p->cursor < p->end; + p->cursor++, ret++) { + switch (*p->cursor) { + case '.': /* float */ + if (!ret) + return -EINVAL; + continue; + case 'x': /* hex */ + if (ret != 1 || *(p->cursor - 1) != '0') + return -EINVAL; + hex = 1; + continue; + case '-': /* negative */ + if (ret) + return -EINVAL; + continue; + default: + if (!hex && !isdigit(*p->cursor)) + goto done; + if (hex && !isxdigit(*p->cursor)) + goto done; + + continue; + } + } + + return -EINVAL; + +done: + if (p->state == JS_KEY) + return -EINVAL; + else if (p->state == JS_VALUE) + ret = got_value(p, ret); + + p->cursor--; + + return ret; +} + +int parse_json(struct json_parser *p) +{ + int ret; + + p->key = -1; + p->level = 0; + p->stack[0] = ARRAY; + + for (p->cursor = p->buffer, p->state = JS_VALUE; + p->cursor < p->end; p->cursor++) { + switch (*p->cursor) { + case ' ': + case '\t': + case '\n': + /* doesn't change state */ + continue; + case '{': + if (p->state != JS_VALUE) + return -EINVAL; + + p->stack[++p->level] = OBJECT; + p->state = JS_KEY; + break; + case '}': + if ((p->state != JS_END && p->state != JS_KEY) || + !p->level) + return -EINVAL; + + p->level--; + p->state = JS_END; + break; + case ':': + if (p->state != JS_SEPARATOR) + return -EINVAL; + + p->state = JS_VALUE; + break; + case '[': + if (p->state != JS_VALUE) + return -EINVAL; + + p->stack[++p->level] = OBJECT; + p->state = JS_VALUE; + break; + case ']': + if ((p->state != JS_END && p->state != JS_VALUE) || + !p->level) + return -EINVAL; + + p->level--; + /* still JS_END */ + break; + case '"': + /* consume everything up to the matching quote */ + ret = consume_string(p); + if (ret < 0) + return ret; + + switch (p->state) { + case JS_KEY: + p->state = JS_SEPARATOR; + break; + case JS_VALUE: + p->state = JS_END; + break; + default: + return -EINVAL; + } + + continue; + case ',': + if (p->state != JS_END) + return -EINVAL; + + if (p->stack[p->level] == ARRAY) + p->state = JS_VALUE; + else /* OBJECT */ + p->state = JS_KEY; + break; + default: + if (p->state != JS_VALUE) + return -EINVAL; + + if (isdigit(*p->cursor) || *p->cursor == '-') { + ret = consume_number(p); + if (ret < 0) + return ret; + } else if (!strncasecmp(p->cursor, "null", 4)) { + p->cursor += 3; + got_value(p, 4); + } else if (!strncasecmp(p->cursor, "true", 4)) { + p->cursor += 3; + got_value(p, 4); + } else if (!strncasecmp(p->cursor, "false", 5)) { + p->cursor += 4; + got_value(p, 5); + } else { + return -EINVAL; + } + + p->state = JS_END; + break; + } + } + + if (p->level || p->state != JS_END) + return -EINVAL; + + return 0; +} + diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 349bc96ca1..af5acb9a02 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -12,6 +12,7 @@ libperf-y += evsel.o libperf-y += exec_cmd.o libperf-y += find_next_bit.o libperf-y += help.o +libperf-y += json.o libperf-y += kallsyms.o libperf-y += levenshtein.o libperf-y += llvm-utils.o @@ -147,6 +148,10 @@ $(OUTPUT)util/find_next_bit.o: ../lib/util/find_next_bit.c FORCE $(call rule_mkdir) $(call if_changed_dep,cc_o_c) +$(OUTPUT)util/json.o: ../lib/util/json.c FORCE + $(call rule_mkdir) + $(call if_changed_dep,cc_o_c) + $(OUTPUT)util/rbtree.o: ../lib/rbtree.c FORCE $(call rule_mkdir) $(call if_changed_dep,cc_o_c) -- 2.5.1