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 file transforms the perfmon data files into C files and manual pages. 18 */ 19 20 #include <stdio.h> 21 #include <stdarg.h> 22 #include <unistd.h> 23 #include <err.h> 24 #include <libgen.h> 25 #include <libnvpair.h> 26 #include <strings.h> 27 #include <errno.h> 28 #include <limits.h> 29 #include <sys/mman.h> 30 #include <sys/param.h> 31 #include <assert.h> 32 #include <ctype.h> 33 #include <sys/types.h> 34 #include <sys/stat.h> 35 #include <fcntl.h> 36 37 #include <json_nvlist.h> 38 39 #define EXIT_USAGE 2 40 #define CPROC_MAX_STEPPINGS 16 41 42 typedef struct cpc_proc { 43 struct cpc_proc *cproc_next; 44 uint_t cproc_family; 45 uint_t cproc_model; 46 uint_t cproc_nsteps; 47 uint_t cproc_steppings[CPROC_MAX_STEPPINGS]; 48 } cpc_proc_t; 49 50 typedef enum cpc_file_type { 51 CPC_FILE_CORE = 1 << 0, 52 CPC_FILE_OFF_CORE = 1 << 1, 53 CPC_FILE_UNCORE = 1 << 2, 54 CPC_FILE_FP_MATH = 1 << 3, 55 CPC_FILE_UNCORE_EXP = 1 << 4 56 } cpc_type_t; 57 58 typedef struct cpc_map { 59 struct cpc_map *cmap_next; 60 cpc_type_t cmap_type; 61 nvlist_t *cmap_data; 62 char *cmap_path; 63 const char *cmap_name; 64 cpc_proc_t *cmap_procs; 65 } cpc_map_t; 66 67 typedef struct cpc_whitelist { 68 const char *cwhite_short; 69 const char *cwhite_human; 70 uint_t cwhite_mask; 71 } cpc_whitelist_t; 72 73 /* 74 * List of architectures that we support generating this data for. This is done 75 * so that processors that illumos doesn't support or run on aren't generated 76 * (generally the Xeon Phi). 77 */ 78 static cpc_whitelist_t cpcgen_whitelist[] = { 79 /* Nehalem */ 80 { "NHM-EP", "nhm_ep", CPC_FILE_CORE }, 81 { "NHM-EX", "nhm_ex", CPC_FILE_CORE }, 82 /* Westmere */ 83 { "WSM-EP-DP", "wsm_ep_dp", CPC_FILE_CORE }, 84 { "WSM-EP-SP", "wsm_ep_sp", CPC_FILE_CORE }, 85 { "WSM-EX", "wsm_ex", CPC_FILE_CORE }, 86 /* Sandy Bridge */ 87 { "SNB", "snb", CPC_FILE_CORE }, 88 { "JKT", "jkt", CPC_FILE_CORE }, 89 /* Ivy Bridge */ 90 { "IVB", "ivb", CPC_FILE_CORE }, 91 { "IVT", "ivt", CPC_FILE_CORE }, 92 /* Haswell */ 93 { "HSW", "hsw", CPC_FILE_CORE }, 94 { "HSX", "hsx", CPC_FILE_CORE }, 95 /* Broadwell */ 96 { "BDW", "bdw", CPC_FILE_CORE }, 97 { "BDW-DE", "bdw_de", CPC_FILE_CORE }, 98 { "BDX", "bdx", CPC_FILE_CORE }, 99 /* Skylake */ 100 { "SKL", "skl", CPC_FILE_CORE }, 101 { "SKX", "skx", CPC_FILE_CORE }, 102 /* Cascade Lake */ 103 { "CLX", "clx", CPC_FILE_CORE }, 104 /* Atom */ 105 { "BNL", "bnl", CPC_FILE_CORE }, 106 { "SLM", "slm", CPC_FILE_CORE }, 107 { "GLM", "glm", CPC_FILE_CORE }, 108 { "GLP", "glp", CPC_FILE_CORE }, 109 { NULL } 110 }; 111 112 typedef struct cpc_papi { 113 const char *cpapi_intc; 114 const char *cpapi_papi; 115 } cpc_papi_t; 116 117 /* 118 * This table maps events with an Intel specific name to the corresponding PAPI 119 * name. There may be multiple Intel events which map to the same PAPI event. 120 * This is usually because different processors have different names for an 121 * event. We use the title as opposed to the event codes because those can 122 * change somewhat arbitrarily between processor generations. 123 */ 124 static cpc_papi_t cpcgen_papi_map[] = { 125 { "CPU_CLK_UNHALTED.THREAD_P", "PAPI_tot_cyc" }, 126 { "INST_RETIRED.ANY_P", "PAPI_tot_ins" }, 127 { "BR_INST_RETIRED.ALL_BRANCHES", "PAPI_br_ins" }, 128 { "BR_MISP_RETIRED.ALL_BRANCHES", "PAPI_br_msp" }, 129 { "BR_INST_RETIRED.CONDITIONAL", "PAPI_br_cn" }, 130 { "CYCLE_ACTIVITY.CYCLES_L1D_MISS", "PAPI_l1_dcm" }, 131 { "L1I.HITS", "PAPI_l1_ich" }, 132 { "ICACHE.HIT", "PAPI_l1_ich" }, 133 { "L1I.MISS", "PAPI_L1_icm" }, 134 { "ICACHE.MISSES", "PAPI_l1_icm" }, 135 { "L1I.READS", "PAPI_l1_ica" }, 136 { "ICACHE.ACCESSES", "PAPI_l1_ica" }, 137 { "L1I.READS", "PAPI_l1_icr" }, 138 { "ICACHE.ACCESSES", "PAPI_l1_icr" }, 139 { "L2_RQSTS.CODE_RD_MISS", "PAPI_l2_icm" }, 140 { "L2_RQSTS.MISS", "PAPI_l2_tcm" }, 141 { "ITLB_MISSES.MISS_CAUSES_A_WALK", "PAPI_tlb_im" }, 142 { "DTLB_LOAD_MISSES.MISS_CAUSES_A_WALK", "PAPI_tlb_dm" }, 143 { "PAGE_WALKS.D_SIDE_WALKS", "PAPI_tlb_dm" }, 144 { "PAGE_WALKS.I_SIDE_WALKS", "PAPI_tlb_im" }, 145 { "PAGE_WALKS.WALKS", "PAPI_tlb_tl" }, 146 { "INST_QUEUE_WRITES", "PAPI_tot_iis" }, 147 { "MEM_INST_RETIRED.STORES" "PAPI_sr_ins" }, 148 { "MEM_INST_RETIRED.LOADS" "PAPI_ld_ins" }, 149 { NULL, NULL } 150 }; 151 152 typedef struct cpcgen_ops { 153 char *(*cgen_op_name)(cpc_map_t *); 154 boolean_t (*cgen_op_file_before)(FILE *, cpc_map_t *); 155 boolean_t (*cgen_op_file_after)(FILE *, cpc_map_t *); 156 boolean_t (*cgen_op_event)(FILE *, nvlist_t *, const char *, uint32_t); 157 } cpcgen_ops_t; 158 159 static cpcgen_ops_t cpcgen_ops; 160 static const char *cpcgen_mapfile = "/mapfile.csv"; 161 static const char *cpcgen_progname; 162 static cpc_map_t *cpcgen_maps; 163 164 /* 165 * Constants used for generating data. 166 */ 167 /* BEGIN CSTYLED */ 168 static const char *cpcgen_cfile_header = "" 169 "/*\n" 170 " * Copyright (c) 2018, Intel Corporation\n" 171 " * Copyright (c) 2018, Joyent, Inc\n" 172 " * All rights reserved.\n" 173 " *\n" 174 " * Redistribution and use in source and binary forms, with or without\n" 175 " * modification, are permitted provided that the following conditions are met:\n" 176 " * \n" 177 " * 1. Redistributions of source code must retain the above copyright notice,\n" 178 " * this list of conditions and the following disclaimer.\n" 179 " * \n" 180 " * 2. Redistributions in binary form must reproduce the above copyright \n" 181 " * notice, this list of conditions and the following disclaimer in the\n" 182 " * documentation and/or other materials provided with the distribution.\n" 183 " * \n" 184 " * 3. Neither the name of the Intel Corporation nor the names of its \n" 185 " * contributors may be used to endorse or promote products derived from\n" 186 " * this software without specific prior written permission.\n" 187 " *\n" 188 " * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n" 189 " * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n" 190 " * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n" 191 " * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n" 192 " * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n" 193 " * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n" 194 " * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n" 195 " * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n" 196 " * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n" 197 " * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n" 198 " * POSSIBILITY OF SUCH DAMAGE.\n" 199 " *\n" 200 " * This file was automatically generated by cpcgen from the data file\n" 201 " * data/perfmon%s\n" 202 " *\n" 203 " * Do not modify this file. Your changes will be lost!\n" 204 " */\n" 205 "\n"; 206 /* END CSTYLED */ 207 208 static const char *cpcgen_cfile_table_start = "" 209 "#include <core_pcbe_table.h>\n" 210 "\n" 211 "const struct events_table_t pcbe_core_events_%s[] = {\n"; 212 213 static const char *cpcgen_cfile_table_end = "" 214 "\t{ NT_END, 0, 0, \"\" }\n" 215 "};\n"; 216 217 /* BEGIN CSTYLED */ 218 static const char *cpcgen_manual_header = "" 219 ".\\\" Copyright (c) 2018, Intel Corporation \n" 220 ".\\\" Copyright (c) 2018, Joyent, Inc.\n" 221 ".\\\" All rights reserved.\n" 222 ".\\\"\n" 223 ".\\\" Redistribution and use in source and binary forms, with or without \n" 224 ".\\\" modification, are permitted provided that the following conditions are met:\n" 225 ".\\\"\n" 226 ".\\\" 1. Redistributions of source code must retain the above copyright notice,\n" 227 ".\\\" this list of conditions and the following disclaimer.\n" 228 ".\\\"\n" 229 ".\\\" 2. Redistributions in binary form must reproduce the above copyright\n" 230 ".\\\" notice, this list of conditions and the following disclaimer in the\n" 231 ".\\\" documentation and/or other materials provided with the distribution.\n" 232 ".\\\"\n" 233 ".\\\" 3. Neither the name of the Intel Corporation nor the names of its\n" 234 ".\\\" contributors may be used to endorse or promote products derived from\n" 235 ".\\\" this software without specific prior written permission.\n" 236 ".\\\"\n" 237 ".\\\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n" 238 ".\\\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n" 239 ".\\\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n" 240 ".\\\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n" 241 ".\\\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n" 242 ".\\\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n" 243 ".\\\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n" 244 ".\\\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n" 245 ".\\\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n" 246 ".\\\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n" 247 ".\\\" POSSIBILITY OF SUCH DAMAGE.\n" 248 ".\\\"\n" 249 ".\\\" This file was automatically generated by cpcgen from the data file\n" 250 ".\\\" data/perfmon%s\n" 251 ".\\\"\n" 252 ".\\\" Do not modify this file. Your changes will be lost!\n" 253 ".\\\"\n" 254 ".\\\" We would like to thank Intel for providing the perfmon data for use in\n" 255 ".\\\" our manual pages.\n" 256 ".Dd June 18, 2018\n" 257 ".Dt %s_EVENTS 3CPC\n" 258 ".Os\n" 259 ".Sh NAME\n" 260 ".Nm %s_events\n" 261 ".Nd processor model specific performance counter events\n" 262 ".Sh DESCRIPTION\n" 263 "This manual page describes events specific to the following Intel CPU\n" 264 "models and is derived from Intel's perfmon data.\n" 265 "For more information, please consult the Intel Software Developer's Manual " 266 "or Intel's perfmon website.\n" 267 ".Pp\n" 268 "CPU models described by this document:\n" 269 ".Bl -bullet\n"; 270 /* END CSTYLED */ 271 272 static const char *cpcgen_manual_data = "" 273 ".El\n" 274 ".Pp\n" 275 "The following events are supported:\n" 276 ".Bl -tag -width Sy\n"; 277 278 static const char *cpcgen_manual_trailer = "" 279 ".El\n" 280 ".Sh SEE ALSO\n" 281 ".Xr cpc 3CPC\n" 282 ".Pp\n" 283 ".Lk https://download.01.org/perfmon/index/"; 284 285 static cpc_map_t * 286 cpcgen_map_lookup(const char *path) 287 { 288 cpc_map_t *m; 289 290 for (m = cpcgen_maps; m != NULL; m = m->cmap_next) { 291 if (strcmp(path, m->cmap_path) == 0) { 292 return (m); 293 } 294 } 295 296 return (NULL); 297 } 298 299 /* 300 * Parse a string of the form 'GenuineIntel-6-2E' and get out the family and 301 * model. 302 */ 303 static void 304 cpcgen_parse_model(char *fsr, uint_t *family, uint_t *model, uint_t *nstepp, 305 uint_t *steppings) 306 { 307 const char *bstr = "GenuineIntel"; 308 const char *brand, *fam, *mod, *step; 309 char *last; 310 long l; 311 uint_t nstep = 0; 312 313 /* 314 * Tokeninze the string. There may be an optional stepping portion, 315 * which has a range of steppings enclosed by '[' and ']' characters. 316 * While the other parts are required, the stepping may be missing. 317 */ 318 if ((brand = strtok_r(fsr, "-", &last)) == NULL || 319 (fam = strtok_r(NULL, "-", &last)) == NULL || 320 (mod = strtok_r(NULL, "-", &last)) == NULL) { 321 errx(EXIT_FAILURE, "failed to parse processor id \"%s\"", fsr); 322 } 323 step = strtok_r(NULL, "-", &last); 324 325 if (strcmp(bstr, brand) != 0) { 326 errx(EXIT_FAILURE, "brand string \"%s\" did not match \"%s\"", 327 brand, bstr); 328 } 329 330 errno = 0; 331 l = strtol(fam, &last, 16); 332 if (errno != 0 || l < 0 || l > UINT_MAX || *last != '\0') { 333 errx(EXIT_FAILURE, "failed to parse family \"%s\"", fam); 334 } 335 *family = (uint_t)l; 336 337 l = strtol(mod, &last, 16); 338 if (errno != 0 || l < 0 || l > UINT_MAX || *last != '\0') { 339 errx(EXIT_FAILURE, "failed to parse model \"%s\"", mod); 340 } 341 *model = (uint_t)l; 342 343 if (step == NULL) { 344 *nstepp = 0; 345 return; 346 } 347 348 if (*step != '[' || ((last = strrchr(step, ']')) == NULL)) { 349 errx(EXIT_FAILURE, "failed to parse stepping \"%s\": missing " 350 "stepping range brackets", step); 351 } 352 step++; 353 *last = '\0'; 354 while (*step != '\0') { 355 if (!isxdigit(*step)) { 356 errx(EXIT_FAILURE, "failed to parse stepping: invalid " 357 "stepping identifier '0x%x'", *step); 358 } 359 360 if (nstep >= CPROC_MAX_STEPPINGS) { 361 errx(EXIT_FAILURE, "failed to parse stepping: " 362 "encountered too many steppings"); 363 } 364 365 switch (*step) { 366 case '0': 367 steppings[nstep] = 0x0; 368 break; 369 case '1': 370 steppings[nstep] = 0x1; 371 break; 372 case '2': 373 steppings[nstep] = 0x2; 374 break; 375 case '3': 376 steppings[nstep] = 0x3; 377 break; 378 case '4': 379 steppings[nstep] = 0x4; 380 break; 381 case '5': 382 steppings[nstep] = 0x5; 383 break; 384 case '6': 385 steppings[nstep] = 0x6; 386 break; 387 case '7': 388 steppings[nstep] = 0x7; 389 break; 390 case '8': 391 steppings[nstep] = 0x8; 392 break; 393 case '9': 394 steppings[nstep] = 0x9; 395 break; 396 case 'a': 397 case 'A': 398 steppings[nstep] = 0xa; 399 break; 400 case 'b': 401 case 'B': 402 steppings[nstep] = 0xb; 403 break; 404 case 'c': 405 case 'C': 406 steppings[nstep] = 0xc; 407 break; 408 case 'd': 409 case 'D': 410 steppings[nstep] = 0xd; 411 break; 412 case 'e': 413 case 'E': 414 steppings[nstep] = 0xe; 415 break; 416 case 'f': 417 case 'F': 418 steppings[nstep] = 0xf; 419 break; 420 default: 421 errx(EXIT_FAILURE, "encountered non-hex stepping " 422 "character: '%c'", *step); 423 } 424 nstep++; 425 step++; 426 } 427 428 *nstepp = nstep; 429 } 430 431 static nvlist_t * 432 cpcgen_read_datafile(const char *datadir, const char *file) 433 { 434 int fd; 435 char *path; 436 struct stat st; 437 void *map; 438 nvlist_t *nvl; 439 nvlist_parse_json_error_t jerr; 440 441 if (asprintf(&path, "%s/%s", datadir, file) == -1) { 442 err(EXIT_FAILURE, "failed to construct path to data file %s", 443 file); 444 } 445 446 if ((fd = open(path, O_RDONLY)) < 0) { 447 err(EXIT_FAILURE, "failed to open data file %s", path); 448 } 449 450 if (fstat(fd, &st) != 0) { 451 err(EXIT_FAILURE, "failed to stat %s", path); 452 } 453 454 if ((map = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, 455 fd, 0)) == MAP_FAILED) { 456 err(EXIT_FAILURE, "failed to mmap %s", path); 457 } 458 459 if (nvlist_parse_json(map, st.st_size, &nvl, NVJSON_FORCE_INTEGER, 460 &jerr) != 0) { 461 errx(EXIT_FAILURE, "failed to parse file %s at pos %ld: %s", 462 path, jerr.nje_pos, jerr.nje_message); 463 } 464 465 if (munmap(map, st.st_size) != 0) { 466 err(EXIT_FAILURE, "failed to munmap %s", path); 467 } 468 469 if (close(fd) != 0) { 470 err(EXIT_FAILURE, "failed to close data file %s", path); 471 } 472 free(path); 473 474 return (nvl); 475 } 476 477 /* 478 * Check the whitelist to see if we should use this model. 479 */ 480 static const char * 481 cpcgen_use_arch(const char *path, cpc_type_t type, const char *platform) 482 { 483 const char *slash; 484 size_t len; 485 uint_t i; 486 487 if (*path != '/') { 488 errx(EXIT_FAILURE, "invalid path in mapfile: \"%s\": missing " 489 "leading '/'", path); 490 } 491 if ((slash = strchr(path + 1, '/')) == NULL) { 492 errx(EXIT_FAILURE, "invalid path in mapfile: \"%s\": missing " 493 "second '/'", path); 494 } 495 /* Account for the last '/' character. */ 496 len = slash - path - 1; 497 assert(len > 0); 498 499 for (i = 0; cpcgen_whitelist[i].cwhite_short != NULL; i++) { 500 if (platform != NULL && strcasecmp(platform, 501 cpcgen_whitelist[i].cwhite_short) != 0) 502 continue; 503 if (strncmp(path + 1, cpcgen_whitelist[i].cwhite_short, 504 len) == 0 && 505 (cpcgen_whitelist[i].cwhite_mask & type) == type) { 506 return (cpcgen_whitelist[i].cwhite_human); 507 } 508 } 509 510 return (NULL); 511 } 512 513 /* 514 * Read in the mapfile.csv that is used to map between processor families and 515 * parse this. Each line has a comma separated value. 516 */ 517 static void 518 cpcgen_read_mapfile(const char *datadir, const char *platform) 519 { 520 FILE *map; 521 char *mappath, *last; 522 char *data = NULL; 523 size_t datalen = 0; 524 uint_t lineno; 525 526 if (asprintf(&mappath, "%s/%s", datadir, cpcgen_mapfile) == -1) { 527 err(EXIT_FAILURE, "failed to construct path to mapfile"); 528 } 529 530 if ((map = fopen(mappath, "r")) == NULL) { 531 err(EXIT_FAILURE, "failed to open data mapfile %s", mappath); 532 } 533 534 lineno = 0; 535 while (getline(&data, &datalen, map) != -1) { 536 char *fstr, *path, *tstr; 537 const char *name; 538 uint_t family, model, nsteps; 539 uint_t steppings[CPROC_MAX_STEPPINGS]; 540 541 cpc_type_t type; 542 cpc_map_t *map; 543 cpc_proc_t *proc; 544 545 /* 546 * The first line contains the header: 547 * Family-model,Version,Filename,EventType 548 */ 549 lineno++; 550 if (lineno == 1) { 551 continue; 552 } 553 554 if ((fstr = strtok_r(data, ",", &last)) == NULL || 555 strtok_r(NULL, ",", &last) == NULL || 556 (path = strtok_r(NULL, ",", &last)) == NULL || 557 (tstr = strtok_r(NULL, "\n", &last)) == NULL) { 558 errx(EXIT_FAILURE, "failed to parse mapfile line " 559 "%u in %s", lineno, mappath); 560 } 561 562 cpcgen_parse_model(fstr, &family, &model, &nsteps, steppings); 563 564 if (strcmp(tstr, "core") == 0) { 565 type = CPC_FILE_CORE; 566 } else if (strcmp(tstr, "offcore") == 0) { 567 type = CPC_FILE_OFF_CORE; 568 } else if (strcmp(tstr, "uncore") == 0) { 569 type = CPC_FILE_UNCORE; 570 } else if (strcmp(tstr, "fp_arith_inst") == 0) { 571 type = CPC_FILE_FP_MATH; 572 } else if (strcmp(tstr, "uncore experimental") == 0) { 573 type = CPC_FILE_UNCORE_EXP; 574 } else { 575 errx(EXIT_FAILURE, "unknown file type \"%s\" on line " 576 "%u", tstr, lineno); 577 } 578 579 if ((name = cpcgen_use_arch(path, type, platform)) == NULL) 580 continue; 581 582 if ((map = cpcgen_map_lookup(path)) == NULL) { 583 nvlist_t *parsed; 584 585 parsed = cpcgen_read_datafile(datadir, path); 586 587 if ((map = calloc(1, sizeof (cpc_map_t))) == NULL) { 588 err(EXIT_FAILURE, "failed to allocate space " 589 "for cpc file"); 590 } 591 592 if ((map->cmap_path = strdup(path)) == NULL) { 593 err(EXIT_FAILURE, "failed to duplicate path " 594 "string"); 595 } 596 597 map->cmap_type = type; 598 map->cmap_data = parsed; 599 map->cmap_next = cpcgen_maps; 600 map->cmap_name = name; 601 map->cmap_procs = NULL; 602 cpcgen_maps = map; 603 } 604 605 if ((proc = calloc(1, sizeof (cpc_proc_t))) == NULL) { 606 err(EXIT_FAILURE, "failed to allocate memory for " 607 "family and model tracking"); 608 } 609 610 proc->cproc_family = family; 611 proc->cproc_model = model; 612 proc->cproc_nsteps = nsteps; 613 if (nsteps > 0) { 614 bcopy(steppings, proc->cproc_steppings, 615 sizeof (steppings)); 616 } 617 proc->cproc_next = map->cmap_procs; 618 map->cmap_procs = proc; 619 } 620 621 if (errno != 0 || ferror(map)) { 622 err(EXIT_FAILURE, "failed to read %s", mappath); 623 } 624 625 if (fclose(map) == EOF) { 626 err(EXIT_FAILURE, "failed to close %s", mappath); 627 } 628 free(data); 629 free(mappath); 630 } 631 632 static char * 633 cpcgen_manual_name(cpc_map_t *map) 634 { 635 char *name; 636 637 if (asprintf(&name, "%s_events.3cpc", map->cmap_name) == -1) { 638 warn("failed to assemble manual page name for %s", 639 map->cmap_path); 640 return (NULL); 641 } 642 643 return (name); 644 } 645 646 static boolean_t 647 cpcgen_manual_file_before(FILE *f, cpc_map_t *map) 648 { 649 size_t i; 650 char *upper; 651 cpc_proc_t *proc; 652 653 if ((upper = strdup(map->cmap_name)) == NULL) { 654 warn("failed to duplicate manual name for %s", map->cmap_name); 655 return (B_FALSE); 656 } 657 658 for (i = 0; upper[i] != '\0'; i++) { 659 upper[i] = toupper(upper[i]); 660 } 661 662 if (fprintf(f, cpcgen_manual_header, map->cmap_path, upper, 663 map->cmap_name) == -1) { 664 warn("failed to write out manual header for %s", 665 map->cmap_name); 666 free(upper); 667 return (B_FALSE); 668 } 669 670 for (proc = map->cmap_procs; proc != NULL; proc = proc->cproc_next) { 671 if (proc->cproc_nsteps > 0) { 672 uint_t step; 673 674 for (step = 0; step < proc->cproc_nsteps; step++) { 675 if (fprintf(f, ".It\n.Sy Family 0x%x, Model " 676 "0x%x, Stepping 0x%x\n", 677 proc->cproc_family, proc->cproc_model, 678 proc->cproc_steppings[step]) == -1) { 679 warn("failed to write out model " 680 "information for %s", 681 map->cmap_name); 682 free(upper); 683 return (B_FALSE); 684 } 685 } 686 } else { 687 if (fprintf(f, ".It\n.Sy Family 0x%x, Model 0x%x\n", 688 proc->cproc_family, proc->cproc_model) == -1) { 689 warn("failed to write out model information " 690 "for %s", map->cmap_name); 691 free(upper); 692 return (B_FALSE); 693 } 694 } 695 } 696 697 if (fprintf(f, cpcgen_manual_data, map->cmap_path, upper, 698 map->cmap_name) == -1) { 699 warn("failed to write out manual header for %s", 700 map->cmap_name); 701 free(upper); 702 return (B_FALSE); 703 } 704 705 free(upper); 706 return (B_TRUE); 707 } 708 709 static boolean_t 710 cpcgen_manual_file_after(FILE *f, cpc_map_t *map) 711 { 712 if (fprintf(f, cpcgen_manual_trailer) == -1) { 713 warn("failed to write out manual header for %s", 714 map->cmap_name); 715 return (B_FALSE); 716 } 717 718 return (B_TRUE); 719 } 720 721 static boolean_t 722 cpcgen_manual_event(FILE *f, nvlist_t *nvl, const char *path, uint32_t ent) 723 { 724 char *event, *lname, *brief = NULL, *public = NULL, *errata = NULL; 725 size_t i; 726 727 if (nvlist_lookup_string(nvl, "EventName", &event) != 0) { 728 warnx("Found event without 'EventName' property " 729 "in %s, entry %u", path, ent); 730 return (B_FALSE); 731 } 732 733 /* 734 * Intel uses capital names. CPC historically uses lower case names. 735 */ 736 if ((lname = strdup(event)) == NULL) { 737 err(EXIT_FAILURE, "failed to duplicate event name %s", event); 738 } 739 for (i = 0; lname[i] != '\0'; i++) { 740 lname[i] = tolower(event[i]); 741 } 742 743 /* 744 * Try to get the other event fields, but if they're not there, don't 745 * worry about it. 746 */ 747 (void) nvlist_lookup_string(nvl, "BriefDescription", &brief); 748 (void) nvlist_lookup_string(nvl, "PublicDescription", &public); 749 (void) nvlist_lookup_string(nvl, "Errata", &errata); 750 if (errata != NULL && (strcmp(errata, "0") == 0 || 751 strcmp(errata, "null") == 0)) { 752 errata = NULL; 753 } 754 755 if (fprintf(f, ".It Sy %s\n", lname) == -1) { 756 warn("failed to write out probe entry %s", event); 757 free(lname); 758 return (B_FALSE); 759 } 760 761 if (public != NULL) { 762 if (fprintf(f, "%s\n", public) == -1) { 763 warn("failed to write out probe entry %s", event); 764 free(lname); 765 return (B_FALSE); 766 } 767 } else if (brief != NULL) { 768 if (fprintf(f, "%s\n", brief) == -1) { 769 warn("failed to write out probe entry %s", event); 770 free(lname); 771 return (B_FALSE); 772 } 773 } 774 775 if (errata != NULL) { 776 if (fprintf(f, ".Pp\nThe following errata may apply to this: " 777 "%s\n", errata) == -1) { 778 779 warn("failed to write out probe entry %s", event); 780 free(lname); 781 return (B_FALSE); 782 } 783 } 784 785 free(lname); 786 return (B_TRUE); 787 } 788 789 static char * 790 cpcgen_cfile_name(cpc_map_t *map) 791 { 792 char *name; 793 794 if (asprintf(&name, "core_pcbe_%s.c", map->cmap_name) == -1) { 795 warn("failed to assemble file name for %s", map->cmap_path); 796 return (NULL); 797 } 798 799 return (name); 800 } 801 802 static boolean_t 803 cpcgen_cfile_file_before(FILE *f, cpc_map_t *map) 804 { 805 if (fprintf(f, cpcgen_cfile_header, map->cmap_path) == -1) { 806 warn("failed to write header to temporary file for %s", 807 map->cmap_path); 808 return (B_FALSE); 809 } 810 811 if (fprintf(f, cpcgen_cfile_table_start, map->cmap_name) == -1) { 812 warn("failed to write header to temporary file for %s", 813 map->cmap_path); 814 return (B_FALSE); 815 } 816 817 return (B_TRUE); 818 } 819 820 static boolean_t 821 cpcgen_cfile_file_after(FILE *f, cpc_map_t *map) 822 { 823 if (fprintf(f, cpcgen_cfile_table_end) == -1) { 824 warn("failed to write footer to temporary file for %s", 825 map->cmap_path); 826 return (B_FALSE); 827 } 828 829 return (B_TRUE); 830 } 831 832 static boolean_t 833 cpcgen_cfile_event(FILE *f, nvlist_t *nvl, const char *path, uint_t ent) 834 { 835 char *ecode, *umask, *name, *counter, *lname, *cmask; 836 size_t i; 837 838 if (nvlist_lookup_string(nvl, "EventName", &name) != 0) { 839 warnx("Found event without 'EventName' property " 840 "in %s, entry %u", path, ent); 841 return (B_FALSE); 842 } 843 844 if (nvlist_lookup_string(nvl, "EventCode", &ecode) != 0 || 845 nvlist_lookup_string(nvl, "UMask", &umask) != 0 || 846 nvlist_lookup_string(nvl, "Counter", &counter) != 0) { 847 warnx("event %s (index %u) from %s, missing " 848 "required properties for C file translation", 849 name, ent, path); 850 return (B_FALSE); 851 } 852 853 /* 854 * While we could try and parse the counters manually, just do this the 855 * max power way for now based on all possible values. 856 */ 857 if (strcmp(counter, "0") == 0 || strcmp(counter, "0,") == 0) { 858 cmask = "C0"; 859 } else if (strcmp(counter, "1") == 0) { 860 cmask = "C1"; 861 } else if (strcmp(counter, "2") == 0) { 862 cmask = "C2"; 863 } else if (strcmp(counter, "3") == 0) { 864 cmask = "C3"; 865 } else if (strcmp(counter, "0,1") == 0) { 866 cmask = "C0|C1"; 867 } else if (strcmp(counter, "0,1,2") == 0) { 868 cmask = "C0|C1|C2"; 869 } else if (strcmp(counter, "0,1,2,3") == 0) { 870 cmask = "C0|C1|C2|C3"; 871 } else if (strcmp(counter, "0,2,3") == 0) { 872 cmask = "C0|C2|C3"; 873 } else if (strcmp(counter, "1,2,3") == 0) { 874 cmask = "C1|C2|C3"; 875 } else if (strcmp(counter, "2,3") == 0) { 876 cmask = "C2|C3"; 877 } else { 878 warnx("event %s (index %u) from %s, has unknown " 879 "counter value \"%s\"", name, ent, path, counter); 880 return (B_FALSE); 881 } 882 883 884 /* 885 * Intel uses capital names. CPC historically uses lower case names. 886 */ 887 if ((lname = strdup(name)) == NULL) { 888 err(EXIT_FAILURE, "failed to duplicate event name %s", name); 889 } 890 for (i = 0; lname[i] != '\0'; i++) { 891 lname[i] = tolower(name[i]); 892 } 893 894 if (fprintf(f, "\t{ %s, %s, %s, \"%s\" },\n", ecode, umask, cmask, 895 lname) == -1) { 896 warn("failed to write out entry %s from %s", name, path); 897 free(lname); 898 return (B_FALSE); 899 } 900 901 free(lname); 902 903 /* 904 * Check if we have any PAPI aliases. 905 */ 906 for (i = 0; cpcgen_papi_map[i].cpapi_intc != NULL; i++) { 907 if (strcmp(name, cpcgen_papi_map[i].cpapi_intc) != 0) 908 continue; 909 910 if (fprintf(f, "\t{ %s, %s, %s, \"%s\" },\n", ecode, umask, 911 cmask, cpcgen_papi_map[i].cpapi_papi) == -1) { 912 warn("failed to write out entry %s from %s", name, 913 path); 914 return (B_FALSE); 915 } 916 } 917 918 return (B_TRUE); 919 } 920 921 static boolean_t 922 cpcgen_generate_map(FILE *f, cpc_map_t *map, boolean_t start) 923 { 924 cpc_proc_t *p; 925 926 if (fprintf(f, "\t%sif (", start ? "" : "} else ") == -1) { 927 return (B_FALSE); 928 } 929 930 for (p = map->cmap_procs; p != NULL; p = p->cproc_next) { 931 /* 932 * Make sure the line is padded so the generated C code looks 933 * like reasonable C style. 934 */ 935 if (p != map->cmap_procs) { 936 if (fputs("\t ", f) == -1) { 937 return (B_FALSE); 938 } 939 } 940 941 if (p->cproc_nsteps > 0) { 942 uint_t i; 943 944 if (fprintf(f, "(model == 0x%x &&\n\t (", 945 p->cproc_model) == -1) { 946 return (B_FALSE); 947 } 948 949 for (i = 0; i < p->cproc_nsteps; i++) { 950 if (fprintf(f, "stepping == 0x%x%s", 951 p->cproc_steppings[i], 952 i + 1 != p->cproc_nsteps ? 953 " ||\n\t " : "") == -1) { 954 return (B_FALSE); 955 } 956 } 957 958 if (fputs("))", f) == -1) { 959 return (B_FALSE); 960 } 961 } else if (fprintf(f, "model == 0x%x", p->cproc_model) == -1) { 962 return (B_FALSE); 963 } 964 965 if (fprintf(f, "%s\n", 966 p->cproc_next != NULL ? " ||" : ") {") == -1) { 967 return (B_FALSE); 968 } 969 } 970 971 if (fprintf(f, "\t\t\treturn (pcbe_core_events_%s);\n", 972 map->cmap_name) == -1) { 973 return (B_FALSE); 974 } 975 976 return (B_TRUE); 977 } 978 979 /* 980 * Generate a header file that declares all of these arrays and provide a map 981 * for models to the corresponding table to use. 982 */ 983 static void 984 cpcgen_common_files(int dirfd) 985 { 986 const char *fname = "core_pcbe_cpcgen.h"; 987 char *tmpname; 988 int fd; 989 FILE *f; 990 cpc_map_t *map; 991 992 if (asprintf(&tmpname, ".%s.%d", fname, getpid()) == -1) { 993 err(EXIT_FAILURE, "failed to construct temporary file name"); 994 } 995 996 if ((fd = openat(dirfd, tmpname, O_RDWR | O_CREAT, 0644)) < 0) { 997 err(EXIT_FAILURE, "failed to create temporary file %s", 998 tmpname); 999 } 1000 1001 if ((f = fdopen(fd, "w")) == NULL) { 1002 int e = errno; 1003 (void) unlinkat(dirfd, tmpname, 0); 1004 errno = e; 1005 err(EXIT_FAILURE, "failed to fdopen temporary file"); 1006 } 1007 1008 if (fprintf(f, cpcgen_cfile_header, cpcgen_mapfile) == -1) { 1009 int e = errno; 1010 (void) unlinkat(dirfd, tmpname, 0); 1011 errno = e; 1012 errx(EXIT_FAILURE, "failed to write header to temporary file " 1013 "for %s", fname); 1014 } 1015 1016 if (fprintf(f, "#ifndef _CORE_PCBE_CPCGEN_H\n" 1017 "#define\t_CORE_PCBE_CPCGEN_H\n" 1018 "\n" 1019 "#ifdef __cplusplus\n" 1020 "extern \"C\" {\n" 1021 "#endif\n" 1022 "\n" 1023 "extern const struct events_table_t *core_cpcgen_table(uint_t, " 1024 "uint_t);\n" 1025 "\n") == -1) { 1026 int e = errno; 1027 (void) unlinkat(dirfd, tmpname, 0); 1028 errno = e; 1029 errx(EXIT_FAILURE, "failed to write header to " 1030 "temporary file for %s", fname); 1031 } 1032 1033 for (map = cpcgen_maps; map != NULL; map = map->cmap_next) { 1034 if (fprintf(f, "extern const struct events_table_t " 1035 "pcbe_core_events_%s[];\n", map->cmap_name) == -1) { 1036 int e = errno; 1037 (void) unlinkat(dirfd, tmpname, 0); 1038 errno = e; 1039 errx(EXIT_FAILURE, "failed to write entry to " 1040 "temporary file for %s", fname); 1041 } 1042 } 1043 1044 if (fprintf(f, "\n" 1045 "#ifdef __cplusplus\n" 1046 "}\n" 1047 "#endif\n" 1048 "\n" 1049 "#endif /* _CORE_PCBE_CPCGEN_H */\n") == -1) { 1050 int e = errno; 1051 (void) unlinkat(dirfd, tmpname, 0); 1052 errno = e; 1053 errx(EXIT_FAILURE, "failed to write header to " 1054 "temporary file for %s", fname); 1055 } 1056 1057 if (fflush(f) != 0 || fclose(f) != 0) { 1058 int e = errno; 1059 (void) unlinkat(dirfd, tmpname, 0); 1060 errno = e; 1061 err(EXIT_FAILURE, "failed to flush and close temporary file"); 1062 } 1063 1064 if (renameat(dirfd, tmpname, dirfd, fname) != 0) { 1065 err(EXIT_FAILURE, "failed to rename temporary file %s", 1066 tmpname); 1067 } 1068 1069 free(tmpname); 1070 1071 /* Now again for the .c file. */ 1072 fname = "core_pcbe_cpcgen.c"; 1073 if (asprintf(&tmpname, ".%s.%d", fname, getpid()) == -1) { 1074 err(EXIT_FAILURE, "failed to construct temporary file name"); 1075 } 1076 1077 if ((fd = openat(dirfd, tmpname, O_RDWR | O_CREAT, 0644)) < 0) { 1078 err(EXIT_FAILURE, "failed to create temporary file %s", 1079 tmpname); 1080 } 1081 1082 if ((f = fdopen(fd, "w")) == NULL) { 1083 int e = errno; 1084 (void) unlinkat(dirfd, tmpname, 0); 1085 errno = e; 1086 err(EXIT_FAILURE, "failed to fdopen temporary file"); 1087 } 1088 1089 if (fprintf(f, cpcgen_cfile_header, cpcgen_mapfile) == -1) { 1090 int e = errno; 1091 (void) unlinkat(dirfd, tmpname, 0); 1092 errno = e; 1093 errx(EXIT_FAILURE, "failed to write header to temporary file " 1094 "for %s", fname); 1095 } 1096 1097 if (fprintf(f, "#include <core_pcbe_table.h>\n" 1098 "#include <sys/null.h>\n" 1099 "#include \"core_pcbe_cpcgen.h\"\n" 1100 "\n" 1101 "const struct events_table_t *\n" 1102 "core_cpcgen_table(uint_t model, uint_t stepping)\n" 1103 "{\n") == -1) { 1104 int e = errno; 1105 (void) unlinkat(dirfd, tmpname, 0); 1106 errno = e; 1107 errx(EXIT_FAILURE, "failed to write header to " 1108 "temporary file for %s", fname); 1109 } 1110 1111 for (map = cpcgen_maps; map != NULL; map = map->cmap_next) { 1112 if (!cpcgen_generate_map(f, map, map == cpcgen_maps)) { 1113 int e = errno; 1114 (void) unlinkat(dirfd, tmpname, 0); 1115 errno = e; 1116 errx(EXIT_FAILURE, "failed to write to temporary " 1117 "file for %s", fname); 1118 } 1119 } 1120 1121 if (fprintf(f, "\t} else {\n" 1122 "\t\t\treturn (NULL);\n" 1123 "\t}\n" 1124 "}\n") == -1) { 1125 int e = errno; 1126 (void) unlinkat(dirfd, tmpname, 0); 1127 errno = e; 1128 errx(EXIT_FAILURE, "failed to write header to " 1129 "temporary file for %s", fname); 1130 } 1131 1132 if (fflush(f) != 0 || fclose(f) != 0) { 1133 int e = errno; 1134 (void) unlinkat(dirfd, tmpname, 0); 1135 errno = e; 1136 err(EXIT_FAILURE, "failed to flush and close temporary file"); 1137 } 1138 1139 if (renameat(dirfd, tmpname, dirfd, fname) != 0) { 1140 err(EXIT_FAILURE, "failed to rename temporary file %s", 1141 tmpname); 1142 } 1143 1144 free(tmpname); 1145 } 1146 1147 /* 1148 * Look at a rule to determine whether or not we should consider including it or 1149 * not. At this point we've already filtered things such that we only get core 1150 * events. 1151 * 1152 * To consider an entry, we currently apply the following criteria: 1153 * 1154 * - The MSRIndex and MSRValue are zero. Programming additional MSRs is no 1155 * supported right now. 1156 * - TakenAlone is non-zero, which means that it cannot run at the same time as 1157 * another field. 1158 * - Offcore is one, indicating that it is off the core and we need to figure 1159 * out if we can support this. 1160 * - If the counter is fixed, don't use it for now. 1161 * - If more than one value is specified in the EventCode or UMask values 1162 */ 1163 static boolean_t 1164 cpcgen_skip_entry(nvlist_t *nvl, const char *path, uint_t ent) 1165 { 1166 char *event, *msridx, *msrval, *taken, *offcore, *counter; 1167 char *ecode, *umask; 1168 1169 /* 1170 * Require EventName, it's kind of useless without that. 1171 */ 1172 if (nvlist_lookup_string(nvl, "EventName", &event) != 0) { 1173 errx(EXIT_FAILURE, "Found event without 'EventName' property " 1174 "in %s, entry %u", path, ent); 1175 } 1176 1177 /* 1178 * If we can't find an expected value, whine about it. 1179 */ 1180 if (nvlist_lookup_string(nvl, "MSRIndex", &msridx) != 0 || 1181 nvlist_lookup_string(nvl, "MSRValue", &msrval) != 0 || 1182 nvlist_lookup_string(nvl, "Counter", &counter) != 0 || 1183 nvlist_lookup_string(nvl, "EventCode", &ecode) != 0 || 1184 nvlist_lookup_string(nvl, "UMask", &umask) != 0 || 1185 nvlist_lookup_string(nvl, "Offcore", &offcore) != 0) { 1186 warnx("Skipping event %s (index %u) from %s, missing required " 1187 "property", event, ent, path); 1188 return (B_TRUE); 1189 } 1190 1191 /* 1192 * MSRIndex and MSRvalue comes as either "0" or "0x00". 1193 */ 1194 if ((strcmp(msridx, "0") != 0 && strcmp(msridx, "0x00") != 0) || 1195 (strcmp(msrval, "0") != 0 && strcmp(msridx, "0x00") != 0) || 1196 strcmp(offcore, "0") != 0 || strchr(ecode, ',') != NULL || 1197 strchr(umask, ',') != NULL) { 1198 return (B_TRUE); 1199 } 1200 1201 /* 1202 * Unfortunately, not everything actually has "TakenAlone". If it 1203 * doesn't, we assume that it doesn't have to be. 1204 */ 1205 if (nvlist_lookup_string(nvl, "TakenAlone", &taken) == 0 && 1206 strcmp(taken, "0") != 0) { 1207 return (B_TRUE); 1208 } 1209 1210 1211 if (strncasecmp(counter, "fixed", strlen("fixed")) == 0) 1212 return (B_TRUE); 1213 1214 return (B_FALSE); 1215 } 1216 1217 /* 1218 * For each processor family, generate a data file that contains all of the 1219 * events that we support. Also generate a header that can be included that 1220 * declares all of the tables. 1221 */ 1222 static void 1223 cpcgen_gen(int dirfd) 1224 { 1225 cpc_map_t *map = cpcgen_maps; 1226 1227 if (map == NULL) { 1228 errx(EXIT_FAILURE, "no platforms found or matched"); 1229 } 1230 1231 for (map = cpcgen_maps; map != NULL; map = map->cmap_next) { 1232 int fd, ret; 1233 FILE *f; 1234 char *tmpname, *name; 1235 uint32_t length, i; 1236 1237 if ((name = cpcgen_ops.cgen_op_name(map)) == NULL) { 1238 exit(EXIT_FAILURE); 1239 } 1240 1241 if (asprintf(&tmpname, ".%s.%d", name, getpid()) == -1) { 1242 err(EXIT_FAILURE, "failed to construct temporary file " 1243 "name"); 1244 } 1245 1246 if ((fd = openat(dirfd, tmpname, O_RDWR | O_CREAT, 0444)) < 0) { 1247 err(EXIT_FAILURE, "failed to create temporary file %s", 1248 tmpname); 1249 } 1250 1251 if ((f = fdopen(fd, "w")) == NULL) { 1252 int e = errno; 1253 (void) unlinkat(dirfd, tmpname, 0); 1254 errno = e; 1255 err(EXIT_FAILURE, "failed to fdopen temporary file"); 1256 } 1257 1258 if (!cpcgen_ops.cgen_op_file_before(f, map)) { 1259 (void) unlinkat(dirfd, tmpname, 0); 1260 exit(EXIT_FAILURE); 1261 } 1262 1263 /* 1264 * Iterate over array contents. 1265 */ 1266 if ((ret = nvlist_lookup_uint32(map->cmap_data, "length", 1267 &length)) != 0) { 1268 errx(EXIT_FAILURE, "failed to look up length property " 1269 "in parsed data for %s: %s", map->cmap_path, 1270 strerror(ret)); 1271 } 1272 1273 for (i = 0; i < length; i++) { 1274 nvlist_t *nvl; 1275 char num[64]; 1276 1277 (void) snprintf(num, sizeof (num), "%u", i); 1278 if ((ret = nvlist_lookup_nvlist(map->cmap_data, 1279 num, &nvl)) != 0) { 1280 errx(EXIT_FAILURE, "failed to look up array " 1281 "entry %u in parsed data for %s: %s", i, 1282 map->cmap_path, strerror(ret)); 1283 } 1284 1285 if (cpcgen_skip_entry(nvl, map->cmap_path, i)) 1286 continue; 1287 1288 if (!cpcgen_ops.cgen_op_event(f, nvl, map->cmap_path, 1289 i)) { 1290 (void) unlinkat(dirfd, tmpname, 0); 1291 exit(EXIT_FAILURE); 1292 } 1293 } 1294 1295 if (!cpcgen_ops.cgen_op_file_after(f, map)) { 1296 (void) unlinkat(dirfd, tmpname, 0); 1297 exit(EXIT_FAILURE); 1298 } 1299 1300 if (fflush(f) != 0 || fclose(f) != 0) { 1301 int e = errno; 1302 (void) unlinkat(dirfd, tmpname, 0); 1303 errno = e; 1304 err(EXIT_FAILURE, "failed to flush and close " 1305 "temporary file"); 1306 } 1307 1308 if (renameat(dirfd, tmpname, dirfd, name) != 0) { 1309 err(EXIT_FAILURE, "failed to rename temporary file %s", 1310 tmpname); 1311 } 1312 1313 free(name); 1314 free(tmpname); 1315 } 1316 } 1317 1318 static void 1319 cpcgen_usage(const char *fmt, ...) 1320 { 1321 if (fmt != NULL) { 1322 va_list ap; 1323 1324 (void) fprintf(stderr, "%s: ", cpcgen_progname); 1325 va_start(ap, fmt); 1326 (void) vfprintf(stderr, fmt, ap); 1327 va_end(ap); 1328 } 1329 1330 (void) fprintf(stderr, "Usage: %s -a|-p platform -c|-H|-m -d datadir " 1331 "-o outdir\n" 1332 "\n" 1333 "\t-a generate data for all platforms\n" 1334 "\t-c generate C file for CPC\n" 1335 "\t-d specify the directory containt perfmon data\n" 1336 "\t-h generate header file and common files\n" 1337 "\t-m generate manual pages for CPC data\n" 1338 "\t-o outut files in directory outdir\n" 1339 "\t-p generate data for a specified platform\n", 1340 cpcgen_progname); 1341 } 1342 1343 int 1344 main(int argc, char *argv[]) 1345 { 1346 int c, outdirfd; 1347 boolean_t do_mpage = B_FALSE, do_cfile = B_FALSE, do_header = B_FALSE, 1348 do_all = B_FALSE; 1349 const char *datadir = NULL, *outdir = NULL, *platform = NULL; 1350 uint_t count = 0; 1351 1352 cpcgen_progname = basename(argv[0]); 1353 1354 while ((c = getopt(argc, argv, ":acd:hHmo:p:")) != -1) { 1355 switch (c) { 1356 case 'a': 1357 do_all = B_TRUE; 1358 break; 1359 case 'c': 1360 do_cfile = B_TRUE; 1361 break; 1362 case 'd': 1363 datadir = optarg; 1364 break; 1365 case 'm': 1366 do_mpage = B_TRUE; 1367 break; 1368 case 'H': 1369 do_header = B_TRUE; 1370 break; 1371 case 'o': 1372 outdir = optarg; 1373 break; 1374 case 'p': 1375 platform = optarg; 1376 break; 1377 case ':': 1378 cpcgen_usage("Option -%c requires an operand\n", 1379 optopt); 1380 return (2); 1381 case '?': 1382 cpcgen_usage("Unknown option: -%c\n", optopt); 1383 return (2); 1384 case 'h': 1385 default: 1386 cpcgen_usage(NULL); 1387 return (2); 1388 } 1389 } 1390 1391 count = 0; 1392 if (do_mpage) 1393 count++; 1394 if (do_cfile) 1395 count++; 1396 if (do_header) 1397 count++; 1398 if (count > 1) { 1399 cpcgen_usage("Only one of -c, -h, and -m may be specified\n"); 1400 return (2); 1401 } else if (count == 0) { 1402 cpcgen_usage("One of -c, -h, and -m is required\n"); 1403 return (2); 1404 } 1405 1406 count = 0; 1407 if (do_all) 1408 count++; 1409 if (platform != NULL) 1410 count++; 1411 if (count > 1) { 1412 cpcgen_usage("Only one of -a and -p may be specified\n"); 1413 return (2); 1414 } else if (count == 0) { 1415 cpcgen_usage("One of -a and -p is required\n"); 1416 return (2); 1417 } 1418 1419 1420 if (outdir == NULL) { 1421 cpcgen_usage("Missing required output directory (-o)\n"); 1422 return (2); 1423 } 1424 1425 if ((outdirfd = open(outdir, O_RDONLY)) < 0) { 1426 err(EXIT_FAILURE, "failed to open output directory %s", outdir); 1427 } 1428 1429 if (datadir == NULL) { 1430 cpcgen_usage("Missing required data directory (-d)\n"); 1431 return (2); 1432 } 1433 1434 cpcgen_read_mapfile(datadir, platform); 1435 1436 if (do_header) { 1437 cpcgen_common_files(outdirfd); 1438 return (0); 1439 } 1440 1441 if (do_mpage) { 1442 cpcgen_ops.cgen_op_name = cpcgen_manual_name; 1443 cpcgen_ops.cgen_op_file_before = cpcgen_manual_file_before; 1444 cpcgen_ops.cgen_op_file_after = cpcgen_manual_file_after; 1445 cpcgen_ops.cgen_op_event = cpcgen_manual_event; 1446 } 1447 1448 if (do_cfile) { 1449 cpcgen_ops.cgen_op_name = cpcgen_cfile_name; 1450 cpcgen_ops.cgen_op_file_before = cpcgen_cfile_file_before; 1451 cpcgen_ops.cgen_op_file_after = cpcgen_cfile_file_after; 1452 cpcgen_ops.cgen_op_event = cpcgen_cfile_event; 1453 } 1454 1455 1456 cpcgen_gen(outdirfd); 1457 1458 return (0); 1459 } 1460