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