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