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