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