xref: /illumos-gate/usr/src/tools/cpcgen/cpcgen.c (revision a92282e44f968185a6bba094d1e5fece2da819cf)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2019, Joyent, Inc.
14  */
15 
16 /*
17  * This program transforms Intel perfmon and AMD PMC data files into C files and
18  * manual pages.
19  */
20 
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <unistd.h>
24 #include <err.h>
25 #include <libgen.h>
26 #include <libnvpair.h>
27 #include <strings.h>
28 #include <errno.h>
29 #include <limits.h>
30 #include <sys/mman.h>
31 #include <sys/param.h>
32 #include <assert.h>
33 #include <ctype.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <dirent.h>
38 
39 #include <json_nvlist.h>
40 
41 #define	EXIT_USAGE	2
42 #define	CPROC_MAX_STEPPINGS	16
43 
44 typedef enum {
45 	CPCGEN_MODE_UNKNOWN = 0,
46 	CPCGEN_MODE_INTEL,
47 	CPCGEN_MODE_AMD
48 } cpc_mode_t;
49 
50 typedef struct cpc_proc {
51 	struct cpc_proc *cproc_next;
52 	uint_t		cproc_family;
53 	uint_t		cproc_model;
54 	uint_t		cproc_nsteps;
55 	uint_t		cproc_steppings[CPROC_MAX_STEPPINGS];
56 } cpc_proc_t;
57 
58 typedef enum cpc_file_type {
59 	CPC_FILE_CORE		= 1 << 0,
60 	CPC_FILE_OFF_CORE	= 1 << 1,
61 	CPC_FILE_UNCORE		= 1 << 2,
62 	CPC_FILE_FP_MATH	= 1 << 3,
63 	CPC_FILE_UNCORE_EXP	= 1 << 4
64 } cpc_type_t;
65 
66 typedef struct cpc_map {
67 	struct cpc_map	*cmap_next;
68 	cpc_type_t	cmap_type;
69 	nvlist_t	*cmap_data;
70 	char		*cmap_path;
71 	const char	*cmap_name;
72 	cpc_proc_t	*cmap_procs;
73 } cpc_map_t;
74 
75 typedef struct cpc_whitelist {
76 	const char	*cwhite_short;
77 	const char	*cwhite_human;
78 	uint_t		cwhite_mask;
79 } cpc_whitelist_t;
80 
81 /*
82  * List of architectures that we support generating this data for. This is done
83  * so that processors that illumos doesn't support or run on aren't generated
84  * (generally the Xeon Phi).
85  */
86 static cpc_whitelist_t cpcgen_intel_whitelist[] = {
87 	/* Nehalem */
88 	{ "NHM-EP", "nhm_ep", CPC_FILE_CORE },
89 	{ "NHM-EX", "nhm_ex", CPC_FILE_CORE },
90 	/* Westmere */
91 	{ "WSM-EP-DP", "wsm_ep_dp", CPC_FILE_CORE },
92 	{ "WSM-EP-SP", "wsm_ep_sp", CPC_FILE_CORE },
93 	{ "WSM-EX", "wsm_ex", CPC_FILE_CORE },
94 	/* Sandy Bridge */
95 	{ "SNB", "snb", CPC_FILE_CORE },
96 	{ "JKT", "jkt", CPC_FILE_CORE },
97 	/* Ivy Bridge */
98 	{ "IVB", "ivb", CPC_FILE_CORE },
99 	{ "IVT", "ivt", CPC_FILE_CORE },
100 	/* Haswell */
101 	{ "HSW", "hsw", CPC_FILE_CORE },
102 	{ "HSX", "hsx", CPC_FILE_CORE },
103 	/* Broadwell */
104 	{ "BDW", "bdw", CPC_FILE_CORE },
105 	{ "BDW-DE", "bdw_de", CPC_FILE_CORE },
106 	{ "BDX", "bdx", CPC_FILE_CORE },
107 	/* Skylake */
108 	{ "SKL", "skl", CPC_FILE_CORE },
109 	{ "SKX", "skx", CPC_FILE_CORE },
110 	/* Cascade Lake */
111 	{ "CLX", "clx", CPC_FILE_CORE },
112 	/* Atom */
113 	{ "BNL", "bnl", CPC_FILE_CORE },
114 	{ "SLM", "slm", CPC_FILE_CORE },
115 	{ "GLM", "glm", CPC_FILE_CORE },
116 	{ "GLP", "glp", CPC_FILE_CORE },
117 	{ NULL }
118 };
119 
120 typedef struct cpc_papi {
121 	const char	*cpapi_intc;
122 	const char	*cpapi_papi;
123 } cpc_papi_t;
124 
125 /*
126  * This table maps events with an Intel specific name to the corresponding PAPI
127  * name. There may be multiple Intel events which map to the same PAPI event.
128  * This is usually because different processors have different names for an
129  * event. We use the title as opposed to the event codes because those can
130  * change somewhat arbitrarily between processor generations.
131  */
132 static cpc_papi_t cpcgen_intel_papi_map[] = {
133 	{ "CPU_CLK_UNHALTED.THREAD_P", "PAPI_tot_cyc" },
134 	{ "INST_RETIRED.ANY_P", "PAPI_tot_ins" },
135 	{ "BR_INST_RETIRED.ALL_BRANCHES", "PAPI_br_ins" },
136 	{ "BR_MISP_RETIRED.ALL_BRANCHES", "PAPI_br_msp" },
137 	{ "BR_INST_RETIRED.CONDITIONAL", "PAPI_br_cn" },
138 	{ "CYCLE_ACTIVITY.CYCLES_L1D_MISS", "PAPI_l1_dcm" },
139 	{ "L1I.HITS", "PAPI_l1_ich" },
140 	{ "ICACHE.HIT", "PAPI_l1_ich" },
141 	{ "L1I.MISS", "PAPI_L1_icm" },
142 	{ "ICACHE.MISSES", "PAPI_l1_icm" },
143 	{ "L1I.READS", "PAPI_l1_ica" },
144 	{ "ICACHE.ACCESSES", "PAPI_l1_ica" },
145 	{ "L1I.READS", "PAPI_l1_icr" },
146 	{ "ICACHE.ACCESSES", "PAPI_l1_icr" },
147 	{ "L2_RQSTS.CODE_RD_MISS", "PAPI_l2_icm" },
148 	{ "L2_RQSTS.MISS", "PAPI_l2_tcm" },
149 	{ "ITLB_MISSES.MISS_CAUSES_A_WALK", "PAPI_tlb_im" },
150 	{ "DTLB_LOAD_MISSES.MISS_CAUSES_A_WALK", "PAPI_tlb_dm" },
151 	{ "PAGE_WALKS.D_SIDE_WALKS", "PAPI_tlb_dm" },
152 	{ "PAGE_WALKS.I_SIDE_WALKS", "PAPI_tlb_im" },
153 	{ "PAGE_WALKS.WALKS", "PAPI_tlb_tl" },
154 	{ "INST_QUEUE_WRITES", "PAPI_tot_iis" },
155 	{ "MEM_INST_RETIRED.STORES" "PAPI_sr_ins" },
156 	{ "MEM_INST_RETIRED.LOADS" "PAPI_ld_ins" },
157 	{ NULL, NULL }
158 };
159 
160 typedef struct cpcgen_ops {
161 	void (*cgen_op_gather)(const char *, const char *);
162 	void (*cgen_op_common)(int);
163 	char *(*cgen_op_name)(cpc_map_t *);
164 	boolean_t (*cgen_op_skip)(nvlist_t *, const char *, uint_t);
165 	boolean_t (*cgen_op_file_before)(FILE *, cpc_map_t *);
166 	boolean_t (*cgen_op_file_after)(FILE *, cpc_map_t *);
167 	boolean_t (*cgen_op_event)(FILE *, nvlist_t *, const char *, uint32_t);
168 } cpcgen_ops_t;
169 
170 static cpcgen_ops_t cpcgen_ops;
171 static const char *cpcgen_intel_mapfile = "/mapfile.csv";
172 static const char *cpcgen_progname;
173 static cpc_map_t *cpcgen_maps;
174 static cpc_mode_t cpcgen_mode = CPCGEN_MODE_UNKNOWN;
175 
176 /*
177  * Constants used for generating data.
178  */
179 /* BEGIN CSTYLED */
180 static const char *cpcgen_cfile_intel_header = ""
181 "/*\n"
182 " *  Copyright (c) 2018, Intel Corporation\n"
183 " *  Copyright (c) 2018, Joyent, Inc\n"
184 " *  All rights reserved.\n"
185 " *\n"
186 " *  Redistribution and use in source and binary forms, with or without\n"
187 " *  modification, are permitted provided that the following conditions are met:\n"
188 " * \n"
189 " *   1. Redistributions of source code must retain the above copyright notice,\n"
190 " *      this list of conditions and the following disclaimer.\n"
191 " * \n"
192 " *   2. Redistributions in binary form must reproduce the above copyright \n"
193 " *      notice, this list of conditions and the following disclaimer in the\n"
194 " *      documentation and/or other materials provided with the distribution.\n"
195 " * \n"
196 " *   3. Neither the name of the Intel Corporation nor the names of its \n"
197 " *      contributors may be used to endorse or promote products derived from\n"
198 " *      this software without specific prior written permission.\n"
199 " *\n"
200 " *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n"
201 " *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n"
202 " *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
203 " *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n"
204 " *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n"
205 " *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n"
206 " *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n"
207 " *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n"
208 " *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n"
209 " *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n"
210 " *  POSSIBILITY OF SUCH DAMAGE.\n"
211 " *\n"
212 " * This file was automatically generated by cpcgen from the data file\n"
213 " * data/perfmon%s\n"
214 " *\n"
215 " * Do not modify this file. Your changes will be lost!\n"
216 " */\n"
217 "\n";
218 /* END CSTYLED */
219 
220 static const char *cpcgen_cfile_intel_table_start = ""
221 "#include <core_pcbe_table.h>\n"
222 "\n"
223 "const struct events_table_t pcbe_core_events_%s[] = {\n";
224 
225 static const char *cpcgen_cfile_intel_table_end = ""
226 "\t{ NT_END, 0, 0, \"\" }\n"
227 "};\n";
228 
229 /* BEGIN CSTYLED */
230 static const char *cpcgen_manual_intel_intel_header = ""
231 ".\\\" Copyright (c) 2018, Intel Corporation \n"
232 ".\\\" Copyright (c) 2018, Joyent, Inc.\n"
233 ".\\\" All rights reserved.\n"
234 ".\\\"\n"
235 ".\\\" Redistribution and use in source and binary forms, with or without \n"
236 ".\\\" modification, are permitted provided that the following conditions are met:\n"
237 ".\\\"\n"
238 ".\\\"  1. Redistributions of source code must retain the above copyright notice,\n"
239 ".\\\"     this list of conditions and the following disclaimer.\n"
240 ".\\\"\n"
241 ".\\\"  2. Redistributions in binary form must reproduce the above copyright\n"
242 ".\\\"     notice, this list of conditions and the following disclaimer in the\n"
243 ".\\\"     documentation and/or other materials provided with the distribution.\n"
244 ".\\\"\n"
245 ".\\\"  3. Neither the name of the Intel Corporation nor the names of its\n"
246 ".\\\"     contributors may be used to endorse or promote products derived from\n"
247 ".\\\"     this software without specific prior written permission.\n"
248 ".\\\"\n"
249 ".\\\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n"
250 ".\\\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n"
251 ".\\\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
252 ".\\\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n"
253 ".\\\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n"
254 ".\\\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n"
255 ".\\\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n"
256 ".\\\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n"
257 ".\\\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n"
258 ".\\\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n"
259 ".\\\" POSSIBILITY OF SUCH DAMAGE.\n"
260 ".\\\"\n"
261 ".\\\" This file was automatically generated by cpcgen from the data file\n"
262 ".\\\" data/perfmon%s\n"
263 ".\\\"\n"
264 ".\\\" Do not modify this file. Your changes will be lost!\n"
265 ".\\\"\n"
266 ".\\\" We would like to thank Intel for providing the perfmon data for use in\n"
267 ".\\\" our manual pages.\n"
268 ".Dd June 18, 2018\n"
269 ".Dt %s_EVENTS 3CPC\n"
270 ".Os\n"
271 ".Sh NAME\n"
272 ".Nm %s_events\n"
273 ".Nd processor model specific performance counter events\n"
274 ".Sh DESCRIPTION\n"
275 "This manual page describes events specific to the following Intel CPU\n"
276 "models and is derived from Intel's perfmon data.\n"
277 "For more information, please consult the Intel Software Developer's Manual "
278 "or Intel's perfmon website.\n"
279 ".Pp\n"
280 "CPU models described by this document:\n"
281 ".Bl -bullet\n";
282 /* END CSTYLED */
283 
284 static const char *cpcgen_manual_intel_data = ""
285 ".El\n"
286 ".Pp\n"
287 "The following events are supported:\n"
288 ".Bl -tag -width Sy\n";
289 
290 static const char *cpcgen_manual_intel_trailer = ""
291 ".El\n"
292 ".Sh SEE ALSO\n"
293 ".Xr cpc 3CPC\n"
294 ".Pp\n"
295 ".Lk https://download.01.org/perfmon/index/";
296 
297 static const char *cpcgen_cfile_cddl_header = ""
298 "/*\n"
299 " * This file and its contents are supplied under the terms of the\n"
300 " * Common Development and Distribution License (\"CDDL\"), version 1.0.\n"
301 " * You may only use this file in accordance with the terms of version\n"
302 " * 1.0 of the CDDL.\n"
303 " *\n"
304 " * A full copy of the text of the CDDL should have accompanied this\n"
305 " * source.  A copy of the CDDL is also available via the Internet at\n"
306 " * http://www.illumos.org/license/CDDL.\n"
307 " */\n"
308 "\n"
309 "/*\n"
310 " * Copyright 2019 Joyent, Inc\n"
311 " */\n"
312 "\n"
313 "/*\n"
314 " * This file was automatically generated by cpcgen.\n"
315 " */\n"
316 "\n"
317 "/*\n"
318 " * Do not modify this file. Your changes will be lost!\n"
319 " */\n"
320 "\n";
321 
322 static const char *cpcgen_manual_amd_header = ""
323 ".\\\" This file was automatically generated by cpcgen from the data file\n"
324 ".\\\" data/amdpmc/%s\n"
325 ".\\\"\n"
326 ".\\\" Do not modify this file. Your changes will be lost!\n"
327 ".\\\"\n"
328 ".\\\" We would like to thank AMD for providing the PMC data for use in\n"
329 ".\\\" our manual pages.\n"
330 ".Dd March 25, 2019\n"
331 ".Dt AMD_%s_EVENTS 3CPC\n"
332 ".Os\n"
333 ".Sh NAME\n"
334 ".Nm amd_%s_events\n"
335 ".Nd AMD Family %s processor performance monitoring events\n"
336 ".Sh DESCRIPTION\n"
337 "This manual page describes events specfic to AMD Family %s processors.\n"
338 "For more information, please consult the appropriate AMD BIOS and Kernel\n"
339 "Developer's guide or Open-Source Register Reference.\n"
340 ".Pp\n"
341 "Each of the events listed below includes the AMD mnemonic which matches\n"
342 "the name found in the AMD manual and a brief summary of the event.\n"
343 "If available, a more detailed description of the event follows and then\n"
344 "any additional unit values that modify the event.\n"
345 "Each unit can be combined to create a new event in the system by placing\n"
346 "the '.' character between the event name and the unit name.\n"
347 ".Pp\n"
348 "The following events are supported:\n"
349 ".Bl -tag -width Sy\n";
350 
351 static const char *cpcgen_manual_amd_trailer = ""
352 ".El\n"
353 ".Sh SEE ALSO\n"
354 ".Xr cpc 3CPC\n";
355 
356 static const char *cpcgen_cfile_amd_header = ""
357 "/*\n"
358 " * This file was automatically generated by cpcgen from the data file\n"
359 " * data/perfmon%s\n"
360 " *\n"
361 " * Do not modify this file. Your changes will be lost!\n"
362 " */\n"
363 "\n";
364 
365 static const char *cpcgen_cfile_amd_table_start = ""
366 "#include <opteron_pcbe_table.h>\n"
367 "#include <sys/null.h>\n"
368 "\n"
369 "const amd_event_t opteron_pcbe_%s_events[] = {\n";
370 
371 static const char *cpcgen_cfile_amd_table_end = ""
372 "\t{ NULL, 0, 0 }\n"
373 "};\n";
374 
375 static cpc_map_t *
376 cpcgen_map_lookup(const char *path)
377 {
378 	cpc_map_t *m;
379 
380 	for (m = cpcgen_maps; m != NULL; m = m->cmap_next) {
381 		if (strcmp(path, m->cmap_path) == 0) {
382 			return (m);
383 		}
384 	}
385 
386 	return (NULL);
387 }
388 
389 /*
390  * Parse a string of the form 'GenuineIntel-6-2E' and get out the family and
391  * model.
392  */
393 static void
394 cpcgen_parse_model(char *fsr, uint_t *family, uint_t *model, uint_t *nstepp,
395     uint_t *steppings)
396 {
397 	const char *bstr = "GenuineIntel";
398 	const char *brand, *fam, *mod, *step;
399 	char *last;
400 	long l;
401 	uint_t nstep = 0;
402 
403 	/*
404 	 * Tokeninze the string. There may be an optional stepping portion,
405 	 * which has a range of steppings enclosed by '[' and ']' characters.
406 	 * While the other parts are required, the stepping may be missing.
407 	 */
408 	if ((brand = strtok_r(fsr, "-", &last)) == NULL ||
409 	    (fam = strtok_r(NULL, "-", &last)) == NULL ||
410 	    (mod = strtok_r(NULL, "-", &last)) == NULL) {
411 		errx(EXIT_FAILURE, "failed to parse processor id \"%s\"", fsr);
412 	}
413 	step = strtok_r(NULL, "-", &last);
414 
415 	if (strcmp(bstr, brand) != 0) {
416 		errx(EXIT_FAILURE, "brand string \"%s\" did not match \"%s\"",
417 		    brand, bstr);
418 	}
419 
420 	errno = 0;
421 	l = strtol(fam, &last, 16);
422 	if (errno != 0 || l < 0 || l >= INT_MAX || *last != '\0') {
423 		errx(EXIT_FAILURE, "failed to parse family \"%s\"", fam);
424 	}
425 	*family = (uint_t)l;
426 
427 	l = strtol(mod, &last, 16);
428 	if (errno != 0 || l < 0 || l >= INT_MAX || *last != '\0') {
429 		errx(EXIT_FAILURE, "failed to parse model \"%s\"", mod);
430 	}
431 	*model = (uint_t)l;
432 
433 	if (step == NULL) {
434 		*nstepp = 0;
435 		return;
436 	}
437 
438 	if (*step != '[' || ((last = strrchr(step, ']')) == NULL)) {
439 		errx(EXIT_FAILURE, "failed to parse stepping \"%s\": missing "
440 		    "stepping range brackets", step);
441 	}
442 	step++;
443 	*last = '\0';
444 	while (*step != '\0') {
445 		if (!isxdigit(*step)) {
446 			errx(EXIT_FAILURE, "failed to parse stepping: invalid "
447 			    "stepping identifier '0x%x'", *step);
448 		}
449 
450 		if (nstep >= CPROC_MAX_STEPPINGS) {
451 			errx(EXIT_FAILURE, "failed to parse stepping: "
452 			    "encountered too many steppings");
453 		}
454 
455 		switch (*step) {
456 		case '0':
457 			steppings[nstep] = 0x0;
458 			break;
459 		case '1':
460 			steppings[nstep] = 0x1;
461 			break;
462 		case '2':
463 			steppings[nstep] = 0x2;
464 			break;
465 		case '3':
466 			steppings[nstep] = 0x3;
467 			break;
468 		case '4':
469 			steppings[nstep] = 0x4;
470 			break;
471 		case '5':
472 			steppings[nstep] = 0x5;
473 			break;
474 		case '6':
475 			steppings[nstep] = 0x6;
476 			break;
477 		case '7':
478 			steppings[nstep] = 0x7;
479 			break;
480 		case '8':
481 			steppings[nstep] = 0x8;
482 			break;
483 		case '9':
484 			steppings[nstep] = 0x9;
485 			break;
486 		case 'a':
487 		case 'A':
488 			steppings[nstep] = 0xa;
489 			break;
490 		case 'b':
491 		case 'B':
492 			steppings[nstep] = 0xb;
493 			break;
494 		case 'c':
495 		case 'C':
496 			steppings[nstep] = 0xc;
497 			break;
498 		case 'd':
499 		case 'D':
500 			steppings[nstep] = 0xd;
501 			break;
502 		case 'e':
503 		case 'E':
504 			steppings[nstep] = 0xe;
505 			break;
506 		case 'f':
507 		case 'F':
508 			steppings[nstep] = 0xf;
509 			break;
510 		default:
511 			errx(EXIT_FAILURE, "encountered non-hex stepping "
512 			    "character: '%c'", *step);
513 		}
514 		nstep++;
515 		step++;
516 	}
517 
518 	*nstepp = nstep;
519 }
520 
521 static nvlist_t *
522 cpcgen_read_datafile(const char *datadir, const char *file)
523 {
524 	int fd;
525 	char *path;
526 	struct stat st;
527 	void *map;
528 	nvlist_t *nvl;
529 	nvlist_parse_json_error_t jerr;
530 
531 	if (asprintf(&path, "%s/%s", datadir, file) == -1) {
532 		err(EXIT_FAILURE, "failed to construct path to data file %s",
533 		    file);
534 	}
535 
536 	if ((fd = open(path, O_RDONLY)) < 0) {
537 		err(EXIT_FAILURE, "failed to open data file %s", path);
538 	}
539 
540 	if (fstat(fd, &st) != 0) {
541 		err(EXIT_FAILURE, "failed to stat %s", path);
542 	}
543 
544 	if ((map = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE,
545 	    fd, 0)) == MAP_FAILED) {
546 		err(EXIT_FAILURE, "failed to mmap %s", path);
547 	}
548 
549 	if (nvlist_parse_json(map, st.st_size, &nvl, NVJSON_FORCE_INTEGER,
550 	    &jerr) != 0) {
551 		errx(EXIT_FAILURE, "failed to parse file %s at pos %ld: %s",
552 		    path, jerr.nje_pos, jerr.nje_message);
553 	}
554 
555 	if (munmap(map, st.st_size) != 0) {
556 		err(EXIT_FAILURE, "failed to munmap %s", path);
557 	}
558 
559 	if (close(fd) != 0) {
560 		err(EXIT_FAILURE, "failed to close data file %s", path);
561 	}
562 	free(path);
563 
564 	return (nvl);
565 }
566 
567 /*
568  * Check the whitelist to see if we should use this model.
569  */
570 static const char *
571 cpcgen_use_arch(const char *path, cpc_type_t type, const char *platform)
572 {
573 	const char *slash;
574 	size_t len;
575 	uint_t i;
576 
577 	if (*path != '/') {
578 		errx(EXIT_FAILURE, "invalid path in mapfile: \"%s\": missing "
579 		    "leading '/'", path);
580 	}
581 	if ((slash = strchr(path + 1, '/')) == NULL) {
582 		errx(EXIT_FAILURE, "invalid path in mapfile: \"%s\": missing "
583 		    "second '/'", path);
584 	}
585 	/* Account for the last '/' character. */
586 	len = slash - path - 1;
587 	assert(len > 0);
588 
589 	for (i = 0; cpcgen_intel_whitelist[i].cwhite_short != NULL; i++) {
590 		if (platform != NULL && strcasecmp(platform,
591 		    cpcgen_intel_whitelist[i].cwhite_short) != 0)
592 			continue;
593 		if (strncmp(path + 1, cpcgen_intel_whitelist[i].cwhite_short,
594 		    len) == 0 &&
595 		    (cpcgen_intel_whitelist[i].cwhite_mask & type) == type) {
596 			return (cpcgen_intel_whitelist[i].cwhite_human);
597 		}
598 	}
599 
600 	return (NULL);
601 }
602 
603 /*
604  * Determine which CPU Vendor we're transmuting data from.
605  */
606 static void
607 cpcgen_determine_vendor(const char *datadir)
608 {
609 	char *mappath;
610 	struct stat st;
611 
612 	if (asprintf(&mappath, "%s/%s", datadir, cpcgen_intel_mapfile) == -1) {
613 		err(EXIT_FAILURE, "failed to construct path to mapfile");
614 	}
615 
616 	if (stat(mappath, &st) == 0) {
617 		cpcgen_mode = CPCGEN_MODE_INTEL;
618 	} else {
619 		if (errno != ENOENT) {
620 			err(EXIT_FAILURE, "stat(2) of %s failed unexpectedly");
621 		}
622 
623 		cpcgen_mode = CPCGEN_MODE_AMD;
624 	}
625 
626 	free(mappath);
627 }
628 
629 /*
630  * Read in all the data files that exist for AMD.
631  *
632  * Our family names for AMD systems are based on the family and type so a given
633  * name will look like f17h_<core>_core.json.
634  */
635 static void
636 cpcgen_read_amd(const char *datadir, const char *platform)
637 {
638 	DIR *dir;
639 	struct dirent *d;
640 	const char *suffix = ".json";
641 	const size_t slen = strlen(suffix);
642 
643 	if ((dir = opendir(datadir)) == NULL) {
644 		err(EXIT_FAILURE, "failed to open directory %s", datadir);
645 	}
646 
647 	while ((d = readdir(dir)) != NULL) {
648 		char *name, *c;
649 		cpc_map_t *map;
650 		nvlist_t *parsed;
651 
652 		if ((name = strdup(d->d_name)) == NULL) {
653 			errx(EXIT_FAILURE, "ran out of memory duplicating "
654 			    "name %s", d->d_name);
655 		}
656 		c = strstr(name, suffix);
657 
658 		if (c == NULL) {
659 			free(name);
660 			continue;
661 		}
662 
663 		/*
664 		 * Chop off the .json. Next, make sure we have both _ present.
665 		 */
666 		if (*(c + slen) != '\0') {
667 			free(name);
668 			continue;
669 		}
670 		*c = '\0';
671 
672 		c = strchr(name, '_');
673 		if (c == NULL) {
674 			errx(EXIT_FAILURE, "unexpected AMD JSON file name: %s",
675 			    d->d_name);
676 		}
677 
678 		c++;
679 		c = strchr(c, '_');
680 		if (c == NULL) {
681 			errx(EXIT_FAILURE, "unexpected AMD JSON file name: %s",
682 			    d->d_name);
683 		}
684 		*c = '\0';
685 		c++;
686 		if (strcmp(c, "core") != 0) {
687 			errx(EXIT_FAILURE, "unexpected AMD JSON file name: %s",
688 			    d->d_name);
689 		}
690 
691 		if (platform != NULL && strcmp(platform, name) != 0) {
692 			free(name);
693 			continue;
694 		}
695 
696 		if ((map = calloc(1, sizeof (cpc_map_t))) == NULL) {
697 			err(EXIT_FAILURE, "failed to allocate space for cpc "
698 			    "file");
699 		}
700 
701 		parsed = cpcgen_read_datafile(datadir, d->d_name);
702 		if ((map->cmap_path = strdup(d->d_name)) == NULL) {
703 			err(EXIT_FAILURE, "failed to duplicate path string");
704 		}
705 		map->cmap_type = CPC_FILE_CORE;
706 		map->cmap_data = parsed;
707 		map->cmap_name = name;
708 		map->cmap_procs = NULL;
709 
710 		map->cmap_next = cpcgen_maps;
711 		cpcgen_maps = map;
712 	}
713 }
714 
715 /*
716  * Read in the mapfile.csv that is used to map between processor families and
717  * parse this. Each line has a comma separated value.
718  */
719 static void
720 cpcgen_read_intel(const char *datadir, const char *platform)
721 {
722 	FILE *map;
723 	char *mappath, *last;
724 	char *data = NULL;
725 	size_t datalen = 0;
726 	uint_t lineno;
727 
728 	if (asprintf(&mappath, "%s/%s", datadir, cpcgen_intel_mapfile) == -1) {
729 		err(EXIT_FAILURE, "failed to construct path to mapfile");
730 	}
731 
732 	if ((map = fopen(mappath, "r")) == NULL) {
733 		err(EXIT_FAILURE, "failed to open data mapfile %s", mappath);
734 	}
735 
736 	lineno = 0;
737 	while (getline(&data, &datalen, map) != -1) {
738 		char *fstr, *path, *tstr;
739 		const char *name;
740 		uint_t family, model, nsteps;
741 		uint_t steppings[CPROC_MAX_STEPPINGS];
742 
743 		cpc_type_t type;
744 		cpc_map_t *map;
745 		cpc_proc_t *proc;
746 
747 		/*
748 		 * The first line contains the header:
749 		 * Family-model,Version,Filename,EventType
750 		 */
751 		lineno++;
752 		if (lineno == 1) {
753 			continue;
754 		}
755 
756 		if ((fstr = strtok_r(data, ",", &last)) == NULL ||
757 		    strtok_r(NULL, ",", &last) == NULL ||
758 		    (path = strtok_r(NULL, ",", &last)) == NULL ||
759 		    (tstr = strtok_r(NULL, "\n", &last)) == NULL) {
760 			errx(EXIT_FAILURE, "failed to parse mapfile line "
761 			    "%u in %s", lineno, mappath);
762 		}
763 
764 		cpcgen_parse_model(fstr, &family, &model, &nsteps, steppings);
765 
766 		if (strcmp(tstr, "core") == 0) {
767 			type = CPC_FILE_CORE;
768 		} else if (strcmp(tstr, "offcore") == 0) {
769 			type = CPC_FILE_OFF_CORE;
770 		} else if (strcmp(tstr, "uncore") == 0) {
771 			type = CPC_FILE_UNCORE;
772 		} else if (strcmp(tstr, "fp_arith_inst") == 0) {
773 			type = CPC_FILE_FP_MATH;
774 		} else if (strcmp(tstr, "uncore experimental") == 0) {
775 			type = CPC_FILE_UNCORE_EXP;
776 		} else {
777 			errx(EXIT_FAILURE, "unknown file type \"%s\" on line "
778 			    "%u", tstr, lineno);
779 		}
780 
781 		if ((name = cpcgen_use_arch(path, type, platform)) == NULL)
782 			continue;
783 
784 		if ((map = cpcgen_map_lookup(path)) == NULL) {
785 			nvlist_t *parsed;
786 
787 			parsed = cpcgen_read_datafile(datadir, path);
788 
789 			if ((map = calloc(1, sizeof (cpc_map_t))) == NULL) {
790 				err(EXIT_FAILURE, "failed to allocate space "
791 				    "for cpc file");
792 			}
793 
794 			if ((map->cmap_path = strdup(path)) == NULL) {
795 				err(EXIT_FAILURE, "failed to duplicate path "
796 				    "string");
797 			}
798 
799 			map->cmap_type = type;
800 			map->cmap_data = parsed;
801 			map->cmap_name = name;
802 			map->cmap_procs = NULL;
803 
804 			map->cmap_next = cpcgen_maps;
805 			cpcgen_maps = map;
806 		}
807 
808 		if ((proc = calloc(1, sizeof (cpc_proc_t))) == NULL) {
809 			err(EXIT_FAILURE, "failed to allocate memory for "
810 			    "family and model tracking");
811 		}
812 
813 		proc->cproc_family = family;
814 		proc->cproc_model = model;
815 		proc->cproc_nsteps = nsteps;
816 		if (nsteps > 0) {
817 			bcopy(steppings, proc->cproc_steppings,
818 			    sizeof (steppings));
819 		}
820 		proc->cproc_next = map->cmap_procs;
821 		map->cmap_procs = proc;
822 	}
823 
824 	if (errno != 0 || ferror(map)) {
825 		err(EXIT_FAILURE, "failed to read %s", mappath);
826 	}
827 
828 	if (fclose(map) == EOF) {
829 		err(EXIT_FAILURE, "failed to close %s", mappath);
830 	}
831 	free(data);
832 	free(mappath);
833 }
834 
835 static char *
836 cpcgen_manual_intel_name(cpc_map_t *map)
837 {
838 	char *name;
839 
840 	if (asprintf(&name, "%s_events.3cpc", map->cmap_name) == -1) {
841 		warn("failed to assemble manual page name for %s",
842 		    map->cmap_path);
843 		return (NULL);
844 	}
845 
846 	return (name);
847 }
848 
849 static boolean_t
850 cpcgen_manual_intel_file_before(FILE *f, cpc_map_t *map)
851 {
852 	size_t i;
853 	char *upper;
854 	cpc_proc_t *proc;
855 
856 	if ((upper = strdup(map->cmap_name)) == NULL) {
857 		warn("failed to duplicate manual name for %s", map->cmap_name);
858 		return (B_FALSE);
859 	}
860 
861 	for (i = 0; upper[i] != '\0'; i++) {
862 		upper[i] = toupper(upper[i]);
863 	}
864 
865 	if (fprintf(f, cpcgen_manual_intel_intel_header, map->cmap_path, upper,
866 	    map->cmap_name) == -1) {
867 		warn("failed to write out manual header for %s",
868 		    map->cmap_name);
869 		free(upper);
870 		return (B_FALSE);
871 	}
872 	free(upper);
873 
874 	for (proc = map->cmap_procs; proc != NULL; proc = proc->cproc_next) {
875 		if (proc->cproc_nsteps > 0) {
876 			uint_t step;
877 
878 			for (step = 0; step < proc->cproc_nsteps; step++) {
879 				if (fprintf(f, ".It\n.Sy Family 0x%x, Model "
880 				    "0x%x, Stepping 0x%x\n",
881 				    proc->cproc_family, proc->cproc_model,
882 				    proc->cproc_steppings[step]) == -1) {
883 					warn("failed to write out model "
884 					    "information for %s",
885 					    map->cmap_name);
886 					return (B_FALSE);
887 				}
888 			}
889 		} else {
890 			if (fprintf(f, ".It\n.Sy Family 0x%x, Model 0x%x\n",
891 			    proc->cproc_family, proc->cproc_model) == -1) {
892 				warn("failed to write out model information "
893 				    "for %s", map->cmap_name);
894 				return (B_FALSE);
895 			}
896 		}
897 	}
898 
899 	if (fprintf(f, cpcgen_manual_intel_data) == -1) {
900 		warn("failed to write out manual header for %s",
901 		    map->cmap_name);
902 		return (B_FALSE);
903 	}
904 
905 	return (B_TRUE);
906 }
907 
908 static boolean_t
909 cpcgen_manual_intel_file_after(FILE *f, cpc_map_t *map)
910 {
911 	if (fprintf(f, cpcgen_manual_intel_trailer) == -1) {
912 		warn("failed to write out manual header for %s",
913 		    map->cmap_name);
914 		return (B_FALSE);
915 	}
916 
917 	return (B_TRUE);
918 }
919 
920 static boolean_t
921 cpcgen_manual_intel_event(FILE *f, nvlist_t *nvl, const char *path,
922     uint32_t ent)
923 {
924 	char *event, *lname, *brief = NULL, *public = NULL, *errata = NULL;
925 	size_t i;
926 
927 	if (nvlist_lookup_string(nvl, "EventName", &event) != 0) {
928 		warnx("Found event without 'EventName' property "
929 		    "in %s, entry %u", path, ent);
930 		return (B_FALSE);
931 	}
932 
933 	/*
934 	 * Intel uses capital names. CPC historically uses lower case names.
935 	 */
936 	if ((lname = strdup(event)) == NULL) {
937 		err(EXIT_FAILURE, "failed to duplicate event name %s", event);
938 	}
939 	for (i = 0; lname[i] != '\0'; i++) {
940 		lname[i] = tolower(event[i]);
941 	}
942 
943 	/*
944 	 * Try to get the other event fields, but if they're not there, don't
945 	 * worry about it.
946 	 */
947 	(void) nvlist_lookup_string(nvl, "BriefDescription", &brief);
948 	(void) nvlist_lookup_string(nvl, "PublicDescription", &public);
949 	(void) nvlist_lookup_string(nvl, "Errata", &errata);
950 	if (errata != NULL && (strcmp(errata, "0") == 0 ||
951 	    strcmp(errata, "null") == 0)) {
952 		errata = NULL;
953 	}
954 
955 	if (fprintf(f, ".It Sy %s\n", lname) == -1) {
956 		warn("failed to write out event entry %s", event);
957 		free(lname);
958 		return (B_FALSE);
959 	}
960 
961 	if (public != NULL) {
962 		if (fprintf(f, "%s\n", public) == -1) {
963 			warn("failed to write out event entry %s", event);
964 			free(lname);
965 			return (B_FALSE);
966 		}
967 	} else if (brief != NULL) {
968 		if (fprintf(f, "%s\n", brief) == -1) {
969 			warn("failed to write out event entry %s", event);
970 			free(lname);
971 			return (B_FALSE);
972 		}
973 	}
974 
975 	if (errata != NULL) {
976 		if (fprintf(f, ".Pp\nThe following errata may apply to this: "
977 		    "%s\n", errata) == -1) {
978 
979 			warn("failed to write out event entry %s", event);
980 			free(lname);
981 			return (B_FALSE);
982 		}
983 	}
984 
985 	free(lname);
986 	return (B_TRUE);
987 }
988 
989 static char *
990 cpcgen_cfile_intel_name(cpc_map_t *map)
991 {
992 	char *name;
993 
994 	if (asprintf(&name, "core_pcbe_%s.c", map->cmap_name) == -1) {
995 		warn("failed to assemble file name for %s", map->cmap_path);
996 		return (NULL);
997 	}
998 
999 	return (name);
1000 }
1001 
1002 static boolean_t
1003 cpcgen_cfile_intel_before(FILE *f, cpc_map_t *map)
1004 {
1005 	if (fprintf(f, cpcgen_cfile_intel_header, map->cmap_path) == -1) {
1006 		warn("failed to write header to temporary file for %s",
1007 		    map->cmap_path);
1008 		return (B_FALSE);
1009 	}
1010 
1011 	if (fprintf(f, cpcgen_cfile_intel_table_start, map->cmap_name) == -1) {
1012 		warn("failed to write header to temporary file for %s",
1013 		    map->cmap_path);
1014 		return (B_FALSE);
1015 	}
1016 
1017 	return (B_TRUE);
1018 }
1019 
1020 static boolean_t
1021 cpcgen_cfile_intel_after(FILE *f, cpc_map_t *map)
1022 {
1023 	if (fprintf(f, cpcgen_cfile_intel_table_end) == -1) {
1024 		warn("failed to write footer to temporary file for %s",
1025 		    map->cmap_path);
1026 		return (B_FALSE);
1027 	}
1028 
1029 	return (B_TRUE);
1030 }
1031 
1032 static boolean_t
1033 cpcgen_cfile_intel_event(FILE *f, nvlist_t *nvl, const char *path, uint_t ent)
1034 {
1035 	char *ecode, *umask, *name, *counter, *lname, *cmask;
1036 	size_t i;
1037 
1038 	if (nvlist_lookup_string(nvl, "EventName", &name) != 0) {
1039 		warnx("Found event without 'EventName' property "
1040 		    "in %s, entry %u", path, ent);
1041 		return (B_FALSE);
1042 	}
1043 
1044 	if (nvlist_lookup_string(nvl, "EventCode", &ecode) != 0 ||
1045 	    nvlist_lookup_string(nvl, "UMask", &umask) != 0 ||
1046 	    nvlist_lookup_string(nvl, "Counter", &counter) != 0) {
1047 		warnx("event %s (index %u) from %s, missing "
1048 		    "required properties for C file translation",
1049 		    name, ent, path);
1050 		return (B_FALSE);
1051 	}
1052 
1053 	/*
1054 	 * While we could try and parse the counters manually, just do this the
1055 	 * max power way for now based on all possible values.
1056 	 */
1057 	if (strcmp(counter, "0") == 0 || strcmp(counter, "0,") == 0) {
1058 		cmask = "C0";
1059 	} else if (strcmp(counter, "1") == 0) {
1060 		cmask = "C1";
1061 	} else if (strcmp(counter, "2") == 0) {
1062 		cmask = "C2";
1063 	} else if (strcmp(counter, "3") == 0) {
1064 		cmask = "C3";
1065 	} else if (strcmp(counter, "0,1") == 0) {
1066 		cmask = "C0|C1";
1067 	} else if (strcmp(counter, "0,1,2") == 0) {
1068 		cmask = "C0|C1|C2";
1069 	} else if (strcmp(counter, "0,1,2,3") == 0) {
1070 		cmask = "C0|C1|C2|C3";
1071 	} else if (strcmp(counter, "0,2,3") == 0) {
1072 		cmask = "C0|C2|C3";
1073 	} else if (strcmp(counter, "1,2,3") == 0) {
1074 		cmask = "C1|C2|C3";
1075 	} else if (strcmp(counter, "2,3") == 0) {
1076 		cmask = "C2|C3";
1077 	} else {
1078 		warnx("event %s (index %u) from %s, has unknown "
1079 		    "counter value \"%s\"", name, ent, path, counter);
1080 		return (B_FALSE);
1081 	}
1082 
1083 
1084 	/*
1085 	 * Intel uses capital names. CPC historically uses lower case names.
1086 	 */
1087 	if ((lname = strdup(name)) == NULL) {
1088 		err(EXIT_FAILURE, "failed to duplicate event name %s", name);
1089 	}
1090 	for (i = 0; lname[i] != '\0'; i++) {
1091 		lname[i] = tolower(name[i]);
1092 	}
1093 
1094 	if (fprintf(f, "\t{ %s, %s, %s, \"%s\" },\n", ecode, umask, cmask,
1095 	    lname) == -1) {
1096 		warn("failed to write out entry %s from %s", name, path);
1097 		free(lname);
1098 		return (B_FALSE);
1099 	}
1100 
1101 	free(lname);
1102 
1103 	/*
1104 	 * Check if we have any PAPI aliases.
1105 	 */
1106 	for (i = 0; cpcgen_intel_papi_map[i].cpapi_intc != NULL; i++) {
1107 		if (strcmp(name, cpcgen_intel_papi_map[i].cpapi_intc) != 0)
1108 			continue;
1109 
1110 		if (fprintf(f, "\t{ %s, %s, %s, \"%s\" },\n", ecode, umask,
1111 		    cmask, cpcgen_intel_papi_map[i].cpapi_papi) == -1) {
1112 			warn("failed to write out entry %s from %s", name,
1113 			    path);
1114 			return (B_FALSE);
1115 		}
1116 	}
1117 
1118 	return (B_TRUE);
1119 }
1120 
1121 static boolean_t
1122 cpcgen_generate_map(FILE *f, cpc_map_t *map, boolean_t start)
1123 {
1124 	cpc_proc_t *p;
1125 
1126 	if (fprintf(f, "\t%sif (", start ? "" : "} else ") == -1) {
1127 		return (B_FALSE);
1128 	}
1129 
1130 	for (p = map->cmap_procs; p != NULL; p = p->cproc_next) {
1131 		/*
1132 		 * Make sure the line is padded so the generated C code looks
1133 		 * like reasonable C style.
1134 		 */
1135 		if (p != map->cmap_procs) {
1136 			if (fputs("\t    ", f) == -1) {
1137 				return (B_FALSE);
1138 			}
1139 		}
1140 
1141 		if (p->cproc_nsteps > 0) {
1142 			uint_t i;
1143 
1144 			if (fprintf(f, "(model == 0x%x &&\n\t    (",
1145 			    p->cproc_model) == -1) {
1146 				return (B_FALSE);
1147 			}
1148 
1149 			for (i = 0; i < p->cproc_nsteps; i++) {
1150 				if (fprintf(f, "stepping == 0x%x%s",
1151 				    p->cproc_steppings[i],
1152 				    i + 1 != p->cproc_nsteps ?
1153 				    " ||\n\t    " : "") == -1) {
1154 					return (B_FALSE);
1155 				}
1156 			}
1157 
1158 			if (fputs("))", f) == -1) {
1159 				return (B_FALSE);
1160 			}
1161 		} else if (fprintf(f, "model == 0x%x", p->cproc_model) == -1) {
1162 			return (B_FALSE);
1163 		}
1164 
1165 		if (fprintf(f, "%s\n",
1166 		    p->cproc_next != NULL ? " ||" : ") {") == -1) {
1167 			return (B_FALSE);
1168 		}
1169 	}
1170 
1171 	if (fprintf(f, "\t\t\treturn (pcbe_core_events_%s);\n",
1172 	    map->cmap_name) == -1) {
1173 		return (B_FALSE);
1174 	}
1175 
1176 	return (B_TRUE);
1177 }
1178 
1179 /*
1180  * This is a wrapper around unlinkat that makes sure that we don't clobber
1181  * errno, which is used for properly printing out error messages below.
1182  */
1183 static void
1184 cpcgen_remove_tmpfile(int dirfd, const char *path)
1185 {
1186 	int e = errno;
1187 	(void) unlinkat(dirfd, path, 0);
1188 	errno = e;
1189 }
1190 
1191 /*
1192  * Generate a header file that declares all of these arrays and provide a map
1193  * for models to the corresponding table to use.
1194  */
1195 static void
1196 cpcgen_common_intel_files(int dirfd)
1197 {
1198 	const char *fname = "core_pcbe_cpcgen.h";
1199 	char *tmpname;
1200 	int fd;
1201 	FILE *f;
1202 	cpc_map_t *map;
1203 
1204 	if (asprintf(&tmpname, ".%s.%d", fname, getpid()) == -1) {
1205 		err(EXIT_FAILURE, "failed to construct temporary file name");
1206 	}
1207 
1208 	if ((fd = openat(dirfd, tmpname, O_RDWR | O_CREAT, 0644)) < 0) {
1209 		err(EXIT_FAILURE, "failed to create temporary file %s",
1210 		    tmpname);
1211 	}
1212 
1213 	if ((f = fdopen(fd, "w")) == NULL) {
1214 		cpcgen_remove_tmpfile(dirfd, tmpname);
1215 		err(EXIT_FAILURE, "failed to fdopen temporary file");
1216 	}
1217 
1218 	if (fprintf(f, cpcgen_cfile_intel_header, cpcgen_intel_mapfile) == -1) {
1219 		cpcgen_remove_tmpfile(dirfd, tmpname);
1220 		errx(EXIT_FAILURE, "failed to write header to temporary file "
1221 		    "for %s", fname);
1222 	}
1223 
1224 	if (fprintf(f, "#ifndef _CORE_PCBE_CPCGEN_H\n"
1225 	    "#define\t_CORE_PCBE_CPCGEN_H\n"
1226 	    "\n"
1227 	    "#ifdef __cplusplus\n"
1228 	    "extern \"C\" {\n"
1229 	    "#endif\n"
1230 	    "\n"
1231 	    "extern const struct events_table_t *core_cpcgen_table(uint_t, "
1232 	    "uint_t);\n"
1233 	    "\n") == -1) {
1234 		cpcgen_remove_tmpfile(dirfd, tmpname);
1235 		errx(EXIT_FAILURE, "failed to write header to "
1236 		    "temporary file for %s", fname);
1237 	}
1238 
1239 	for (map = cpcgen_maps; map != NULL; map = map->cmap_next) {
1240 		if (fprintf(f, "extern const struct events_table_t "
1241 		    "pcbe_core_events_%s[];\n", map->cmap_name) == -1) {
1242 			cpcgen_remove_tmpfile(dirfd, tmpname);
1243 			errx(EXIT_FAILURE, "failed to write entry to "
1244 			    "temporary file for %s", fname);
1245 		}
1246 	}
1247 
1248 	if (fprintf(f, "\n"
1249 	    "#ifdef __cplusplus\n"
1250 	    "}\n"
1251 	    "#endif\n"
1252 	    "\n"
1253 	    "#endif /* _CORE_PCBE_CPCGEN_H */\n") == -1) {
1254 		cpcgen_remove_tmpfile(dirfd, tmpname);
1255 		errx(EXIT_FAILURE, "failed to write header to "
1256 		    "temporary file for %s", fname);
1257 	}
1258 
1259 	if (fflush(f) != 0 || fclose(f) != 0) {
1260 		cpcgen_remove_tmpfile(dirfd, tmpname);
1261 		err(EXIT_FAILURE, "failed to flush and close temporary file");
1262 	}
1263 
1264 	if (renameat(dirfd, tmpname, dirfd, fname) != 0) {
1265 		err(EXIT_FAILURE, "failed to rename temporary file %s",
1266 		    tmpname);
1267 	}
1268 
1269 	free(tmpname);
1270 
1271 	/* Now again for the .c file. */
1272 	fname = "core_pcbe_cpcgen.c";
1273 	if (asprintf(&tmpname, ".%s.%d", fname, getpid()) == -1) {
1274 		err(EXIT_FAILURE, "failed to construct temporary file name");
1275 	}
1276 
1277 	if ((fd = openat(dirfd, tmpname, O_RDWR | O_CREAT, 0644)) < 0) {
1278 		err(EXIT_FAILURE, "failed to create temporary file %s",
1279 		    tmpname);
1280 	}
1281 
1282 	if ((f = fdopen(fd, "w")) == NULL) {
1283 		cpcgen_remove_tmpfile(dirfd, tmpname);
1284 		err(EXIT_FAILURE, "failed to fdopen temporary file");
1285 	}
1286 
1287 	if (fprintf(f, cpcgen_cfile_intel_header, cpcgen_intel_mapfile) == -1) {
1288 		cpcgen_remove_tmpfile(dirfd, tmpname);
1289 		errx(EXIT_FAILURE, "failed to write header to temporary file "
1290 		    "for %s", fname);
1291 	}
1292 
1293 	if (fprintf(f, "#include <core_pcbe_table.h>\n"
1294 	    "#include <sys/null.h>\n"
1295 	    "#include \"core_pcbe_cpcgen.h\"\n"
1296 	    "\n"
1297 	    "const struct events_table_t *\n"
1298 	    "core_cpcgen_table(uint_t model, uint_t stepping)\n"
1299 	    "{\n") == -1) {
1300 		cpcgen_remove_tmpfile(dirfd, tmpname);
1301 		errx(EXIT_FAILURE, "failed to write header to "
1302 		    "temporary file for %s", fname);
1303 	}
1304 
1305 	for (map = cpcgen_maps; map != NULL; map = map->cmap_next) {
1306 		if (!cpcgen_generate_map(f, map, map == cpcgen_maps)) {
1307 			cpcgen_remove_tmpfile(dirfd, tmpname);
1308 			errx(EXIT_FAILURE, "failed to write to temporary "
1309 			    "file for %s", fname);
1310 		}
1311 	}
1312 
1313 	if (fprintf(f, "\t} else {\n"
1314 	    "\t\t\treturn (NULL);\n"
1315 	    "\t}\n"
1316 	    "}\n") == -1) {
1317 		cpcgen_remove_tmpfile(dirfd, tmpname);
1318 		errx(EXIT_FAILURE, "failed to write header to "
1319 		    "temporary file for %s", fname);
1320 	}
1321 
1322 	if (fflush(f) != 0 || fclose(f) != 0) {
1323 		cpcgen_remove_tmpfile(dirfd, tmpname);
1324 		err(EXIT_FAILURE, "failed to flush and close temporary file");
1325 	}
1326 
1327 	if (renameat(dirfd, tmpname, dirfd, fname) != 0) {
1328 		err(EXIT_FAILURE, "failed to rename temporary file %s",
1329 		    tmpname);
1330 	}
1331 
1332 	free(tmpname);
1333 }
1334 
1335 /*
1336  * Look at a rule to determine whether or not we should consider including it or
1337  * not. At this point we've already filtered things such that we only get core
1338  * events.
1339  *
1340  * To consider an entry, we currently apply the following criteria:
1341  *
1342  * - The MSRIndex and MSRValue are zero. Programming additional MSRs is no
1343  *   supported right now.
1344  * - TakenAlone is non-zero, which means that it cannot run at the same time as
1345  *   another field.
1346  * - Offcore is one, indicating that it is off the core and we need to figure
1347  *   out if we can support this.
1348  * - If the counter is fixed, don't use it for now.
1349  * - If more than one value is specified in the EventCode or UMask values
1350  */
1351 static boolean_t
1352 cpcgen_skip_intel_entry(nvlist_t *nvl, const char *path, uint_t ent)
1353 {
1354 	char *event, *msridx, *msrval, *taken, *offcore, *counter;
1355 	char *ecode, *umask;
1356 
1357 	/*
1358 	 * Require EventName, it's kind of useless without that.
1359 	 */
1360 	if (nvlist_lookup_string(nvl, "EventName", &event) != 0) {
1361 		errx(EXIT_FAILURE, "Found event without 'EventName' property "
1362 		    "in %s, entry %u", path, ent);
1363 	}
1364 
1365 	/*
1366 	 * If we can't find an expected value, whine about it.
1367 	 */
1368 	if (nvlist_lookup_string(nvl, "MSRIndex", &msridx) != 0 ||
1369 	    nvlist_lookup_string(nvl, "MSRValue", &msrval) != 0 ||
1370 	    nvlist_lookup_string(nvl, "Counter", &counter) != 0 ||
1371 	    nvlist_lookup_string(nvl, "EventCode", &ecode) != 0 ||
1372 	    nvlist_lookup_string(nvl, "UMask", &umask) != 0 ||
1373 	    nvlist_lookup_string(nvl, "Offcore", &offcore) != 0) {
1374 		warnx("Skipping event %s (index %u) from %s, missing required "
1375 		    "property", event, ent, path);
1376 		return (B_TRUE);
1377 	}
1378 
1379 	/*
1380 	 * MSRIndex and MSRvalue comes as either "0" or "0x00".
1381 	 */
1382 	if ((strcmp(msridx, "0") != 0 && strcmp(msridx, "0x00") != 0) ||
1383 	    (strcmp(msrval, "0") != 0 && strcmp(msridx, "0x00") != 0) ||
1384 	    strcmp(offcore, "0") != 0 || strchr(ecode, ',') != NULL ||
1385 	    strchr(umask, ',') != NULL) {
1386 		return (B_TRUE);
1387 	}
1388 
1389 	/*
1390 	 * Unfortunately, not everything actually has "TakenAlone". If it
1391 	 * doesn't, we assume that it doesn't have to be.
1392 	 */
1393 	if (nvlist_lookup_string(nvl, "TakenAlone", &taken) == 0 &&
1394 	    strcmp(taken, "0") != 0) {
1395 		return (B_TRUE);
1396 	}
1397 
1398 
1399 	if (strncasecmp(counter, "fixed", strlen("fixed")) == 0)
1400 		return (B_TRUE);
1401 
1402 	return (B_FALSE);
1403 }
1404 static char *
1405 cpcgen_manual_amd_name(cpc_map_t *map)
1406 {
1407 	char *name;
1408 
1409 	if (asprintf(&name, "amd_%s_events.3cpc", map->cmap_name) == -1) {
1410 		warn("failed to assemble file name for %s", map->cmap_path);
1411 		return (NULL);
1412 	}
1413 
1414 	return (name);
1415 }
1416 
1417 static boolean_t
1418 cpcgen_manual_amd_file_before(FILE *f, cpc_map_t *map)
1419 {
1420 	size_t i;
1421 	char *upper, *desc, *c;
1422 
1423 	if ((upper = strdup(map->cmap_name)) == NULL) {
1424 		warn("failed to duplicate manual name for %s", map->cmap_name);
1425 		return (B_FALSE);
1426 	}
1427 
1428 	if ((desc = strdup(map->cmap_name)) == NULL) {
1429 		warn("failed to duplicate manual name for %s", map->cmap_name);
1430 		free(upper);
1431 		return (B_FALSE);
1432 	}
1433 
1434 	for (i = 0; upper[i] != '\0'; i++) {
1435 		upper[i] = toupper(upper[i]);
1436 	}
1437 
1438 	desc++;
1439 	c = strchr(desc, '_');
1440 	if (c != NULL) {
1441 		*c = ' ';
1442 		c++;
1443 		*c = toupper(*c);
1444 	}
1445 
1446 	if (fprintf(f, cpcgen_manual_amd_header, map->cmap_path, upper,
1447 	    map->cmap_name, desc, desc) == -1) {
1448 		warn("failed to write out manual header for %s",
1449 		    map->cmap_name);
1450 		free(upper);
1451 		free(desc);
1452 		return (B_FALSE);
1453 	}
1454 
1455 	free(upper);
1456 	free(desc);
1457 	return (B_TRUE);
1458 }
1459 
1460 static boolean_t
1461 cpcgen_manual_amd_file_after(FILE *f, cpc_map_t *map)
1462 {
1463 	if (fprintf(f, cpcgen_manual_amd_trailer) == -1) {
1464 		warn("failed to write out manual header for %s",
1465 		    map->cmap_name);
1466 		return (B_FALSE);
1467 	}
1468 
1469 	return (B_TRUE);
1470 }
1471 
1472 static boolean_t
1473 cpcgen_manual_amd_event(FILE *f, nvlist_t *nvl, const char *path, uint32_t ent)
1474 {
1475 	char *name, *mnemonic = NULL, *summary = NULL, *desc = NULL;
1476 	char *umode;
1477 	nvlist_t *units = NULL;
1478 	uint32_t i, length;
1479 
1480 	if (nvlist_lookup_string(nvl, "name", &name) != 0) {
1481 		warnx("Found event without 'name' property in %s, entry %u",
1482 		    path, ent);
1483 		return (B_FALSE);
1484 	}
1485 
1486 	if (nvlist_lookup_string(nvl, "mnemonic", &mnemonic) != 0 ||
1487 	    nvlist_lookup_string(nvl, "summary", &summary) != 0) {
1488 		warnx("event %s in %s, entry %u, missing required fields",
1489 		    name, path, ent);
1490 		return (B_FALSE);
1491 	}
1492 
1493 	/*
1494 	 * Allow the other fields to be missing.
1495 	 */
1496 	(void) nvlist_lookup_string(nvl, "description", &desc);
1497 	(void) nvlist_lookup_nvlist(nvl, "units", &units);
1498 
1499 	if (fprintf(f, ".It Sy %s\n", name) == -1) {
1500 		warn("failed to write out event entry %s", name);
1501 	}
1502 
1503 	if (fprintf(f, ".Sy %s -\n"
1504 	    "%s\n", mnemonic, summary) == -1) {
1505 		warn("failed to write out event entry %s", name);
1506 		return (B_FALSE);
1507 	}
1508 
1509 	if (desc != NULL) {
1510 		if (fprintf(f, ".Pp\n%s\n", desc) == -1) {
1511 			warn("failed to write out event entry %s", name);
1512 			return (B_FALSE);
1513 		}
1514 	}
1515 
1516 	if (units == NULL)
1517 		return (B_TRUE);
1518 
1519 	/*
1520 	 * Skip units we don't know how to handle.
1521 	 */
1522 	if (nvlist_lookup_string(nvl, "unit_mode", &umode) == 0) {
1523 		return (B_TRUE);
1524 	}
1525 
1526 	if (fprintf(f, ".Pp\n"
1527 	    "This event has the following units which may be used\n"
1528 	    "to modify the behavior of the event:\n"
1529 	    ".Bl -tag -width Sy\n") == -1) {
1530 		warn("failed to write out event entry %s", name);
1531 		return (B_FALSE);
1532 	}
1533 
1534 	if (nvlist_lookup_uint32(units, "length", &length) != 0) {
1535 		warnx("found units array, but could not look up length "
1536 		    "property for events %s (index %u) in file %s",
1537 		    name, ent, path);
1538 		return (B_FALSE);
1539 	}
1540 
1541 	for (i = 0; i < length; i++) {
1542 		nvlist_t *uvl;
1543 		char num[64];
1544 		char *uname, *udesc = NULL;
1545 
1546 		(void) snprintf(num, sizeof (num), "%u", i);
1547 		if (nvlist_lookup_nvlist(units, num, &uvl) != 0) {
1548 			warnx("failed to look up unit %u for event %s (index "
1549 			    "%u) in file %s", i, name, ent, path);
1550 			return (B_FALSE);
1551 		}
1552 
1553 		if (nvlist_lookup_string(uvl, "name", &uname) != 0) {
1554 			warnx("failed to find required members for unit array "
1555 			    "entry %u of event %s (index %u) from file %s",
1556 			    i, name, ent, path);
1557 			return (B_FALSE);
1558 		}
1559 		(void) nvlist_lookup_string(uvl, "description", &udesc);
1560 		if (fprintf(f, ".It Sy %s\n", uname) == -1) {
1561 			warn("failed to write out event entry %s", name);
1562 			return (B_FALSE);
1563 		}
1564 
1565 		if (udesc != NULL) {
1566 			if (fprintf(f, "%s\n", udesc) == -1) {
1567 				warn("failed to write out event entry %s",
1568 				    name);
1569 				return (B_FALSE);
1570 			}
1571 		}
1572 	}
1573 
1574 	if (fprintf(f, ".El\n") == -1) {
1575 		warn("failed to write out event entry %s",
1576 		    name);
1577 		return (B_FALSE);
1578 	}
1579 
1580 	return (B_TRUE);
1581 }
1582 
1583 static char *
1584 cpcgen_cfile_amd_name(cpc_map_t *map)
1585 {
1586 	char *name;
1587 
1588 	if (asprintf(&name, "opteron_pcbe_%s.c", map->cmap_name) == -1) {
1589 		warn("failed to assemble file name for %s", map->cmap_path);
1590 		return (NULL);
1591 	}
1592 
1593 	return (name);
1594 }
1595 
1596 /*
1597  * Generate a header file that can be used to synthesize the data events we care
1598  * about.
1599  */
1600 static void
1601 cpcgen_common_amd_files(int dirfd)
1602 {
1603 	const char *fname = "opteron_pcbe_cpcgen.h";
1604 	char *tmpname;
1605 	int fd;
1606 	FILE *f;
1607 	cpc_map_t *map;
1608 
1609 	if (asprintf(&tmpname, ".%s.%d", fname, getpid()) == -1) {
1610 		err(EXIT_FAILURE, "failed to construct temporary file name");
1611 	}
1612 
1613 	if ((fd = openat(dirfd, tmpname, O_RDWR | O_CREAT, 0644)) < 0) {
1614 		err(EXIT_FAILURE, "failed to create temporary file %s",
1615 		    tmpname);
1616 	}
1617 
1618 	if ((f = fdopen(fd, "w")) == NULL) {
1619 		cpcgen_remove_tmpfile(dirfd, tmpname);
1620 		err(EXIT_FAILURE, "failed to fdopen temporary file");
1621 	}
1622 
1623 	if (fprintf(f, cpcgen_cfile_cddl_header) == -1) {
1624 		cpcgen_remove_tmpfile(dirfd, tmpname);
1625 		err(EXIT_FAILURE, "failed to write header to "
1626 		    "temporary file for %s", fname);
1627 	}
1628 
1629 	if (fprintf(f, "#ifndef _OPTERON_PCBE_CPCGEN_H\n"
1630 	    "#define\t_OPTERON_PCBE_CPCGEN_H\n"
1631 	    "\n"
1632 	    "#ifdef __cplusplus\n"
1633 	    "extern \"C\" {\n"
1634 	    "#endif\n"
1635 	    "\n") == -1) {
1636 		cpcgen_remove_tmpfile(dirfd, tmpname);
1637 		err(EXIT_FAILURE, "failed to write header to "
1638 		    "temporary file for %s", fname);
1639 	}
1640 
1641 	for (map = cpcgen_maps; map != NULL; map = map->cmap_next) {
1642 		if (fprintf(f, "extern const amd_event_t "
1643 		    "opteron_pcbe_%s_events[];\n", map->cmap_name) == -1) {
1644 			cpcgen_remove_tmpfile(dirfd, tmpname);
1645 			err(EXIT_FAILURE, "failed to write header to "
1646 			    "temporary file for %s", fname);
1647 		}
1648 	}
1649 
1650 	if (fprintf(f, "\n"
1651 	    "#ifdef __cplusplus\n"
1652 	    "}\n"
1653 	    "#endif\n"
1654 	    "\n"
1655 	    "#endif /* _OPTERON_PCBE_CPCGEN_H */\n") == -1) {
1656 		cpcgen_remove_tmpfile(dirfd, tmpname);
1657 		err(EXIT_FAILURE, "failed to write header to "
1658 		    "temporary file for %s", fname);
1659 	}
1660 
1661 
1662 
1663 	if (fflush(f) != 0 || fclose(f) != 0) {
1664 		cpcgen_remove_tmpfile(dirfd, tmpname);
1665 		err(EXIT_FAILURE, "failed to flush and close temporary file");
1666 	}
1667 
1668 	if (renameat(dirfd, tmpname, dirfd, fname) != 0) {
1669 		err(EXIT_FAILURE, "failed to rename temporary file %s",
1670 		    tmpname);
1671 	}
1672 
1673 	free(tmpname);
1674 }
1675 
1676 static boolean_t
1677 cpcgen_cfile_amd_before(FILE *f, cpc_map_t *map)
1678 {
1679 	if (fprintf(f, cpcgen_cfile_amd_header, map->cmap_name) == -1) {
1680 		warn("failed to write header to temporary file for %s",
1681 		    map->cmap_path);
1682 		return (B_FALSE);
1683 	}
1684 
1685 	if (fprintf(f, cpcgen_cfile_amd_table_start, map->cmap_name) == -1) {
1686 		warn("failed to write header to temporary file for %s",
1687 		    map->cmap_path);
1688 		return (B_FALSE);
1689 	}
1690 
1691 
1692 	return (B_TRUE);
1693 }
1694 
1695 static boolean_t
1696 cpcgen_cfile_amd_after(FILE *f, cpc_map_t *map)
1697 {
1698 	if (fprintf(f, cpcgen_cfile_amd_table_end) == -1) {
1699 		warn("failed to write footer to temporary file for %s",
1700 		    map->cmap_path);
1701 		return (B_FALSE);
1702 	}
1703 
1704 	return (B_TRUE);
1705 }
1706 
1707 static boolean_t
1708 cpcgen_cfile_amd_event(FILE *f, nvlist_t *nvl, const char *path, uint_t ent)
1709 {
1710 	char *name, *code, *umode;
1711 	uint32_t i, length;
1712 	nvlist_t *units;
1713 
1714 	if (nvlist_lookup_string(nvl, "name", &name) != 0) {
1715 		warnx("Found event without 'name' property in %s, entry %u",
1716 		    path, ent);
1717 		return (B_FALSE);
1718 	}
1719 
1720 	if (nvlist_lookup_string(nvl, "code", &code) != 0) {
1721 		warnx("event %s (index %u) from %s missing required properties "
1722 		    "for C translation", name, path, ent);
1723 		return (B_FALSE);
1724 	}
1725 
1726 	if (fprintf(f, "\t{ \"%s\", %s, 0 },\n", name, code) == -1) {
1727 		warn("failed to write out entry %s from %s", name, path);
1728 		return (B_FALSE);
1729 	}
1730 
1731 	/*
1732 	 * The 'units' array is optional. If the rule has a specific 'unit_mode'
1733 	 * indicating how the units should be combined, skip that. We don't know
1734 	 * how to properly process that right now.
1735 	 */
1736 	if (nvlist_lookup_nvlist(nvl, "units", &units) != 0) {
1737 		return (B_TRUE);
1738 	}
1739 
1740 	if (nvlist_lookup_string(nvl, "unit_mode", &umode) == 0) {
1741 		return (B_TRUE);
1742 	}
1743 
1744 	if (nvlist_lookup_uint32(units, "length", &length) != 0) {
1745 		warnx("found units array, but could not look up length "
1746 		    "property for events %s (index %u) in file %s",
1747 		    name, ent, path);
1748 		return (B_FALSE);
1749 	}
1750 
1751 	for (i = 0; i < length; i++) {
1752 		nvlist_t *uvl;
1753 		char num[64];
1754 		char *uname, *urw;
1755 		int32_t bit;
1756 
1757 		(void) snprintf(num, sizeof (num), "%u", i);
1758 		if (nvlist_lookup_nvlist(units, num, &uvl) != 0) {
1759 			warnx("failed to look up unit %u for event %s (index "
1760 			    "%u) in file %s", i, name, ent, path);
1761 			return (B_FALSE);
1762 		}
1763 
1764 		if (nvlist_lookup_string(uvl, "name", &uname) != 0 ||
1765 		    nvlist_lookup_string(uvl, "rw", &urw) != 0 ||
1766 		    nvlist_lookup_int32(uvl, "bit", &bit) != 0) {
1767 			warnx("failed to find required members for unit array "
1768 			    "entry %u of event %s (index %u) from file %s",
1769 			    i, name, ent, path);
1770 			dump_nvlist(uvl, 0);
1771 			return (B_FALSE);
1772 		}
1773 
1774 		if (bit < 0 || bit > 31) {
1775 			warnx("event %s (index %u) from file %s has invalid "
1776 			    "bit value: %d; skipping", name, ent, path, bit);
1777 			continue;
1778 		}
1779 
1780 		if (strcasecmp(urw, "Read-write") != 0)
1781 			continue;
1782 
1783 		if (fprintf(f, "\t{ \"%s.%s\", %s, 0x%x },\n", name, uname,
1784 		    code, 1U << bit) == -1) {
1785 			warn("failed to write out entry %s from %s", name,
1786 			    path);
1787 			return (B_FALSE);
1788 		}
1789 	}
1790 
1791 	return (B_TRUE);
1792 }
1793 
1794 /*
1795  * For each processor family, generate a data file that contains all of the
1796  * events that we support. Also generate a header that can be included that
1797  * declares all of the tables.
1798  */
1799 static void
1800 cpcgen_gen(int dirfd)
1801 {
1802 	cpc_map_t *map = cpcgen_maps;
1803 
1804 	if (map == NULL) {
1805 		errx(EXIT_FAILURE, "no platforms found or matched");
1806 	}
1807 
1808 	for (map = cpcgen_maps; map != NULL; map = map->cmap_next) {
1809 		int fd, ret;
1810 		FILE *f;
1811 		char *tmpname, *name;
1812 		uint32_t length, i;
1813 
1814 		if ((name = cpcgen_ops.cgen_op_name(map)) == NULL) {
1815 			exit(EXIT_FAILURE);
1816 		}
1817 
1818 		if (asprintf(&tmpname, ".%s.%d", name, getpid()) == -1) {
1819 			err(EXIT_FAILURE, "failed to construct temporary file "
1820 			    "name");
1821 		}
1822 
1823 		if ((fd = openat(dirfd, tmpname, O_RDWR | O_CREAT, 0444)) < 0) {
1824 			err(EXIT_FAILURE, "failed to create temporary file %s",
1825 			    tmpname);
1826 		}
1827 
1828 		if ((f = fdopen(fd, "w")) == NULL) {
1829 			cpcgen_remove_tmpfile(dirfd, tmpname);
1830 			err(EXIT_FAILURE, "failed to fdopen temporary file");
1831 		}
1832 
1833 		if (!cpcgen_ops.cgen_op_file_before(f, map)) {
1834 			cpcgen_remove_tmpfile(dirfd, tmpname);
1835 			exit(EXIT_FAILURE);
1836 		}
1837 
1838 		/*
1839 		 * Iterate over array contents.
1840 		 */
1841 		if ((ret = nvlist_lookup_uint32(map->cmap_data, "length",
1842 		    &length)) != 0) {
1843 			errx(EXIT_FAILURE, "failed to look up length property "
1844 			    "in parsed data for %s: %s", map->cmap_path,
1845 			    strerror(ret));
1846 		}
1847 
1848 		for (i = 0; i < length; i++) {
1849 			nvlist_t *nvl;
1850 			char num[64];
1851 
1852 			(void) snprintf(num, sizeof (num), "%u", i);
1853 			if ((ret = nvlist_lookup_nvlist(map->cmap_data,
1854 			    num, &nvl)) != 0) {
1855 				cpcgen_remove_tmpfile(dirfd, tmpname);
1856 				errx(EXIT_FAILURE, "failed to look up array "
1857 				    "entry %u in parsed data for %s: %s", i,
1858 				    map->cmap_path, strerror(ret));
1859 			}
1860 
1861 			if (cpcgen_ops.cgen_op_skip != NULL &&
1862 			    cpcgen_ops.cgen_op_skip(nvl, map->cmap_path, i)) {
1863 				continue;
1864 			}
1865 
1866 			if (!cpcgen_ops.cgen_op_event(f, nvl, map->cmap_path,
1867 			    i)) {
1868 				cpcgen_remove_tmpfile(dirfd, tmpname);
1869 				exit(EXIT_FAILURE);
1870 			}
1871 		}
1872 
1873 		if (!cpcgen_ops.cgen_op_file_after(f, map)) {
1874 			cpcgen_remove_tmpfile(dirfd, tmpname);
1875 			exit(EXIT_FAILURE);
1876 		}
1877 
1878 		if (fflush(f) != 0 || fclose(f) != 0) {
1879 			cpcgen_remove_tmpfile(dirfd, tmpname);
1880 			err(EXIT_FAILURE, "failed to flush and close "
1881 			    "temporary file");
1882 		}
1883 
1884 		if (renameat(dirfd, tmpname, dirfd, name) != 0) {
1885 			err(EXIT_FAILURE, "failed to rename temporary file %s",
1886 			    tmpname);
1887 		}
1888 
1889 		free(name);
1890 		free(tmpname);
1891 	}
1892 }
1893 
1894 static void
1895 cpcgen_usage(const char *fmt, ...)
1896 {
1897 	if (fmt != NULL) {
1898 		va_list ap;
1899 
1900 		(void) fprintf(stderr, "%s: ", cpcgen_progname);
1901 		va_start(ap, fmt);
1902 		(void) vfprintf(stderr, fmt, ap);
1903 		va_end(ap);
1904 	}
1905 
1906 	(void) fprintf(stderr, "Usage: %s -a|-p platform -c|-H|-m -d datadir "
1907 	    "-o outdir\n"
1908 	    "\n"
1909 	    "\t-a  generate data for all platforms\n"
1910 	    "\t-c  generate C file for CPC\n"
1911 	    "\t-d  specify the directory containt perfmon data\n"
1912 	    "\t-H  generate header file and common files\n"
1913 	    "\t-m  generate manual pages for CPC data\n"
1914 	    "\t-o  output files in directory outdir\n"
1915 	    "\t-p  generate data for a specified platform\n",
1916 	    cpcgen_progname);
1917 }
1918 
1919 int
1920 main(int argc, char *argv[])
1921 {
1922 	int c, outdirfd;
1923 	boolean_t do_mpage = B_FALSE, do_cfile = B_FALSE, do_header = B_FALSE,
1924 	    do_all = B_FALSE;
1925 	const char *datadir = NULL, *outdir = NULL, *platform = NULL;
1926 	uint_t count = 0;
1927 
1928 	cpcgen_progname = basename(argv[0]);
1929 
1930 	while ((c = getopt(argc, argv, ":acd:hHmo:p:")) != -1) {
1931 		switch (c) {
1932 		case 'a':
1933 			do_all = B_TRUE;
1934 			break;
1935 		case 'c':
1936 			do_cfile = B_TRUE;
1937 			break;
1938 		case 'd':
1939 			datadir = optarg;
1940 			break;
1941 		case 'm':
1942 			do_mpage = B_TRUE;
1943 			break;
1944 		case 'H':
1945 			do_header = B_TRUE;
1946 			break;
1947 		case 'o':
1948 			outdir = optarg;
1949 			break;
1950 		case 'p':
1951 			platform = optarg;
1952 			break;
1953 		case ':':
1954 			cpcgen_usage("Option -%c requires an operand\n",
1955 			    optopt);
1956 			return (2);
1957 		case '?':
1958 			cpcgen_usage("Unknown option: -%c\n", optopt);
1959 			return (2);
1960 		case 'h':
1961 		default:
1962 			cpcgen_usage(NULL);
1963 			return (2);
1964 		}
1965 	}
1966 
1967 	count = 0;
1968 	if (do_mpage)
1969 		count++;
1970 	if (do_cfile)
1971 		count++;
1972 	if (do_header)
1973 		count++;
1974 	if (count > 1) {
1975 		cpcgen_usage("Only one of -c, -h, and -m may be specified\n");
1976 		return (2);
1977 	} else if (count == 0) {
1978 		cpcgen_usage("One of -c, -h, and -m is required\n");
1979 		return (2);
1980 	}
1981 
1982 	count = 0;
1983 	if (do_all)
1984 		count++;
1985 	if (platform != NULL)
1986 		count++;
1987 	if (count > 1) {
1988 		cpcgen_usage("Only one of -a and -p may be specified\n");
1989 		return (2);
1990 	} else if (count == 0) {
1991 		cpcgen_usage("One of -a and -p is required\n");
1992 		return (2);
1993 	}
1994 
1995 	if (outdir == NULL) {
1996 		cpcgen_usage("Missing required output directory (-o)\n");
1997 		return (2);
1998 	}
1999 
2000 	if ((outdirfd = open(outdir, O_RDONLY)) < 0) {
2001 		err(EXIT_FAILURE, "failed to open output directory %s", outdir);
2002 	}
2003 
2004 	if (datadir == NULL) {
2005 		cpcgen_usage("Missing required data directory (-d)\n");
2006 		return (2);
2007 	}
2008 
2009 	cpcgen_determine_vendor(datadir);
2010 
2011 	switch (cpcgen_mode) {
2012 	case CPCGEN_MODE_INTEL:
2013 		cpcgen_ops.cgen_op_gather = cpcgen_read_intel;
2014 		cpcgen_ops.cgen_op_common = cpcgen_common_intel_files;
2015 		cpcgen_ops.cgen_op_skip = cpcgen_skip_intel_entry;
2016 		if (do_mpage) {
2017 			cpcgen_ops.cgen_op_name = cpcgen_manual_intel_name;
2018 			cpcgen_ops.cgen_op_file_before =
2019 			    cpcgen_manual_intel_file_before;
2020 			cpcgen_ops.cgen_op_file_after =
2021 			    cpcgen_manual_intel_file_after;
2022 			cpcgen_ops.cgen_op_event = cpcgen_manual_intel_event;
2023 		} else {
2024 			cpcgen_ops.cgen_op_name = cpcgen_cfile_intel_name;
2025 			cpcgen_ops.cgen_op_file_before =
2026 			    cpcgen_cfile_intel_before;
2027 			cpcgen_ops.cgen_op_file_after =
2028 			    cpcgen_cfile_intel_after;
2029 			cpcgen_ops.cgen_op_event = cpcgen_cfile_intel_event;
2030 		}
2031 		break;
2032 	case CPCGEN_MODE_AMD:
2033 		cpcgen_ops.cgen_op_gather = cpcgen_read_amd;
2034 		cpcgen_ops.cgen_op_common = cpcgen_common_amd_files;
2035 		cpcgen_ops.cgen_op_skip = NULL;
2036 		if (do_mpage) {
2037 			cpcgen_ops.cgen_op_name = cpcgen_manual_amd_name;
2038 			cpcgen_ops.cgen_op_file_before =
2039 			    cpcgen_manual_amd_file_before;
2040 			cpcgen_ops.cgen_op_file_after =
2041 			    cpcgen_manual_amd_file_after;
2042 			cpcgen_ops.cgen_op_event = cpcgen_manual_amd_event;
2043 		} else {
2044 			cpcgen_ops.cgen_op_name = cpcgen_cfile_amd_name;
2045 			cpcgen_ops.cgen_op_file_before =
2046 			    cpcgen_cfile_amd_before;
2047 			cpcgen_ops.cgen_op_file_after = cpcgen_cfile_amd_after;
2048 			cpcgen_ops.cgen_op_event = cpcgen_cfile_amd_event;
2049 
2050 		}
2051 		break;
2052 	default:
2053 		errx(EXIT_FAILURE, "failed to determine if operating on AMD or "
2054 		    "Intel");
2055 		break;
2056 	}
2057 
2058 	cpcgen_ops.cgen_op_gather(datadir, platform);
2059 
2060 	if (do_header) {
2061 		cpcgen_ops.cgen_op_common(outdirfd);
2062 		return (0);
2063 	}
2064 
2065 	cpcgen_gen(outdirfd);
2066 
2067 	return (0);
2068 }
2069