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