From bef3619d259b43f4bf4bfd43db1338ddb10a8c39 Mon Sep 17 00:00:00 2001 From: Dave Barach Date: Mon, 17 Dec 2018 15:55:52 -0500 Subject: [PATCH] Improve perfmon json table picker Built a tool to chew up https://download.01.org/perfmon/mapfile.csv, and output a table in this format: typedef struct { u8 model; u8 stepping; u8 has_stepping; char *filename; } file_by_model_and_stepping_t; static const file_by_model_and_stepping_t fms_table [] = { /* model, stepping, stepping valid, file */ { 0x2E, 0x0, 0, "NehalemEX_core_V2.json" }, { 0x1E, 0x0, 0, "NehalemEP_core_V2.json" }, { 0x55, 0x5, 1, "cascadelakex_core_v1.00.json" }, { 0x55, 0x6, 1, "cascadelakex_core_v1.00.json" }, { 0x55, 0x7, 1, "cascadelakex_core_v1.00.json" }, Change-Id: Ie0e8a7e851799e9d060b966047745039c066ec7b Signed-off-by: Dave Barach --- src/plugins/perfmon/CMakeLists.txt | 8 ++ src/plugins/perfmon/mapfile_tool.c | 241 +++++++++++++++++++++++++++++++++++++ src/plugins/perfmon/parse_util.c | 3 +- src/plugins/perfmon/perfmon.c | 112 +++++++++++++---- 4 files changed, 341 insertions(+), 23 deletions(-) create mode 100644 src/plugins/perfmon/mapfile_tool.c diff --git a/src/plugins/perfmon/CMakeLists.txt b/src/plugins/perfmon/CMakeLists.txt index 845dd3cc6c8..340c5608f33 100644 --- a/src/plugins/perfmon/CMakeLists.txt +++ b/src/plugins/perfmon/CMakeLists.txt @@ -23,3 +23,11 @@ install( DESTINATION share/vpp/plugins/perfmon COMPONENT vpp-dev ) + +option(VPP_BUILD_MAPFILE_TOOL "Build perfmon mapfile utility." OFF) +if(VPP_BUILD_MAPFILE_TOOL) + add_vpp_executable(mapfile_tool + SOURCES mapfile_tool.c + LINK_LIBRARIES vppinfra Threads::Threads + ) +endif(VPP_BUILD_MAPFILE_TOOL) diff --git a/src/plugins/perfmon/mapfile_tool.c b/src/plugins/perfmon/mapfile_tool.c new file mode 100644 index 00000000000..750e12b4970 --- /dev/null +++ b/src/plugins/perfmon/mapfile_tool.c @@ -0,0 +1,241 @@ +/* + * mapfile_tool.c - skeleton vpp engine plug-in + * + * Copyright (c) 2018 Cisco Systems and/or affiliates + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +typedef struct +{ + u8 *ifile; + u8 *ofile; + u8 *mapfile; + u8 *table; + FILE *ofp; +} mapfile_tool_main_t; + +mapfile_tool_main_t mapfile_tool_main; + +static char *top_boilerplate = + "typedef struct {\n" + " u8 model;\n" + " u8 stepping;\n" + " u8 has_stepping;\n" + " char *filename;\n" + "} file_by_model_and_stepping_t;\n\n" + "static const file_by_model_and_stepping_t fms_table [] =\n" + "{\n" " /* model, stepping, stepping valid, file */\n"; + +static char *bottom_boilerplate = "};\n"; + +static void +print_chunk (mapfile_tool_main_t * mtm, char *chunk) +{ + fformat (mtm->ofp, "%s", chunk); +} + +static int +parse_mapfile (mapfile_tool_main_t * mtm) +{ + u8 *cp = mtm->mapfile; + int i; + char model[3]; + u8 *stepping = 0; + u8 *filename = 0; + int has_stepping; + + /* Skip header line */ + while (*cp && *cp != '\n') + cp++; + + if (*cp == 0) + { + fformat (stderr, "mapfile broken or empty\n"); + return 1; + } + /* skip newline */ + cp++; + + /* GenuineIntel-6-55-[01234],V1.12,/SKX/skylakex_uncore_v1.12.json,uncore */ + /* skip 15 ^ */ + + /* Across payload lines... */ + while (1) + { + if (*cp == 0) + return 0; + + for (i = 0; i < 15; i++) + { + if (*cp == 0) + { + bad: + fformat (stderr, "mapfile broken\n"); + return 1; + } + cp++; + } + /* should point at model */ + model[0] = *cp++; + model[1] = *cp++; + model[2] = 0; + vec_reset_length (stepping); + /* Stepping significant? */ + if (*cp == '-') + { + cp += 2; + while (*cp != ']') + { + vec_add1 (stepping, *cp); + cp++; + } + cp++; + } + /* Skip dirname */ + while (*cp != '/') + cp++; + cp++; + while (*cp != '/') + *cp++; + cp++; + vec_reset_length (filename); + while (*cp != ',') + { + vec_add1 (filename, *cp); + cp++; + } + + cp++; + /* We only want ",core" entries */ + if (memcmp (cp, "core", 4)) + { + while (*cp && *cp != '\n') + cp++; + if (*cp) + cp++; + continue; + } + + /* Skip to start of next line */ + while (*cp && *cp != '\n') + cp++; + if (*cp) + cp++; + + has_stepping = 1; + + if (vec_len (stepping) == 0) + { + vec_add1 (stepping, '0'); + has_stepping = 0; + } + + for (i = 0; i < vec_len (stepping); i++) + { + mtm->table = + format (mtm->table, " { 0x%s, 0x%c, %d, \"%v\" },\n", + model, stepping[i], has_stepping, filename); + } + } + + /* NOTREACHED */ + return -11; +} + +static int +mapfile_main (unformat_input_t * input, mapfile_tool_main_t * mtm) +{ + u8 *mapfile; + int rv; + clib_error_t *error; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "in %s", &mtm->ifile)) + ; + else if (unformat (input, "out %s", &mtm->ofile)) + ; + else + { + fformat (stderr, "unknown input '%U'\n", format_unformat_error, + input); + usage: + fformat (stderr, "usage: mapfile_tool in out \n"); + return 1; + } + } + + if (mtm->ifile == 0) + { + fformat (stderr, "input file not specified\n"); + goto usage; + } + + if (mtm->ofile == 0) + mtm->ofile = format (0, "perfmon_version.c%c", 0); + + mtm->ofp = fopen ((char *) mtm->ofile, "w"); + if (mtm->ofp == NULL) + { + fformat (stderr, "Couldn't create '%s'\n", mtm->ofile); + return 1; + } + + error = unix_proc_file_contents ((char *) mtm->ifile, &mapfile); + + if (error) + { + clib_error_free (error); + fformat (stderr, "Failed to read mapfile from %s", mtm->ifile); + return 1; + } + + mtm->mapfile = mapfile; + + rv = parse_mapfile (mtm); + if (rv) + return rv; + + print_chunk (mtm, top_boilerplate); + print_chunk (mtm, (char *) mtm->table); + print_chunk (mtm, bottom_boilerplate); + return 0; +} + +int +main (int argc, char *argv[]) +{ + unformat_input_t input; + mapfile_tool_main_t *mtm = &mapfile_tool_main; + int r; + + clib_mem_init (0, 128 << 20); + + unformat_init_command_line (&input, argv); + r = mapfile_main (&input, mtm); + unformat_free (&input); + return r; +} + + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/perfmon/parse_util.c b/src/plugins/perfmon/parse_util.c index 6abb645a714..8c9e9be59f5 100644 --- a/src/plugins/perfmon/parse_util.c +++ b/src/plugins/perfmon/parse_util.c @@ -69,7 +69,8 @@ perfmon_parse_table (perfmon_main_t * pm, char *path, char *table_name) if (error) { vlib_log_err (pm->log_class, - "Failed to read CPU-specific counter table"); + "Failed to read CPU-specific counter table %s", + json_filename); vlib_log_err (pm->log_class, "Please install the vpp-dev package and then:"); vlib_log_err diff --git a/src/plugins/perfmon/perfmon.c b/src/plugins/perfmon/perfmon.c index 5bc3b73bf54..c6a80224e0e 100644 --- a/src/plugins/perfmon/perfmon.c +++ b/src/plugins/perfmon/perfmon.c @@ -28,6 +28,72 @@ perfmon_main_t perfmon_main; static char *perfmon_json_path = "/usr/share/vpp/plugins/perfmon"; +typedef struct +{ + u8 model; + u8 stepping; + u8 has_stepping; + char *filename; +} file_by_model_and_stepping_t; + +/* Created by parsing mapfile.csv, see mapfile_tool.c */ + +static const file_by_model_and_stepping_t fms_table[] = { + /* model, stepping, stepping valid, file */ + {0x2E, 0x0, 0, "NehalemEX_core_V2.json"}, + {0x1E, 0x0, 0, "NehalemEP_core_V2.json"}, + {0x1F, 0x0, 0, "NehalemEP_core_V2.json"}, + {0x1A, 0x0, 0, "NehalemEP_core_V2.json"}, + {0x2F, 0x0, 0, "WestmereEX_core_V2.json"}, + {0x25, 0x0, 0, "WestmereEP-SP_core_V2.json"}, + {0x2C, 0x0, 0, "WestmereEP-DP_core_V2.json"}, + {0x37, 0x0, 0, "Silvermont_core_V14.json"}, + {0x4D, 0x0, 0, "Silvermont_core_V14.json"}, + {0x4C, 0x0, 0, "Silvermont_core_V14.json"}, + {0x5C, 0x0, 0, "goldmont_core_v13.json"}, + {0x5F, 0x0, 0, "goldmont_core_v13.json"}, + {0x1C, 0x0, 0, "Bonnell_core_V4.json"}, + {0x26, 0x0, 0, "Bonnell_core_V4.json"}, + {0x27, 0x0, 0, "Bonnell_core_V4.json"}, + {0x36, 0x0, 0, "Bonnell_core_V4.json"}, + {0x35, 0x0, 0, "Bonnell_core_V4.json"}, + {0x2A, 0x0, 0, "sandybridge_core_v16.json"}, + {0x2D, 0x0, 0, "Jaketown_core_V20.json"}, + {0x3A, 0x0, 0, "ivybridge_core_v21.json"}, + {0x3E, 0x0, 0, "ivytown_core_v20.json"}, + {0x3C, 0x0, 0, "haswell_core_v28.json"}, + {0x45, 0x0, 0, "haswell_core_v28.json"}, + {0x46, 0x0, 0, "haswell_core_v28.json"}, + {0x3F, 0x0, 0, "haswellx_core_v20.json"}, + {0x3D, 0x0, 0, "broadwell_core_v23.json"}, + {0x47, 0x0, 0, "broadwell_core_v23.json"}, + {0x4F, 0x0, 0, "broadwellx_core_v14.json"}, + {0x56, 0x0, 0, "broadwellde_core_v7.json"}, + {0x4E, 0x0, 0, "skylake_core_v42.json"}, + {0x5E, 0x0, 0, "skylake_core_v42.json"}, + {0x8E, 0x0, 0, "skylake_core_v42.json"}, + {0x9E, 0x0, 0, "skylake_core_v42.json"}, + {0x57, 0x0, 0, "KnightsLanding_core_V9.json"}, + {0x85, 0x0, 0, "KnightsLanding_core_V9.json"}, + {0x55, 0x0, 1, "skylakex_core_v1.12.json"}, + {0x55, 0x1, 1, "skylakex_core_v1.12.json"}, + {0x55, 0x2, 1, "skylakex_core_v1.12.json"}, + {0x55, 0x3, 1, "skylakex_core_v1.12.json"}, + {0x55, 0x4, 1, "skylakex_core_v1.12.json"}, + {0x55, 0x5, 1, "cascadelakex_core_v1.00.json"}, + {0x55, 0x6, 1, "cascadelakex_core_v1.00.json"}, + {0x55, 0x7, 1, "cascadelakex_core_v1.00.json"}, + {0x55, 0x8, 1, "cascadelakex_core_v1.00.json"}, + {0x55, 0x9, 1, "cascadelakex_core_v1.00.json"}, + {0x55, 0xA, 1, "cascadelakex_core_v1.00.json"}, + {0x55, 0xB, 1, "cascadelakex_core_v1.00.json"}, + {0x55, 0xC, 1, "cascadelakex_core_v1.00.json"}, + {0x55, 0xD, 1, "cascadelakex_core_v1.00.json"}, + {0x55, 0xE, 1, "cascadelakex_core_v1.00.json"}, + {0x55, 0xF, 1, "cascadelakex_core_v1.00.json"}, + {0x7A, 0x0, 0, "goldmontplus_core_v1.01.json"}, +}; + static void set_perfmon_json_path () { @@ -58,15 +124,6 @@ set_perfmon_json_path () perfmon_json_path = (char *) s; } -#define foreach_cpuid_table \ -_(0x0106E5, NehalemEP_core_V2.json) /* Intel(R) Xeon(R) CPU X3430 @ 2.40GHz */ \ -_(0x0306C3, haswell_core_v28.json) /* Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz */ \ -_(0x0306F2, haswell_core_v28.json) /* Intel(R) Xeon(R) CPU E5-2640 v3 @ 2.60GHz */ \ -_(0x040661, haswell_core_v28.json) /* Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz */ \ -_(0x0406D8, Silvermont_core_V14.json) /* Intel(R) Atom(TM) CPU C2758 @ 2.40GHz */ \ -_(0x0406E3, skylake_core_v42.json) /* Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz */ \ -_(0x0506E3, skylake_core_v42.json) /* Intel(R) Core(TM) i5-6600 CPU @ 3.30GHz */ - static inline u32 get_cpuid (void) { @@ -88,6 +145,8 @@ perfmon_init (vlib_main_t * vm) u32 cpuid; uword *ht; int found_a_table = 0; + int i; + u8 model, stepping; pm->vlib_main = vm; pm->vnet_main = vnet_get_main (); @@ -110,23 +169,32 @@ perfmon_init (vlib_main_t * vm) cpuid = get_cpuid (); - if (0) + for (i = 0; i < ARRAY_LEN (fms_table); i++) { - } -#define _(id,table) \ - else if (cpuid == id) \ - { \ - vlib_log_debug (pm->log_class, "Found table %s", #table); \ - ht = perfmon_parse_table (pm, perfmon_json_path, #table); \ - found_a_table = 1; \ - } - foreach_cpuid_table; -#undef _ + model = ((cpuid >> 12) & 0xf0) | ((cpuid >> 4) & 0xf); + stepping = cpuid & 0xf; + + if (fms_table[i].model != model) + continue; + + if (fms_table[i].has_stepping) + { + if (fms_table[i].stepping != stepping) + continue; + } + found_a_table = 1; + ht = perfmon_parse_table (pm, perfmon_json_path, fms_table[i].filename); + break; + } pm->perfmon_table = ht; - if (found_a_table == 0) - vlib_log_err (pm->log_class, "No table for cpuid %x", cpuid); + if (found_a_table == 0 || pm->perfmon_table == 0 || hash_elts (ht) == 0) + { + vlib_log_err (pm->log_class, "No table for cpuid %x", cpuid); + vlib_log_err (pm->log_class, " model %x, stepping %x", + model, stepping); + } return error; } -- 2.16.6