aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndi Kleen <ak@linux.intel.com>2016-09-15 18:24:38 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2016-10-03 10:40:36 -0400
commit867a979a83d38fb82c4e7534a0281a02ef1700a3 (patch)
treee4a3404a2d41282b8752751d298f7e1e1de6376c
parent6b3db6f9b970001b751a6cd001bc2ab581ce0fb3 (diff)
perf tools: Add jsmn `jasmine' JSON parser
I need a JSON parser. This adds the simplest JSON parser I could find -- Serge Zaitsev's jsmn `jasmine' -- to the perf library. I merely converted it to (mostly) Linux style and added support for non 0 terminated input. The parser is quite straight forward and does not copy any data, just returns tokens with offsets into the input buffer. So it's relatively efficient and simple to use. The code is not fully checkpatch clean, but I didn't want to completely fork the upstream code. Original source: http://zserge.bitbucket.org/jsmn.html In addition I added a simple wrapper that mmaps a json file and provides some straight forward access functions. Used in follow-on patches to parse event files. Signed-off-by: Andi Kleen <ak@linux.intel.com> Acked-by: Ingo Molnar <mingo@kernel.org> Acked-by: Jiri Olsa <jolsa@redhat.com> Acked-by: Namhyung Kim <namhyung@kernel.org> Cc: Madhavan Srinivasan <maddy@linux.vnet.ibm.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: linuxppc-dev@lists.ozlabs.org Link: http://lkml.kernel.org/r/1473978296-20712-2-git-send-email-sukadev@linux.vnet.ibm.com Signed-off-by: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com> [ Use fcntl.h instead of sys/fcntl.h to fix the build on Alpine Linux 3.4/musl libc, use stdbool.h to avoid clashing with 'bool' typedef there ] Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r--tools/perf/pmu-events/jsmn.c313
-rw-r--r--tools/perf/pmu-events/jsmn.h67
-rw-r--r--tools/perf/pmu-events/json.c162
-rw-r--r--tools/perf/pmu-events/json.h32
4 files changed, 574 insertions, 0 deletions
diff --git a/tools/perf/pmu-events/jsmn.c b/tools/perf/pmu-events/jsmn.c
new file mode 100644
index 000000000000..11d1fa18bfa5
--- /dev/null
+++ b/tools/perf/pmu-events/jsmn.c
@@ -0,0 +1,313 @@
1/*
2 * Copyright (c) 2010 Serge A. Zaitsev
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 *
22 * Slightly modified by AK to not assume 0 terminated input.
23 */
24
25#include <stdlib.h>
26#include "jsmn.h"
27
28/*
29 * Allocates a fresh unused token from the token pool.
30 */
31static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
32 jsmntok_t *tokens, size_t num_tokens)
33{
34 jsmntok_t *tok;
35
36 if ((unsigned)parser->toknext >= num_tokens)
37 return NULL;
38 tok = &tokens[parser->toknext++];
39 tok->start = tok->end = -1;
40 tok->size = 0;
41 return tok;
42}
43
44/*
45 * Fills token type and boundaries.
46 */
47static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
48 int start, int end)
49{
50 token->type = type;
51 token->start = start;
52 token->end = end;
53 token->size = 0;
54}
55
56/*
57 * Fills next available token with JSON primitive.
58 */
59static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
60 size_t len,
61 jsmntok_t *tokens, size_t num_tokens)
62{
63 jsmntok_t *token;
64 int start;
65
66 start = parser->pos;
67
68 for (; parser->pos < len; parser->pos++) {
69 switch (js[parser->pos]) {
70#ifndef JSMN_STRICT
71 /*
72 * In strict mode primitive must be followed by ","
73 * or "}" or "]"
74 */
75 case ':':
76#endif
77 case '\t':
78 case '\r':
79 case '\n':
80 case ' ':
81 case ',':
82 case ']':
83 case '}':
84 goto found;
85 default:
86 break;
87 }
88 if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
89 parser->pos = start;
90 return JSMN_ERROR_INVAL;
91 }
92 }
93#ifdef JSMN_STRICT
94 /*
95 * In strict mode primitive must be followed by a
96 * comma/object/array.
97 */
98 parser->pos = start;
99 return JSMN_ERROR_PART;
100#endif
101
102found:
103 token = jsmn_alloc_token(parser, tokens, num_tokens);
104 if (token == NULL) {
105 parser->pos = start;
106 return JSMN_ERROR_NOMEM;
107 }
108 jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
109 parser->pos--; /* parent sees closing brackets */
110 return JSMN_SUCCESS;
111}
112
113/*
114 * Fills next token with JSON string.
115 */
116static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
117 size_t len,
118 jsmntok_t *tokens, size_t num_tokens)
119{
120 jsmntok_t *token;
121 int start = parser->pos;
122
123 /* Skip starting quote */
124 parser->pos++;
125
126 for (; parser->pos < len; parser->pos++) {
127 char c = js[parser->pos];
128
129 /* Quote: end of string */
130 if (c == '\"') {
131 token = jsmn_alloc_token(parser, tokens, num_tokens);
132 if (token == NULL) {
133 parser->pos = start;
134 return JSMN_ERROR_NOMEM;
135 }
136 jsmn_fill_token(token, JSMN_STRING, start+1,
137 parser->pos);
138 return JSMN_SUCCESS;
139 }
140
141 /* Backslash: Quoted symbol expected */
142 if (c == '\\') {
143 parser->pos++;
144 switch (js[parser->pos]) {
145 /* Allowed escaped symbols */
146 case '\"':
147 case '/':
148 case '\\':
149 case 'b':
150 case 'f':
151 case 'r':
152 case 'n':
153 case 't':
154 break;
155 /* Allows escaped symbol \uXXXX */
156 case 'u':
157 /* TODO */
158 break;
159 /* Unexpected symbol */
160 default:
161 parser->pos = start;
162 return JSMN_ERROR_INVAL;
163 }
164 }
165 }
166 parser->pos = start;
167 return JSMN_ERROR_PART;
168}
169
170/*
171 * Parse JSON string and fill tokens.
172 */
173jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
174 jsmntok_t *tokens, unsigned int num_tokens)
175{
176 jsmnerr_t r;
177 int i;
178 jsmntok_t *token;
179
180 for (; parser->pos < len; parser->pos++) {
181 char c;
182 jsmntype_t type;
183
184 c = js[parser->pos];
185 switch (c) {
186 case '{':
187 case '[':
188 token = jsmn_alloc_token(parser, tokens, num_tokens);
189 if (token == NULL)
190 return JSMN_ERROR_NOMEM;
191 if (parser->toksuper != -1)
192 tokens[parser->toksuper].size++;
193 token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
194 token->start = parser->pos;
195 parser->toksuper = parser->toknext - 1;
196 break;
197 case '}':
198 case ']':
199 type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
200 for (i = parser->toknext - 1; i >= 0; i--) {
201 token = &tokens[i];
202 if (token->start != -1 && token->end == -1) {
203 if (token->type != type)
204 return JSMN_ERROR_INVAL;
205 parser->toksuper = -1;
206 token->end = parser->pos + 1;
207 break;
208 }
209 }
210 /* Error if unmatched closing bracket */
211 if (i == -1)
212 return JSMN_ERROR_INVAL;
213 for (; i >= 0; i--) {
214 token = &tokens[i];
215 if (token->start != -1 && token->end == -1) {
216 parser->toksuper = i;
217 break;
218 }
219 }
220 break;
221 case '\"':
222 r = jsmn_parse_string(parser, js, len, tokens,
223 num_tokens);
224 if (r < 0)
225 return r;
226 if (parser->toksuper != -1)
227 tokens[parser->toksuper].size++;
228 break;
229 case '\t':
230 case '\r':
231 case '\n':
232 case ':':
233 case ',':
234 case ' ':
235 break;
236#ifdef JSMN_STRICT
237 /*
238 * In strict mode primitives are:
239 * numbers and booleans.
240 */
241 case '-':
242 case '0':
243 case '1':
244 case '2':
245 case '3':
246 case '4':
247 case '5':
248 case '6':
249 case '7':
250 case '8':
251 case '9':
252 case 't':
253 case 'f':
254 case 'n':
255#else
256 /*
257 * In non-strict mode every unquoted value
258 * is a primitive.
259 */
260 /*FALL THROUGH */
261 default:
262#endif
263 r = jsmn_parse_primitive(parser, js, len, tokens,
264 num_tokens);
265 if (r < 0)
266 return r;
267 if (parser->toksuper != -1)
268 tokens[parser->toksuper].size++;
269 break;
270
271#ifdef JSMN_STRICT
272 /* Unexpected char in strict mode */
273 default:
274 return JSMN_ERROR_INVAL;
275#endif
276 }
277 }
278
279 for (i = parser->toknext - 1; i >= 0; i--) {
280 /* Unmatched opened object or array */
281 if (tokens[i].start != -1 && tokens[i].end == -1)
282 return JSMN_ERROR_PART;
283 }
284
285 return JSMN_SUCCESS;
286}
287
288/*
289 * Creates a new parser based over a given buffer with an array of tokens
290 * available.
291 */
292void jsmn_init(jsmn_parser *parser)
293{
294 parser->pos = 0;
295 parser->toknext = 0;
296 parser->toksuper = -1;
297}
298
299const char *jsmn_strerror(jsmnerr_t err)
300{
301 switch (err) {
302 case JSMN_ERROR_NOMEM:
303 return "No enough tokens";
304 case JSMN_ERROR_INVAL:
305 return "Invalid character inside JSON string";
306 case JSMN_ERROR_PART:
307 return "The string is not a full JSON packet, more bytes expected";
308 case JSMN_SUCCESS:
309 return "Success";
310 default:
311 return "Unknown json error";
312 }
313}
diff --git a/tools/perf/pmu-events/jsmn.h b/tools/perf/pmu-events/jsmn.h
new file mode 100644
index 000000000000..d666b10cf25b
--- /dev/null
+++ b/tools/perf/pmu-events/jsmn.h
@@ -0,0 +1,67 @@
1#ifndef __JSMN_H_
2#define __JSMN_H_
3
4/*
5 * JSON type identifier. Basic types are:
6 * o Object
7 * o Array
8 * o String
9 * o Other primitive: number, boolean (true/false) or null
10 */
11typedef enum {
12 JSMN_PRIMITIVE = 0,
13 JSMN_OBJECT = 1,
14 JSMN_ARRAY = 2,
15 JSMN_STRING = 3
16} jsmntype_t;
17
18typedef enum {
19 /* Not enough tokens were provided */
20 JSMN_ERROR_NOMEM = -1,
21 /* Invalid character inside JSON string */
22 JSMN_ERROR_INVAL = -2,
23 /* The string is not a full JSON packet, more bytes expected */
24 JSMN_ERROR_PART = -3,
25 /* Everything was fine */
26 JSMN_SUCCESS = 0
27} jsmnerr_t;
28
29/*
30 * JSON token description.
31 * @param type type (object, array, string etc.)
32 * @param start start position in JSON data string
33 * @param end end position in JSON data string
34 */
35typedef struct {
36 jsmntype_t type;
37 int start;
38 int end;
39 int size;
40} jsmntok_t;
41
42/*
43 * JSON parser. Contains an array of token blocks available. Also stores
44 * the string being parsed now and current position in that string
45 */
46typedef struct {
47 unsigned int pos; /* offset in the JSON string */
48 int toknext; /* next token to allocate */
49 int toksuper; /* superior token node, e.g parent object or array */
50} jsmn_parser;
51
52/*
53 * Create JSON parser over an array of tokens
54 */
55void jsmn_init(jsmn_parser *parser);
56
57/*
58 * Run JSON parser. It parses a JSON data string into and array of tokens,
59 * each describing a single JSON object.
60 */
61jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js,
62 size_t len,
63 jsmntok_t *tokens, unsigned int num_tokens);
64
65const char *jsmn_strerror(jsmnerr_t err);
66
67#endif /* __JSMN_H_ */
diff --git a/tools/perf/pmu-events/json.c b/tools/perf/pmu-events/json.c
new file mode 100644
index 000000000000..f67bbb0aa36e
--- /dev/null
+++ b/tools/perf/pmu-events/json.c
@@ -0,0 +1,162 @@
1/* Parse JSON files using the JSMN parser. */
2
3/*
4 * Copyright (c) 2014, Intel Corporation
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28 * OF THE POSSIBILITY OF SUCH DAMAGE.
29*/
30
31#include <stdlib.h>
32#include <string.h>
33#include <sys/mman.h>
34#include <sys/stat.h>
35#include <fcntl.h>
36#include <stdio.h>
37#include <errno.h>
38#include <unistd.h>
39#include "jsmn.h"
40#include "json.h"
41#include <linux/kernel.h>
42
43
44static char *mapfile(const char *fn, size_t *size)
45{
46 unsigned ps = sysconf(_SC_PAGESIZE);
47 struct stat st;
48 char *map = NULL;
49 int err;
50 int fd = open(fn, O_RDONLY);
51
52 if (fd < 0 && verbose && fn) {
53 pr_err("Error opening events file '%s': %s\n", fn,
54 strerror(errno));
55 }
56
57 if (fd < 0)
58 return NULL;
59 err = fstat(fd, &st);
60 if (err < 0)
61 goto out;
62 *size = st.st_size;
63 map = mmap(NULL,
64 (st.st_size + ps - 1) & ~(ps - 1),
65 PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
66 if (map == MAP_FAILED)
67 map = NULL;
68out:
69 close(fd);
70 return map;
71}
72
73static void unmapfile(char *map, size_t size)
74{
75 unsigned ps = sysconf(_SC_PAGESIZE);
76 munmap(map, roundup(size, ps));
77}
78
79/*
80 * Parse json file using jsmn. Return array of tokens,
81 * and mapped file. Caller needs to free array.
82 */
83jsmntok_t *parse_json(const char *fn, char **map, size_t *size, int *len)
84{
85 jsmn_parser parser;
86 jsmntok_t *tokens;
87 jsmnerr_t res;
88 unsigned sz;
89
90 *map = mapfile(fn, size);
91 if (!*map)
92 return NULL;
93 /* Heuristic */
94 sz = *size * 16;
95 tokens = malloc(sz);
96 if (!tokens)
97 goto error;
98 jsmn_init(&parser);
99 res = jsmn_parse(&parser, *map, *size, tokens,
100 sz / sizeof(jsmntok_t));
101 if (res != JSMN_SUCCESS) {
102 pr_err("%s: json error %s\n", fn, jsmn_strerror(res));
103 goto error_free;
104 }
105 if (len)
106 *len = parser.toknext;
107 return tokens;
108error_free:
109 free(tokens);
110error:
111 unmapfile(*map, *size);
112 return NULL;
113}
114
115void free_json(char *map, size_t size, jsmntok_t *tokens)
116{
117 free(tokens);
118 unmapfile(map, size);
119}
120
121static int countchar(char *map, char c, int end)
122{
123 int i;
124 int count = 0;
125 for (i = 0; i < end; i++)
126 if (map[i] == c)
127 count++;
128 return count;
129}
130
131/* Return line number of a jsmn token */
132int json_line(char *map, jsmntok_t *t)
133{
134 return countchar(map, '\n', t->start) + 1;
135}
136
137static const char * const jsmn_types[] = {
138 [JSMN_PRIMITIVE] = "primitive",
139 [JSMN_ARRAY] = "array",
140 [JSMN_OBJECT] = "object",
141 [JSMN_STRING] = "string"
142};
143
144#define LOOKUP(a, i) ((i) < (sizeof(a)/sizeof(*(a))) ? ((a)[i]) : "?")
145
146/* Return type name of a jsmn token */
147const char *json_name(jsmntok_t *t)
148{
149 return LOOKUP(jsmn_types, t->type);
150}
151
152int json_len(jsmntok_t *t)
153{
154 return t->end - t->start;
155}
156
157/* Is string t equal to s? */
158int json_streq(char *map, jsmntok_t *t, const char *s)
159{
160 unsigned len = json_len(t);
161 return len == strlen(s) && !strncasecmp(map + t->start, s, len);
162}
diff --git a/tools/perf/pmu-events/json.h b/tools/perf/pmu-events/json.h
new file mode 100644
index 000000000000..f2745c7a87bc
--- /dev/null
+++ b/tools/perf/pmu-events/json.h
@@ -0,0 +1,32 @@
1#ifndef JSON_H
2#define JSON_H 1
3
4#include "jsmn.h"
5
6jsmntok_t *parse_json(const char *fn, char **map, size_t *size, int *len);
7void free_json(char *map, size_t size, jsmntok_t *tokens);
8int json_line(char *map, jsmntok_t *t);
9const char *json_name(jsmntok_t *t);
10int json_streq(char *map, jsmntok_t *t, const char *s);
11int json_len(jsmntok_t *t);
12
13extern int verbose;
14
15#include <stdbool.h>
16
17extern int eprintf(int level, int var, const char *fmt, ...);
18#define pr_fmt(fmt) fmt
19
20#define pr_err(fmt, ...) \
21 eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__)
22
23#ifndef roundup
24#define roundup(x, y) ( \
25{ \
26 const typeof(y) __y = y; \
27 (((x) + (__y - 1)) / __y) * __y; \
28} \
29)
30#endif
31
32#endif