summaryrefslogtreecommitdiff
path: root/usr/src/tools/cpcgen/cpcgen.c
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@joyent.com>2019-04-05 17:24:09 +0000
committerRobert Mustacchi <rm@joyent.com>2019-04-23 21:20:59 +0000
commita6a0e2aee988bd529ac3621e19f33518fe40a0b9 (patch)
treedf187bedac85f81a5b56b69aa48aab9e86f7ef14 /usr/src/tools/cpcgen/cpcgen.c
parent27896fdc5eb580ff60d4bb646cf7488e1b8545b9 (diff)
downloadillumos-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.c813
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);