diff options
author | Robert Mustacchi <rm@joyent.com> | 2019-04-05 17:24:09 +0000 |
---|---|---|
committer | Robert Mustacchi <rm@joyent.com> | 2019-04-23 21:20:59 +0000 |
commit | a6a0e2aee988bd529ac3621e19f33518fe40a0b9 (patch) | |
tree | df187bedac85f81a5b56b69aa48aab9e86f7ef14 /usr/src/tools/cpcgen/cpcgen.c | |
parent | 27896fdc5eb580ff60d4bb646cf7488e1b8545b9 (diff) | |
download | illumos-joyent-a6a0e2aee988bd529ac3621e19f33518fe40a0b9.tar.gz |
OS-7165 Want support for AMD Zen CPC events
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
Reviewed by: Dan McDonald <danmcd@joyent.com>
Approved by: Dan McDonald <danmcd@joyent.com>
Diffstat (limited to 'usr/src/tools/cpcgen/cpcgen.c')
-rw-r--r-- | usr/src/tools/cpcgen/cpcgen.c | 813 |
1 files changed, 700 insertions, 113 deletions
diff --git a/usr/src/tools/cpcgen/cpcgen.c b/usr/src/tools/cpcgen/cpcgen.c index a7b0d2ca57..401911bbb9 100644 --- a/usr/src/tools/cpcgen/cpcgen.c +++ b/usr/src/tools/cpcgen/cpcgen.c @@ -14,7 +14,8 @@ */ /* - * This file transforms the perfmon data files into C files and manual pages. + * This program transforms Intel perfmon and AMD PMC data files into C files and + * manual pages. */ #include <stdio.h> @@ -33,12 +34,19 @@ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> +#include <dirent.h> #include <json_nvlist.h> #define EXIT_USAGE 2 #define CPROC_MAX_STEPPINGS 16 +typedef enum { + CPCGEN_MODE_UNKNOWN = 0, + CPCGEN_MODE_INTEL, + CPCGEN_MODE_AMD +} cpc_mode_t; + typedef struct cpc_proc { struct cpc_proc *cproc_next; uint_t cproc_family; @@ -48,7 +56,7 @@ typedef struct cpc_proc { } cpc_proc_t; typedef enum cpc_file_type { - CPC_FILE_CORE = 1 << 0, + CPC_FILE_CORE = 1 << 0, CPC_FILE_OFF_CORE = 1 << 1, CPC_FILE_UNCORE = 1 << 2, CPC_FILE_FP_MATH = 1 << 3, @@ -75,7 +83,7 @@ typedef struct cpc_whitelist { * so that processors that illumos doesn't support or run on aren't generated * (generally the Xeon Phi). */ -static cpc_whitelist_t cpcgen_whitelist[] = { +static cpc_whitelist_t cpcgen_intel_whitelist[] = { /* Nehalem */ { "NHM-EP", "nhm_ep", CPC_FILE_CORE }, { "NHM-EX", "nhm_ex", CPC_FILE_CORE }, @@ -121,7 +129,7 @@ typedef struct cpc_papi { * event. We use the title as opposed to the event codes because those can * change somewhat arbitrarily between processor generations. */ -static cpc_papi_t cpcgen_papi_map[] = { +static cpc_papi_t cpcgen_intel_papi_map[] = { { "CPU_CLK_UNHALTED.THREAD_P", "PAPI_tot_cyc" }, { "INST_RETIRED.ANY_P", "PAPI_tot_ins" }, { "BR_INST_RETIRED.ALL_BRANCHES", "PAPI_br_ins" }, @@ -150,22 +158,26 @@ static cpc_papi_t cpcgen_papi_map[] = { }; typedef struct cpcgen_ops { + void (*cgen_op_gather)(const char *, const char *); + void (*cgen_op_common)(int); char *(*cgen_op_name)(cpc_map_t *); + boolean_t (*cgen_op_skip)(nvlist_t *, const char *, uint_t); boolean_t (*cgen_op_file_before)(FILE *, cpc_map_t *); boolean_t (*cgen_op_file_after)(FILE *, cpc_map_t *); boolean_t (*cgen_op_event)(FILE *, nvlist_t *, const char *, uint32_t); } cpcgen_ops_t; static cpcgen_ops_t cpcgen_ops; -static const char *cpcgen_mapfile = "/mapfile.csv"; +static const char *cpcgen_intel_mapfile = "/mapfile.csv"; static const char *cpcgen_progname; static cpc_map_t *cpcgen_maps; +static cpc_mode_t cpcgen_mode = CPCGEN_MODE_UNKNOWN; /* * Constants used for generating data. */ /* BEGIN CSTYLED */ -static const char *cpcgen_cfile_header = "" +static const char *cpcgen_cfile_intel_header = "" "/*\n" " * Copyright (c) 2018, Intel Corporation\n" " * Copyright (c) 2018, Joyent, Inc\n" @@ -205,17 +217,17 @@ static const char *cpcgen_cfile_header = "" "\n"; /* END CSTYLED */ -static const char *cpcgen_cfile_table_start = "" +static const char *cpcgen_cfile_intel_table_start = "" "#include <core_pcbe_table.h>\n" "\n" "const struct events_table_t pcbe_core_events_%s[] = {\n"; -static const char *cpcgen_cfile_table_end = "" +static const char *cpcgen_cfile_intel_table_end = "" "\t{ NT_END, 0, 0, \"\" }\n" "};\n"; /* BEGIN CSTYLED */ -static const char *cpcgen_manual_header = "" +static const char *cpcgen_manual_intel_intel_header = "" ".\\\" Copyright (c) 2018, Intel Corporation \n" ".\\\" Copyright (c) 2018, Joyent, Inc.\n" ".\\\" All rights reserved.\n" @@ -269,19 +281,97 @@ static const char *cpcgen_manual_header = "" ".Bl -bullet\n"; /* END CSTYLED */ -static const char *cpcgen_manual_data = "" +static const char *cpcgen_manual_intel_data = "" ".El\n" ".Pp\n" "The following events are supported:\n" ".Bl -tag -width Sy\n"; -static const char *cpcgen_manual_trailer = "" +static const char *cpcgen_manual_intel_trailer = "" ".El\n" ".Sh SEE ALSO\n" ".Xr cpc 3CPC\n" ".Pp\n" ".Lk https://download.01.org/perfmon/index/"; +static const char *cpcgen_cfile_cddl_header = "" +"/*\n" +" * This file and its contents are supplied under the terms of the\n" +" * Common Development and Distribution License (\"CDDL\"), version 1.0.\n" +" * You may only use this file in accordance with the terms of version\n" +" * 1.0 of the CDDL.\n" +" *\n" +" * A full copy of the text of the CDDL should have accompanied this\n" +" * source. A copy of the CDDL is also available via the Internet at\n" +" * http://www.illumos.org/license/CDDL.\n" +" */\n" +"\n" +"/*\n" +" * Copyright 2019 Joyent, Inc\n" +" */\n" +"\n" +"/*\n" +" * This file was automatically generated by cpcgen.\n" +" */\n" +"\n" +"/*\n" +" * Do not modify this file. Your changes will be lost!\n" +" */\n" +"\n"; + +static const char *cpcgen_manual_amd_header = "" +".\\\" This file was automatically generated by cpcgen from the data file\n" +".\\\" data/amdpmc/%s\n" +".\\\"\n" +".\\\" Do not modify this file. Your changes will be lost!\n" +".\\\"\n" +".\\\" We would like to thank AMD for providing the PMC data for use in\n" +".\\\" our manual pages.\n" +".Dd March 25, 2019\n" +".Dt AMD_%s_EVENTS 3CPC\n" +".Os\n" +".Sh NAME\n" +".Nm amd_%s_events\n" +".Nd AMD family %s processor performance monitoring events\n" +".Sh DESCRIPTION\n" +"This manual page describes events specfic to AMD family %s processors.\n" +"For more information, please consult the appropriate AMD BIOS and Kernel\n" +"Developer's guide or Open-Source Register Reference manual.\n" +".Pp\n" +"Each of the events listed below includes the AMD mnemonic which matches\n" +"the name found in the AMD manual and a brief summary of the event.\n" +"If available, a more detailed description of the event follows and then\n" +"any additional unit values that modify the event.\n" +"Each unit can be combined to create a new event in the system by placing\n" +"the '.' character between the event name and the unit name.\n" +".Pp\n" +"The following events are supported:\n" +".Bl -tag -width Sy\n"; + +static const char *cpcgen_manual_amd_trailer = "" +".El\n" +".Sh SEE ALSO\n" +".Xr cpc 3CPC\n"; + +static const char *cpcgen_cfile_amd_header = "" +"/*\n" +" * This file was automatically generated by cpcgen from the data file\n" +" * data/perfmon%s\n" +" *\n" +" * Do not modify this file. Your changes will be lost!\n" +" */\n" +"\n"; + +static const char *cpcgen_cfile_amd_table_start = "" +"#include <opteron_pcbe_table.h>\n" +"#include <sys/null.h>\n" +"\n" +"const amd_event_t opteron_pcbe_%s_events[] = {\n"; + +static const char *cpcgen_cfile_amd_table_end = "" +"\t{ NULL, 0, 0 }\n" +"};\n"; + static cpc_map_t * cpcgen_map_lookup(const char *path) { @@ -496,14 +586,14 @@ cpcgen_use_arch(const char *path, cpc_type_t type, const char *platform) len = slash - path - 1; assert(len > 0); - for (i = 0; cpcgen_whitelist[i].cwhite_short != NULL; i++) { + for (i = 0; cpcgen_intel_whitelist[i].cwhite_short != NULL; i++) { if (platform != NULL && strcasecmp(platform, - cpcgen_whitelist[i].cwhite_short) != 0) + cpcgen_intel_whitelist[i].cwhite_short) != 0) continue; - if (strncmp(path + 1, cpcgen_whitelist[i].cwhite_short, + if (strncmp(path + 1, cpcgen_intel_whitelist[i].cwhite_short, len) == 0 && - (cpcgen_whitelist[i].cwhite_mask & type) == type) { - return (cpcgen_whitelist[i].cwhite_human); + (cpcgen_intel_whitelist[i].cwhite_mask & type) == type) { + return (cpcgen_intel_whitelist[i].cwhite_human); } } @@ -511,11 +601,113 @@ cpcgen_use_arch(const char *path, cpc_type_t type, const char *platform) } /* + * Determine which CPU Vendor we're transmuting data from. + */ +static void +cpcgen_determine_vendor(const char *datadir) +{ + char *mappath; + struct stat st; + + if (asprintf(&mappath, "%s/%s", datadir, cpcgen_intel_mapfile) == -1) { + err(EXIT_FAILURE, "failed to construct path to mapfile"); + } + + if (stat(mappath, &st) == 0) { + cpcgen_mode = CPCGEN_MODE_INTEL; + } else { + if (errno != ENOENT) { + err(EXIT_FAILURE, "stat(2) of %s failed unexpectedly"); + } + + cpcgen_mode = CPCGEN_MODE_AMD; + } + + free(mappath); +} + +/* + * Read in all the data files that exist for AMD. + * + * Our family names for AMD systems are based on the family and type so a given + * name will look like f17h_core.json. + */ +static void +cpcgen_read_amd(const char *datadir, const char *platform) +{ + DIR *dir; + struct dirent *d; + const char *suffix = ".json"; + const size_t slen = strlen(suffix); + + if ((dir = opendir(datadir)) == NULL) { + err(EXIT_FAILURE, "failed to open directory %s", datadir); + } + + while ((d = readdir(dir)) != NULL) { + char *name, *c; + cpc_map_t *map; + nvlist_t *parsed; + + if ((name = strdup(d->d_name)) == NULL) { + errx(EXIT_FAILURE, "ran out of memory duplicating " + "name %s", d->d_name); + } + c = strstr(name, suffix); + + if (c == NULL) { + free(name); + continue; + } + + if (*(c + slen) != '\0') { + free(name); + continue; + } + + *c = '\0'; + c = strchr(name, '_'); + if (c == NULL) { + free(name); + continue; + } + *c = '\0'; + c++; + if (strcmp(c, "core") != 0) { + errx(EXIT_FAILURE, "unexpected AMD JSON file name: %s", + d->d_name); + } + + if (platform != NULL && strcmp(platform, name) != 0) { + free(name); + continue; + } + + if ((map = calloc(1, sizeof (cpc_map_t))) == NULL) { + err(EXIT_FAILURE, "failed to allocate space for cpc " + "file"); + } + + parsed = cpcgen_read_datafile(datadir, d->d_name); + if ((map->cmap_path = strdup(d->d_name)) == NULL) { + err(EXIT_FAILURE, "failed to duplicate path string"); + } + map->cmap_type = CPC_FILE_CORE; + map->cmap_data = parsed; + map->cmap_name = name; + map->cmap_procs = NULL; + + map->cmap_next = cpcgen_maps; + cpcgen_maps = map; + } +} + +/* * Read in the mapfile.csv that is used to map between processor families and * parse this. Each line has a comma separated value. */ static void -cpcgen_read_mapfile(const char *datadir, const char *platform) +cpcgen_read_intel(const char *datadir, const char *platform) { FILE *map; char *mappath, *last; @@ -523,7 +715,7 @@ cpcgen_read_mapfile(const char *datadir, const char *platform) size_t datalen = 0; uint_t lineno; - if (asprintf(&mappath, "%s/%s", datadir, cpcgen_mapfile) == -1) { + if (asprintf(&mappath, "%s/%s", datadir, cpcgen_intel_mapfile) == -1) { err(EXIT_FAILURE, "failed to construct path to mapfile"); } @@ -596,9 +788,10 @@ cpcgen_read_mapfile(const char *datadir, const char *platform) map->cmap_type = type; map->cmap_data = parsed; - map->cmap_next = cpcgen_maps; map->cmap_name = name; map->cmap_procs = NULL; + + map->cmap_next = cpcgen_maps; cpcgen_maps = map; } @@ -630,7 +823,7 @@ cpcgen_read_mapfile(const char *datadir, const char *platform) } static char * -cpcgen_manual_name(cpc_map_t *map) +cpcgen_manual_intel_name(cpc_map_t *map) { char *name; @@ -644,7 +837,7 @@ cpcgen_manual_name(cpc_map_t *map) } static boolean_t -cpcgen_manual_file_before(FILE *f, cpc_map_t *map) +cpcgen_manual_intel_file_before(FILE *f, cpc_map_t *map) { size_t i; char *upper; @@ -659,13 +852,14 @@ cpcgen_manual_file_before(FILE *f, cpc_map_t *map) upper[i] = toupper(upper[i]); } - if (fprintf(f, cpcgen_manual_header, map->cmap_path, upper, + if (fprintf(f, cpcgen_manual_intel_intel_header, map->cmap_path, upper, map->cmap_name) == -1) { warn("failed to write out manual header for %s", map->cmap_name); free(upper); return (B_FALSE); } + free(upper); for (proc = map->cmap_procs; proc != NULL; proc = proc->cproc_next) { if (proc->cproc_nsteps > 0) { @@ -679,7 +873,6 @@ cpcgen_manual_file_before(FILE *f, cpc_map_t *map) warn("failed to write out model " "information for %s", map->cmap_name); - free(upper); return (B_FALSE); } } @@ -688,17 +881,14 @@ cpcgen_manual_file_before(FILE *f, cpc_map_t *map) proc->cproc_family, proc->cproc_model) == -1) { warn("failed to write out model information " "for %s", map->cmap_name); - free(upper); return (B_FALSE); } } } - if (fprintf(f, cpcgen_manual_data, map->cmap_path, upper, - map->cmap_name) == -1) { + if (fprintf(f, cpcgen_manual_intel_data) == -1) { warn("failed to write out manual header for %s", map->cmap_name); - free(upper); return (B_FALSE); } @@ -707,9 +897,9 @@ cpcgen_manual_file_before(FILE *f, cpc_map_t *map) } static boolean_t -cpcgen_manual_file_after(FILE *f, cpc_map_t *map) +cpcgen_manual_intel_file_after(FILE *f, cpc_map_t *map) { - if (fprintf(f, cpcgen_manual_trailer) == -1) { + if (fprintf(f, cpcgen_manual_intel_trailer) == -1) { warn("failed to write out manual header for %s", map->cmap_name); return (B_FALSE); @@ -719,7 +909,8 @@ cpcgen_manual_file_after(FILE *f, cpc_map_t *map) } static boolean_t -cpcgen_manual_event(FILE *f, nvlist_t *nvl, const char *path, uint32_t ent) +cpcgen_manual_intel_event(FILE *f, nvlist_t *nvl, const char *path, + uint32_t ent) { char *event, *lname, *brief = NULL, *public = NULL, *errata = NULL; size_t i; @@ -753,20 +944,20 @@ cpcgen_manual_event(FILE *f, nvlist_t *nvl, const char *path, uint32_t ent) } if (fprintf(f, ".It Sy %s\n", lname) == -1) { - warn("failed to write out probe entry %s", event); + warn("failed to write out event entry %s", event); free(lname); return (B_FALSE); } if (public != NULL) { if (fprintf(f, "%s\n", public) == -1) { - warn("failed to write out probe entry %s", event); + warn("failed to write out event entry %s", event); free(lname); return (B_FALSE); } } else if (brief != NULL) { if (fprintf(f, "%s\n", brief) == -1) { - warn("failed to write out probe entry %s", event); + warn("failed to write out event entry %s", event); free(lname); return (B_FALSE); } @@ -776,7 +967,7 @@ cpcgen_manual_event(FILE *f, nvlist_t *nvl, const char *path, uint32_t ent) if (fprintf(f, ".Pp\nThe following errata may apply to this: " "%s\n", errata) == -1) { - warn("failed to write out probe entry %s", event); + warn("failed to write out event entry %s", event); free(lname); return (B_FALSE); } @@ -787,7 +978,7 @@ cpcgen_manual_event(FILE *f, nvlist_t *nvl, const char *path, uint32_t ent) } static char * -cpcgen_cfile_name(cpc_map_t *map) +cpcgen_cfile_intel_name(cpc_map_t *map) { char *name; @@ -800,15 +991,15 @@ cpcgen_cfile_name(cpc_map_t *map) } static boolean_t -cpcgen_cfile_file_before(FILE *f, cpc_map_t *map) +cpcgen_cfile_intel_before(FILE *f, cpc_map_t *map) { - if (fprintf(f, cpcgen_cfile_header, map->cmap_path) == -1) { + if (fprintf(f, cpcgen_cfile_intel_header, map->cmap_path) == -1) { warn("failed to write header to temporary file for %s", map->cmap_path); return (B_FALSE); } - if (fprintf(f, cpcgen_cfile_table_start, map->cmap_name) == -1) { + if (fprintf(f, cpcgen_cfile_intel_table_start, map->cmap_name) == -1) { warn("failed to write header to temporary file for %s", map->cmap_path); return (B_FALSE); @@ -818,9 +1009,9 @@ cpcgen_cfile_file_before(FILE *f, cpc_map_t *map) } static boolean_t -cpcgen_cfile_file_after(FILE *f, cpc_map_t *map) +cpcgen_cfile_intel_after(FILE *f, cpc_map_t *map) { - if (fprintf(f, cpcgen_cfile_table_end) == -1) { + if (fprintf(f, cpcgen_cfile_intel_table_end) == -1) { warn("failed to write footer to temporary file for %s", map->cmap_path); return (B_FALSE); @@ -830,7 +1021,7 @@ cpcgen_cfile_file_after(FILE *f, cpc_map_t *map) } static boolean_t -cpcgen_cfile_event(FILE *f, nvlist_t *nvl, const char *path, uint_t ent) +cpcgen_cfile_intel_event(FILE *f, nvlist_t *nvl, const char *path, uint_t ent) { char *ecode, *umask, *name, *counter, *lname, *cmask; size_t i; @@ -903,12 +1094,12 @@ cpcgen_cfile_event(FILE *f, nvlist_t *nvl, const char *path, uint_t ent) /* * Check if we have any PAPI aliases. */ - for (i = 0; cpcgen_papi_map[i].cpapi_intc != NULL; i++) { - if (strcmp(name, cpcgen_papi_map[i].cpapi_intc) != 0) + for (i = 0; cpcgen_intel_papi_map[i].cpapi_intc != NULL; i++) { + if (strcmp(name, cpcgen_intel_papi_map[i].cpapi_intc) != 0) continue; if (fprintf(f, "\t{ %s, %s, %s, \"%s\" },\n", ecode, umask, - cmask, cpcgen_papi_map[i].cpapi_papi) == -1) { + cmask, cpcgen_intel_papi_map[i].cpapi_papi) == -1) { warn("failed to write out entry %s from %s", name, path); return (B_FALSE); @@ -977,11 +1168,23 @@ cpcgen_generate_map(FILE *f, cpc_map_t *map, boolean_t start) } /* + * This is a wrapper around unlinkat that makes sure that we don't clobber + * errno, which is used for properly printing out error messages below. + */ +static void +cpcgen_remove_tmpfile(int dirfd, const char *path) +{ + int e = errno; + (void) unlinkat(dirfd, path, 0); + errno = e; +} + +/* * Generate a header file that declares all of these arrays and provide a map * for models to the corresponding table to use. */ static void -cpcgen_common_files(int dirfd) +cpcgen_common_intel_files(int dirfd) { const char *fname = "core_pcbe_cpcgen.h"; char *tmpname; @@ -999,16 +1202,12 @@ cpcgen_common_files(int dirfd) } if ((f = fdopen(fd, "w")) == NULL) { - int e = errno; - (void) unlinkat(dirfd, tmpname, 0); - errno = e; + cpcgen_remove_tmpfile(dirfd, tmpname); err(EXIT_FAILURE, "failed to fdopen temporary file"); } - if (fprintf(f, cpcgen_cfile_header, cpcgen_mapfile) == -1) { - int e = errno; - (void) unlinkat(dirfd, tmpname, 0); - errno = e; + if (fprintf(f, cpcgen_cfile_intel_header, cpcgen_intel_mapfile) == -1) { + cpcgen_remove_tmpfile(dirfd, tmpname); errx(EXIT_FAILURE, "failed to write header to temporary file " "for %s", fname); } @@ -1023,9 +1222,7 @@ cpcgen_common_files(int dirfd) "extern const struct events_table_t *core_cpcgen_table(uint_t, " "uint_t);\n" "\n") == -1) { - int e = errno; - (void) unlinkat(dirfd, tmpname, 0); - errno = e; + cpcgen_remove_tmpfile(dirfd, tmpname); errx(EXIT_FAILURE, "failed to write header to " "temporary file for %s", fname); } @@ -1033,9 +1230,7 @@ cpcgen_common_files(int dirfd) for (map = cpcgen_maps; map != NULL; map = map->cmap_next) { if (fprintf(f, "extern const struct events_table_t " "pcbe_core_events_%s[];\n", map->cmap_name) == -1) { - int e = errno; - (void) unlinkat(dirfd, tmpname, 0); - errno = e; + cpcgen_remove_tmpfile(dirfd, tmpname); errx(EXIT_FAILURE, "failed to write entry to " "temporary file for %s", fname); } @@ -1047,17 +1242,13 @@ cpcgen_common_files(int dirfd) "#endif\n" "\n" "#endif /* _CORE_PCBE_CPCGEN_H */\n") == -1) { - int e = errno; - (void) unlinkat(dirfd, tmpname, 0); - errno = e; + cpcgen_remove_tmpfile(dirfd, tmpname); errx(EXIT_FAILURE, "failed to write header to " "temporary file for %s", fname); } if (fflush(f) != 0 || fclose(f) != 0) { - int e = errno; - (void) unlinkat(dirfd, tmpname, 0); - errno = e; + cpcgen_remove_tmpfile(dirfd, tmpname); err(EXIT_FAILURE, "failed to flush and close temporary file"); } @@ -1080,16 +1271,12 @@ cpcgen_common_files(int dirfd) } if ((f = fdopen(fd, "w")) == NULL) { - int e = errno; - (void) unlinkat(dirfd, tmpname, 0); - errno = e; + cpcgen_remove_tmpfile(dirfd, tmpname); err(EXIT_FAILURE, "failed to fdopen temporary file"); } - if (fprintf(f, cpcgen_cfile_header, cpcgen_mapfile) == -1) { - int e = errno; - (void) unlinkat(dirfd, tmpname, 0); - errno = e; + if (fprintf(f, cpcgen_cfile_intel_header, cpcgen_intel_mapfile) == -1) { + cpcgen_remove_tmpfile(dirfd, tmpname); errx(EXIT_FAILURE, "failed to write header to temporary file " "for %s", fname); } @@ -1101,18 +1288,14 @@ cpcgen_common_files(int dirfd) "const struct events_table_t *\n" "core_cpcgen_table(uint_t model, uint_t stepping)\n" "{\n") == -1) { - int e = errno; - (void) unlinkat(dirfd, tmpname, 0); - errno = e; + cpcgen_remove_tmpfile(dirfd, tmpname); errx(EXIT_FAILURE, "failed to write header to " "temporary file for %s", fname); } for (map = cpcgen_maps; map != NULL; map = map->cmap_next) { if (!cpcgen_generate_map(f, map, map == cpcgen_maps)) { - int e = errno; - (void) unlinkat(dirfd, tmpname, 0); - errno = e; + cpcgen_remove_tmpfile(dirfd, tmpname); errx(EXIT_FAILURE, "failed to write to temporary " "file for %s", fname); } @@ -1122,17 +1305,13 @@ cpcgen_common_files(int dirfd) "\t\t\treturn (NULL);\n" "\t}\n" "}\n") == -1) { - int e = errno; - (void) unlinkat(dirfd, tmpname, 0); - errno = e; + cpcgen_remove_tmpfile(dirfd, tmpname); errx(EXIT_FAILURE, "failed to write header to " "temporary file for %s", fname); } if (fflush(f) != 0 || fclose(f) != 0) { - int e = errno; - (void) unlinkat(dirfd, tmpname, 0); - errno = e; + cpcgen_remove_tmpfile(dirfd, tmpname); err(EXIT_FAILURE, "failed to flush and close temporary file"); } @@ -1161,7 +1340,7 @@ cpcgen_common_files(int dirfd) * - If more than one value is specified in the EventCode or UMask values */ static boolean_t -cpcgen_skip_entry(nvlist_t *nvl, const char *path, uint_t ent) +cpcgen_skip_intel_entry(nvlist_t *nvl, const char *path, uint_t ent) { char *event, *msridx, *msrval, *taken, *offcore, *counter; char *ecode, *umask; @@ -1213,6 +1392,382 @@ cpcgen_skip_entry(nvlist_t *nvl, const char *path, uint_t ent) return (B_FALSE); } +static char * +cpcgen_manual_amd_name(cpc_map_t *map) +{ + char *name; + + if (asprintf(&name, "amd_%s_events.3cpc", map->cmap_name) == -1) { + warn("failed to assemble file name for %s", map->cmap_path); + return (NULL); + } + + return (name); +} + +static boolean_t +cpcgen_manual_amd_file_before(FILE *f, cpc_map_t *map) +{ + size_t i; + char *upper; + const char *family; + + if ((upper = strdup(map->cmap_name)) == NULL) { + warn("failed to duplicate manual name for %s", map->cmap_name); + return (B_FALSE); + } + + for (i = 0; upper[i] != '\0'; i++) { + upper[i] = toupper(upper[i]); + } + + family = map->cmap_name + 1; + + if (fprintf(f, cpcgen_manual_amd_header, map->cmap_path, upper, + family, family, family) == -1) { + warn("failed to write out manual header for %s", + map->cmap_name); + free(upper); + return (B_FALSE); + } + + free(upper); + return (B_TRUE); +} + +static boolean_t +cpcgen_manual_amd_file_after(FILE *f, cpc_map_t *map) +{ + if (fprintf(f, cpcgen_manual_amd_trailer) == -1) { + warn("failed to write out manual header for %s", + map->cmap_name); + return (B_FALSE); + } + + return (B_TRUE); +} + +static boolean_t +cpcgen_manual_amd_event(FILE *f, nvlist_t *nvl, const char *path, uint32_t ent) +{ + char *name, *mnemonic = NULL, *summary = NULL, *desc = NULL; + char *umode; + nvlist_t *units = NULL; + uint32_t i, length; + + if (nvlist_lookup_string(nvl, "name", &name) != 0) { + warnx("Found event without 'name' property in %s, entry %u", + path, ent); + return (B_FALSE); + } + + if (nvlist_lookup_string(nvl, "mnemonic", &mnemonic) != 0 || + nvlist_lookup_string(nvl, "summary", &summary) != 0) { + warnx("event %s in %s, entry %u, missing required fields", + name, path, ent); + return (B_FALSE); + } + + /* + * Allow the other fields to be missing. + */ + (void) nvlist_lookup_string(nvl, "description", &desc); + (void) nvlist_lookup_nvlist(nvl, "units", &units); + + if (fprintf(f, ".It Sy %s\n", name) == -1) { + warn("failed to write out event entry %s", name); + } + + if (fprintf(f, ".Sy %s -\n" + "%s\n", mnemonic, summary) == -1) { + warn("failed to write out event entry %s", name); + return (B_FALSE); + } + + if (desc != NULL) { + if (fprintf(f, ".Pp\n%s\n", desc) == -1) { + warn("failed to write out event entry %s", name); + return (B_FALSE); + } + } + + if (units == NULL) + return (B_TRUE); + + /* + * Skip units we don't know how to handle. + */ + if (nvlist_lookup_string(nvl, "unit_mode", &umode) == 0) { + return (B_TRUE); + } + + if (fprintf(f, ".Pp\n" + "This event has the following units which may be used\n" + "to modify the behavior of the event:\n" + ".Bl -tag -width Sy\n") == -1) { + warn("failed to write out event entry %s", name); + return (B_FALSE); + } + + if (nvlist_lookup_uint32(units, "length", &length) != 0) { + warnx("found units array, but could not look up length " + "property for events %s (index %u) in file %s", + name, ent, path); + return (B_FALSE); + } + + for (i = 0; i < length; i++) { + nvlist_t *uvl; + char num[64]; + char *uname, *udesc = NULL; + + (void) snprintf(num, sizeof (num), "%u", i); + if (nvlist_lookup_nvlist(units, num, &uvl) != 0) { + warnx("failed to look up unit %u for event %s (index " + "%u) in file %s", i, name, ent, path); + return (B_FALSE); + } + + if (nvlist_lookup_string(uvl, "name", &uname) != 0) { + warnx("failed to find required members for unit array " + "entry %u of event %s (index %u) from file %s", + i, name, ent, path); + return (B_FALSE); + } + (void) nvlist_lookup_string(uvl, "description", &udesc); + if (fprintf(f, ".It Sy %s\n", uname) == -1) { + warn("failed to write out event entry %s", name); + return (B_FALSE); + } + + if (udesc != NULL) { + if (fprintf(f, "%s\n", udesc) == -1) { + warn("failed to write out event entry %s", + name); + return (B_FALSE); + } + } + } + + if (fprintf(f, ".El\n") == -1) { + warn("failed to write out event entry %s", + name); + return (B_FALSE); + } + + return (B_TRUE); +} + +static char * +cpcgen_cfile_amd_name(cpc_map_t *map) +{ + char *name; + + if (asprintf(&name, "opteron_pcbe_%s.c", map->cmap_name) == -1) { + warn("failed to assemble file name for %s", map->cmap_path); + return (NULL); + } + + return (name); +} + +/* + * Generate a header file that can be used to synthesize the data events we care + * about. + */ +static void +cpcgen_common_amd_files(int dirfd) +{ + const char *fname = "opteron_pcbe_cpcgen.h"; + char *tmpname; + int fd; + FILE *f; + cpc_map_t *map; + + if (asprintf(&tmpname, ".%s.%d", fname, getpid()) == -1) { + err(EXIT_FAILURE, "failed to construct temporary file name"); + } + + if ((fd = openat(dirfd, tmpname, O_RDWR | O_CREAT, 0644)) < 0) { + err(EXIT_FAILURE, "failed to create temporary file %s", + tmpname); + } + + if ((f = fdopen(fd, "w")) == NULL) { + cpcgen_remove_tmpfile(dirfd, tmpname); + err(EXIT_FAILURE, "failed to fdopen temporary file"); + } + + if (fprintf(f, cpcgen_cfile_cddl_header) == -1) { + cpcgen_remove_tmpfile(dirfd, tmpname); + err(EXIT_FAILURE, "failed to write header to " + "temporary file for %s", fname); + } + + if (fprintf(f, "#ifndef _OPTERON_PCBE_CPCGEN_H\n" + "#define\t_OPTERON_PCBE_CPCGEN_H\n" + "\n" + "#ifdef __cplusplus\n" + "extern \"C\" {\n" + "#endif\n" + "\n") == -1) { + cpcgen_remove_tmpfile(dirfd, tmpname); + err(EXIT_FAILURE, "failed to write header to " + "temporary file for %s", fname); + } + + for (map = cpcgen_maps; map != NULL; map = map->cmap_next) { + if (fprintf(f, "extern const amd_event_t " + "opteron_pcbe_%s_events[];\n", map->cmap_name) == -1) { + cpcgen_remove_tmpfile(dirfd, tmpname); + err(EXIT_FAILURE, "failed to write header to " + "temporary file for %s", fname); + } + } + + if (fprintf(f, "\n" + "#ifdef __cplusplus\n" + "}\n" + "#endif\n" + "\n" + "#endif /* _OPTERON_PCBE_CPCGEN_H */\n") == -1) { + cpcgen_remove_tmpfile(dirfd, tmpname); + err(EXIT_FAILURE, "failed to write header to " + "temporary file for %s", fname); + } + + + + if (fflush(f) != 0 || fclose(f) != 0) { + cpcgen_remove_tmpfile(dirfd, tmpname); + err(EXIT_FAILURE, "failed to flush and close temporary file"); + } + + if (renameat(dirfd, tmpname, dirfd, fname) != 0) { + err(EXIT_FAILURE, "failed to rename temporary file %s", + tmpname); + } + + free(tmpname); +} + +static boolean_t +cpcgen_cfile_amd_before(FILE *f, cpc_map_t *map) +{ + if (fprintf(f, cpcgen_cfile_amd_header, map->cmap_name) == -1) { + warn("failed to write header to temporary file for %s", + map->cmap_path); + return (B_FALSE); + } + + if (fprintf(f, cpcgen_cfile_amd_table_start, map->cmap_name) == -1) { + warn("failed to write header to temporary file for %s", + map->cmap_path); + return (B_FALSE); + } + + + return (B_TRUE); +} + +static boolean_t +cpcgen_cfile_amd_after(FILE *f, cpc_map_t *map) +{ + if (fprintf(f, cpcgen_cfile_amd_table_end) == -1) { + warn("failed to write footer to temporary file for %s", + map->cmap_path); + return (B_FALSE); + } + + return (B_TRUE); +} + +static boolean_t +cpcgen_cfile_amd_event(FILE *f, nvlist_t *nvl, const char *path, uint_t ent) +{ + char *name, *code, *umode; + uint32_t i, length; + nvlist_t *units; + + if (nvlist_lookup_string(nvl, "name", &name) != 0) { + warnx("Found event without 'name' property in %s, entry %u", + path, ent); + return (B_FALSE); + } + + if (nvlist_lookup_string(nvl, "code", &code) != 0) { + warnx("event %s (index %u) from %s missing required properties " + "for C translation", name, path, ent); + return (B_FALSE); + } + + if (fprintf(f, "\t{ \"%s\", %s, 0 },\n", name, code) == -1) { + warn("failed to write out entry %s from %s", name, path); + return (B_FALSE); + } + + /* + * The 'units' array is optional. If the rule has a specific 'unit_mode' + * indicating how the units should be combined, skip that. We don't know + * how to properly process that right now. + */ + if (nvlist_lookup_nvlist(nvl, "units", &units) != 0) { + return (B_TRUE); + } + + if (nvlist_lookup_string(nvl, "unit_mode", &umode) == 0) { + return (B_TRUE); + } + + if (nvlist_lookup_uint32(units, "length", &length) != 0) { + warnx("found units array, but could not look up length " + "property for events %s (index %u) in file %s", + name, ent, path); + return (B_FALSE); + } + + for (i = 0; i < length; i++) { + nvlist_t *uvl; + char num[64]; + char *uname, *urw; + int32_t bit; + + (void) snprintf(num, sizeof (num), "%u", i); + if (nvlist_lookup_nvlist(units, num, &uvl) != 0) { + warnx("failed to look up unit %u for event %s (index " + "%u) in file %s", i, name, ent, path); + return (B_FALSE); + } + + if (nvlist_lookup_string(uvl, "name", &uname) != 0 || + nvlist_lookup_string(uvl, "rw", &urw) != 0 || + nvlist_lookup_int32(uvl, "bit", &bit) != 0) { + warnx("failed to find required members for unit array " + "entry %u of event %s (index %u) from file %s", + i, name, ent, path); + dump_nvlist(uvl, 0); + return (B_FALSE); + } + + if (bit < 0 || bit > 31) { + warnx("event %s (index %u) from file %s has invalid " + "bit value: %d; skipping", name, ent, path, bit); + continue; + } + + if (strcasecmp(urw, "Read-write") != 0) + continue; + + if (fprintf(f, "\t{ \"%s.%s\", %s, 0x%x },\n", name, uname, + code, 1U << bit) == -1) { + warn("failed to write out entry %s from %s", name, + path); + return (B_FALSE); + } + } + + return (B_TRUE); +} /* * For each processor family, generate a data file that contains all of the @@ -1249,14 +1804,12 @@ cpcgen_gen(int dirfd) } if ((f = fdopen(fd, "w")) == NULL) { - int e = errno; - (void) unlinkat(dirfd, tmpname, 0); - errno = e; + cpcgen_remove_tmpfile(dirfd, tmpname); err(EXIT_FAILURE, "failed to fdopen temporary file"); } if (!cpcgen_ops.cgen_op_file_before(f, map)) { - (void) unlinkat(dirfd, tmpname, 0); + cpcgen_remove_tmpfile(dirfd, tmpname); exit(EXIT_FAILURE); } @@ -1277,30 +1830,31 @@ cpcgen_gen(int dirfd) (void) snprintf(num, sizeof (num), "%u", i); if ((ret = nvlist_lookup_nvlist(map->cmap_data, num, &nvl)) != 0) { + cpcgen_remove_tmpfile(dirfd, tmpname); errx(EXIT_FAILURE, "failed to look up array " "entry %u in parsed data for %s: %s", i, map->cmap_path, strerror(ret)); } - if (cpcgen_skip_entry(nvl, map->cmap_path, i)) + if (cpcgen_ops.cgen_op_skip != NULL && + cpcgen_ops.cgen_op_skip(nvl, map->cmap_path, i)) { continue; + } if (!cpcgen_ops.cgen_op_event(f, nvl, map->cmap_path, i)) { - (void) unlinkat(dirfd, tmpname, 0); + cpcgen_remove_tmpfile(dirfd, tmpname); exit(EXIT_FAILURE); } } if (!cpcgen_ops.cgen_op_file_after(f, map)) { - (void) unlinkat(dirfd, tmpname, 0); + cpcgen_remove_tmpfile(dirfd, tmpname); exit(EXIT_FAILURE); } if (fflush(f) != 0 || fclose(f) != 0) { - int e = errno; - (void) unlinkat(dirfd, tmpname, 0); - errno = e; + cpcgen_remove_tmpfile(dirfd, tmpname); err(EXIT_FAILURE, "failed to flush and close " "temporary file"); } @@ -1333,9 +1887,9 @@ cpcgen_usage(const char *fmt, ...) "\t-a generate data for all platforms\n" "\t-c generate C file for CPC\n" "\t-d specify the directory containt perfmon data\n" - "\t-h generate header file and common files\n" + "\t-H generate header file and common files\n" "\t-m generate manual pages for CPC data\n" - "\t-o outut files in directory outdir\n" + "\t-o output files in directory outdir\n" "\t-p generate data for a specified platform\n", cpcgen_progname); } @@ -1416,7 +1970,6 @@ main(int argc, char *argv[]) return (2); } - if (outdir == NULL) { cpcgen_usage("Missing required output directory (-o)\n"); return (2); @@ -1431,28 +1984,62 @@ main(int argc, char *argv[]) return (2); } - cpcgen_read_mapfile(datadir, platform); + cpcgen_determine_vendor(datadir); + + switch (cpcgen_mode) { + case CPCGEN_MODE_INTEL: + cpcgen_ops.cgen_op_gather = cpcgen_read_intel; + cpcgen_ops.cgen_op_common = cpcgen_common_intel_files; + cpcgen_ops.cgen_op_skip = cpcgen_skip_intel_entry; + if (do_mpage) { + cpcgen_ops.cgen_op_name = cpcgen_manual_intel_name; + cpcgen_ops.cgen_op_file_before = + cpcgen_manual_intel_file_before; + cpcgen_ops.cgen_op_file_after = + cpcgen_manual_intel_file_after; + cpcgen_ops.cgen_op_event = cpcgen_manual_intel_event; + } else { + cpcgen_ops.cgen_op_name = cpcgen_cfile_intel_name; + cpcgen_ops.cgen_op_file_before = + cpcgen_cfile_intel_before; + cpcgen_ops.cgen_op_file_after = + cpcgen_cfile_intel_after; + cpcgen_ops.cgen_op_event = cpcgen_cfile_intel_event; + } + break; + case CPCGEN_MODE_AMD: + cpcgen_ops.cgen_op_gather = cpcgen_read_amd; + cpcgen_ops.cgen_op_common = cpcgen_common_amd_files; + cpcgen_ops.cgen_op_skip = NULL; + if (do_mpage) { + cpcgen_ops.cgen_op_name = cpcgen_manual_amd_name; + cpcgen_ops.cgen_op_file_before = + cpcgen_manual_amd_file_before; + cpcgen_ops.cgen_op_file_after = + cpcgen_manual_amd_file_after; + cpcgen_ops.cgen_op_event = cpcgen_manual_amd_event; + } else { + cpcgen_ops.cgen_op_name = cpcgen_cfile_amd_name; + cpcgen_ops.cgen_op_file_before = + cpcgen_cfile_amd_before; + cpcgen_ops.cgen_op_file_after = cpcgen_cfile_amd_after; + cpcgen_ops.cgen_op_event = cpcgen_cfile_amd_event; - if (do_header) { - cpcgen_common_files(outdirfd); - return (0); + } + break; + default: + errx(EXIT_FAILURE, "failed to determine if operating on AMD or " + "Intel"); + break; } - if (do_mpage) { - cpcgen_ops.cgen_op_name = cpcgen_manual_name; - cpcgen_ops.cgen_op_file_before = cpcgen_manual_file_before; - cpcgen_ops.cgen_op_file_after = cpcgen_manual_file_after; - cpcgen_ops.cgen_op_event = cpcgen_manual_event; - } + cpcgen_ops.cgen_op_gather(datadir, platform); - if (do_cfile) { - cpcgen_ops.cgen_op_name = cpcgen_cfile_name; - cpcgen_ops.cgen_op_file_before = cpcgen_cfile_file_before; - cpcgen_ops.cgen_op_file_after = cpcgen_cfile_file_after; - cpcgen_ops.cgen_op_event = cpcgen_cfile_event; + if (do_header) { + cpcgen_ops.cgen_op_common(outdirfd); + return (0); } - cpcgen_gen(outdirfd); return (0); |