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