xref: /illumos-gate/usr/src/tools/cpcgen/cpcgen.c (revision 00fc50d1)
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 manual.\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.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 		if (*(c + slen) != '\0') {
664 			free(name);
665 			continue;
666 		}
667 
668 		*c = '\0';
669 		c = strchr(name, '_');
670 		if (c == NULL) {
671 			free(name);
672 			continue;
673 		}
674 		*c = '\0';
675 		c++;
676 		if (strcmp(c, "core") != 0) {
677 			errx(EXIT_FAILURE, "unexpected AMD JSON file name: %s",
678 			    d->d_name);
679 		}
680 
681 		if (platform != NULL && strcmp(platform, name) != 0) {
682 			free(name);
683 			continue;
684 		}
685 
686 		if ((map = calloc(1, sizeof (cpc_map_t))) == NULL) {
687 			err(EXIT_FAILURE, "failed to allocate space for cpc "
688 			    "file");
689 		}
690 
691 		parsed = cpcgen_read_datafile(datadir, d->d_name);
692 		if ((map->cmap_path = strdup(d->d_name)) == NULL) {
693 			err(EXIT_FAILURE, "failed to duplicate path string");
694 		}
695 		map->cmap_type = CPC_FILE_CORE;
696 		map->cmap_data = parsed;
697 		map->cmap_name = name;
698 		map->cmap_procs = NULL;
699 
700 		map->cmap_next = cpcgen_maps;
701 		cpcgen_maps = map;
702 	}
703 }
704 
705 /*
706  * Read in the mapfile.csv that is used to map between processor families and
707  * parse this. Each line has a comma separated value.
708  */
709 static void
710 cpcgen_read_intel(const char *datadir, const char *platform)
711 {
712 	FILE *map;
713 	char *mappath, *last;
714 	char *data = NULL;
715 	size_t datalen = 0;
716 	uint_t lineno;
717 
718 	if (asprintf(&mappath, "%s/%s", datadir, cpcgen_intel_mapfile) == -1) {
719 		err(EXIT_FAILURE, "failed to construct path to mapfile");
720 	}
721 
722 	if ((map = fopen(mappath, "r")) == NULL) {
723 		err(EXIT_FAILURE, "failed to open data mapfile %s", mappath);
724 	}
725 
726 	lineno = 0;
727 	while (getline(&data, &datalen, map) != -1) {
728 		char *fstr, *path, *tstr;
729 		const char *name;
730 		uint_t family, model, nsteps;
731 		uint_t steppings[CPROC_MAX_STEPPINGS];
732 
733 		cpc_type_t type;
734 		cpc_map_t *map;
735 		cpc_proc_t *proc;
736 
737 		/*
738 		 * The first line contains the header:
739 		 * Family-model,Version,Filename,EventType
740 		 */
741 		lineno++;
742 		if (lineno == 1) {
743 			continue;
744 		}
745 
746 		if ((fstr = strtok_r(data, ",", &last)) == NULL ||
747 		    strtok_r(NULL, ",", &last) == NULL ||
748 		    (path = strtok_r(NULL, ",", &last)) == NULL ||
749 		    (tstr = strtok_r(NULL, "\n", &last)) == NULL) {
750 			errx(EXIT_FAILURE, "failed to parse mapfile line "
751 			    "%u in %s", lineno, mappath);
752 		}
753 
754 		cpcgen_parse_model(fstr, &family, &model, &nsteps, steppings);
755 
756 		if (strcmp(tstr, "core") == 0) {
757 			type = CPC_FILE_CORE;
758 		} else if (strcmp(tstr, "offcore") == 0) {
759 			type = CPC_FILE_OFF_CORE;
760 		} else if (strcmp(tstr, "uncore") == 0) {
761 			type = CPC_FILE_UNCORE;
762 		} else if (strcmp(tstr, "fp_arith_inst") == 0) {
763 			type = CPC_FILE_FP_MATH;
764 		} else if (strcmp(tstr, "uncore experimental") == 0) {
765 			type = CPC_FILE_UNCORE_EXP;
766 		} else {
767 			errx(EXIT_FAILURE, "unknown file type \"%s\" on line "
768 			    "%u", tstr, lineno);
769 		}
770 
771 		if ((name = cpcgen_use_arch(path, type, platform)) == NULL)
772 			continue;
773 
774 		if ((map = cpcgen_map_lookup(path)) == NULL) {
775 			nvlist_t *parsed;
776 
777 			parsed = cpcgen_read_datafile(datadir, path);
778 
779 			if ((map = calloc(1, sizeof (cpc_map_t))) == NULL) {
780 				err(EXIT_FAILURE, "failed to allocate space "
781 				    "for cpc file");
782 			}
783 
784 			if ((map->cmap_path = strdup(path)) == NULL) {
785 				err(EXIT_FAILURE, "failed to duplicate path "
786 				    "string");
787 			}
788 
789 			map->cmap_type = type;
790 			map->cmap_data = parsed;
791 			map->cmap_name = name;
792 			map->cmap_procs = NULL;
793 
794 			map->cmap_next = cpcgen_maps;
795 			cpcgen_maps = map;
796 		}
797 
798 		if ((proc = calloc(1, sizeof (cpc_proc_t))) == NULL) {
799 			err(EXIT_FAILURE, "failed to allocate memory for "
800 			    "family and model tracking");
801 		}
802 
803 		proc->cproc_family = family;
804 		proc->cproc_model = model;
805 		proc->cproc_nsteps = nsteps;
806 		if (nsteps > 0) {
807 			bcopy(steppings, proc->cproc_steppings,
808 			    sizeof (steppings));
809 		}
810 		proc->cproc_next = map->cmap_procs;
811 		map->cmap_procs = proc;
812 	}
813 
814 	if (errno != 0 || ferror(map)) {
815 		err(EXIT_FAILURE, "failed to read %s", mappath);
816 	}
817 
818 	if (fclose(map) == EOF) {
819 		err(EXIT_FAILURE, "failed to close %s", mappath);
820 	}
821 	free(data);
822 	free(mappath);
823 }
824 
825 static char *
826 cpcgen_manual_intel_name(cpc_map_t *map)
827 {
828 	char *name;
829 
830 	if (asprintf(&name, "%s_events.3cpc", map->cmap_name) == -1) {
831 		warn("failed to assemble manual page name for %s",
832 		    map->cmap_path);
833 		return (NULL);
834 	}
835 
836 	return (name);
837 }
838 
839 static boolean_t
840 cpcgen_manual_intel_file_before(FILE *f, cpc_map_t *map)
841 {
842 	size_t i;
843 	char *upper;
844 	cpc_proc_t *proc;
845 
846 	if ((upper = strdup(map->cmap_name)) == NULL) {
847 		warn("failed to duplicate manual name for %s", map->cmap_name);
848 		return (B_FALSE);
849 	}
850 
851 	for (i = 0; upper[i] != '\0'; i++) {
852 		upper[i] = toupper(upper[i]);
853 	}
854 
855 	if (fprintf(f, cpcgen_manual_intel_intel_header, map->cmap_path, upper,
856 	    map->cmap_name) == -1) {
857 		warn("failed to write out manual header for %s",
858 		    map->cmap_name);
859 		free(upper);
860 		return (B_FALSE);
861 	}
862 	free(upper);
863 
864 	for (proc = map->cmap_procs; proc != NULL; proc = proc->cproc_next) {
865 		if (proc->cproc_nsteps > 0) {
866 			uint_t step;
867 
868 			for (step = 0; step < proc->cproc_nsteps; step++) {
869 				if (fprintf(f, ".It\n.Sy Family 0x%x, Model "
870 				    "0x%x, Stepping 0x%x\n",
871 				    proc->cproc_family, proc->cproc_model,
872 				    proc->cproc_steppings[step]) == -1) {
873 					warn("failed to write out model "
874 					    "information for %s",
875 					    map->cmap_name);
876 					return (B_FALSE);
877 				}
878 			}
879 		} else {
880 			if (fprintf(f, ".It\n.Sy Family 0x%x, Model 0x%x\n",
881 			    proc->cproc_family, proc->cproc_model) == -1) {
882 				warn("failed to write out model information "
883 				    "for %s", map->cmap_name);
884 				return (B_FALSE);
885 			}
886 		}
887 	}
888 
889 	if (fprintf(f, cpcgen_manual_intel_data) == -1) {
890 		warn("failed to write out manual header for %s",
891 		    map->cmap_name);
892 		return (B_FALSE);
893 	}
894 
895 	return (B_TRUE);
896 }
897 
898 static boolean_t
899 cpcgen_manual_intel_file_after(FILE *f, cpc_map_t *map)
900 {
901 	if (fprintf(f, cpcgen_manual_intel_trailer) == -1) {
902 		warn("failed to write out manual header for %s",
903 		    map->cmap_name);
904 		return (B_FALSE);
905 	}
906 
907 	return (B_TRUE);
908 }
909 
910 static boolean_t
911 cpcgen_manual_intel_event(FILE *f, nvlist_t *nvl, const char *path,
912     uint32_t ent)
913 {
914 	char *event, *lname, *brief = NULL, *public = NULL, *errata = NULL;
915 	size_t i;
916 
917 	if (nvlist_lookup_string(nvl, "EventName", &event) != 0) {
918 		warnx("Found event without 'EventName' property "
919 		    "in %s, entry %u", path, ent);
920 		return (B_FALSE);
921 	}
922 
923 	/*
924 	 * Intel uses capital names. CPC historically uses lower case names.
925 	 */
926 	if ((lname = strdup(event)) == NULL) {
927 		err(EXIT_FAILURE, "failed to duplicate event name %s", event);
928 	}
929 	for (i = 0; lname[i] != '\0'; i++) {
930 		lname[i] = tolower(event[i]);
931 	}
932 
933 	/*
934 	 * Try to get the other event fields, but if they're not there, don't
935 	 * worry about it.
936 	 */
937 	(void) nvlist_lookup_string(nvl, "BriefDescription", &brief);
938 	(void) nvlist_lookup_string(nvl, "PublicDescription", &public);
939 	(void) nvlist_lookup_string(nvl, "Errata", &errata);
940 	if (errata != NULL && (strcmp(errata, "0") == 0 ||
941 	    strcmp(errata, "null") == 0)) {
942 		errata = NULL;
943 	}
944 
945 	if (fprintf(f, ".It Sy %s\n", lname) == -1) {
946 		warn("failed to write out event entry %s", event);
947 		free(lname);
948 		return (B_FALSE);
949 	}
950 
951 	if (public != NULL) {
952 		if (fprintf(f, "%s\n", public) == -1) {
953 			warn("failed to write out event entry %s", event);
954 			free(lname);
955 			return (B_FALSE);
956 		}
957 	} else if (brief != NULL) {
958 		if (fprintf(f, "%s\n", brief) == -1) {
959 			warn("failed to write out event entry %s", event);
960 			free(lname);
961 			return (B_FALSE);
962 		}
963 	}
964 
965 	if (errata != NULL) {
966 		if (fprintf(f, ".Pp\nThe following errata may apply to this: "
967 		    "%s\n", errata) == -1) {
968 
969 			warn("failed to write out event entry %s", event);
970 			free(lname);
971 			return (B_FALSE);
972 		}
973 	}
974 
975 	free(lname);
976 	return (B_TRUE);
977 }
978 
979 static char *
980 cpcgen_cfile_intel_name(cpc_map_t *map)
981 {
982 	char *name;
983 
984 	if (asprintf(&name, "core_pcbe_%s.c", map->cmap_name) == -1) {
985 		warn("failed to assemble file name for %s", map->cmap_path);
986 		return (NULL);
987 	}
988 
989 	return (name);
990 }
991 
992 static boolean_t
993 cpcgen_cfile_intel_before(FILE *f, cpc_map_t *map)
994 {
995 	if (fprintf(f, cpcgen_cfile_intel_header, map->cmap_path) == -1) {
996 		warn("failed to write header to temporary file for %s",
997 		    map->cmap_path);
998 		return (B_FALSE);
999 	}
1000 
1001 	if (fprintf(f, cpcgen_cfile_intel_table_start, map->cmap_name) == -1) {
1002 		warn("failed to write header to temporary file for %s",
1003 		    map->cmap_path);
1004 		return (B_FALSE);
1005 	}
1006 
1007 	return (B_TRUE);
1008 }
1009 
1010 static boolean_t
1011 cpcgen_cfile_intel_after(FILE *f, cpc_map_t *map)
1012 {
1013 	if (fprintf(f, cpcgen_cfile_intel_table_end) == -1) {
1014 		warn("failed to write footer to temporary file for %s",
1015 		    map->cmap_path);
1016 		return (B_FALSE);
1017 	}
1018 
1019 	return (B_TRUE);
1020 }
1021 
1022 static boolean_t
1023 cpcgen_cfile_intel_event(FILE *f, nvlist_t *nvl, const char *path, uint_t ent)
1024 {
1025 	char *ecode, *umask, *name, *counter, *lname, *cmask;
1026 	size_t i;
1027 
1028 	if (nvlist_lookup_string(nvl, "EventName", &name) != 0) {
1029 		warnx("Found event without 'EventName' property "
1030 		    "in %s, entry %u", path, ent);
1031 		return (B_FALSE);
1032 	}
1033 
1034 	if (nvlist_lookup_string(nvl, "EventCode", &ecode) != 0 ||
1035 	    nvlist_lookup_string(nvl, "UMask", &umask) != 0 ||
1036 	    nvlist_lookup_string(nvl, "Counter", &counter) != 0) {
1037 		warnx("event %s (index %u) from %s, missing "
1038 		    "required properties for C file translation",
1039 		    name, ent, path);
1040 		return (B_FALSE);
1041 	}
1042 
1043 	/*
1044 	 * While we could try and parse the counters manually, just do this the
1045 	 * max power way for now based on all possible values.
1046 	 */
1047 	if (strcmp(counter, "0") == 0 || strcmp(counter, "0,") == 0) {
1048 		cmask = "C0";
1049 	} else if (strcmp(counter, "1") == 0) {
1050 		cmask = "C1";
1051 	} else if (strcmp(counter, "2") == 0) {
1052 		cmask = "C2";
1053 	} else if (strcmp(counter, "3") == 0) {
1054 		cmask = "C3";
1055 	} else if (strcmp(counter, "0,1") == 0) {
1056 		cmask = "C0|C1";
1057 	} else if (strcmp(counter, "0,1,2") == 0) {
1058 		cmask = "C0|C1|C2";
1059 	} else if (strcmp(counter, "0,1,2,3") == 0) {
1060 		cmask = "C0|C1|C2|C3";
1061 	} else if (strcmp(counter, "0,2,3") == 0) {
1062 		cmask = "C0|C2|C3";
1063 	} else if (strcmp(counter, "1,2,3") == 0) {
1064 		cmask = "C1|C2|C3";
1065 	} else if (strcmp(counter, "2,3") == 0) {
1066 		cmask = "C2|C3";
1067 	} else {
1068 		warnx("event %s (index %u) from %s, has unknown "
1069 		    "counter value \"%s\"", name, ent, path, counter);
1070 		return (B_FALSE);
1071 	}
1072 
1073 
1074 	/*
1075 	 * Intel uses capital names. CPC historically uses lower case names.
1076 	 */
1077 	if ((lname = strdup(name)) == NULL) {
1078 		err(EXIT_FAILURE, "failed to duplicate event name %s", name);
1079 	}
1080 	for (i = 0; lname[i] != '\0'; i++) {
1081 		lname[i] = tolower(name[i]);
1082 	}
1083 
1084 	if (fprintf(f, "\t{ %s, %s, %s, \"%s\" },\n", ecode, umask, cmask,
1085 	    lname) == -1) {
1086 		warn("failed to write out entry %s from %s", name, path);
1087 		free(lname);
1088 		return (B_FALSE);
1089 	}
1090 
1091 	free(lname);
1092 
1093 	/*
1094 	 * Check if we have any PAPI aliases.
1095 	 */
1096 	for (i = 0; cpcgen_intel_papi_map[i].cpapi_intc != NULL; i++) {
1097 		if (strcmp(name, cpcgen_intel_papi_map[i].cpapi_intc) != 0)
1098 			continue;
1099 
1100 		if (fprintf(f, "\t{ %s, %s, %s, \"%s\" },\n", ecode, umask,
1101 		    cmask, cpcgen_intel_papi_map[i].cpapi_papi) == -1) {
1102 			warn("failed to write out entry %s from %s", name,
1103 			    path);
1104 			return (B_FALSE);
1105 		}
1106 	}
1107 
1108 	return (B_TRUE);
1109 }
1110 
1111 static boolean_t
1112 cpcgen_generate_map(FILE *f, cpc_map_t *map, boolean_t start)
1113 {
1114 	cpc_proc_t *p;
1115 
1116 	if (fprintf(f, "\t%sif (", start ? "" : "} else ") == -1) {
1117 		return (B_FALSE);
1118 	}
1119 
1120 	for (p = map->cmap_procs; p != NULL; p = p->cproc_next) {
1121 		/*
1122 		 * Make sure the line is padded so the generated C code looks
1123 		 * like reasonable C style.
1124 		 */
1125 		if (p != map->cmap_procs) {
1126 			if (fputs("\t    ", f) == -1) {
1127 				return (B_FALSE);
1128 			}
1129 		}
1130 
1131 		if (p->cproc_nsteps > 0) {
1132 			uint_t i;
1133 
1134 			if (fprintf(f, "(model == 0x%x &&\n\t    (",
1135 			    p->cproc_model) == -1) {
1136 				return (B_FALSE);
1137 			}
1138 
1139 			for (i = 0; i < p->cproc_nsteps; i++) {
1140 				if (fprintf(f, "stepping == 0x%x%s",
1141 				    p->cproc_steppings[i],
1142 				    i + 1 != p->cproc_nsteps ?
1143 				    " ||\n\t    " : "") == -1) {
1144 					return (B_FALSE);
1145 				}
1146 			}
1147 
1148 			if (fputs("))", f) == -1) {
1149 				return (B_FALSE);
1150 			}
1151 		} else if (fprintf(f, "model == 0x%x", p->cproc_model) == -1) {
1152 			return (B_FALSE);
1153 		}
1154 
1155 		if (fprintf(f, "%s\n",
1156 		    p->cproc_next != NULL ? " ||" : ") {") == -1) {
1157 			return (B_FALSE);
1158 		}
1159 	}
1160 
1161 	if (fprintf(f, "\t\t\treturn (pcbe_core_events_%s);\n",
1162 	    map->cmap_name) == -1) {
1163 		return (B_FALSE);
1164 	}
1165 
1166 	return (B_TRUE);
1167 }
1168 
1169 /*
1170  * This is a wrapper around unlinkat that makes sure that we don't clobber
1171  * errno, which is used for properly printing out error messages below.
1172  */
1173 static void
1174 cpcgen_remove_tmpfile(int dirfd, const char *path)
1175 {
1176 	int e = errno;
1177 	(void) unlinkat(dirfd, path, 0);
1178 	errno = e;
1179 }
1180 
1181 /*
1182  * Generate a header file that declares all of these arrays and provide a map
1183  * for models to the corresponding table to use.
1184  */
1185 static void
1186 cpcgen_common_intel_files(int dirfd)
1187 {
1188 	const char *fname = "core_pcbe_cpcgen.h";
1189 	char *tmpname;
1190 	int fd;
1191 	FILE *f;
1192 	cpc_map_t *map;
1193 
1194 	if (asprintf(&tmpname, ".%s.%d", fname, getpid()) == -1) {
1195 		err(EXIT_FAILURE, "failed to construct temporary file name");
1196 	}
1197 
1198 	if ((fd = openat(dirfd, tmpname, O_RDWR | O_CREAT, 0644)) < 0) {
1199 		err(EXIT_FAILURE, "failed to create temporary file %s",
1200 		    tmpname);
1201 	}
1202 
1203 	if ((f = fdopen(fd, "w")) == NULL) {
1204 		cpcgen_remove_tmpfile(dirfd, tmpname);
1205 		err(EXIT_FAILURE, "failed to fdopen temporary file");
1206 	}
1207 
1208 	if (fprintf(f, cpcgen_cfile_intel_header, cpcgen_intel_mapfile) == -1) {
1209 		cpcgen_remove_tmpfile(dirfd, tmpname);
1210 		errx(EXIT_FAILURE, "failed to write header to temporary file "
1211 		    "for %s", fname);
1212 	}
1213 
1214 	if (fprintf(f, "#ifndef _CORE_PCBE_CPCGEN_H\n"
1215 	    "#define\t_CORE_PCBE_CPCGEN_H\n"
1216 	    "\n"
1217 	    "#ifdef __cplusplus\n"
1218 	    "extern \"C\" {\n"
1219 	    "#endif\n"
1220 	    "\n"
1221 	    "extern const struct events_table_t *core_cpcgen_table(uint_t, "
1222 	    "uint_t);\n"
1223 	    "\n") == -1) {
1224 		cpcgen_remove_tmpfile(dirfd, tmpname);
1225 		errx(EXIT_FAILURE, "failed to write header to "
1226 		    "temporary file for %s", fname);
1227 	}
1228 
1229 	for (map = cpcgen_maps; map != NULL; map = map->cmap_next) {
1230 		if (fprintf(f, "extern const struct events_table_t "
1231 		    "pcbe_core_events_%s[];\n", map->cmap_name) == -1) {
1232 			cpcgen_remove_tmpfile(dirfd, tmpname);
1233 			errx(EXIT_FAILURE, "failed to write entry to "
1234 			    "temporary file for %s", fname);
1235 		}
1236 	}
1237 
1238 	if (fprintf(f, "\n"
1239 	    "#ifdef __cplusplus\n"
1240 	    "}\n"
1241 	    "#endif\n"
1242 	    "\n"
1243 	    "#endif /* _CORE_PCBE_CPCGEN_H */\n") == -1) {
1244 		cpcgen_remove_tmpfile(dirfd, tmpname);
1245 		errx(EXIT_FAILURE, "failed to write header to "
1246 		    "temporary file for %s", fname);
1247 	}
1248 
1249 	if (fflush(f) != 0 || fclose(f) != 0) {
1250 		cpcgen_remove_tmpfile(dirfd, tmpname);
1251 		err(EXIT_FAILURE, "failed to flush and close temporary file");
1252 	}
1253 
1254 	if (renameat(dirfd, tmpname, dirfd, fname) != 0) {
1255 		err(EXIT_FAILURE, "failed to rename temporary file %s",
1256 		    tmpname);
1257 	}
1258 
1259 	free(tmpname);
1260 
1261 	/* Now again for the .c file. */
1262 	fname = "core_pcbe_cpcgen.c";
1263 	if (asprintf(&tmpname, ".%s.%d", fname, getpid()) == -1) {
1264 		err(EXIT_FAILURE, "failed to construct temporary file name");
1265 	}
1266 
1267 	if ((fd = openat(dirfd, tmpname, O_RDWR | O_CREAT, 0644)) < 0) {
1268 		err(EXIT_FAILURE, "failed to create temporary file %s",
1269 		    tmpname);
1270 	}
1271 
1272 	if ((f = fdopen(fd, "w")) == NULL) {
1273 		cpcgen_remove_tmpfile(dirfd, tmpname);
1274 		err(EXIT_FAILURE, "failed to fdopen temporary file");
1275 	}
1276 
1277 	if (fprintf(f, cpcgen_cfile_intel_header, cpcgen_intel_mapfile) == -1) {
1278 		cpcgen_remove_tmpfile(dirfd, tmpname);
1279 		errx(EXIT_FAILURE, "failed to write header to temporary file "
1280 		    "for %s", fname);
1281 	}
1282 
1283 	if (fprintf(f, "#include <core_pcbe_table.h>\n"
1284 	    "#include <sys/null.h>\n"
1285 	    "#include \"core_pcbe_cpcgen.h\"\n"
1286 	    "\n"
1287 	    "const struct events_table_t *\n"
1288 	    "core_cpcgen_table(uint_t model, uint_t stepping)\n"
1289 	    "{\n") == -1) {
1290 		cpcgen_remove_tmpfile(dirfd, tmpname);
1291 		errx(EXIT_FAILURE, "failed to write header to "
1292 		    "temporary file for %s", fname);
1293 	}
1294 
1295 	for (map = cpcgen_maps; map != NULL; map = map->cmap_next) {
1296 		if (!cpcgen_generate_map(f, map, map == cpcgen_maps)) {
1297 			cpcgen_remove_tmpfile(dirfd, tmpname);
1298 			errx(EXIT_FAILURE, "failed to write to temporary "
1299 			    "file for %s", fname);
1300 		}
1301 	}
1302 
1303 	if (fprintf(f, "\t} else {\n"
1304 	    "\t\t\treturn (NULL);\n"
1305 	    "\t}\n"
1306 	    "}\n") == -1) {
1307 		cpcgen_remove_tmpfile(dirfd, tmpname);
1308 		errx(EXIT_FAILURE, "failed to write header to "
1309 		    "temporary file for %s", fname);
1310 	}
1311 
1312 	if (fflush(f) != 0 || fclose(f) != 0) {
1313 		cpcgen_remove_tmpfile(dirfd, tmpname);
1314 		err(EXIT_FAILURE, "failed to flush and close temporary file");
1315 	}
1316 
1317 	if (renameat(dirfd, tmpname, dirfd, fname) != 0) {
1318 		err(EXIT_FAILURE, "failed to rename temporary file %s",
1319 		    tmpname);
1320 	}
1321 
1322 	free(tmpname);
1323 }
1324 
1325 /*
1326  * Look at a rule to determine whether or not we should consider including it or
1327  * not. At this point we've already filtered things such that we only get core
1328  * events.
1329  *
1330  * To consider an entry, we currently apply the following criteria:
1331  *
1332  * - The MSRIndex and MSRValue are zero. Programming additional MSRs is no
1333  *   supported right now.
1334  * - TakenAlone is non-zero, which means that it cannot run at the same time as
1335  *   another field.
1336  * - Offcore is one, indicating that it is off the core and we need to figure
1337  *   out if we can support this.
1338  * - If the counter is fixed, don't use it for now.
1339  * - If more than one value is specified in the EventCode or UMask values
1340  */
1341 static boolean_t
1342 cpcgen_skip_intel_entry(nvlist_t *nvl, const char *path, uint_t ent)
1343 {
1344 	char *event, *msridx, *msrval, *taken, *offcore, *counter;
1345 	char *ecode, *umask;
1346 
1347 	/*
1348 	 * Require EventName, it's kind of useless without that.
1349 	 */
1350 	if (nvlist_lookup_string(nvl, "EventName", &event) != 0) {
1351 		errx(EXIT_FAILURE, "Found event without 'EventName' property "
1352 		    "in %s, entry %u", path, ent);
1353 	}
1354 
1355 	/*
1356 	 * If we can't find an expected value, whine about it.
1357 	 */
1358 	if (nvlist_lookup_string(nvl, "MSRIndex", &msridx) != 0 ||
1359 	    nvlist_lookup_string(nvl, "MSRValue", &msrval) != 0 ||
1360 	    nvlist_lookup_string(nvl, "Counter", &counter) != 0 ||
1361 	    nvlist_lookup_string(nvl, "EventCode", &ecode) != 0 ||
1362 	    nvlist_lookup_string(nvl, "UMask", &umask) != 0 ||
1363 	    nvlist_lookup_string(nvl, "Offcore", &offcore) != 0) {
1364 		warnx("Skipping event %s (index %u) from %s, missing required "
1365 		    "property", event, ent, path);
1366 		return (B_TRUE);
1367 	}
1368 
1369 	/*
1370 	 * MSRIndex and MSRvalue comes as either "0" or "0x00".
1371 	 */
1372 	if ((strcmp(msridx, "0") != 0 && strcmp(msridx, "0x00") != 0) ||
1373 	    (strcmp(msrval, "0") != 0 && strcmp(msridx, "0x00") != 0) ||
1374 	    strcmp(offcore, "0") != 0 || strchr(ecode, ',') != NULL ||
1375 	    strchr(umask, ',') != NULL) {
1376 		return (B_TRUE);
1377 	}
1378 
1379 	/*
1380 	 * Unfortunately, not everything actually has "TakenAlone". If it
1381 	 * doesn't, we assume that it doesn't have to be.
1382 	 */
1383 	if (nvlist_lookup_string(nvl, "TakenAlone", &taken) == 0 &&
1384 	    strcmp(taken, "0") != 0) {
1385 		return (B_TRUE);
1386 	}
1387 
1388 
1389 	if (strncasecmp(counter, "fixed", strlen("fixed")) == 0)
1390 		return (B_TRUE);
1391 
1392 	return (B_FALSE);
1393 }
1394 static char *
1395 cpcgen_manual_amd_name(cpc_map_t *map)
1396 {
1397 	char *name;
1398 
1399 	if (asprintf(&name, "amd_%s_events.3cpc", map->cmap_name) == -1) {
1400 		warn("failed to assemble file name for %s", map->cmap_path);
1401 		return (NULL);
1402 	}
1403 
1404 	return (name);
1405 }
1406 
1407 static boolean_t
1408 cpcgen_manual_amd_file_before(FILE *f, cpc_map_t *map)
1409 {
1410 	size_t i;
1411 	char *upper;
1412 	const char *family;
1413 
1414 	if ((upper = strdup(map->cmap_name)) == NULL) {
1415 		warn("failed to duplicate manual name for %s", map->cmap_name);
1416 		return (B_FALSE);
1417 	}
1418 
1419 	for (i = 0; upper[i] != '\0'; i++) {
1420 		upper[i] = toupper(upper[i]);
1421 	}
1422 
1423 	family = map->cmap_name + 1;
1424 
1425 	if (fprintf(f, cpcgen_manual_amd_header, map->cmap_path, upper,
1426 	    family, family, family) == -1) {
1427 		warn("failed to write out manual header for %s",
1428 		    map->cmap_name);
1429 		free(upper);
1430 		return (B_FALSE);
1431 	}
1432 
1433 	free(upper);
1434 	return (B_TRUE);
1435 }
1436 
1437 static boolean_t
1438 cpcgen_manual_amd_file_after(FILE *f, cpc_map_t *map)
1439 {
1440 	if (fprintf(f, cpcgen_manual_amd_trailer) == -1) {
1441 		warn("failed to write out manual header for %s",
1442 		    map->cmap_name);
1443 		return (B_FALSE);
1444 	}
1445 
1446 	return (B_TRUE);
1447 }
1448 
1449 static boolean_t
1450 cpcgen_manual_amd_event(FILE *f, nvlist_t *nvl, const char *path, uint32_t ent)
1451 {
1452 	char *name, *mnemonic = NULL, *summary = NULL, *desc = NULL;
1453 	char *umode;
1454 	nvlist_t *units = NULL;
1455 	uint32_t i, length;
1456 
1457 	if (nvlist_lookup_string(nvl, "name", &name) != 0) {
1458 		warnx("Found event without 'name' property in %s, entry %u",
1459 		    path, ent);
1460 		return (B_FALSE);
1461 	}
1462 
1463 	if (nvlist_lookup_string(nvl, "mnemonic", &mnemonic) != 0 ||
1464 	    nvlist_lookup_string(nvl, "summary", &summary) != 0) {
1465 		warnx("event %s in %s, entry %u, missing required fields",
1466 		    name, path, ent);
1467 		return (B_FALSE);
1468 	}
1469 
1470 	/*
1471 	 * Allow the other fields to be missing.
1472 	 */
1473 	(void) nvlist_lookup_string(nvl, "description", &desc);
1474 	(void) nvlist_lookup_nvlist(nvl, "units", &units);
1475 
1476 	if (fprintf(f, ".It Sy %s\n", name) == -1) {
1477 		warn("failed to write out event entry %s", name);
1478 	}
1479 
1480 	if (fprintf(f, ".Sy %s -\n"
1481 	    "%s\n", mnemonic, summary) == -1) {
1482 		warn("failed to write out event entry %s", name);
1483 		return (B_FALSE);
1484 	}
1485 
1486 	if (desc != NULL) {
1487 		if (fprintf(f, ".Pp\n%s\n", desc) == -1) {
1488 			warn("failed to write out event entry %s", name);
1489 			return (B_FALSE);
1490 		}
1491 	}
1492 
1493 	if (units == NULL)
1494 		return (B_TRUE);
1495 
1496 	/*
1497 	 * Skip units we don't know how to handle.
1498 	 */
1499 	if (nvlist_lookup_string(nvl, "unit_mode", &umode) == 0) {
1500 		return (B_TRUE);
1501 	}
1502 
1503 	if (fprintf(f, ".Pp\n"
1504 	    "This event has the following units which may be used\n"
1505 	    "to modify the behavior of the event:\n"
1506 	    ".Bl -tag -width Sy\n") == -1) {
1507 		warn("failed to write out event entry %s", name);
1508 		return (B_FALSE);
1509 	}
1510 
1511 	if (nvlist_lookup_uint32(units, "length", &length) != 0) {
1512 		warnx("found units array, but could not look up length "
1513 		    "property for events %s (index %u) in file %s",
1514 		    name, ent, path);
1515 		return (B_FALSE);
1516 	}
1517 
1518 	for (i = 0; i < length; i++) {
1519 		nvlist_t *uvl;
1520 		char num[64];
1521 		char *uname, *udesc = NULL;
1522 
1523 		(void) snprintf(num, sizeof (num), "%u", i);
1524 		if (nvlist_lookup_nvlist(units, num, &uvl) != 0) {
1525 			warnx("failed to look up unit %u for event %s (index "
1526 			    "%u) in file %s", i, name, ent, path);
1527 			return (B_FALSE);
1528 		}
1529 
1530 		if (nvlist_lookup_string(uvl, "name", &uname) != 0) {
1531 			warnx("failed to find required members for unit array "
1532 			    "entry %u of event %s (index %u) from file %s",
1533 			    i, name, ent, path);
1534 			return (B_FALSE);
1535 		}
1536 		(void) nvlist_lookup_string(uvl, "description", &udesc);
1537 		if (fprintf(f, ".It Sy %s\n", uname) == -1) {
1538 			warn("failed to write out event entry %s", name);
1539 			return (B_FALSE);
1540 		}
1541 
1542 		if (udesc != NULL) {
1543 			if (fprintf(f, "%s\n", udesc) == -1) {
1544 				warn("failed to write out event entry %s",
1545 				    name);
1546 				return (B_FALSE);
1547 			}
1548 		}
1549 	}
1550 
1551 	if (fprintf(f, ".El\n") == -1) {
1552 		warn("failed to write out event entry %s",
1553 		    name);
1554 		return (B_FALSE);
1555 	}
1556 
1557 	return (B_TRUE);
1558 }
1559 
1560 static char *
1561 cpcgen_cfile_amd_name(cpc_map_t *map)
1562 {
1563 	char *name;
1564 
1565 	if (asprintf(&name, "opteron_pcbe_%s.c", map->cmap_name) == -1) {
1566 		warn("failed to assemble file name for %s", map->cmap_path);
1567 		return (NULL);
1568 	}
1569 
1570 	return (name);
1571 }
1572 
1573 /*
1574  * Generate a header file that can be used to synthesize the data events we care
1575  * about.
1576  */
1577 static void
1578 cpcgen_common_amd_files(int dirfd)
1579 {
1580 	const char *fname = "opteron_pcbe_cpcgen.h";
1581 	char *tmpname;
1582 	int fd;
1583 	FILE *f;
1584 	cpc_map_t *map;
1585 
1586 	if (asprintf(&tmpname, ".%s.%d", fname, getpid()) == -1) {
1587 		err(EXIT_FAILURE, "failed to construct temporary file name");
1588 	}
1589 
1590 	if ((fd = openat(dirfd, tmpname, O_RDWR | O_CREAT, 0644)) < 0) {
1591 		err(EXIT_FAILURE, "failed to create temporary file %s",
1592 		    tmpname);
1593 	}
1594 
1595 	if ((f = fdopen(fd, "w")) == NULL) {
1596 		cpcgen_remove_tmpfile(dirfd, tmpname);
1597 		err(EXIT_FAILURE, "failed to fdopen temporary file");
1598 	}
1599 
1600 	if (fprintf(f, cpcgen_cfile_cddl_header) == -1) {
1601 		cpcgen_remove_tmpfile(dirfd, tmpname);
1602 		err(EXIT_FAILURE, "failed to write header to "
1603 		    "temporary file for %s", fname);
1604 	}
1605 
1606 	if (fprintf(f, "#ifndef _OPTERON_PCBE_CPCGEN_H\n"
1607 	    "#define\t_OPTERON_PCBE_CPCGEN_H\n"
1608 	    "\n"
1609 	    "#ifdef __cplusplus\n"
1610 	    "extern \"C\" {\n"
1611 	    "#endif\n"
1612 	    "\n") == -1) {
1613 		cpcgen_remove_tmpfile(dirfd, tmpname);
1614 		err(EXIT_FAILURE, "failed to write header to "
1615 		    "temporary file for %s", fname);
1616 	}
1617 
1618 	for (map = cpcgen_maps; map != NULL; map = map->cmap_next) {
1619 		if (fprintf(f, "extern const amd_event_t "
1620 		    "opteron_pcbe_%s_events[];\n", map->cmap_name) == -1) {
1621 			cpcgen_remove_tmpfile(dirfd, tmpname);
1622 			err(EXIT_FAILURE, "failed to write header to "
1623 			    "temporary file for %s", fname);
1624 		}
1625 	}
1626 
1627 	if (fprintf(f, "\n"
1628 	    "#ifdef __cplusplus\n"
1629 	    "}\n"
1630 	    "#endif\n"
1631 	    "\n"
1632 	    "#endif /* _OPTERON_PCBE_CPCGEN_H */\n") == -1) {
1633 		cpcgen_remove_tmpfile(dirfd, tmpname);
1634 		err(EXIT_FAILURE, "failed to write header to "
1635 		    "temporary file for %s", fname);
1636 	}
1637 
1638 
1639 
1640 	if (fflush(f) != 0 || fclose(f) != 0) {
1641 		cpcgen_remove_tmpfile(dirfd, tmpname);
1642 		err(EXIT_FAILURE, "failed to flush and close temporary file");
1643 	}
1644 
1645 	if (renameat(dirfd, tmpname, dirfd, fname) != 0) {
1646 		err(EXIT_FAILURE, "failed to rename temporary file %s",
1647 		    tmpname);
1648 	}
1649 
1650 	free(tmpname);
1651 }
1652 
1653 static boolean_t
1654 cpcgen_cfile_amd_before(FILE *f, cpc_map_t *map)
1655 {
1656 	if (fprintf(f, cpcgen_cfile_amd_header, map->cmap_name) == -1) {
1657 		warn("failed to write header to temporary file for %s",
1658 		    map->cmap_path);
1659 		return (B_FALSE);
1660 	}
1661 
1662 	if (fprintf(f, cpcgen_cfile_amd_table_start, map->cmap_name) == -1) {
1663 		warn("failed to write header to temporary file for %s",
1664 		    map->cmap_path);
1665 		return (B_FALSE);
1666 	}
1667 
1668 
1669 	return (B_TRUE);
1670 }
1671 
1672 static boolean_t
1673 cpcgen_cfile_amd_after(FILE *f, cpc_map_t *map)
1674 {
1675 	if (fprintf(f, cpcgen_cfile_amd_table_end) == -1) {
1676 		warn("failed to write footer to temporary file for %s",
1677 		    map->cmap_path);
1678 		return (B_FALSE);
1679 	}
1680 
1681 	return (B_TRUE);
1682 }
1683 
1684 static boolean_t
1685 cpcgen_cfile_amd_event(FILE *f, nvlist_t *nvl, const char *path, uint_t ent)
1686 {
1687 	char *name, *code, *umode;
1688 	uint32_t i, length;
1689 	nvlist_t *units;
1690 
1691 	if (nvlist_lookup_string(nvl, "name", &name) != 0) {
1692 		warnx("Found event without 'name' property in %s, entry %u",
1693 		    path, ent);
1694 		return (B_FALSE);
1695 	}
1696 
1697 	if (nvlist_lookup_string(nvl, "code", &code) != 0) {
1698 		warnx("event %s (index %u) from %s missing required properties "
1699 		    "for C translation", name, path, ent);
1700 		return (B_FALSE);
1701 	}
1702 
1703 	if (fprintf(f, "\t{ \"%s\", %s, 0 },\n", name, code) == -1) {
1704 		warn("failed to write out entry %s from %s", name, path);
1705 		return (B_FALSE);
1706 	}
1707 
1708 	/*
1709 	 * The 'units' array is optional. If the rule has a specific 'unit_mode'
1710 	 * indicating how the units should be combined, skip that. We don't know
1711 	 * how to properly process that right now.
1712 	 */
1713 	if (nvlist_lookup_nvlist(nvl, "units", &units) != 0) {
1714 		return (B_TRUE);
1715 	}
1716 
1717 	if (nvlist_lookup_string(nvl, "unit_mode", &umode) == 0) {
1718 		return (B_TRUE);
1719 	}
1720 
1721 	if (nvlist_lookup_uint32(units, "length", &length) != 0) {
1722 		warnx("found units array, but could not look up length "
1723 		    "property for events %s (index %u) in file %s",
1724 		    name, ent, path);
1725 		return (B_FALSE);
1726 	}
1727 
1728 	for (i = 0; i < length; i++) {
1729 		nvlist_t *uvl;
1730 		char num[64];
1731 		char *uname, *urw;
1732 		int32_t bit;
1733 
1734 		(void) snprintf(num, sizeof (num), "%u", i);
1735 		if (nvlist_lookup_nvlist(units, num, &uvl) != 0) {
1736 			warnx("failed to look up unit %u for event %s (index "
1737 			    "%u) in file %s", i, name, ent, path);
1738 			return (B_FALSE);
1739 		}
1740 
1741 		if (nvlist_lookup_string(uvl, "name", &uname) != 0 ||
1742 		    nvlist_lookup_string(uvl, "rw", &urw) != 0 ||
1743 		    nvlist_lookup_int32(uvl, "bit", &bit) != 0) {
1744 			warnx("failed to find required members for unit array "
1745 			    "entry %u of event %s (index %u) from file %s",
1746 			    i, name, ent, path);
1747 			dump_nvlist(uvl, 0);
1748 			return (B_FALSE);
1749 		}
1750 
1751 		if (bit < 0 || bit > 31) {
1752 			warnx("event %s (index %u) from file %s has invalid "
1753 			    "bit value: %d; skipping", name, ent, path, bit);
1754 			continue;
1755 		}
1756 
1757 		if (strcasecmp(urw, "Read-write") != 0)
1758 			continue;
1759 
1760 		if (fprintf(f, "\t{ \"%s.%s\", %s, 0x%x },\n", name, uname,
1761 		    code, 1U << bit) == -1) {
1762 			warn("failed to write out entry %s from %s", name,
1763 			    path);
1764 			return (B_FALSE);
1765 		}
1766 	}
1767 
1768 	return (B_TRUE);
1769 }
1770 
1771 /*
1772  * For each processor family, generate a data file that contains all of the
1773  * events that we support. Also generate a header that can be included that
1774  * declares all of the tables.
1775  */
1776 static void
1777 cpcgen_gen(int dirfd)
1778 {
1779 	cpc_map_t *map = cpcgen_maps;
1780 
1781 	if (map == NULL) {
1782 		errx(EXIT_FAILURE, "no platforms found or matched");
1783 	}
1784 
1785 	for (map = cpcgen_maps; map != NULL; map = map->cmap_next) {
1786 		int fd, ret;
1787 		FILE *f;
1788 		char *tmpname, *name;
1789 		uint32_t length, i;
1790 
1791 		if ((name = cpcgen_ops.cgen_op_name(map)) == NULL) {
1792 			exit(EXIT_FAILURE);
1793 		}
1794 
1795 		if (asprintf(&tmpname, ".%s.%d", name, getpid()) == -1) {
1796 			err(EXIT_FAILURE, "failed to construct temporary file "
1797 			    "name");
1798 		}
1799 
1800 		if ((fd = openat(dirfd, tmpname, O_RDWR | O_CREAT, 0444)) < 0) {
1801 			err(EXIT_FAILURE, "failed to create temporary file %s",
1802 			    tmpname);
1803 		}
1804 
1805 		if ((f = fdopen(fd, "w")) == NULL) {
1806 			cpcgen_remove_tmpfile(dirfd, tmpname);
1807 			err(EXIT_FAILURE, "failed to fdopen temporary file");
1808 		}
1809 
1810 		if (!cpcgen_ops.cgen_op_file_before(f, map)) {
1811 			cpcgen_remove_tmpfile(dirfd, tmpname);
1812 			exit(EXIT_FAILURE);
1813 		}
1814 
1815 		/*
1816 		 * Iterate over array contents.
1817 		 */
1818 		if ((ret = nvlist_lookup_uint32(map->cmap_data, "length",
1819 		    &length)) != 0) {
1820 			errx(EXIT_FAILURE, "failed to look up length property "
1821 			    "in parsed data for %s: %s", map->cmap_path,
1822 			    strerror(ret));
1823 		}
1824 
1825 		for (i = 0; i < length; i++) {
1826 			nvlist_t *nvl;
1827 			char num[64];
1828 
1829 			(void) snprintf(num, sizeof (num), "%u", i);
1830 			if ((ret = nvlist_lookup_nvlist(map->cmap_data,
1831 			    num, &nvl)) != 0) {
1832 				cpcgen_remove_tmpfile(dirfd, tmpname);
1833 				errx(EXIT_FAILURE, "failed to look up array "
1834 				    "entry %u in parsed data for %s: %s", i,
1835 				    map->cmap_path, strerror(ret));
1836 			}
1837 
1838 			if (cpcgen_ops.cgen_op_skip != NULL &&
1839 			    cpcgen_ops.cgen_op_skip(nvl, map->cmap_path, i)) {
1840 				continue;
1841 			}
1842 
1843 			if (!cpcgen_ops.cgen_op_event(f, nvl, map->cmap_path,
1844 			    i)) {
1845 				cpcgen_remove_tmpfile(dirfd, tmpname);
1846 				exit(EXIT_FAILURE);
1847 			}
1848 		}
1849 
1850 		if (!cpcgen_ops.cgen_op_file_after(f, map)) {
1851 			cpcgen_remove_tmpfile(dirfd, tmpname);
1852 			exit(EXIT_FAILURE);
1853 		}
1854 
1855 		if (fflush(f) != 0 || fclose(f) != 0) {
1856 			cpcgen_remove_tmpfile(dirfd, tmpname);
1857 			err(EXIT_FAILURE, "failed to flush and close "
1858 			    "temporary file");
1859 		}
1860 
1861 		if (renameat(dirfd, tmpname, dirfd, name) != 0) {
1862 			err(EXIT_FAILURE, "failed to rename temporary file %s",
1863 			    tmpname);
1864 		}
1865 
1866 		free(name);
1867 		free(tmpname);
1868 	}
1869 }
1870 
1871 static void
1872 cpcgen_usage(const char *fmt, ...)
1873 {
1874 	if (fmt != NULL) {
1875 		va_list ap;
1876 
1877 		(void) fprintf(stderr, "%s: ", cpcgen_progname);
1878 		va_start(ap, fmt);
1879 		(void) vfprintf(stderr, fmt, ap);
1880 		va_end(ap);
1881 	}
1882 
1883 	(void) fprintf(stderr, "Usage: %s -a|-p platform -c|-H|-m -d datadir "
1884 	    "-o outdir\n"
1885 	    "\n"
1886 	    "\t-a  generate data for all platforms\n"
1887 	    "\t-c  generate C file for CPC\n"
1888 	    "\t-d  specify the directory containt perfmon data\n"
1889 	    "\t-H  generate header file and common files\n"
1890 	    "\t-m  generate manual pages for CPC data\n"
1891 	    "\t-o  output files in directory outdir\n"
1892 	    "\t-p  generate data for a specified platform\n",
1893 	    cpcgen_progname);
1894 }
1895 
1896 int
1897 main(int argc, char *argv[])
1898 {
1899 	int c, outdirfd;
1900 	boolean_t do_mpage = B_FALSE, do_cfile = B_FALSE, do_header = B_FALSE,
1901 	    do_all = B_FALSE;
1902 	const char *datadir = NULL, *outdir = NULL, *platform = NULL;
1903 	uint_t count = 0;
1904 
1905 	cpcgen_progname = basename(argv[0]);
1906 
1907 	while ((c = getopt(argc, argv, ":acd:hHmo:p:")) != -1) {
1908 		switch (c) {
1909 		case 'a':
1910 			do_all = B_TRUE;
1911 			break;
1912 		case 'c':
1913 			do_cfile = B_TRUE;
1914 			break;
1915 		case 'd':
1916 			datadir = optarg;
1917 			break;
1918 		case 'm':
1919 			do_mpage = B_TRUE;
1920 			break;
1921 		case 'H':
1922 			do_header = B_TRUE;
1923 			break;
1924 		case 'o':
1925 			outdir = optarg;
1926 			break;
1927 		case 'p':
1928 			platform = optarg;
1929 			break;
1930 		case ':':
1931 			cpcgen_usage("Option -%c requires an operand\n",
1932 			    optopt);
1933 			return (2);
1934 		case '?':
1935 			cpcgen_usage("Unknown option: -%c\n", optopt);
1936 			return (2);
1937 		case 'h':
1938 		default:
1939 			cpcgen_usage(NULL);
1940 			return (2);
1941 		}
1942 	}
1943 
1944 	count = 0;
1945 	if (do_mpage)
1946 		count++;
1947 	if (do_cfile)
1948 		count++;
1949 	if (do_header)
1950 		count++;
1951 	if (count > 1) {
1952 		cpcgen_usage("Only one of -c, -h, and -m may be specified\n");
1953 		return (2);
1954 	} else if (count == 0) {
1955 		cpcgen_usage("One of -c, -h, and -m is required\n");
1956 		return (2);
1957 	}
1958 
1959 	count = 0;
1960 	if (do_all)
1961 		count++;
1962 	if (platform != NULL)
1963 		count++;
1964 	if (count > 1) {
1965 		cpcgen_usage("Only one of -a and -p may be specified\n");
1966 		return (2);
1967 	} else if (count == 0) {
1968 		cpcgen_usage("One of -a and -p is required\n");
1969 		return (2);
1970 	}
1971 
1972 	if (outdir == NULL) {
1973 		cpcgen_usage("Missing required output directory (-o)\n");
1974 		return (2);
1975 	}
1976 
1977 	if ((outdirfd = open(outdir, O_RDONLY)) < 0) {
1978 		err(EXIT_FAILURE, "failed to open output directory %s", outdir);
1979 	}
1980 
1981 	if (datadir == NULL) {
1982 		cpcgen_usage("Missing required data directory (-d)\n");
1983 		return (2);
1984 	}
1985 
1986 	cpcgen_determine_vendor(datadir);
1987 
1988 	switch (cpcgen_mode) {
1989 	case CPCGEN_MODE_INTEL:
1990 		cpcgen_ops.cgen_op_gather = cpcgen_read_intel;
1991 		cpcgen_ops.cgen_op_common = cpcgen_common_intel_files;
1992 		cpcgen_ops.cgen_op_skip = cpcgen_skip_intel_entry;
1993 		if (do_mpage) {
1994 			cpcgen_ops.cgen_op_name = cpcgen_manual_intel_name;
1995 			cpcgen_ops.cgen_op_file_before =
1996 			    cpcgen_manual_intel_file_before;
1997 			cpcgen_ops.cgen_op_file_after =
1998 			    cpcgen_manual_intel_file_after;
1999 			cpcgen_ops.cgen_op_event = cpcgen_manual_intel_event;
2000 		} else {
2001 			cpcgen_ops.cgen_op_name = cpcgen_cfile_intel_name;
2002 			cpcgen_ops.cgen_op_file_before =
2003 			    cpcgen_cfile_intel_before;
2004 			cpcgen_ops.cgen_op_file_after =
2005 			    cpcgen_cfile_intel_after;
2006 			cpcgen_ops.cgen_op_event = cpcgen_cfile_intel_event;
2007 		}
2008 		break;
2009 	case CPCGEN_MODE_AMD:
2010 		cpcgen_ops.cgen_op_gather = cpcgen_read_amd;
2011 		cpcgen_ops.cgen_op_common = cpcgen_common_amd_files;
2012 		cpcgen_ops.cgen_op_skip = NULL;
2013 		if (do_mpage) {
2014 			cpcgen_ops.cgen_op_name = cpcgen_manual_amd_name;
2015 			cpcgen_ops.cgen_op_file_before =
2016 			    cpcgen_manual_amd_file_before;
2017 			cpcgen_ops.cgen_op_file_after =
2018 			    cpcgen_manual_amd_file_after;
2019 			cpcgen_ops.cgen_op_event = cpcgen_manual_amd_event;
2020 		} else {
2021 			cpcgen_ops.cgen_op_name = cpcgen_cfile_amd_name;
2022 			cpcgen_ops.cgen_op_file_before =
2023 			    cpcgen_cfile_amd_before;
2024 			cpcgen_ops.cgen_op_file_after = cpcgen_cfile_amd_after;
2025 			cpcgen_ops.cgen_op_event = cpcgen_cfile_amd_event;
2026 
2027 		}
2028 		break;
2029 	default:
2030 		errx(EXIT_FAILURE, "failed to determine if operating on AMD or "
2031 		    "Intel");
2032 		break;
2033 	}
2034 
2035 	cpcgen_ops.cgen_op_gather(datadir, platform);
2036 
2037 	if (do_header) {
2038 		cpcgen_ops.cgen_op_common(outdirfd);
2039 		return (0);
2040 	}
2041 
2042 	cpcgen_gen(outdirfd);
2043 
2044 	return (0);
2045 }
2046