aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndi Kleen <ak@linux.intel.com>2016-09-19 16:39:33 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2016-10-03 18:55:55 -0400
commit80eeb67fe577aa76b2d1bb5b029bca097f0f25bc (patch)
tree3bfae95d352a58bffdeb71015d20d53a287f4526
parent867a979a83d38fb82c4e7534a0281a02ef1700a3 (diff)
perf jevents: Program to convert JSON file
This is a modified version of an earlier patch by Andi Kleen. We expect architectures to create JSON files describing the performance monitoring (PMU) events that each CPU model/family of the architecture supports. Following is an example of the JSON file entry for an x86 event: [ ... { "EventCode": "0x00", "UMask": "0x01", "EventName": "INST_RETIRED.ANY", "BriefDescription": "Instructions retired from execution.", "PublicDescription": "Instructions retired from execution.", "Counter": "Fixed counter 1", "CounterHTOff": "Fixed counter 1", "SampleAfterValue": "2000003", "SampleAfterValue": "2000003", "MSRIndex": "0", "MSRValue": "0", "TakenAlone": "0", "CounterMask": "0", "Invert": "0", "AnyThread": "0", "EdgeDetect": "0", "PEBS": "0", "PRECISE_STORE": "0", "Errata": "null", "Offcore": "0" }, ... ] All the PMU events supported by a CPU model/family must be grouped into "topics" such as "Pipelining", "Floating-point", "Virtual-memory" etc. All events belonging to a topic must be placed in a separate JSON file (eg: "Pipelining.json") and all the topic JSON files for a CPU model must be in a separate directory. Eg: for the CPU model "Silvermont_core": $ ls tools/perf/pmu-events/arch/x86/Silvermont_core Floating-point.json Memory.json Other.json Pipelining.json Virtualmemory.json Finally, to allow multiple CPU models to share a single set of JSON files, architectures must provide a mapping between a model and its set of events: $ grep Silvermont tools/perf/pmu-events/arch/x86/mapfile.csv GenuineIntel-6-4D,V13,Silvermont_core,core GenuineIntel-6-4C,V13,Silvermont_core,core which maps each CPU, identified by [vendor, family, model, version, type] to a directory of JSON files. Thus two (or more) CPU models support the set of PMU events listed in the directory. tools/perf/pmu-events/arch/x86/Silvermont_core/ Given this organization of files, the program, jevents: - locates all JSON files for each CPU-model of the architecture, - parses all JSON files for the CPU-model and generates a C-style "PMU-events table" (pmu-events.c) for the model - locates a mapfile for the architecture - builds a global table, mapping each model of CPU to the corresponding PMU-events table. The 'pmu-events.c' is generated when building perf and added to libperf.a. The global table pmu_events_map[] table in this pmu-events.c will be used in perf in a follow-on patch. If the architecture does not have any JSON files or there is an error in processing them, an empty mapping file is created. This would allow the build of perf to proceed even if we are not able to provide aliases for events. The parser for JSON files allows parsing Intel style JSON event files. This allows to use an Intel event list directly with perf. The Intel event lists can be quite large and are too big to store in unswappable kernel memory. The conversion from JSON to C-style is straight forward. The parser knows (very little) Intel specific information, and can be easily extended to handle fields for other CPUs. The parser code is partially shared with an independent parsing library, which is 2-clause BSD licensed. To avoid any conflicts I marked those files as BSD licensed too. As part of perf they become GPLv2. Committer notes: Fixes: 1) Limit maxfds to 512 to avoid nftd() segfaulting on alloca() with a big rlim_max, as in docker containers - acme 2) Make jevents a hostprog, supporting cross compilation - jolsa 3) Use HOSTCC for jevents final step - acme 4) Define _GNU_SOURCE for asprintf, as we can't use CC's EXTRA_CFLAGS, that has to have --sysroot on the Android NDK 24 - acme 5) Removed $(srctree)/tools/perf/pmu-events/pmu-events.c from the 'clean' target, it is generated on $(OUTPUT)pmu-events/pmu-events.c, which is already taken care of in the original patch - acme Signed-off-by: Andi Kleen <ak@linux.intel.com> Signed-off-by: Jiri Olsa <jolsa@redhat.com> Signed-off-by: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Acked-by: Ingo Molnar <mingo@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Madhavan Srinivasan <maddy@linux.vnet.ibm.com> Cc: linuxppc-dev@lists.ozlabs.org Link: http://lkml.kernel.org/r/1473978296-20712-3-git-send-email-sukadev@linux.vnet.ibm.com Link: http://lkml.kernel.org/r/20160927141846.GA6589@krava Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r--tools/perf/Makefile.perf34
-rw-r--r--tools/perf/pmu-events/Build13
-rw-r--r--tools/perf/pmu-events/jevents.c767
-rw-r--r--tools/perf/pmu-events/jevents.h17
-rw-r--r--tools/perf/pmu-events/json.h6
-rw-r--r--tools/perf/pmu-events/pmu-events.h36
6 files changed, 869 insertions, 4 deletions
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index d710db16b963..982d6439bb07 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -144,6 +144,10 @@ $(call allow-override,LD,$(CROSS_COMPILE)ld)
144 144
145LD += $(EXTRA_LDFLAGS) 145LD += $(EXTRA_LDFLAGS)
146 146
147HOSTCC ?= gcc
148HOSTLD ?= ld
149HOSTAR ?= ar
150
147PKG_CONFIG = $(CROSS_COMPILE)pkg-config 151PKG_CONFIG = $(CROSS_COMPILE)pkg-config
148 152
149RM = rm -f 153RM = rm -f
@@ -345,8 +349,18 @@ strip: $(PROGRAMS) $(OUTPUT)perf
345PERF_IN := $(OUTPUT)perf-in.o 349PERF_IN := $(OUTPUT)perf-in.o
346 350
347export srctree OUTPUT RM CC LD AR CFLAGS V BISON FLEX AWK 351export srctree OUTPUT RM CC LD AR CFLAGS V BISON FLEX AWK
352export HOSTCC HOSTLD HOSTAR
348include $(srctree)/tools/build/Makefile.include 353include $(srctree)/tools/build/Makefile.include
349 354
355JEVENTS := $(OUTPUT)pmu-events/jevents
356JEVENTS_IN := $(OUTPUT)pmu-events/jevents-in.o
357
358PMU_EVENTS_IN := $(OUTPUT)pmu-events/pmu-events-in.o
359
360export JEVENTS
361
362build := -f $(srctree)/tools/build/Makefile.build dir=. obj
363
350$(PERF_IN): prepare FORCE 364$(PERF_IN): prepare FORCE
351 @(test -f ../../include/uapi/linux/perf_event.h && ( \ 365 @(test -f ../../include/uapi/linux/perf_event.h && ( \
352 (diff -B ../include/uapi/linux/perf_event.h ../../include/uapi/linux/perf_event.h >/dev/null) \ 366 (diff -B ../include/uapi/linux/perf_event.h ../../include/uapi/linux/perf_event.h >/dev/null) \
@@ -443,9 +457,18 @@ $(PERF_IN): prepare FORCE
443 || echo "Warning: tools/include/uapi/linux/mman.h differs from kernel" >&2 )) || true 457 || echo "Warning: tools/include/uapi/linux/mman.h differs from kernel" >&2 )) || true
444 $(Q)$(MAKE) $(build)=perf 458 $(Q)$(MAKE) $(build)=perf
445 459
446$(OUTPUT)perf: $(PERFLIBS) $(PERF_IN) $(LIBTRACEEVENT_DYNAMIC_LIST) 460$(JEVENTS_IN): FORCE
461 $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=pmu-events obj=jevents
462
463$(JEVENTS): $(JEVENTS_IN)
464 $(QUIET_LINK)$(HOSTCC) $(JEVENTS_IN) -o $@
465
466$(PMU_EVENTS_IN): $(JEVENTS) FORCE
467 $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=pmu-events obj=pmu-events
468
469$(OUTPUT)perf: $(PERFLIBS) $(PERF_IN) $(PMU_EVENTS_IN) $(LIBTRACEEVENT_DYNAMIC_LIST)
447 $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS) \ 470 $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS) \
448 $(PERF_IN) $(LIBS) -o $@ 471 $(PERF_IN) $(PMU_EVENTS_IN) $(LIBS) -o $@
449 472
450$(GTK_IN): fixdep FORCE 473$(GTK_IN): fixdep FORCE
451 $(Q)$(MAKE) $(build)=gtk 474 $(Q)$(MAKE) $(build)=gtk
@@ -474,6 +497,8 @@ perf.spec $(SCRIPTS) \
474ifneq ($(OUTPUT),) 497ifneq ($(OUTPUT),)
475%.o: $(OUTPUT)%.o 498%.o: $(OUTPUT)%.o
476 @echo " # Redirected target $@ => $(OUTPUT)$@" 499 @echo " # Redirected target $@ => $(OUTPUT)$@"
500pmu-events/%.o: $(OUTPUT)pmu-events/%.o
501 @echo " # Redirected target $@ => $(OUTPUT)$@"
477util/%.o: $(OUTPUT)util/%.o 502util/%.o: $(OUTPUT)util/%.o
478 @echo " # Redirected target $@ => $(OUTPUT)$@" 503 @echo " # Redirected target $@ => $(OUTPUT)$@"
479bench/%.o: $(OUTPUT)bench/%.o 504bench/%.o: $(OUTPUT)bench/%.o
@@ -729,10 +754,11 @@ clean:: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clea
729 $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS) 754 $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS)
730 $(Q)find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete 755 $(Q)find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
731 $(Q)$(RM) $(OUTPUT).config-detected 756 $(Q)$(RM) $(OUTPUT).config-detected
732 $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 757 $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 $(OUTPUT)pmu-events/jevents
733 $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* \ 758 $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* \
734 $(OUTPUT)util/intel-pt-decoder/inat-tables.c $(OUTPUT)fixdep \ 759 $(OUTPUT)util/intel-pt-decoder/inat-tables.c $(OUTPUT)fixdep \
735 $(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c 760 $(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c \
761 $(OUTPUT)pmu-events/pmu-events.c
736 $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean 762 $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean
737 $(python-clean) 763 $(python-clean)
738 764
diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build
new file mode 100644
index 000000000000..9213a1273697
--- /dev/null
+++ b/tools/perf/pmu-events/Build
@@ -0,0 +1,13 @@
1hostprogs := jevents
2
3jevents-y += json.o jsmn.o jevents.o
4pmu-events-y += pmu-events.o
5JDIR = pmu-events/arch/$(ARCH)
6JSON = $(shell [ -d $(JDIR) ] && \
7 find $(JDIR) -name '*.json' -o -name 'mapfile.csv')
8#
9# Locate/process JSON files in pmu-events/arch/
10# directory and create tables in pmu-events.c.
11#
12$(OUTPUT)pmu-events/pmu-events.c: $(JSON) $(JEVENTS)
13 $(Q)$(call echo-cmd,gen)$(JEVENTS) $(ARCH) pmu-events/arch $(OUTPUT)pmu-events/pmu-events.c $(V)
diff --git a/tools/perf/pmu-events/jevents.c b/tools/perf/pmu-events/jevents.c
new file mode 100644
index 000000000000..c4c074a49b6e
--- /dev/null
+++ b/tools/perf/pmu-events/jevents.c
@@ -0,0 +1,767 @@
1#define _XOPEN_SOURCE 500 /* needed for nftw() */
2#define _GNU_SOURCE /* needed for asprintf() */
3
4/* Parse event JSON files */
5
6/*
7 * Copyright (c) 2014, Intel Corporation
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31 * OF THE POSSIBILITY OF SUCH DAMAGE.
32*/
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <errno.h>
37#include <string.h>
38#include <ctype.h>
39#include <unistd.h>
40#include <stdarg.h>
41#include <libgen.h>
42#include <dirent.h>
43#include <sys/time.h> /* getrlimit */
44#include <sys/resource.h> /* getrlimit */
45#include <ftw.h>
46#include <sys/stat.h>
47#include "jsmn.h"
48#include "json.h"
49#include "jevents.h"
50
51#ifndef __maybe_unused
52#define __maybe_unused __attribute__((unused))
53#endif
54
55int verbose;
56char *prog;
57
58int eprintf(int level, int var, const char *fmt, ...)
59{
60
61 int ret;
62 va_list args;
63
64 if (var < level)
65 return 0;
66
67 va_start(args, fmt);
68
69 ret = vfprintf(stderr, fmt, args);
70
71 va_end(args);
72
73 return ret;
74}
75
76__attribute__((weak)) char *get_cpu_str(void)
77{
78 return NULL;
79}
80
81static void addfield(char *map, char **dst, const char *sep,
82 const char *a, jsmntok_t *bt)
83{
84 unsigned int len = strlen(a) + 1 + strlen(sep);
85 int olen = *dst ? strlen(*dst) : 0;
86 int blen = bt ? json_len(bt) : 0;
87 char *out;
88
89 out = realloc(*dst, len + olen + blen);
90 if (!out) {
91 /* Don't add field in this case */
92 return;
93 }
94 *dst = out;
95
96 if (!olen)
97 *(*dst) = 0;
98 else
99 strcat(*dst, sep);
100 strcat(*dst, a);
101 if (bt)
102 strncat(*dst, map + bt->start, blen);
103}
104
105static void fixname(char *s)
106{
107 for (; *s; s++)
108 *s = tolower(*s);
109}
110
111static void fixdesc(char *s)
112{
113 char *e = s + strlen(s);
114
115 /* Remove trailing dots that look ugly in perf list */
116 --e;
117 while (e >= s && isspace(*e))
118 --e;
119 if (*e == '.')
120 *e = 0;
121}
122
123static struct msrmap {
124 const char *num;
125 const char *pname;
126} msrmap[] = {
127 { "0x3F6", "ldlat=" },
128 { "0x1A6", "offcore_rsp=" },
129 { "0x1A7", "offcore_rsp=" },
130 { NULL, NULL }
131};
132
133static struct field {
134 const char *field;
135 const char *kernel;
136} fields[] = {
137 { "EventCode", "event=" },
138 { "UMask", "umask=" },
139 { "CounterMask", "cmask=" },
140 { "Invert", "inv=" },
141 { "AnyThread", "any=" },
142 { "EdgeDetect", "edge=" },
143 { "SampleAfterValue", "period=" },
144 { NULL, NULL }
145};
146
147static void cut_comma(char *map, jsmntok_t *newval)
148{
149 int i;
150
151 /* Cut off everything after comma */
152 for (i = newval->start; i < newval->end; i++) {
153 if (map[i] == ',')
154 newval->end = i;
155 }
156}
157
158static int match_field(char *map, jsmntok_t *field, int nz,
159 char **event, jsmntok_t *val)
160{
161 struct field *f;
162 jsmntok_t newval = *val;
163
164 for (f = fields; f->field; f++)
165 if (json_streq(map, field, f->field) && nz) {
166 cut_comma(map, &newval);
167 addfield(map, event, ",", f->kernel, &newval);
168 return 1;
169 }
170 return 0;
171}
172
173static struct msrmap *lookup_msr(char *map, jsmntok_t *val)
174{
175 jsmntok_t newval = *val;
176 static bool warned;
177 int i;
178
179 cut_comma(map, &newval);
180 for (i = 0; msrmap[i].num; i++)
181 if (json_streq(map, &newval, msrmap[i].num))
182 return &msrmap[i];
183 if (!warned) {
184 warned = true;
185 pr_err("%s: Unknown MSR in event file %.*s\n", prog,
186 json_len(val), map + val->start);
187 }
188 return NULL;
189}
190
191#define EXPECT(e, t, m) do { if (!(e)) { \
192 jsmntok_t *loc = (t); \
193 if (!(t)->start && (t) > tokens) \
194 loc = (t) - 1; \
195 pr_err("%s:%d: " m ", got %s\n", fn, \
196 json_line(map, loc), \
197 json_name(t)); \
198 goto out_free; \
199} } while (0)
200
201#define TOPIC_DEPTH 256
202static char *topic_array[TOPIC_DEPTH];
203static int topic_level;
204
205static char *get_topic(void)
206{
207 char *tp_old, *tp = NULL;
208 int i;
209
210 for (i = 0; i < topic_level + 1; i++) {
211 int n;
212
213 tp_old = tp;
214 n = asprintf(&tp, "%s%s", tp ?: "", topic_array[i]);
215 if (n < 0) {
216 pr_info("%s: asprintf() error %s\n", prog);
217 return NULL;
218 }
219 free(tp_old);
220 }
221
222 for (i = 0; i < (int) strlen(tp); i++) {
223 char c = tp[i];
224
225 if (c == '-')
226 tp[i] = ' ';
227 else if (c == '.') {
228 tp[i] = '\0';
229 break;
230 }
231 }
232
233 return tp;
234}
235
236static int add_topic(int level, char *bname)
237{
238 char *topic;
239
240 level -= 2;
241
242 if (level >= TOPIC_DEPTH)
243 return -EINVAL;
244
245 topic = strdup(bname);
246 if (!topic) {
247 pr_info("%s: strdup() error %s for file %s\n", prog,
248 strerror(errno), bname);
249 return -ENOMEM;
250 }
251
252 free(topic_array[topic_level]);
253 topic_array[topic_level] = topic;
254 topic_level = level;
255 return 0;
256}
257
258struct perf_entry_data {
259 FILE *outfp;
260 char *topic;
261};
262
263static int close_table;
264
265static void print_events_table_prefix(FILE *fp, const char *tblname)
266{
267 fprintf(fp, "struct pmu_event %s[] = {\n", tblname);
268 close_table = 1;
269}
270
271static int print_events_table_entry(void *data, char *name, char *event,
272 char *desc)
273{
274 struct perf_entry_data *pd = data;
275 FILE *outfp = pd->outfp;
276 char *topic = pd->topic;
277
278 /*
279 * TODO: Remove formatting chars after debugging to reduce
280 * string lengths.
281 */
282 fprintf(outfp, "{\n");
283
284 fprintf(outfp, "\t.name = \"%s\",\n", name);
285 fprintf(outfp, "\t.event = \"%s\",\n", event);
286 fprintf(outfp, "\t.desc = \"%s\",\n", desc);
287 fprintf(outfp, "\t.topic = \"%s\",\n", topic);
288
289 fprintf(outfp, "},\n");
290
291 return 0;
292}
293
294static void print_events_table_suffix(FILE *outfp)
295{
296 fprintf(outfp, "{\n");
297
298 fprintf(outfp, "\t.name = 0,\n");
299 fprintf(outfp, "\t.event = 0,\n");
300 fprintf(outfp, "\t.desc = 0,\n");
301
302 fprintf(outfp, "},\n");
303 fprintf(outfp, "};\n");
304 close_table = 0;
305}
306
307/* Call func with each event in the json file */
308int json_events(const char *fn,
309 int (*func)(void *data, char *name, char *event, char *desc),
310 void *data)
311{
312 int err = -EIO;
313 size_t size;
314 jsmntok_t *tokens, *tok;
315 int i, j, len;
316 char *map;
317
318 if (!fn)
319 return -ENOENT;
320
321 tokens = parse_json(fn, &map, &size, &len);
322 if (!tokens)
323 return -EIO;
324 EXPECT(tokens->type == JSMN_ARRAY, tokens, "expected top level array");
325 tok = tokens + 1;
326 for (i = 0; i < tokens->size; i++) {
327 char *event = NULL, *desc = NULL, *name = NULL;
328 struct msrmap *msr = NULL;
329 jsmntok_t *msrval = NULL;
330 jsmntok_t *precise = NULL;
331 jsmntok_t *obj = tok++;
332
333 EXPECT(obj->type == JSMN_OBJECT, obj, "expected object");
334 for (j = 0; j < obj->size; j += 2) {
335 jsmntok_t *field, *val;
336 int nz;
337
338 field = tok + j;
339 EXPECT(field->type == JSMN_STRING, tok + j,
340 "Expected field name");
341 val = tok + j + 1;
342 EXPECT(val->type == JSMN_STRING, tok + j + 1,
343 "Expected string value");
344
345 nz = !json_streq(map, val, "0");
346 if (match_field(map, field, nz, &event, val)) {
347 /* ok */
348 } else if (json_streq(map, field, "EventName")) {
349 addfield(map, &name, "", "", val);
350 } else if (json_streq(map, field, "BriefDescription")) {
351 addfield(map, &desc, "", "", val);
352 fixdesc(desc);
353 } else if (json_streq(map, field, "PEBS") && nz) {
354 precise = val;
355 } else if (json_streq(map, field, "MSRIndex") && nz) {
356 msr = lookup_msr(map, val);
357 } else if (json_streq(map, field, "MSRValue")) {
358 msrval = val;
359 } else if (json_streq(map, field, "Errata") &&
360 !json_streq(map, val, "null")) {
361 addfield(map, &desc, ". ",
362 " Spec update: ", val);
363 } else if (json_streq(map, field, "Data_LA") && nz) {
364 addfield(map, &desc, ". ",
365 " Supports address when precise",
366 NULL);
367 }
368 /* ignore unknown fields */
369 }
370 if (precise && desc && !strstr(desc, "(Precise Event)")) {
371 if (json_streq(map, precise, "2"))
372 addfield(map, &desc, " ", "(Must be precise)",
373 NULL);
374 else
375 addfield(map, &desc, " ",
376 "(Precise event)", NULL);
377 }
378 if (msr != NULL)
379 addfield(map, &event, ",", msr->pname, msrval);
380 fixname(name);
381 err = func(data, name, event, desc);
382 free(event);
383 free(desc);
384 free(name);
385 if (err)
386 break;
387 tok += j;
388 }
389 EXPECT(tok - tokens == len, tok, "unexpected objects at end");
390 err = 0;
391out_free:
392 free_json(map, size, tokens);
393 return err;
394}
395
396static char *file_name_to_table_name(char *fname)
397{
398 unsigned int i;
399 int n;
400 int c;
401 char *tblname;
402
403 /*
404 * Ensure tablename starts with alphabetic character.
405 * Derive rest of table name from basename of the JSON file,
406 * replacing hyphens and stripping out .json suffix.
407 */
408 n = asprintf(&tblname, "pme_%s", basename(fname));
409 if (n < 0) {
410 pr_info("%s: asprintf() error %s for file %s\n", prog,
411 strerror(errno), fname);
412 return NULL;
413 }
414
415 for (i = 0; i < strlen(tblname); i++) {
416 c = tblname[i];
417
418 if (c == '-')
419 tblname[i] = '_';
420 else if (c == '.') {
421 tblname[i] = '\0';
422 break;
423 } else if (!isalnum(c) && c != '_') {
424 pr_err("%s: Invalid character '%c' in file name %s\n",
425 prog, c, basename(fname));
426 free(tblname);
427 tblname = NULL;
428 break;
429 }
430 }
431
432 return tblname;
433}
434
435static void print_mapping_table_prefix(FILE *outfp)
436{
437 fprintf(outfp, "struct pmu_events_map pmu_events_map[] = {\n");
438}
439
440static void print_mapping_table_suffix(FILE *outfp)
441{
442 /*
443 * Print the terminating, NULL entry.
444 */
445 fprintf(outfp, "{\n");
446 fprintf(outfp, "\t.cpuid = 0,\n");
447 fprintf(outfp, "\t.version = 0,\n");
448 fprintf(outfp, "\t.type = 0,\n");
449 fprintf(outfp, "\t.table = 0,\n");
450 fprintf(outfp, "},\n");
451
452 /* and finally, the closing curly bracket for the struct */
453 fprintf(outfp, "};\n");
454}
455
456static int process_mapfile(FILE *outfp, char *fpath)
457{
458 int n = 16384;
459 FILE *mapfp;
460 char *save = NULL;
461 char *line, *p;
462 int line_num;
463 char *tblname;
464
465 pr_info("%s: Processing mapfile %s\n", prog, fpath);
466
467 line = malloc(n);
468 if (!line)
469 return -1;
470
471 mapfp = fopen(fpath, "r");
472 if (!mapfp) {
473 pr_info("%s: Error %s opening %s\n", prog, strerror(errno),
474 fpath);
475 return -1;
476 }
477
478 print_mapping_table_prefix(outfp);
479
480 line_num = 0;
481 while (1) {
482 char *cpuid, *version, *type, *fname;
483
484 line_num++;
485 p = fgets(line, n, mapfp);
486 if (!p)
487 break;
488
489 if (line[0] == '#' || line[0] == '\n')
490 continue;
491
492 if (line[strlen(line)-1] != '\n') {
493 /* TODO Deal with lines longer than 16K */
494 pr_info("%s: Mapfile %s: line %d too long, aborting\n",
495 prog, fpath, line_num);
496 return -1;
497 }
498 line[strlen(line)-1] = '\0';
499
500 cpuid = strtok_r(p, ",", &save);
501 version = strtok_r(NULL, ",", &save);
502 fname = strtok_r(NULL, ",", &save);
503 type = strtok_r(NULL, ",", &save);
504
505 tblname = file_name_to_table_name(fname);
506 fprintf(outfp, "{\n");
507 fprintf(outfp, "\t.cpuid = \"%s\",\n", cpuid);
508 fprintf(outfp, "\t.version = \"%s\",\n", version);
509 fprintf(outfp, "\t.type = \"%s\",\n", type);
510
511 /*
512 * CHECK: We can't use the type (eg "core") field in the
513 * table name. For us to do that, we need to somehow tweak
514 * the other caller of file_name_to_table(), process_json()
515 * to determine the type. process_json() file has no way
516 * of knowing these are "core" events unless file name has
517 * core in it. If filename has core in it, we can safely
518 * ignore the type field here also.
519 */
520 fprintf(outfp, "\t.table = %s\n", tblname);
521 fprintf(outfp, "},\n");
522 }
523
524 print_mapping_table_suffix(outfp);
525
526 return 0;
527}
528
529/*
530 * If we fail to locate/process JSON and map files, create a NULL mapping
531 * table. This would at least allow perf to build even if we can't find/use
532 * the aliases.
533 */
534static void create_empty_mapping(const char *output_file)
535{
536 FILE *outfp;
537
538 pr_info("%s: Creating empty pmu_events_map[] table\n", prog);
539
540 /* Truncate file to clear any partial writes to it */
541 outfp = fopen(output_file, "w");
542 if (!outfp) {
543 perror("fopen()");
544 _Exit(1);
545 }
546
547 fprintf(outfp, "#include \"../../pmu-events/pmu-events.h\"\n");
548 print_mapping_table_prefix(outfp);
549 print_mapping_table_suffix(outfp);
550 fclose(outfp);
551}
552
553static int get_maxfds(void)
554{
555 struct rlimit rlim;
556
557 if (getrlimit(RLIMIT_NOFILE, &rlim) == 0)
558 return min((int)rlim.rlim_max / 2, 512);
559
560 return 512;
561}
562
563/*
564 * nftw() doesn't let us pass an argument to the processing function,
565 * so use a global variables.
566 */
567static FILE *eventsfp;
568static char *mapfile;
569
570static int process_one_file(const char *fpath, const struct stat *sb,
571 int typeflag, struct FTW *ftwbuf)
572{
573 char *tblname, *bname = (char *) fpath + ftwbuf->base;
574 int is_dir = typeflag == FTW_D;
575 int is_file = typeflag == FTW_F;
576 int level = ftwbuf->level;
577 int err = 0;
578
579 pr_debug("%s %d %7jd %-20s %s\n",
580 is_file ? "f" : is_dir ? "d" : "x",
581 level, sb->st_size, bname, fpath);
582
583 /* base dir */
584 if (level == 0)
585 return 0;
586
587 /* model directory, reset topic */
588 if (level == 1 && is_dir) {
589 if (close_table)
590 print_events_table_suffix(eventsfp);
591
592 /*
593 * Drop file name suffix. Replace hyphens with underscores.
594 * Fail if file name contains any alphanum characters besides
595 * underscores.
596 */
597 tblname = file_name_to_table_name(bname);
598 if (!tblname) {
599 pr_info("%s: Error determining table name for %s\n", prog,
600 bname);
601 return -1;
602 }
603
604 print_events_table_prefix(eventsfp, tblname);
605 return 0;
606 }
607
608 /*
609 * Save the mapfile name for now. We will process mapfile
610 * after processing all JSON files (so we can write out the
611 * mapping table after all PMU events tables).
612 *
613 * TODO: Allow for multiple mapfiles? Punt for now.
614 */
615 if (level == 1 && is_file) {
616 if (!strncmp(bname, "mapfile.csv", 11)) {
617 if (mapfile) {
618 pr_info("%s: Many mapfiles? Using %s, ignoring %s\n",
619 prog, mapfile, fpath);
620 } else {
621 mapfile = strdup(fpath);
622 }
623 return 0;
624 }
625
626 pr_info("%s: Ignoring file %s\n", prog, fpath);
627 return 0;
628 }
629
630 /*
631 * If the file name does not have a .json extension,
632 * ignore it. It could be a readme.txt for instance.
633 */
634 if (is_file) {
635 char *suffix = bname + strlen(bname) - 5;
636
637 if (strncmp(suffix, ".json", 5)) {
638 pr_info("%s: Ignoring file without .json suffix %s\n", prog,
639 fpath);
640 return 0;
641 }
642 }
643
644 if (level > 1 && add_topic(level, bname))
645 return -ENOMEM;
646
647 /*
648 * Assume all other files are JSON files.
649 *
650 * If mapfile refers to 'power7_core.json', we create a table
651 * named 'power7_core'. Any inconsistencies between the mapfile
652 * and directory tree could result in build failure due to table
653 * names not being found.
654 *
655 * Atleast for now, be strict with processing JSON file names.
656 * i.e. if JSON file name cannot be mapped to C-style table name,
657 * fail.
658 */
659 if (is_file) {
660 struct perf_entry_data data = {
661 .topic = get_topic(),
662 .outfp = eventsfp,
663 };
664
665 err = json_events(fpath, print_events_table_entry, &data);
666
667 free(data.topic);
668 }
669
670 return err;
671}
672
673#ifndef PATH_MAX
674#define PATH_MAX 4096
675#endif
676
677/*
678 * Starting in directory 'start_dirname', find the "mapfile.csv" and
679 * the set of JSON files for the architecture 'arch'.
680 *
681 * From each JSON file, create a C-style "PMU events table" from the
682 * JSON file (see struct pmu_event).
683 *
684 * From the mapfile, create a mapping between the CPU revisions and
685 * PMU event tables (see struct pmu_events_map).
686 *
687 * Write out the PMU events tables and the mapping table to pmu-event.c.
688 *
689 * If unable to process the JSON or arch files, create an empty mapping
690 * table so we can continue to build/use perf even if we cannot use the
691 * PMU event aliases.
692 */
693int main(int argc, char *argv[])
694{
695 int rc;
696 int maxfds;
697 char ldirname[PATH_MAX];
698
699 const char *arch;
700 const char *output_file;
701 const char *start_dirname;
702
703 prog = basename(argv[0]);
704 if (argc < 4) {
705 pr_err("Usage: %s <arch> <starting_dir> <output_file>\n", prog);
706 return 1;
707 }
708
709 arch = argv[1];
710 start_dirname = argv[2];
711 output_file = argv[3];
712
713 if (argc > 4)
714 verbose = atoi(argv[4]);
715
716 eventsfp = fopen(output_file, "w");
717 if (!eventsfp) {
718 pr_err("%s Unable to create required file %s (%s)\n",
719 prog, output_file, strerror(errno));
720 return 2;
721 }
722
723 /* Include pmu-events.h first */
724 fprintf(eventsfp, "#include \"../../pmu-events/pmu-events.h\"\n");
725
726 sprintf(ldirname, "%s/%s", start_dirname, arch);
727
728 /*
729 * The mapfile allows multiple CPUids to point to the same JSON file,
730 * so, not sure if there is a need for symlinks within the pmu-events
731 * directory.
732 *
733 * For now, treat symlinks of JSON files as regular files and create
734 * separate tables for each symlink (presumably, each symlink refers
735 * to specific version of the CPU).
736 */
737
738 maxfds = get_maxfds();
739 mapfile = NULL;
740 rc = nftw(ldirname, process_one_file, maxfds, 0);
741 if (rc && verbose) {
742 pr_info("%s: Error walking file tree %s\n", prog, ldirname);
743 goto empty_map;
744 } else if (rc) {
745 goto empty_map;
746 }
747
748 if (close_table)
749 print_events_table_suffix(eventsfp);
750
751 if (!mapfile) {
752 pr_info("%s: No CPU->JSON mapping?\n", prog);
753 goto empty_map;
754 }
755
756 if (process_mapfile(eventsfp, mapfile)) {
757 pr_info("%s: Error processing mapfile %s\n", prog, mapfile);
758 goto empty_map;
759 }
760
761 return 0;
762
763empty_map:
764 fclose(eventsfp);
765 create_empty_mapping(output_file);
766 return 0;
767}
diff --git a/tools/perf/pmu-events/jevents.h b/tools/perf/pmu-events/jevents.h
new file mode 100644
index 000000000000..996601f828b6
--- /dev/null
+++ b/tools/perf/pmu-events/jevents.h
@@ -0,0 +1,17 @@
1#ifndef JEVENTS_H
2#define JEVENTS_H 1
3
4int json_events(const char *fn,
5 int (*func)(void *data, char *name, char *event, char *desc),
6 void *data);
7char *get_cpu_str(void);
8
9#ifndef min
10#define min(x, y) ({ \
11 typeof(x) _min1 = (x); \
12 typeof(y) _min2 = (y); \
13 (void) (&_min1 == &_min2); \
14 _min1 < _min2 ? _min1 : _min2; })
15#endif
16
17#endif
diff --git a/tools/perf/pmu-events/json.h b/tools/perf/pmu-events/json.h
index f2745c7a87bc..278ebd32cfb6 100644
--- a/tools/perf/pmu-events/json.h
+++ b/tools/perf/pmu-events/json.h
@@ -20,6 +20,12 @@ extern int eprintf(int level, int var, const char *fmt, ...);
20#define pr_err(fmt, ...) \ 20#define pr_err(fmt, ...) \
21 eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__) 21 eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__)
22 22
23#define pr_info(fmt, ...) \
24 eprintf(1, verbose, pr_fmt(fmt), ##__VA_ARGS__)
25
26#define pr_debug(fmt, ...) \
27 eprintf(2, verbose, pr_fmt(fmt), ##__VA_ARGS__)
28
23#ifndef roundup 29#ifndef roundup
24#define roundup(x, y) ( \ 30#define roundup(x, y) ( \
25{ \ 31{ \
diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h
new file mode 100644
index 000000000000..70d54794e3cb
--- /dev/null
+++ b/tools/perf/pmu-events/pmu-events.h
@@ -0,0 +1,36 @@
1#ifndef PMU_EVENTS_H
2#define PMU_EVENTS_H
3
4/*
5 * Describe each PMU event. Each CPU has a table of PMU events.
6 */
7struct pmu_event {
8 const char *name;
9 const char *event;
10 const char *desc;
11 const char *topic;
12};
13
14/*
15 *
16 * Map a CPU to its table of PMU events. The CPU is identified by the
17 * cpuid field, which is an arch-specific identifier for the CPU.
18 * The identifier specified in tools/perf/pmu-events/arch/xxx/mapfile
19 * must match the get_cpustr() in tools/perf/arch/xxx/util/header.c)
20 *
21 * The cpuid can contain any character other than the comma.
22 */
23struct pmu_events_map {
24 const char *cpuid;
25 const char *version;
26 const char *type; /* core, uncore etc */
27 struct pmu_event *table;
28};
29
30/*
31 * Global table mapping each known CPU for the architecture to its
32 * table of PMU events.
33 */
34extern struct pmu_events_map pmu_events_map[];
35
36#endif