xref: /freebsd/lib/libpmc/pmu-events/jevents.c (revision 51f329660f1c56f295fb13bb3a34fb0895f485a1)
1959826caSMatt Macy /* Parse event JSON files */
2959826caSMatt Macy 
3959826caSMatt Macy /*
4959826caSMatt Macy  * Copyright (c) 2014, Intel Corporation
5959826caSMatt Macy  * All rights reserved.
6959826caSMatt Macy  *
7959826caSMatt Macy  * Redistribution and use in source and binary forms, with or without
8959826caSMatt Macy  * modification, are permitted provided that the following conditions are met:
9959826caSMatt Macy  *
10959826caSMatt Macy  * 1. Redistributions of source code must retain the above copyright notice,
11959826caSMatt Macy  * this list of conditions and the following disclaimer.
12959826caSMatt Macy  *
13959826caSMatt Macy  * 2. Redistributions in binary form must reproduce the above copyright
14959826caSMatt Macy  * notice, this list of conditions and the following disclaimer in the
15959826caSMatt Macy  * documentation and/or other materials provided with the distribution.
16959826caSMatt Macy  *
17959826caSMatt Macy  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18959826caSMatt Macy  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19959826caSMatt Macy  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20959826caSMatt Macy  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21959826caSMatt Macy  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22959826caSMatt Macy  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23959826caSMatt Macy  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24959826caSMatt Macy  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25959826caSMatt Macy  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26959826caSMatt Macy  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27959826caSMatt Macy  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28959826caSMatt Macy  * OF THE POSSIBILITY OF SUCH DAMAGE.
29959826caSMatt Macy  *
30959826caSMatt Macy  * $FreeBSD$
31959826caSMatt Macy  *
32959826caSMatt Macy */
33959826caSMatt Macy 
34b3d01a2aSEnji Cooper #include <sys/param.h>
35b3d01a2aSEnji Cooper #include <sys/resource.h>		/* getrlimit */
36b3d01a2aSEnji Cooper #include <sys/stat.h>
37b3d01a2aSEnji Cooper #include <sys/time.h>			/* getrlimit */
38b3d01a2aSEnji Cooper #include <ctype.h>
39b3d01a2aSEnji Cooper #include <dirent.h>
40b3d01a2aSEnji Cooper #include <errno.h>
41b3d01a2aSEnji Cooper #include <libgen.h>
42b3d01a2aSEnji Cooper #include <limits.h>
43b3d01a2aSEnji Cooper #include <stdarg.h>
442fa224b1SAlex Richardson #include <stddef.h>
45959826caSMatt Macy #include <stdio.h>
46959826caSMatt Macy #include <stdlib.h>
47959826caSMatt Macy #include <string.h>
48959826caSMatt Macy #include <unistd.h>
49*51f32966SAlexander Motin #include <ftw.h>
50959826caSMatt Macy #include "list.h"
51959826caSMatt Macy #include "jsmn.h"
52959826caSMatt Macy #include "json.h"
53959826caSMatt Macy #include "jevents.h"
54959826caSMatt Macy 
55334fd3daSMatt Macy static int
56334fd3daSMatt Macy nftw_ordered(const char *path, int (*fn)(const char *, const struct stat *, int,
57334fd3daSMatt Macy 	struct FTW *), int nfds, int ftwflags);
58334fd3daSMatt Macy 
59*51f32966SAlexander Motin _Noreturn void	 _Exit(int);
60*51f32966SAlexander Motin 
61959826caSMatt Macy int verbose;
62959826caSMatt Macy static char *prog;
63959826caSMatt Macy 
64*51f32966SAlexander Motin int eprintf(int level, int var, const char *fmt, ...)
65959826caSMatt Macy {
66959826caSMatt Macy 
67959826caSMatt Macy 	int ret;
68959826caSMatt Macy 	va_list args;
69959826caSMatt Macy 
70959826caSMatt Macy 	if (var < level)
71959826caSMatt Macy 		return 0;
72959826caSMatt Macy 
73959826caSMatt Macy 	va_start(args, fmt);
74959826caSMatt Macy 
75959826caSMatt Macy 	ret = vfprintf(stderr, fmt, args);
76959826caSMatt Macy 
77959826caSMatt Macy 	va_end(args);
78959826caSMatt Macy 
79959826caSMatt Macy 	return ret;
80959826caSMatt Macy }
81959826caSMatt Macy 
82*51f32966SAlexander Motin __attribute__((weak)) char *get_cpu_str(void)
83959826caSMatt Macy {
84959826caSMatt Macy 	return NULL;
85959826caSMatt Macy }
86959826caSMatt Macy 
87*51f32966SAlexander Motin static void addfield(char *map, char **dst, const char *sep,
88*51f32966SAlexander Motin 		     const char *a, jsmntok_t *bt)
89959826caSMatt Macy {
90959826caSMatt Macy 	unsigned int len = strlen(a) + 1 + strlen(sep);
91959826caSMatt Macy 	int olen = *dst ? strlen(*dst) : 0;
92959826caSMatt Macy 	int blen = bt ? json_len(bt) : 0;
93959826caSMatt Macy 	char *out;
94959826caSMatt Macy 
95959826caSMatt Macy 	out = realloc(*dst, len + olen + blen);
96959826caSMatt Macy 	if (!out) {
97959826caSMatt Macy 		/* Don't add field in this case */
98959826caSMatt Macy 		return;
99959826caSMatt Macy 	}
100959826caSMatt Macy 	*dst = out;
101959826caSMatt Macy 
102959826caSMatt Macy 	if (!olen)
103959826caSMatt Macy 		*(*dst) = 0;
104959826caSMatt Macy 	else
105959826caSMatt Macy 		strcat(*dst, sep);
106959826caSMatt Macy 	strcat(*dst, a);
107959826caSMatt Macy 	if (bt)
108959826caSMatt Macy 		strncat(*dst, map + bt->start, blen);
109959826caSMatt Macy }
110959826caSMatt Macy 
111*51f32966SAlexander Motin static void fixname(char *s)
112959826caSMatt Macy {
113959826caSMatt Macy 	for (; *s; s++)
114959826caSMatt Macy 		*s = tolower(*s);
115959826caSMatt Macy }
116959826caSMatt Macy 
117*51f32966SAlexander Motin static void fixdesc(char *s)
118959826caSMatt Macy {
119959826caSMatt Macy 	char *e = s + strlen(s);
120959826caSMatt Macy 
121959826caSMatt Macy 	/* Remove trailing dots that look ugly in perf list */
122959826caSMatt Macy 	--e;
123959826caSMatt Macy 	while (e >= s && isspace(*e))
124959826caSMatt Macy 		--e;
1256a2a926dSEd Maste 	if (e >= s && *e == '.')
126959826caSMatt Macy 		*e = 0;
127959826caSMatt Macy }
128959826caSMatt Macy 
129959826caSMatt Macy /* Add escapes for '\' so they are proper C strings. */
130*51f32966SAlexander Motin static char *fixregex(char *s)
131959826caSMatt Macy {
132959826caSMatt Macy 	int len = 0;
133959826caSMatt Macy 	int esc_count = 0;
134959826caSMatt Macy 	char *fixed = NULL;
135959826caSMatt Macy 	char *p, *q;
136959826caSMatt Macy 
137959826caSMatt Macy 	/* Count the number of '\' in string */
138959826caSMatt Macy 	for (p = s; *p; p++) {
139959826caSMatt Macy 		++len;
140959826caSMatt Macy 		if (*p == '\\')
141959826caSMatt Macy 			++esc_count;
142959826caSMatt Macy 	}
143959826caSMatt Macy 
144959826caSMatt Macy 	if (esc_count == 0)
145959826caSMatt Macy 		return s;
146959826caSMatt Macy 
147959826caSMatt Macy 	/* allocate space for a new string */
148959826caSMatt Macy 	fixed = (char *) malloc(len + 1);
149959826caSMatt Macy 	if (!fixed)
150959826caSMatt Macy 		return NULL;
151959826caSMatt Macy 
152959826caSMatt Macy 	/* copy over the characters */
153959826caSMatt Macy 	q = fixed;
154959826caSMatt Macy 	for (p = s; *p; p++) {
155959826caSMatt Macy 		if (*p == '\\') {
156959826caSMatt Macy 			*q = '\\';
157959826caSMatt Macy 			++q;
158959826caSMatt Macy 		}
159959826caSMatt Macy 		*q = *p;
160959826caSMatt Macy 		++q;
161959826caSMatt Macy 	}
162959826caSMatt Macy 	*q = '\0';
163959826caSMatt Macy 	return fixed;
164959826caSMatt Macy }
165959826caSMatt Macy 
166959826caSMatt Macy static struct msrmap {
167959826caSMatt Macy 	const char *num;
168959826caSMatt Macy 	const char *pname;
169959826caSMatt Macy } msrmap[] = {
170959826caSMatt Macy 	{ "0x3F6", "ldlat=" },
171959826caSMatt Macy 	{ "0x1A6", "offcore_rsp=" },
172959826caSMatt Macy 	{ "0x1A7", "offcore_rsp=" },
173959826caSMatt Macy 	{ "0x3F7", "frontend=" },
174959826caSMatt Macy 	{ NULL, NULL }
175959826caSMatt Macy };
176959826caSMatt Macy 
177959826caSMatt Macy static struct field {
178959826caSMatt Macy 	const char *field;
179959826caSMatt Macy 	const char *kernel;
180959826caSMatt Macy } fields[] = {
181959826caSMatt Macy 	{ "UMask",	"umask=" },
182959826caSMatt Macy 	{ "CounterMask", "cmask=" },
183959826caSMatt Macy 	{ "Invert",	"inv=" },
184959826caSMatt Macy 	{ "AnyThread",	"any=" },
185959826caSMatt Macy 	{ "EdgeDetect",	"edge=" },
186959826caSMatt Macy 	{ "SampleAfterValue", "period=" },
187959826caSMatt Macy 	{ "FCMask",	"fc_mask=" },
188959826caSMatt Macy 	{ "PortMask",	"ch_mask=" },
189dacc43dfSMatt Macy 	{ "L3ThreadMask", "l3_thread_mask=" },
190dacc43dfSMatt Macy 	{ "L3SliceMask", "l3_slice_mask=" },
191959826caSMatt Macy 	{ NULL, NULL }
192959826caSMatt Macy };
193959826caSMatt Macy 
194*51f32966SAlexander Motin static void cut_comma(char *map, jsmntok_t *newval)
195959826caSMatt Macy {
196959826caSMatt Macy 	int i;
197959826caSMatt Macy 
198959826caSMatt Macy 	/* Cut off everything after comma */
199959826caSMatt Macy 	for (i = newval->start; i < newval->end; i++) {
200959826caSMatt Macy 		if (map[i] == ',')
201959826caSMatt Macy 			newval->end = i;
202959826caSMatt Macy 	}
203959826caSMatt Macy }
204959826caSMatt Macy 
205*51f32966SAlexander Motin static int match_field(char *map, jsmntok_t *field, int nz,
206*51f32966SAlexander Motin 		       char **event, jsmntok_t *val)
207959826caSMatt Macy {
208959826caSMatt Macy 	struct field *f;
209959826caSMatt Macy 	jsmntok_t newval = *val;
210959826caSMatt Macy 
211959826caSMatt Macy 	for (f = fields; f->field; f++)
212959826caSMatt Macy 		if (json_streq(map, field, f->field) && nz) {
213959826caSMatt Macy 			cut_comma(map, &newval);
214959826caSMatt Macy 			addfield(map, event, ",", f->kernel, &newval);
215959826caSMatt Macy 			return 1;
216959826caSMatt Macy 		}
217959826caSMatt Macy 	return 0;
218959826caSMatt Macy }
219959826caSMatt Macy 
220*51f32966SAlexander Motin static struct msrmap *lookup_msr(char *map, jsmntok_t *val)
221959826caSMatt Macy {
222959826caSMatt Macy 	jsmntok_t newval = *val;
223959826caSMatt Macy 	static bool warned;
224959826caSMatt Macy 	int i;
225959826caSMatt Macy 
226959826caSMatt Macy 	cut_comma(map, &newval);
227959826caSMatt Macy 	for (i = 0; msrmap[i].num; i++)
228959826caSMatt Macy 		if (json_streq(map, &newval, msrmap[i].num))
229959826caSMatt Macy 			return &msrmap[i];
230959826caSMatt Macy 	if (!warned) {
231959826caSMatt Macy 		warned = true;
232959826caSMatt Macy 		pr_err("%s: Unknown MSR in event file %.*s\n", prog,
233959826caSMatt Macy 			json_len(val), map + val->start);
234959826caSMatt Macy 	}
235959826caSMatt Macy 	return NULL;
236959826caSMatt Macy }
237959826caSMatt Macy 
238959826caSMatt Macy static struct map {
239959826caSMatt Macy 	const char *json;
240959826caSMatt Macy 	const char *perf;
241959826caSMatt Macy } unit_to_pmu[] = {
242959826caSMatt Macy 	{ "CBO", "uncore_cbox" },
243959826caSMatt Macy 	{ "QPI LL", "uncore_qpi" },
244959826caSMatt Macy 	{ "SBO", "uncore_sbox" },
245959826caSMatt Macy 	{ "iMPH-U", "uncore_arb" },
24618054d02SAlexander Motin 	{ "UPI LL", "uncore_upi" },
24718054d02SAlexander Motin 	{ "L3PMC", "amd_l3" },
24818054d02SAlexander Motin 	{ "DFPMC", "amd_df" },
24918054d02SAlexander Motin 	{ "cpu_core", "cpu_core" },
25018054d02SAlexander Motin 	{ "cpu_atom", "cpu_atom" },
251959826caSMatt Macy 	{}
252959826caSMatt Macy };
253959826caSMatt Macy 
254*51f32966SAlexander Motin static const char *field_to_perf(struct map *table, char *map, jsmntok_t *val)
255959826caSMatt Macy {
256959826caSMatt Macy 	int i;
257959826caSMatt Macy 
258959826caSMatt Macy 	for (i = 0; table[i].json; i++) {
259959826caSMatt Macy 		if (json_streq(map, val, table[i].json))
260959826caSMatt Macy 			return table[i].perf;
261959826caSMatt Macy 	}
262959826caSMatt Macy 	return NULL;
263959826caSMatt Macy }
264959826caSMatt Macy 
265959826caSMatt Macy #define EXPECT(e, t, m) do { if (!(e)) {			\
266959826caSMatt Macy 	jsmntok_t *loc = (t);					\
267959826caSMatt Macy 	if (!(t)->start && (t) > tokens)			\
268959826caSMatt Macy 		loc = (t) - 1;					\
269959826caSMatt Macy 	pr_err("%s:%d: " m ", got %s\n", fn,			\
270959826caSMatt Macy 	       json_line(map, loc),				\
271959826caSMatt Macy 	       json_name(t));					\
272959826caSMatt Macy 	err = -EIO;						\
273959826caSMatt Macy 	goto out_free;						\
274959826caSMatt Macy } } while (0)
275959826caSMatt Macy 
276959826caSMatt Macy static char *topic;
277959826caSMatt Macy 
278*51f32966SAlexander Motin static char *get_topic(void)
279959826caSMatt Macy {
280959826caSMatt Macy 	char *tp;
281959826caSMatt Macy 	int i;
282959826caSMatt Macy 
283959826caSMatt Macy 	/* tp is free'd in process_one_file() */
284959826caSMatt Macy 	i = asprintf(&tp, "%s", topic);
285959826caSMatt Macy 	if (i < 0) {
286959826caSMatt Macy 		pr_info("%s: asprintf() error %s\n", prog);
287959826caSMatt Macy 		return NULL;
288959826caSMatt Macy 	}
289959826caSMatt Macy 
290959826caSMatt Macy 	for (i = 0; i < (int) strlen(tp); i++) {
291959826caSMatt Macy 		char c = tp[i];
292959826caSMatt Macy 
293959826caSMatt Macy 		if (c == '-')
294959826caSMatt Macy 			tp[i] = ' ';
295959826caSMatt Macy 		else if (c == '.') {
296959826caSMatt Macy 			tp[i] = '\0';
297959826caSMatt Macy 			break;
298959826caSMatt Macy 		}
299959826caSMatt Macy 	}
300959826caSMatt Macy 
301959826caSMatt Macy 	return tp;
302959826caSMatt Macy }
303959826caSMatt Macy 
304*51f32966SAlexander Motin static int add_topic(const char *bname)
305959826caSMatt Macy {
306959826caSMatt Macy 	free(topic);
307959826caSMatt Macy 	topic = strdup(bname);
308959826caSMatt Macy 	if (!topic) {
309959826caSMatt Macy 		pr_info("%s: strdup() error %s for file %s\n", prog,
310959826caSMatt Macy 				strerror(errno), bname);
311959826caSMatt Macy 		return -ENOMEM;
312959826caSMatt Macy 	}
313959826caSMatt Macy 	return 0;
314959826caSMatt Macy }
315959826caSMatt Macy 
316959826caSMatt Macy struct perf_entry_data {
317959826caSMatt Macy 	FILE *outfp;
318959826caSMatt Macy 	char *topic;
319959826caSMatt Macy };
320959826caSMatt Macy 
321959826caSMatt Macy static int close_table;
322959826caSMatt Macy 
323*51f32966SAlexander Motin static void print_events_table_prefix(FILE *fp, const char *tblname)
324959826caSMatt Macy {
325959826caSMatt Macy 	fprintf(fp, "static struct pmu_event %s[] = {\n", tblname);
326959826caSMatt Macy 	close_table = 1;
327959826caSMatt Macy }
328959826caSMatt Macy 
329*51f32966SAlexander Motin static int print_events_table_entry(void *data, char *name, const char *event,
330*51f32966SAlexander Motin 				    char *desc, char *long_desc,
331*51f32966SAlexander Motin 				    char *pmu, char *unit, char *perpkg,
332*51f32966SAlexander Motin 				    char *metric_expr,
333959826caSMatt Macy 				    char *metric_name, char *metric_group)
334959826caSMatt Macy {
335959826caSMatt Macy 	struct perf_entry_data *pd = data;
336959826caSMatt Macy 	FILE *outfp = pd->outfp;
337959826caSMatt Macy 	char *etopic = pd->topic;
338959826caSMatt Macy 
339959826caSMatt Macy 	/*
340959826caSMatt Macy 	 * TODO: Remove formatting chars after debugging to reduce
341959826caSMatt Macy 	 *	 string lengths.
342959826caSMatt Macy 	 */
343959826caSMatt Macy 	fprintf(outfp, "{\n");
344959826caSMatt Macy 
345959826caSMatt Macy 	if (name)
346959826caSMatt Macy 		fprintf(outfp, "\t.name = \"%s\",\n", name);
347959826caSMatt Macy 	if (event)
348959826caSMatt Macy 		fprintf(outfp, "\t.event = \"%s\",\n", event);
349959826caSMatt Macy 	fprintf(outfp, "\t.desc = \"%s\",\n", desc);
350959826caSMatt Macy 	fprintf(outfp, "\t.topic = \"%s\",\n", etopic);
351959826caSMatt Macy 	if (long_desc && long_desc[0])
352959826caSMatt Macy 		fprintf(outfp, "\t.long_desc = \"%s\",\n", long_desc);
353959826caSMatt Macy 	if (pmu)
354959826caSMatt Macy 		fprintf(outfp, "\t.pmu = \"%s\",\n", pmu);
355959826caSMatt Macy 	if (unit)
356959826caSMatt Macy 		fprintf(outfp, "\t.unit = \"%s\",\n", unit);
357959826caSMatt Macy 	if (perpkg)
358959826caSMatt Macy 		fprintf(outfp, "\t.perpkg = \"%s\",\n", perpkg);
359959826caSMatt Macy 	if (metric_expr)
360959826caSMatt Macy 		fprintf(outfp, "\t.metric_expr = \"%s\",\n", metric_expr);
361959826caSMatt Macy 	if (metric_name)
362959826caSMatt Macy 		fprintf(outfp, "\t.metric_name = \"%s\",\n", metric_name);
363959826caSMatt Macy 	if (metric_group)
364959826caSMatt Macy 		fprintf(outfp, "\t.metric_group = \"%s\",\n", metric_group);
365959826caSMatt Macy 	fprintf(outfp, "},\n");
366959826caSMatt Macy 
367959826caSMatt Macy 	return 0;
368959826caSMatt Macy }
369959826caSMatt Macy 
370959826caSMatt Macy struct event_struct {
371959826caSMatt Macy 	struct list_head list;
372959826caSMatt Macy 	char *name;
373959826caSMatt Macy 	char *event;
374959826caSMatt Macy 	char *desc;
375959826caSMatt Macy 	char *long_desc;
376959826caSMatt Macy 	char *pmu;
377959826caSMatt Macy 	char *unit;
378959826caSMatt Macy 	char *perpkg;
379959826caSMatt Macy 	char *metric_expr;
380959826caSMatt Macy 	char *metric_name;
381959826caSMatt Macy 	char *metric_group;
382959826caSMatt Macy };
383959826caSMatt Macy 
384959826caSMatt Macy #define ADD_EVENT_FIELD(field) do { if (field) {		\
385959826caSMatt Macy 	es->field = strdup(field);				\
386959826caSMatt Macy 	if (!es->field)						\
387959826caSMatt Macy 		goto out_free;					\
388959826caSMatt Macy } } while (0)
389959826caSMatt Macy 
390959826caSMatt Macy #define FREE_EVENT_FIELD(field) free(es->field)
391959826caSMatt Macy 
392959826caSMatt Macy #define TRY_FIXUP_FIELD(field) do { if (es->field && !*field) {\
393959826caSMatt Macy 	*field = strdup(es->field);				\
394959826caSMatt Macy 	if (!*field)						\
395959826caSMatt Macy 		return -ENOMEM;					\
396959826caSMatt Macy } } while (0)
397959826caSMatt Macy 
398959826caSMatt Macy #define FOR_ALL_EVENT_STRUCT_FIELDS(op) do {			\
399959826caSMatt Macy 	op(name);						\
400959826caSMatt Macy 	op(event);						\
401959826caSMatt Macy 	op(desc);						\
402959826caSMatt Macy 	op(long_desc);						\
403959826caSMatt Macy 	op(pmu);						\
404959826caSMatt Macy 	op(unit);						\
405959826caSMatt Macy 	op(perpkg);						\
406959826caSMatt Macy 	op(metric_expr);					\
407959826caSMatt Macy 	op(metric_name);					\
408959826caSMatt Macy 	op(metric_group);					\
409959826caSMatt Macy } while (0)
410959826caSMatt Macy 
411959826caSMatt Macy static LIST_HEAD(arch_std_events);
412959826caSMatt Macy 
413*51f32966SAlexander Motin static void free_arch_std_events(void)
414959826caSMatt Macy {
415959826caSMatt Macy 	struct event_struct *es, *next;
416959826caSMatt Macy 
417959826caSMatt Macy 	list_for_each_entry_safe(es, next, &arch_std_events, list) {
418959826caSMatt Macy 		FOR_ALL_EVENT_STRUCT_FIELDS(FREE_EVENT_FIELD);
419959826caSMatt Macy 		list_del(&es->list);
420959826caSMatt Macy 		free(es);
421959826caSMatt Macy 	}
422959826caSMatt Macy }
423959826caSMatt Macy 
424*51f32966SAlexander Motin static int save_arch_std_events(void *data __unused, char *name, const char *event,
425*51f32966SAlexander Motin 				char *desc, char *long_desc, char *pmu,
426*51f32966SAlexander Motin 				char *unit, char *perpkg, char *metric_expr,
427*51f32966SAlexander Motin 				char *metric_name, char *metric_group)
428959826caSMatt Macy {
429959826caSMatt Macy 	struct event_struct *es;
430959826caSMatt Macy 
431959826caSMatt Macy 	es = malloc(sizeof(*es));
432959826caSMatt Macy 	if (!es)
433959826caSMatt Macy 		return -ENOMEM;
434959826caSMatt Macy 	memset(es, 0, sizeof(*es));
435959826caSMatt Macy 	FOR_ALL_EVENT_STRUCT_FIELDS(ADD_EVENT_FIELD);
436959826caSMatt Macy 	list_add_tail(&es->list, &arch_std_events);
437959826caSMatt Macy 	return 0;
438959826caSMatt Macy out_free:
439959826caSMatt Macy 	FOR_ALL_EVENT_STRUCT_FIELDS(FREE_EVENT_FIELD);
440959826caSMatt Macy 	free(es);
441959826caSMatt Macy 	return -ENOMEM;
442959826caSMatt Macy }
443959826caSMatt Macy 
444*51f32966SAlexander Motin static void print_events_table_suffix(FILE *outfp)
445959826caSMatt Macy {
446959826caSMatt Macy 	fprintf(outfp, "{\n");
447959826caSMatt Macy 
448959826caSMatt Macy 	fprintf(outfp, "\t.name = 0,\n");
449959826caSMatt Macy 	fprintf(outfp, "\t.event = 0,\n");
450959826caSMatt Macy 	fprintf(outfp, "\t.desc = 0,\n");
451959826caSMatt Macy 
452959826caSMatt Macy 	fprintf(outfp, "},\n");
453959826caSMatt Macy 	fprintf(outfp, "};\n");
454959826caSMatt Macy 	close_table = 0;
455959826caSMatt Macy }
456959826caSMatt Macy 
457959826caSMatt Macy static struct fixed {
458959826caSMatt Macy 	const char *name;
459959826caSMatt Macy 	const char *event;
460959826caSMatt Macy } fixed[] = {
461959826caSMatt Macy 	{ "inst_retired.any", "event=0xc0" },
462959826caSMatt Macy 	{ "inst_retired.any_p", "event=0xc0" },
463959826caSMatt Macy 	{ "cpu_clk_unhalted.ref", "event=0x0,umask=0x03" },
464959826caSMatt Macy 	{ "cpu_clk_unhalted.thread", "event=0x3c" },
465959826caSMatt Macy 	{ "cpu_clk_unhalted.thread_any", "event=0x3c,any=1" },
466959826caSMatt Macy 	{ NULL, NULL},
467959826caSMatt Macy };
468959826caSMatt Macy 
469959826caSMatt Macy /*
470959826caSMatt Macy  * Handle different fixed counter encodings between JSON and perf.
471959826caSMatt Macy  */
472*51f32966SAlexander Motin static const char *real_event(const char *name, char *event)
473959826caSMatt Macy {
474959826caSMatt Macy 	int i;
475959826caSMatt Macy 
476959826caSMatt Macy 	if (!name)
477959826caSMatt Macy 		return NULL;
478959826caSMatt Macy 
479959826caSMatt Macy 	for (i = 0; fixed[i].name; i++)
480959826caSMatt Macy 		if (!strcasecmp(name, fixed[i].name))
481959826caSMatt Macy 			return fixed[i].event;
482959826caSMatt Macy 	return event;
483959826caSMatt Macy }
484959826caSMatt Macy 
485959826caSMatt Macy static int
486959826caSMatt Macy try_fixup(const char *fn, char *arch_std, char **event, char **desc,
487959826caSMatt Macy 	  char **name, char **long_desc, char **pmu, char **filter __unused,
488959826caSMatt Macy 	  char **perpkg, char **unit, char **metric_expr, char **metric_name,
489959826caSMatt Macy 	  char **metric_group, unsigned long long eventcode)
490959826caSMatt Macy {
491959826caSMatt Macy 	/* try to find matching event from arch standard values */
492959826caSMatt Macy 	struct event_struct *es;
493959826caSMatt Macy 
494959826caSMatt Macy 	list_for_each_entry(es, &arch_std_events, list) {
495959826caSMatt Macy 		if (!strcmp(arch_std, es->name)) {
496959826caSMatt Macy 			if (!eventcode && es->event) {
497959826caSMatt Macy 				/* allow EventCode to be overridden */
498959826caSMatt Macy 				free(*event);
499959826caSMatt Macy 				*event = NULL;
500959826caSMatt Macy 			}
501959826caSMatt Macy 			FOR_ALL_EVENT_STRUCT_FIELDS(TRY_FIXUP_FIELD);
502959826caSMatt Macy 			return 0;
503959826caSMatt Macy 		}
504959826caSMatt Macy 	}
505959826caSMatt Macy 
506959826caSMatt Macy 	pr_err("%s: could not find matching %s for %s\n",
507959826caSMatt Macy 					prog, arch_std, fn);
508959826caSMatt Macy 	return -1;
509959826caSMatt Macy }
510959826caSMatt Macy 
511959826caSMatt Macy /* Call func with each event in the json file */
512*51f32966SAlexander Motin int json_events(const char *fn,
513959826caSMatt Macy 	  int (*func)(void *data, char *name, const char *event, char *desc,
514959826caSMatt Macy 		      char *long_desc,
515959826caSMatt Macy 		      char *pmu, char *unit, char *perpkg,
516959826caSMatt Macy 		      char *metric_expr,
517959826caSMatt Macy 		      char *metric_name, char *metric_group),
518959826caSMatt Macy 	  void *data)
519959826caSMatt Macy {
520959826caSMatt Macy 	int err;
521959826caSMatt Macy 	size_t size;
522959826caSMatt Macy 	jsmntok_t *tokens, *tok;
523959826caSMatt Macy 	int i, j, len;
524959826caSMatt Macy 	char *map;
525959826caSMatt Macy 	char buf[128];
526959826caSMatt Macy 
527959826caSMatt Macy 	if (!fn)
528959826caSMatt Macy 		return -ENOENT;
529959826caSMatt Macy 
530959826caSMatt Macy 	tokens = parse_json(fn, &map, &size, &len);
531959826caSMatt Macy 	if (!tokens)
532959826caSMatt Macy 		return -EIO;
533959826caSMatt Macy 	EXPECT(tokens->type == JSMN_ARRAY, tokens, "expected top level array");
534959826caSMatt Macy 	tok = tokens + 1;
535959826caSMatt Macy 	for (i = 0; i < tokens->size; i++) {
536959826caSMatt Macy 		char *event = NULL, *desc = NULL, *name = NULL;
537959826caSMatt Macy 		char *long_desc = NULL;
538959826caSMatt Macy 		char *extra_desc = NULL;
539959826caSMatt Macy 		char *pmu = NULL;
540959826caSMatt Macy 		char *filter = NULL;
541959826caSMatt Macy 		char *perpkg = NULL;
542959826caSMatt Macy 		char *unit = NULL;
543959826caSMatt Macy 		char *metric_expr = NULL;
544959826caSMatt Macy 		char *metric_name = NULL;
545959826caSMatt Macy 		char *metric_group = NULL;
546959826caSMatt Macy 		char *arch_std = NULL;
547959826caSMatt Macy 		unsigned long long eventcode = 0;
548959826caSMatt Macy 		struct msrmap *msr = NULL;
549959826caSMatt Macy 		jsmntok_t *msrval = NULL;
550959826caSMatt Macy 		jsmntok_t *precise = NULL;
551959826caSMatt Macy 		jsmntok_t *obj = tok++;
552959826caSMatt Macy 
553959826caSMatt Macy 		EXPECT(obj->type == JSMN_OBJECT, obj, "expected object");
554959826caSMatt Macy 		for (j = 0; j < obj->size; j += 2) {
555959826caSMatt Macy 			jsmntok_t *field, *val;
556959826caSMatt Macy 			int nz;
557959826caSMatt Macy 			char *s;
558959826caSMatt Macy 
559959826caSMatt Macy 			field = tok + j;
560959826caSMatt Macy 			EXPECT(field->type == JSMN_STRING, tok + j,
561959826caSMatt Macy 			       "Expected field name");
562959826caSMatt Macy 			val = tok + j + 1;
563959826caSMatt Macy 			EXPECT(val->type == JSMN_STRING, tok + j + 1,
564959826caSMatt Macy 			       "Expected string value");
565959826caSMatt Macy 
566959826caSMatt Macy 			nz = !json_streq(map, val, "0");
567959826caSMatt Macy 			if (match_field(map, field, nz, &event, val)) {
568959826caSMatt Macy 				/* ok */
569959826caSMatt Macy 			} else if (json_streq(map, field, "EventCode")) {
570959826caSMatt Macy 				char *code = NULL;
571959826caSMatt Macy 				addfield(map, &code, "", "", val);
572959826caSMatt Macy 				eventcode |= strtoul(code, NULL, 0);
573959826caSMatt Macy 				free(code);
574959826caSMatt Macy 			} else if (json_streq(map, field, "ExtSel")) {
575959826caSMatt Macy 				char *code = NULL;
576959826caSMatt Macy 				addfield(map, &code, "", "", val);
577959826caSMatt Macy 				eventcode |= strtoul(code, NULL, 0) << 21;
578959826caSMatt Macy 				free(code);
579959826caSMatt Macy 			} else if (json_streq(map, field, "EventName")) {
580959826caSMatt Macy 				addfield(map, &name, "", "", val);
581959826caSMatt Macy 			} else if (json_streq(map, field, "BriefDescription")) {
582959826caSMatt Macy 				addfield(map, &desc, "", "", val);
583959826caSMatt Macy 				fixdesc(desc);
584959826caSMatt Macy 			} else if (json_streq(map, field,
585959826caSMatt Macy 					     "PublicDescription")) {
586959826caSMatt Macy 				addfield(map, &long_desc, "", "", val);
587959826caSMatt Macy 				fixdesc(long_desc);
588959826caSMatt Macy 			} else if (json_streq(map, field, "PEBS") && nz) {
589959826caSMatt Macy 				precise = val;
590959826caSMatt Macy 			} else if (json_streq(map, field, "MSRIndex") && nz) {
591959826caSMatt Macy 				msr = lookup_msr(map, val);
592959826caSMatt Macy 			} else if (json_streq(map, field, "MSRValue")) {
593959826caSMatt Macy 				msrval = val;
594959826caSMatt Macy 			} else if (json_streq(map, field, "Errata") &&
595959826caSMatt Macy 				   !json_streq(map, val, "null")) {
596959826caSMatt Macy 				addfield(map, &extra_desc, ". ",
597959826caSMatt Macy 					" Spec update: ", val);
598959826caSMatt Macy 			} else if (json_streq(map, field, "Data_LA") && nz) {
599959826caSMatt Macy 				addfield(map, &extra_desc, ". ",
600959826caSMatt Macy 					" Supports address when precise",
601959826caSMatt Macy 					NULL);
602959826caSMatt Macy 			} else if (json_streq(map, field, "Unit")) {
603959826caSMatt Macy 				const char *ppmu;
604959826caSMatt Macy 
605959826caSMatt Macy 				ppmu = field_to_perf(unit_to_pmu, map, val);
606959826caSMatt Macy 				if (ppmu) {
607959826caSMatt Macy 					pmu = strdup(ppmu);
608959826caSMatt Macy 				} else {
609959826caSMatt Macy 					if (!pmu)
610959826caSMatt Macy 						pmu = strdup("uncore_");
611959826caSMatt Macy 					addfield(map, &pmu, "", "", val);
612959826caSMatt Macy 					for (s = pmu; *s; s++)
613959826caSMatt Macy 						*s = tolower(*s);
614959826caSMatt Macy 				}
615959826caSMatt Macy 				addfield(map, &desc, ". ", "Unit: ", NULL);
616959826caSMatt Macy 				addfield(map, &desc, "", pmu, NULL);
617959826caSMatt Macy 				addfield(map, &desc, "", " ", NULL);
618959826caSMatt Macy 			} else if (json_streq(map, field, "Filter")) {
619959826caSMatt Macy 				addfield(map, &filter, "", "", val);
620959826caSMatt Macy 			} else if (json_streq(map, field, "ScaleUnit")) {
621959826caSMatt Macy 				addfield(map, &unit, "", "", val);
622959826caSMatt Macy 			} else if (json_streq(map, field, "PerPkg")) {
623959826caSMatt Macy 				addfield(map, &perpkg, "", "", val);
624959826caSMatt Macy 			} else if (json_streq(map, field, "MetricName")) {
625959826caSMatt Macy 				addfield(map, &metric_name, "", "", val);
626959826caSMatt Macy 			} else if (json_streq(map, field, "MetricGroup")) {
627959826caSMatt Macy 				addfield(map, &metric_group, "", "", val);
628959826caSMatt Macy 			} else if (json_streq(map, field, "MetricExpr")) {
629959826caSMatt Macy 				addfield(map, &metric_expr, "", "", val);
630959826caSMatt Macy 				for (s = metric_expr; *s; s++)
631959826caSMatt Macy 					*s = tolower(*s);
632959826caSMatt Macy 			} else if (json_streq(map, field, "ArchStdEvent")) {
633959826caSMatt Macy 				addfield(map, &arch_std, "", "", val);
634959826caSMatt Macy 				for (s = arch_std; *s; s++)
635959826caSMatt Macy 					*s = tolower(*s);
636959826caSMatt Macy 			}
637959826caSMatt Macy 			/* ignore unknown fields */
638959826caSMatt Macy 		}
639959826caSMatt Macy 		if (precise && desc && !strstr(desc, "(Precise Event)")) {
640959826caSMatt Macy 			if (json_streq(map, precise, "2"))
641959826caSMatt Macy 				addfield(map, &extra_desc, " ",
642959826caSMatt Macy 						"(Must be precise)", NULL);
643959826caSMatt Macy 			else
644959826caSMatt Macy 				addfield(map, &extra_desc, " ",
645959826caSMatt Macy 						"(Precise event)", NULL);
646959826caSMatt Macy 		}
647b3d01a2aSEnji Cooper 		snprintf(buf, sizeof(buf), "event=%#llx", eventcode);
648959826caSMatt Macy 		addfield(map, &event, ",", buf, NULL);
649959826caSMatt Macy 		if (desc && extra_desc)
650959826caSMatt Macy 			addfield(map, &desc, " ", extra_desc, NULL);
651959826caSMatt Macy 		if (long_desc && extra_desc)
652959826caSMatt Macy 			addfield(map, &long_desc, " ", extra_desc, NULL);
653959826caSMatt Macy 		if (filter)
654959826caSMatt Macy 			addfield(map, &event, ",", filter, NULL);
655959826caSMatt Macy 		if (msr != NULL)
656959826caSMatt Macy 			addfield(map, &event, ",", msr->pname, msrval);
657959826caSMatt Macy 		if (name)
658959826caSMatt Macy 			fixname(name);
659959826caSMatt Macy 
660959826caSMatt Macy 		if (arch_std) {
661959826caSMatt Macy 			/*
662959826caSMatt Macy 			 * An arch standard event is referenced, so try to
663959826caSMatt Macy 			 * fixup any unassigned values.
664959826caSMatt Macy 			 */
665959826caSMatt Macy 			err = try_fixup(fn, arch_std, &event, &desc, &name,
666959826caSMatt Macy 					&long_desc, &pmu, &filter, &perpkg,
667959826caSMatt Macy 					&unit, &metric_expr, &metric_name,
668959826caSMatt Macy 					&metric_group, eventcode);
669959826caSMatt Macy 			if (err)
670959826caSMatt Macy 				goto free_strings;
671959826caSMatt Macy 		}
672959826caSMatt Macy 		err = func(data, name, real_event(name, event), desc, long_desc,
673959826caSMatt Macy 			   pmu, unit, perpkg, metric_expr, metric_name, metric_group);
674959826caSMatt Macy free_strings:
675959826caSMatt Macy 		free(event);
676959826caSMatt Macy 		free(desc);
677959826caSMatt Macy 		free(name);
678959826caSMatt Macy 		free(long_desc);
679959826caSMatt Macy 		free(extra_desc);
680959826caSMatt Macy 		free(pmu);
681959826caSMatt Macy 		free(filter);
682959826caSMatt Macy 		free(perpkg);
683959826caSMatt Macy 		free(unit);
684959826caSMatt Macy 		free(metric_expr);
685959826caSMatt Macy 		free(metric_name);
686959826caSMatt Macy 		free(metric_group);
687959826caSMatt Macy 		free(arch_std);
688959826caSMatt Macy 
689959826caSMatt Macy 		if (err)
690959826caSMatt Macy 			break;
691959826caSMatt Macy 		tok += j;
692959826caSMatt Macy 	}
693959826caSMatt Macy 	EXPECT(tok - tokens == len, tok, "unexpected objects at end");
694959826caSMatt Macy 	err = 0;
695959826caSMatt Macy out_free:
696959826caSMatt Macy 	free_json(map, size, tokens);
697959826caSMatt Macy 	return err;
698959826caSMatt Macy }
699959826caSMatt Macy 
700*51f32966SAlexander Motin static char *file_name_to_table_name(const char *fname)
701959826caSMatt Macy {
702959826caSMatt Macy 	unsigned int i;
703959826caSMatt Macy 	int n;
704959826caSMatt Macy 	int c;
705959826caSMatt Macy 	char *tblname;
706959826caSMatt Macy 
707959826caSMatt Macy 
708959826caSMatt Macy 	/*
709959826caSMatt Macy 	 * Ensure tablename starts with alphabetic character.
710959826caSMatt Macy 	 * Derive rest of table name from basename of the JSON file,
711959826caSMatt Macy 	 * replacing hyphens and stripping out .json suffix.
712959826caSMatt Macy 	 */
713959826caSMatt Macy 	n = asprintf(&tblname, "pme_%s", fname);
714959826caSMatt Macy 	if (n < 0) {
715959826caSMatt Macy 		pr_info("%s: asprintf() error %s for file %s\n", prog,
716959826caSMatt Macy 				strerror(errno), fname);
717959826caSMatt Macy 		return NULL;
718959826caSMatt Macy 	}
719959826caSMatt Macy 
720959826caSMatt Macy 	for (i = 0; i < strlen(tblname); i++) {
721959826caSMatt Macy 		c = tblname[i];
722959826caSMatt Macy 
723959826caSMatt Macy 		if (c == '-' || c == '/')
724959826caSMatt Macy 			tblname[i] = '_';
725959826caSMatt Macy 		else if (c == '.') {
726959826caSMatt Macy 			tblname[i] = '\0';
727959826caSMatt Macy 			break;
728959826caSMatt Macy 		} else if (!isalnum(c) && c != '_') {
729959826caSMatt Macy 			char *tmp = strdup(fname);
730959826caSMatt Macy 			pr_err("%s: Invalid character '%c' in file name %s\n",
731959826caSMatt Macy 					prog, c, basename(tmp));
732959826caSMatt Macy 			free(tblname);
733959826caSMatt Macy 			free(tmp);
734959826caSMatt Macy 			tblname = NULL;
735959826caSMatt Macy 			break;
736959826caSMatt Macy 		}
737959826caSMatt Macy 	}
738959826caSMatt Macy 
739959826caSMatt Macy 	return tblname;
740959826caSMatt Macy }
741959826caSMatt Macy 
742*51f32966SAlexander Motin static void print_mapping_table_prefix(FILE *outfp)
743959826caSMatt Macy {
744959826caSMatt Macy 	fprintf(outfp, "struct pmu_events_map pmu_events_map[] = {\n");
745959826caSMatt Macy }
746959826caSMatt Macy 
747*51f32966SAlexander Motin static void print_mapping_table_suffix(FILE *outfp)
748959826caSMatt Macy {
749959826caSMatt Macy 	/*
750959826caSMatt Macy 	 * Print the terminating, NULL entry.
751959826caSMatt Macy 	 */
752959826caSMatt Macy 	fprintf(outfp, "{\n");
753959826caSMatt Macy 	fprintf(outfp, "\t.cpuid = 0,\n");
754959826caSMatt Macy 	fprintf(outfp, "\t.version = 0,\n");
755959826caSMatt Macy 	fprintf(outfp, "\t.type = 0,\n");
756959826caSMatt Macy 	fprintf(outfp, "\t.table = 0,\n");
757959826caSMatt Macy 	fprintf(outfp, "},\n");
758959826caSMatt Macy 
759959826caSMatt Macy 	/* and finally, the closing curly bracket for the struct */
760959826caSMatt Macy 	fprintf(outfp, "};\n");
761959826caSMatt Macy }
762959826caSMatt Macy 
763*51f32966SAlexander Motin static int process_mapfile(FILE *outfp, char *fpath)
764959826caSMatt Macy {
765959826caSMatt Macy 	int n = 16384;
766959826caSMatt Macy 	FILE *mapfp;
767959826caSMatt Macy 	char *save = NULL;
768959826caSMatt Macy 	char *line, *p;
769959826caSMatt Macy 	int line_num;
770959826caSMatt Macy 	char *tblname;
771959826caSMatt Macy 
772959826caSMatt Macy 	pr_info("%s: Processing mapfile %s\n", prog, fpath);
773959826caSMatt Macy 
774959826caSMatt Macy 	line = malloc(n);
775959826caSMatt Macy 	if (!line)
776959826caSMatt Macy 		return -1;
777959826caSMatt Macy 
778959826caSMatt Macy 	mapfp = fopen(fpath, "r");
779959826caSMatt Macy 	if (!mapfp) {
780959826caSMatt Macy 		pr_info("%s: Error %s opening %s\n", prog, strerror(errno),
781959826caSMatt Macy 				fpath);
7824644463cSEric van Gyzen 		free(line);
783959826caSMatt Macy 		return -1;
784959826caSMatt Macy 	}
785959826caSMatt Macy 
786959826caSMatt Macy 	print_mapping_table_prefix(outfp);
787959826caSMatt Macy 
788959826caSMatt Macy 	/* Skip first line (header) */
789959826caSMatt Macy 	p = fgets(line, n, mapfp);
790959826caSMatt Macy 	if (!p)
791959826caSMatt Macy 		goto out;
792959826caSMatt Macy 
793959826caSMatt Macy 	line_num = 1;
794959826caSMatt Macy 	while (1) {
795959826caSMatt Macy 		char *cpuid, *version, *type, *fname;
796959826caSMatt Macy 
797959826caSMatt Macy 		line_num++;
798959826caSMatt Macy 		p = fgets(line, n, mapfp);
799959826caSMatt Macy 		if (!p)
800959826caSMatt Macy 			break;
801959826caSMatt Macy 
802959826caSMatt Macy 		if (line[0] == '#' || line[0] == '\n')
803959826caSMatt Macy 			continue;
804959826caSMatt Macy 
805959826caSMatt Macy 		if (line[strlen(line)-1] != '\n') {
806959826caSMatt Macy 			/* TODO Deal with lines longer than 16K */
807959826caSMatt Macy 			pr_info("%s: Mapfile %s: line %d too long, aborting\n",
808959826caSMatt Macy 					prog, fpath, line_num);
809acde2586SEric van Gyzen 			free(line);
810acde2586SEric van Gyzen 			fclose(mapfp);
811959826caSMatt Macy 			return -1;
812959826caSMatt Macy 		}
813959826caSMatt Macy 		line[strlen(line)-1] = '\0';
814959826caSMatt Macy 
815959826caSMatt Macy 		cpuid = fixregex(strtok_r(p, ",", &save));
816959826caSMatt Macy 		version = strtok_r(NULL, ",", &save);
817959826caSMatt Macy 		fname = strtok_r(NULL, ",", &save);
818959826caSMatt Macy 		type = strtok_r(NULL, ",", &save);
819959826caSMatt Macy 
820959826caSMatt Macy 		tblname = file_name_to_table_name(fname);
821959826caSMatt Macy 		fprintf(outfp, "{\n");
822959826caSMatt Macy 		fprintf(outfp, "\t.cpuid = \"%s\",\n", cpuid);
823959826caSMatt Macy 		fprintf(outfp, "\t.version = \"%s\",\n", version);
824959826caSMatt Macy 		fprintf(outfp, "\t.type = \"%s\",\n", type);
825959826caSMatt Macy 
826959826caSMatt Macy 		/*
827959826caSMatt Macy 		 * CHECK: We can't use the type (eg "core") field in the
828959826caSMatt Macy 		 * table name. For us to do that, we need to somehow tweak
829959826caSMatt Macy 		 * the other caller of file_name_to_table(), process_json()
830959826caSMatt Macy 		 * to determine the type. process_json() file has no way
831959826caSMatt Macy 		 * of knowing these are "core" events unless file name has
832959826caSMatt Macy 		 * core in it. If filename has core in it, we can safely
833959826caSMatt Macy 		 * ignore the type field here also.
834959826caSMatt Macy 		 */
835959826caSMatt Macy 		fprintf(outfp, "\t.table = %s\n", tblname);
836959826caSMatt Macy 		fprintf(outfp, "},\n");
837959826caSMatt Macy 	}
838959826caSMatt Macy 
839959826caSMatt Macy out:
840959826caSMatt Macy 	print_mapping_table_suffix(outfp);
8414644463cSEric van Gyzen 	free(line);
8424644463cSEric van Gyzen 	fclose(mapfp);
843959826caSMatt Macy 	return 0;
844959826caSMatt Macy }
845959826caSMatt Macy 
846959826caSMatt Macy /*
847959826caSMatt Macy  * If we fail to locate/process JSON and map files, create a NULL mapping
848959826caSMatt Macy  * table. This would at least allow perf to build even if we can't find/use
849959826caSMatt Macy  * the aliases.
850959826caSMatt Macy  */
851*51f32966SAlexander Motin static void create_empty_mapping(const char *output_file)
852959826caSMatt Macy {
853959826caSMatt Macy 	FILE *outfp;
854959826caSMatt Macy 
855959826caSMatt Macy 	pr_info("%s: Creating empty pmu_events_map[] table\n", prog);
856959826caSMatt Macy 
857959826caSMatt Macy 	/* Truncate file to clear any partial writes to it */
858959826caSMatt Macy 	outfp = fopen(output_file, "w");
859959826caSMatt Macy 	if (!outfp) {
860959826caSMatt Macy 		perror("fopen()");
861959826caSMatt Macy 		_Exit(1);
862959826caSMatt Macy 	}
863959826caSMatt Macy 
864959826caSMatt Macy 	fprintf(outfp, "#include \"pmu-events/pmu-events.h\"\n");
865959826caSMatt Macy 	print_mapping_table_prefix(outfp);
866959826caSMatt Macy 	print_mapping_table_suffix(outfp);
867959826caSMatt Macy 	fclose(outfp);
868959826caSMatt Macy }
869959826caSMatt Macy 
870*51f32966SAlexander Motin static int get_maxfds(void)
871959826caSMatt Macy {
872959826caSMatt Macy 	struct rlimit rlim;
873959826caSMatt Macy 
8742fa224b1SAlex Richardson 	if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
8752fa224b1SAlex Richardson 		if (rlim.rlim_max == RLIM_INFINITY)
8762fa224b1SAlex Richardson 			return 512;
877b3d01a2aSEnji Cooper 		return MIN(rlim.rlim_max / 2, 512);
8782fa224b1SAlex Richardson 	}
879959826caSMatt Macy 
880959826caSMatt Macy 	return 512;
881959826caSMatt Macy }
882959826caSMatt Macy 
883959826caSMatt Macy /*
884959826caSMatt Macy  * nftw() doesn't let us pass an argument to the processing function,
885959826caSMatt Macy  * so use a global variables.
886959826caSMatt Macy  */
887959826caSMatt Macy static FILE *eventsfp;
888959826caSMatt Macy static char *mapfile;
889959826caSMatt Macy 
890*51f32966SAlexander Motin static int is_leaf_dir(const char *fpath)
891959826caSMatt Macy {
892959826caSMatt Macy 	DIR *d;
893959826caSMatt Macy 	struct dirent *dir;
894959826caSMatt Macy 	int res = 1;
895959826caSMatt Macy 
896959826caSMatt Macy 	d = opendir(fpath);
897959826caSMatt Macy 	if (!d)
898959826caSMatt Macy 		return 0;
899959826caSMatt Macy 
900959826caSMatt Macy 	while ((dir = readdir(d)) != NULL) {
901959826caSMatt Macy 		if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, ".."))
902959826caSMatt Macy 			continue;
903959826caSMatt Macy 
904959826caSMatt Macy 		if (dir->d_type == DT_DIR) {
905959826caSMatt Macy 			res = 0;
906959826caSMatt Macy 			break;
907959826caSMatt Macy 		} else if (dir->d_type == DT_UNKNOWN) {
908959826caSMatt Macy 			char path[PATH_MAX];
909959826caSMatt Macy 			struct stat st;
910959826caSMatt Macy 
911f824ea0cSConrad Meyer 			snprintf(path, sizeof(path), "%s/%s", fpath,
912f824ea0cSConrad Meyer 			    dir->d_name);
913959826caSMatt Macy 			if (stat(path, &st))
914959826caSMatt Macy 				break;
915959826caSMatt Macy 
916959826caSMatt Macy 			if (S_ISDIR(st.st_mode)) {
917959826caSMatt Macy 				res = 0;
918959826caSMatt Macy 				break;
919959826caSMatt Macy 			}
920959826caSMatt Macy 		}
921959826caSMatt Macy 	}
922959826caSMatt Macy 
923959826caSMatt Macy 	closedir(d);
924959826caSMatt Macy 
925959826caSMatt Macy 	return res;
926959826caSMatt Macy }
927959826caSMatt Macy 
928*51f32966SAlexander Motin static int is_json_file(const char *name)
929959826caSMatt Macy {
930959826caSMatt Macy 	const char *suffix;
931959826caSMatt Macy 
932959826caSMatt Macy 	if (strlen(name) < 5)
933959826caSMatt Macy 		return 0;
934959826caSMatt Macy 
935959826caSMatt Macy 	suffix = name + strlen(name) - 5;
936959826caSMatt Macy 
937959826caSMatt Macy 	if (strncmp(suffix, ".json", 5) == 0)
938959826caSMatt Macy 		return 1;
939959826caSMatt Macy 	return 0;
940959826caSMatt Macy }
941959826caSMatt Macy 
942*51f32966SAlexander Motin static int preprocess_arch_std_files(const char *fpath, const struct stat *sb,
943959826caSMatt Macy 				int typeflag, struct FTW *ftwbuf)
944959826caSMatt Macy {
945959826caSMatt Macy 	int level = ftwbuf->level;
946959826caSMatt Macy 	int is_file = typeflag == FTW_F;
947959826caSMatt Macy 
948959826caSMatt Macy 	if (level == 1 && is_file && is_json_file(fpath))
949959826caSMatt Macy 		return json_events(fpath, save_arch_std_events, (void *)(uintptr_t)sb);
950959826caSMatt Macy 
951959826caSMatt Macy 	return 0;
952959826caSMatt Macy }
953959826caSMatt Macy 
954*51f32966SAlexander Motin static int process_one_file(const char *fpath, const struct stat *sb,
955*51f32966SAlexander Motin 			    int typeflag, struct FTW *ftwbuf)
956959826caSMatt Macy {
957959826caSMatt Macy 	char *tblname;
958959826caSMatt Macy 	const char *bname;
959959826caSMatt Macy 	int is_dir  = typeflag == FTW_D;
960959826caSMatt Macy 	int is_file = typeflag == FTW_F;
961959826caSMatt Macy 	int level   = ftwbuf->level;
962959826caSMatt Macy 	int err = 0;
963959826caSMatt Macy 
964959826caSMatt Macy 	if (level == 2 && is_dir) {
965959826caSMatt Macy 		/*
966959826caSMatt Macy 		 * For level 2 directory, bname will include parent name,
967959826caSMatt Macy 		 * like vendor/platform. So search back from platform dir
968959826caSMatt Macy 		 * to find this.
969959826caSMatt Macy 		 */
970959826caSMatt Macy 		bname = fpath + ftwbuf->base - 2;
971959826caSMatt Macy 		for (;;) {
972959826caSMatt Macy 			if (*bname == '/')
973959826caSMatt Macy 				break;
974959826caSMatt Macy 			bname--;
975959826caSMatt Macy 		}
976959826caSMatt Macy 		bname++;
977959826caSMatt Macy 	} else
978959826caSMatt Macy 		bname = fpath + ftwbuf->base;
979959826caSMatt Macy 
980959826caSMatt Macy 	pr_debug("%s %d %7jd %-20s %s\n",
981959826caSMatt Macy 		 is_file ? "f" : is_dir ? "d" : "x",
982959826caSMatt Macy 		 level, sb->st_size, bname, fpath);
983959826caSMatt Macy 
984959826caSMatt Macy 	/* base dir or too deep */
985959826caSMatt Macy 	if (level == 0 || level > 3)
986959826caSMatt Macy 		return 0;
987959826caSMatt Macy 
988959826caSMatt Macy 
989959826caSMatt Macy 	/* model directory, reset topic */
990959826caSMatt Macy 	if ((level == 1 && is_dir && is_leaf_dir(fpath)) ||
991959826caSMatt Macy 	    (level == 2 && is_dir)) {
992959826caSMatt Macy 		if (close_table)
993959826caSMatt Macy 			print_events_table_suffix(eventsfp);
994959826caSMatt Macy 
995959826caSMatt Macy 		/*
996959826caSMatt Macy 		 * Drop file name suffix. Replace hyphens with underscores.
997959826caSMatt Macy 		 * Fail if file name contains any alphanum characters besides
998959826caSMatt Macy 		 * underscores.
999959826caSMatt Macy 		 */
1000959826caSMatt Macy 		tblname = file_name_to_table_name(bname);
1001959826caSMatt Macy 		if (!tblname) {
1002959826caSMatt Macy 			pr_info("%s: Error determining table name for %s\n", prog,
1003959826caSMatt Macy 				bname);
1004959826caSMatt Macy 			return -1;
1005959826caSMatt Macy 		}
1006959826caSMatt Macy 
1007959826caSMatt Macy 		print_events_table_prefix(eventsfp, tblname);
1008959826caSMatt Macy 		return 0;
1009959826caSMatt Macy 	}
1010959826caSMatt Macy 
1011959826caSMatt Macy 	/*
1012959826caSMatt Macy 	 * Save the mapfile name for now. We will process mapfile
1013959826caSMatt Macy 	 * after processing all JSON files (so we can write out the
1014959826caSMatt Macy 	 * mapping table after all PMU events tables).
1015959826caSMatt Macy 	 *
1016959826caSMatt Macy 	 */
1017959826caSMatt Macy 	if (level == 1 && is_file) {
1018959826caSMatt Macy 		if (!strcmp(bname, "mapfile.csv")) {
1019959826caSMatt Macy 			mapfile = strdup(fpath);
1020959826caSMatt Macy 			return 0;
1021959826caSMatt Macy 		}
1022959826caSMatt Macy 
1023959826caSMatt Macy 		pr_info("%s: Ignoring file %s\n", prog, fpath);
1024959826caSMatt Macy 		return 0;
1025959826caSMatt Macy 	}
1026959826caSMatt Macy 
1027959826caSMatt Macy 	/*
1028959826caSMatt Macy 	 * If the file name does not have a .json extension,
1029959826caSMatt Macy 	 * ignore it. It could be a readme.txt for instance.
1030959826caSMatt Macy 	 */
1031959826caSMatt Macy 	if (is_file) {
1032959826caSMatt Macy 		if (!is_json_file(bname)) {
1033959826caSMatt Macy 			pr_info("%s: Ignoring file without .json suffix %s\n", prog,
1034959826caSMatt Macy 				fpath);
1035959826caSMatt Macy 			return 0;
1036959826caSMatt Macy 		}
1037959826caSMatt Macy 	}
1038959826caSMatt Macy 
1039959826caSMatt Macy 	if (level > 1 && add_topic(bname))
1040959826caSMatt Macy 		return -ENOMEM;
1041959826caSMatt Macy 
1042959826caSMatt Macy 	/*
1043959826caSMatt Macy 	 * Assume all other files are JSON files.
1044959826caSMatt Macy 	 *
1045959826caSMatt Macy 	 * If mapfile refers to 'power7_core.json', we create a table
1046959826caSMatt Macy 	 * named 'power7_core'. Any inconsistencies between the mapfile
1047959826caSMatt Macy 	 * and directory tree could result in build failure due to table
1048959826caSMatt Macy 	 * names not being found.
1049959826caSMatt Macy 	 *
1050959826caSMatt Macy 	 * Atleast for now, be strict with processing JSON file names.
1051959826caSMatt Macy 	 * i.e. if JSON file name cannot be mapped to C-style table name,
1052959826caSMatt Macy 	 * fail.
1053959826caSMatt Macy 	 */
1054959826caSMatt Macy 	if (is_file) {
1055959826caSMatt Macy 		struct perf_entry_data data = {
1056959826caSMatt Macy 			.topic = get_topic(),
1057959826caSMatt Macy 			.outfp = eventsfp,
1058959826caSMatt Macy 		};
1059959826caSMatt Macy 
1060959826caSMatt Macy 		err = json_events(fpath, print_events_table_entry, &data);
1061959826caSMatt Macy 
1062959826caSMatt Macy 		free(data.topic);
1063959826caSMatt Macy 	}
1064959826caSMatt Macy 
1065959826caSMatt Macy 	return err;
1066959826caSMatt Macy }
1067959826caSMatt Macy 
1068959826caSMatt Macy /*
1069959826caSMatt Macy  * Starting in directory 'start_dirname', find the "mapfile.csv" and
1070959826caSMatt Macy  * the set of JSON files for the architecture 'arch'.
1071959826caSMatt Macy  *
1072959826caSMatt Macy  * From each JSON file, create a C-style "PMU events table" from the
1073959826caSMatt Macy  * JSON file (see struct pmu_event).
1074959826caSMatt Macy  *
1075959826caSMatt Macy  * From the mapfile, create a mapping between the CPU revisions and
1076959826caSMatt Macy  * PMU event tables (see struct pmu_events_map).
1077959826caSMatt Macy  *
1078959826caSMatt Macy  * Write out the PMU events tables and the mapping table to pmu-event.c.
1079959826caSMatt Macy  */
1080*51f32966SAlexander Motin int main(int argc, char *argv[])
1081959826caSMatt Macy {
1082959826caSMatt Macy 	int rc;
1083959826caSMatt Macy 	int maxfds;
1084959826caSMatt Macy 	char ldirname[PATH_MAX];
1085959826caSMatt Macy 
1086959826caSMatt Macy 	const char *arch;
1087959826caSMatt Macy 	const char *output_file;
1088959826caSMatt Macy 	const char *start_dirname;
1089959826caSMatt Macy 	struct stat stbuf;
1090959826caSMatt Macy 
1091959826caSMatt Macy 	prog = basename(argv[0]);
1092959826caSMatt Macy 	if (argc < 4) {
1093959826caSMatt Macy 		pr_err("Usage: %s <arch> <starting_dir> <output_file>\n", prog);
1094959826caSMatt Macy 		return 1;
1095959826caSMatt Macy 	}
1096959826caSMatt Macy 
1097959826caSMatt Macy 	arch = argv[1];
1098959826caSMatt Macy 	start_dirname = argv[2];
1099959826caSMatt Macy 	output_file = argv[3];
1100959826caSMatt Macy 
1101959826caSMatt Macy 	if (argc > 4)
1102959826caSMatt Macy 		verbose = atoi(argv[4]);
1103959826caSMatt Macy 
1104959826caSMatt Macy 	eventsfp = fopen(output_file, "w");
1105959826caSMatt Macy 	if (!eventsfp) {
1106959826caSMatt Macy 		pr_err("%s Unable to create required file %s (%s)\n",
1107959826caSMatt Macy 				prog, output_file, strerror(errno));
1108959826caSMatt Macy 		return 2;
1109959826caSMatt Macy 	}
1110959826caSMatt Macy 
1111f824ea0cSConrad Meyer 	snprintf(ldirname, sizeof(ldirname), "%s/%s", start_dirname, arch);
1112959826caSMatt Macy 
1113959826caSMatt Macy 	/* If architecture does not have any event lists, bail out */
1114959826caSMatt Macy 	if (stat(ldirname, &stbuf) < 0) {
1115959826caSMatt Macy 		pr_info("%s: Arch %s has no PMU event lists\n", prog, arch);
1116959826caSMatt Macy 		goto empty_map;
1117959826caSMatt Macy 	}
1118959826caSMatt Macy 
1119959826caSMatt Macy 	/* Include pmu-events.h first */
1120959826caSMatt Macy 	fprintf(eventsfp, "#include \"pmu-events/pmu-events.h\"\n");
1121959826caSMatt Macy 
1122959826caSMatt Macy 	/*
1123959826caSMatt Macy 	 * The mapfile allows multiple CPUids to point to the same JSON file,
1124959826caSMatt Macy 	 * so, not sure if there is a need for symlinks within the pmu-events
1125959826caSMatt Macy 	 * directory.
1126959826caSMatt Macy 	 *
1127959826caSMatt Macy 	 * For now, treat symlinks of JSON files as regular files and create
1128959826caSMatt Macy 	 * separate tables for each symlink (presumably, each symlink refers
1129959826caSMatt Macy 	 * to specific version of the CPU).
1130959826caSMatt Macy 	 */
1131959826caSMatt Macy 
1132959826caSMatt Macy 	maxfds = get_maxfds();
1133959826caSMatt Macy 	mapfile = NULL;
1134334fd3daSMatt Macy 	rc = nftw_ordered(ldirname, preprocess_arch_std_files, maxfds, 0);
1135959826caSMatt Macy 	if (rc && verbose) {
11362fa224b1SAlex Richardson 		pr_info("%s: Error preprocessing arch standard files %s: %s\n",
11372fa224b1SAlex Richardson 			prog, ldirname, strerror(errno));
1138959826caSMatt Macy 		goto empty_map;
1139959826caSMatt Macy 	} else if (rc < 0) {
1140959826caSMatt Macy 		/* Make build fail */
1141959826caSMatt Macy 		free_arch_std_events();
1142959826caSMatt Macy 		return 1;
1143959826caSMatt Macy 	} else if (rc) {
1144959826caSMatt Macy 		goto empty_map;
1145959826caSMatt Macy 	}
1146959826caSMatt Macy 
1147334fd3daSMatt Macy 	rc = nftw_ordered(ldirname, process_one_file, maxfds, 0);
1148959826caSMatt Macy 	if (rc && verbose) {
1149959826caSMatt Macy 		pr_info("%s: Error walking file tree %s\n", prog, ldirname);
1150959826caSMatt Macy 		goto empty_map;
1151959826caSMatt Macy 	} else if (rc < 0) {
1152959826caSMatt Macy 		/* Make build fail */
1153959826caSMatt Macy 		free_arch_std_events();
1154959826caSMatt Macy 		return 1;
1155959826caSMatt Macy 	} else if (rc) {
1156959826caSMatt Macy 		goto empty_map;
1157959826caSMatt Macy 	}
1158959826caSMatt Macy 
1159959826caSMatt Macy 	if (close_table)
1160959826caSMatt Macy 		print_events_table_suffix(eventsfp);
1161959826caSMatt Macy 
1162959826caSMatt Macy 	if (!mapfile) {
1163959826caSMatt Macy 		pr_info("%s: No CPU->JSON mapping?\n", prog);
1164959826caSMatt Macy 		goto empty_map;
1165959826caSMatt Macy 	}
1166959826caSMatt Macy 
1167959826caSMatt Macy 	if (process_mapfile(eventsfp, mapfile)) {
1168959826caSMatt Macy 		pr_info("%s: Error processing mapfile %s\n", prog, mapfile);
1169959826caSMatt Macy 		/* Make build fail */
1170959826caSMatt Macy 		return 1;
1171959826caSMatt Macy 	}
1172959826caSMatt Macy 
1173959826caSMatt Macy 	return 0;
1174959826caSMatt Macy 
1175959826caSMatt Macy empty_map:
1176959826caSMatt Macy 	fclose(eventsfp);
1177959826caSMatt Macy 	create_empty_mapping(output_file);
1178959826caSMatt Macy 	free_arch_std_events();
1179959826caSMatt Macy 	return 0;
1180959826caSMatt Macy }
1181334fd3daSMatt Macy 
1182*51f32966SAlexander Motin #include <fts.h>
1183*51f32966SAlexander Motin 
1184334fd3daSMatt Macy static int
1185334fd3daSMatt Macy fts_compare(const FTSENT * const *a, const FTSENT * const *b)
1186334fd3daSMatt Macy {
1187334fd3daSMatt Macy 	return (strcmp((*a)->fts_name, (*b)->fts_name));
1188334fd3daSMatt Macy }
1189334fd3daSMatt Macy 
1190334fd3daSMatt Macy static int
1191334fd3daSMatt Macy nftw_ordered(const char *path, int (*fn)(const char *, const struct stat *, int,
1192334fd3daSMatt Macy      struct FTW *), int nfds, int ftwflags)
1193334fd3daSMatt Macy {
1194334fd3daSMatt Macy 	char * const paths[2] = { (char *)path, NULL };
1195334fd3daSMatt Macy 	struct FTW ftw;
1196334fd3daSMatt Macy 	FTSENT *cur;
1197334fd3daSMatt Macy 	FTS *ftsp;
1198334fd3daSMatt Macy 	int error = 0, ftsflags, fnflag, postorder, sverrno;
1199334fd3daSMatt Macy 
1200334fd3daSMatt Macy 	/* XXX - nfds is currently unused */
1201334fd3daSMatt Macy 	if (nfds < 1) {
1202334fd3daSMatt Macy 		errno = EINVAL;
1203334fd3daSMatt Macy 		return (-1);
1204334fd3daSMatt Macy 	}
1205334fd3daSMatt Macy 
1206334fd3daSMatt Macy 	ftsflags = FTS_COMFOLLOW;
1207334fd3daSMatt Macy 	if (!(ftwflags & FTW_CHDIR))
1208334fd3daSMatt Macy 		ftsflags |= FTS_NOCHDIR;
1209334fd3daSMatt Macy 	if (ftwflags & FTW_MOUNT)
1210334fd3daSMatt Macy 		ftsflags |= FTS_XDEV;
1211334fd3daSMatt Macy 	if (ftwflags & FTW_PHYS)
1212334fd3daSMatt Macy 		ftsflags |= FTS_PHYSICAL;
1213334fd3daSMatt Macy 	else
1214334fd3daSMatt Macy 		ftsflags |= FTS_LOGICAL;
1215334fd3daSMatt Macy 	postorder = (ftwflags & FTW_DEPTH) != 0;
1216334fd3daSMatt Macy 	ftsp = fts_open(paths, ftsflags, fts_compare);
1217334fd3daSMatt Macy 	if (ftsp == NULL)
1218334fd3daSMatt Macy 		return (-1);
1219334fd3daSMatt Macy 	while ((cur = fts_read(ftsp)) != NULL) {
1220334fd3daSMatt Macy 		switch (cur->fts_info) {
1221334fd3daSMatt Macy 		case FTS_D:
1222334fd3daSMatt Macy 			if (postorder)
1223334fd3daSMatt Macy 				continue;
1224334fd3daSMatt Macy 			fnflag = FTW_D;
1225334fd3daSMatt Macy 			break;
1226334fd3daSMatt Macy 		case FTS_DC:
1227334fd3daSMatt Macy 			continue;
1228334fd3daSMatt Macy 		case FTS_DNR:
1229334fd3daSMatt Macy 			fnflag = FTW_DNR;
1230334fd3daSMatt Macy 			break;
1231334fd3daSMatt Macy 		case FTS_DP:
1232334fd3daSMatt Macy 			if (!postorder)
1233334fd3daSMatt Macy 				continue;
1234334fd3daSMatt Macy 			fnflag = FTW_DP;
1235334fd3daSMatt Macy 			break;
1236334fd3daSMatt Macy 		case FTS_F:
1237334fd3daSMatt Macy 		case FTS_DEFAULT:
1238334fd3daSMatt Macy 			fnflag = FTW_F;
1239334fd3daSMatt Macy 			break;
1240334fd3daSMatt Macy 		case FTS_NS:
1241334fd3daSMatt Macy 		case FTS_NSOK:
1242334fd3daSMatt Macy 			fnflag = FTW_NS;
1243334fd3daSMatt Macy 			break;
1244334fd3daSMatt Macy 		case FTS_SL:
1245334fd3daSMatt Macy 			fnflag = FTW_SL;
1246334fd3daSMatt Macy 			break;
1247334fd3daSMatt Macy 		case FTS_SLNONE:
1248334fd3daSMatt Macy 			fnflag = FTW_SLN;
1249334fd3daSMatt Macy 			break;
1250334fd3daSMatt Macy 		default:
1251334fd3daSMatt Macy 			error = -1;
1252334fd3daSMatt Macy 			goto done;
1253334fd3daSMatt Macy 		}
1254334fd3daSMatt Macy 		ftw.base = cur->fts_pathlen - cur->fts_namelen;
1255334fd3daSMatt Macy 		ftw.level = cur->fts_level;
1256334fd3daSMatt Macy 		error = fn(cur->fts_path, cur->fts_statp, fnflag, &ftw);
1257334fd3daSMatt Macy 		if (error != 0)
1258334fd3daSMatt Macy 			break;
1259334fd3daSMatt Macy 	}
1260334fd3daSMatt Macy done:
1261334fd3daSMatt Macy 	sverrno = errno;
1262334fd3daSMatt Macy 	if (fts_close(ftsp) != 0 && error == 0)
1263334fd3daSMatt Macy 		error = -1;
1264334fd3daSMatt Macy 	else
1265334fd3daSMatt Macy 		errno = sverrno;
1266334fd3daSMatt Macy 	return (error);
1267334fd3daSMatt Macy }
1268