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