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 }