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 * 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 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 * 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 * 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 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 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 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 * 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 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 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 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 * 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 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 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 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 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 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 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 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 * 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 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 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 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 * 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 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 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 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 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 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 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 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