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