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