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" }, <snip> { 0x55, 0x5, 1, "cascadelakex_core_v1.00.json" }, { 0x55, 0x6, 1, "cascadelakex_core_v1.00.json" }, { 0x55, 0x7, 1, "cascadelakex_core_v1.00.json" }, <snip> Change-Id: Ie0e8a7e851799e9d060b966047745039c066ec7b Signed-off-by: Dave Barach <dave@barachs.net>
This commit is contained in:
committed by
Florin Coras
parent
82b22f75b9
commit
bef3619d25
@@ -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)
|
||||
|
||||
241
src/plugins/perfmon/mapfile_tool.c
Normal file
241
src/plugins/perfmon/mapfile_tool.c
Normal file
@@ -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 <stdio.h>
|
||||
#include <vppinfra/format.h>
|
||||
#include <vppinfra/error.h>
|
||||
#include <vppinfra/unix.h>
|
||||
|
||||
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 <ifile> out <ofile>\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:
|
||||
*/
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user