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