X86_64 perf counter plugin
Change-Id: Ie5a00c15ee9536cc61afab57f6cadc1aa1972f3c Signed-off-by: Dave Barach <dave@barachs.net>
This commit is contained in:

committed by
Damjan Marion

parent
115a3ac59a
commit
4d1a866aff
@ -28,6 +28,11 @@ do
|
||||
fi
|
||||
done
|
||||
|
||||
# Perf monitor .json files
|
||||
for i in $(find ${1}/vpp/share/vpp/plugins/perfmon -name *.json -type f -print); do
|
||||
echo ../${i} /usr/share/vpp/plugins/perfmon >> ${2}
|
||||
done
|
||||
|
||||
# sample plugin
|
||||
paths=`(cd ..; find src/examples/sample-plugin -type f -print | grep -v autom4te)`
|
||||
|
||||
|
38
src/plugins/perfmon/CMakeLists.txt
Normal file
38
src/plugins/perfmon/CMakeLists.txt
Normal file
@ -0,0 +1,38 @@
|
||||
# Copyright (c) 2018 Cisco and/or its 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.
|
||||
|
||||
add_vpp_plugin(perfmon
|
||||
SOURCES
|
||||
perfmon.c
|
||||
perfmon_periodic.c
|
||||
parse_util.c
|
||||
)
|
||||
|
||||
# Reenable / extend when .json file license issue fixed
|
||||
#
|
||||
# set (PERFMON_JSON_FILES
|
||||
# haswell_core_v28.json
|
||||
# haswellx_core_v20.json
|
||||
# ivybridge_core_v21.json
|
||||
# ivytown_core_v20.json
|
||||
# jaketown_core_v20.json
|
||||
# sandybridge_core_v16.json
|
||||
# skylake_core_v42.json
|
||||
# skylakex_core_v1.12.json
|
||||
# )
|
||||
|
||||
# install(
|
||||
# FILES ${PERFMON_JSON_FILES}
|
||||
# DESTINATION share/vpp/plugins/perfmon
|
||||
# COMPONENT vpp-dev
|
||||
# )
|
235
src/plugins/perfmon/parse_util.c
Normal file
235
src/plugins/perfmon/parse_util.c
Normal file
@ -0,0 +1,235 @@
|
||||
/*
|
||||
* parse_util.c - halfhearted json parser
|
||||
*
|
||||
* Copyright (c) 2018 Cisco Systems and/or its 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 <perfmon/perfmon.h>
|
||||
#include <vppinfra/unix.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
STATE_START,
|
||||
STATE_READ_NAME,
|
||||
STATE_READ_VALUE,
|
||||
} parse_state_t;
|
||||
|
||||
static u8 *
|
||||
downcase (u8 * s)
|
||||
{
|
||||
u8 *rv = 0;
|
||||
u8 c;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < vec_len (s); i++)
|
||||
{
|
||||
c = s[i];
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
c = c + ('a' - 'A');
|
||||
vec_add1 (rv, c);
|
||||
}
|
||||
return (rv);
|
||||
}
|
||||
|
||||
uword *
|
||||
perfmon_parse_table (perfmon_main_t * pm, char *path, char *table_name)
|
||||
{
|
||||
u8 *cp;
|
||||
u8 *event_name;
|
||||
int state = STATE_START;
|
||||
uword *ht;
|
||||
name_value_pair_t *nvp = 0;
|
||||
name_value_pair_t **nvps = 0;
|
||||
u8 *v;
|
||||
int i;
|
||||
u8 *json_filename;
|
||||
clib_error_t *error;
|
||||
|
||||
/* Create the name/value hash table in any case... */
|
||||
ht = hash_create_string (0, sizeof (uword));
|
||||
|
||||
json_filename = format (0, "%s/%s%c", path, table_name, 0);
|
||||
|
||||
vlib_log_debug (pm->log_class, "Try to read perfmon events from %s",
|
||||
json_filename);
|
||||
|
||||
error = unix_proc_file_contents ((char *) json_filename, &cp);
|
||||
|
||||
if (error)
|
||||
{
|
||||
vlib_log_err (pm->log_class,
|
||||
"Failed to read CPU-specific counter table");
|
||||
vlib_log_err (pm->log_class,
|
||||
"Download from https://download.01.org/perfmon, "
|
||||
"and install as %s", json_filename);
|
||||
vec_free (json_filename);
|
||||
clib_error_report (error);
|
||||
return ht;
|
||||
}
|
||||
vlib_log_debug (pm->log_class, "Read OK, parse the event table...");
|
||||
vec_free (json_filename);
|
||||
|
||||
again:
|
||||
while (*cp)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case STATE_START:
|
||||
while (*cp && *cp != '{' && *cp != '}' && *cp != ',')
|
||||
cp++;
|
||||
if (*cp == 0)
|
||||
goto done;
|
||||
|
||||
/* Look for a new event */
|
||||
if (*cp == '{')
|
||||
{
|
||||
if (*cp == 0)
|
||||
{
|
||||
error:
|
||||
clib_warning ("parse fail");
|
||||
hash_free (ht);
|
||||
return 0;
|
||||
}
|
||||
cp++;
|
||||
state = STATE_READ_NAME;
|
||||
goto again;
|
||||
}
|
||||
else if (*cp == '}') /* end of event */
|
||||
{
|
||||
/* Look for the "EventName" nvp */
|
||||
for (i = 0; i < vec_len (nvps); i++)
|
||||
{
|
||||
nvp = nvps[i];
|
||||
if (!strncmp ((char *) nvp->name, "EventName", 9))
|
||||
{
|
||||
event_name = nvp->value;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
/* no name? */
|
||||
for (i = 0; i < vec_len (nvps); i++)
|
||||
{
|
||||
vec_free (nvps[i]->name);
|
||||
vec_free (nvps[i]->value);
|
||||
}
|
||||
vec_free (nvps);
|
||||
cp++;
|
||||
goto again;
|
||||
|
||||
found:
|
||||
event_name = downcase (event_name);
|
||||
hash_set_mem (ht, event_name, nvps);
|
||||
nvp = 0;
|
||||
nvps = 0;
|
||||
cp++;
|
||||
goto again;
|
||||
}
|
||||
else if (*cp == ',') /* punctuation */
|
||||
{
|
||||
cp++;
|
||||
goto again;
|
||||
}
|
||||
|
||||
case STATE_READ_NAME:
|
||||
vec_validate (nvp, 0);
|
||||
v = 0;
|
||||
while (*cp && *cp != '"')
|
||||
cp++;
|
||||
|
||||
if (*cp == 0)
|
||||
{
|
||||
vec_free (nvp);
|
||||
goto error;
|
||||
}
|
||||
|
||||
cp++;
|
||||
while (*cp && *cp != '"')
|
||||
{
|
||||
vec_add1 (v, *cp);
|
||||
cp++;
|
||||
}
|
||||
if (*cp == 0)
|
||||
{
|
||||
vec_free (v);
|
||||
goto error;
|
||||
}
|
||||
cp++;
|
||||
vec_add1 (v, 0);
|
||||
nvp->name = v;
|
||||
state = STATE_READ_VALUE;
|
||||
goto again;
|
||||
|
||||
case STATE_READ_VALUE:
|
||||
while (*cp && *cp != ':')
|
||||
cp++;
|
||||
if (*cp == 0)
|
||||
{
|
||||
vec_free (nvp->name);
|
||||
goto error;
|
||||
}
|
||||
while (*cp && *cp != '"')
|
||||
cp++;
|
||||
if (*cp == 0)
|
||||
{
|
||||
vec_free (nvp->name);
|
||||
goto error;
|
||||
}
|
||||
else
|
||||
cp++;
|
||||
v = 0;
|
||||
while (*cp && *cp != '"')
|
||||
{
|
||||
vec_add1 (v, *cp);
|
||||
cp++;
|
||||
}
|
||||
if (*cp == 0)
|
||||
{
|
||||
vec_free (nvp->name);
|
||||
vec_free (v);
|
||||
goto error;
|
||||
}
|
||||
vec_add1 (v, 0);
|
||||
nvp->value = v;
|
||||
vec_add1 (nvps, nvp);
|
||||
while (*cp && *cp != ',' && *cp != '}')
|
||||
cp++;
|
||||
if (*cp == 0)
|
||||
{
|
||||
vec_free (nvp->name);
|
||||
vec_free (nvp->value);
|
||||
goto error;
|
||||
}
|
||||
else if (*cp == '}')
|
||||
state = STATE_START;
|
||||
else
|
||||
{
|
||||
cp++;
|
||||
state = STATE_READ_NAME;
|
||||
}
|
||||
nvp = 0;
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
return (ht);
|
||||
}
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
* Local Variables:
|
||||
* eval: (c-set-style "gnu")
|
||||
* End:
|
||||
*/
|
615
src/plugins/perfmon/perfmon.c
Normal file
615
src/plugins/perfmon/perfmon.c
Normal file
File diff suppressed because it is too large
Load Diff
145
src/plugins/perfmon/perfmon.h
Normal file
145
src/plugins/perfmon/perfmon.h
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* perfmon.h - performance monitor
|
||||
*
|
||||
* Copyright (c) 2018 Cisco Systems and/or its 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.
|
||||
*/
|
||||
#ifndef __included_perfmon_h__
|
||||
#define __included_perfmon_h__
|
||||
|
||||
#include <vnet/vnet.h>
|
||||
#include <vnet/ip/ip.h>
|
||||
#include <vnet/ethernet/ethernet.h>
|
||||
#include <vlib/log.h>
|
||||
|
||||
#include <vppinfra/hash.h>
|
||||
#include <vppinfra/error.h>
|
||||
|
||||
#include <linux/perf_event.h>
|
||||
|
||||
#define foreach_perfmon_event \
|
||||
_(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES, "cpu-cycles") \
|
||||
_(PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS, "instructions") \
|
||||
_(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES, \
|
||||
"cache-references") \
|
||||
_(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES, "cache-misses") \
|
||||
_(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS, "branches") \
|
||||
_(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES, "branch-misses") \
|
||||
_(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BUS_CYCLES, "bus-cycles") \
|
||||
_(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND, \
|
||||
"stall-frontend") \
|
||||
_(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND, \
|
||||
"stall-backend") \
|
||||
_(PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES, "ref-cpu-cycles") \
|
||||
_(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS, "page-faults") \
|
||||
_(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES, "context-switches") \
|
||||
_(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS, "cpu-migrations") \
|
||||
_(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MIN, "minor-pagefaults") \
|
||||
_(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MAJ, "major-pagefaults") \
|
||||
_(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS, "emulation-faults")
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *name;
|
||||
int pe_type;
|
||||
int pe_config;
|
||||
} perfmon_event_config_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PERFMON_STATE_OFF = 0,
|
||||
PERFMON_STATE_RUNNING,
|
||||
} perfmon_state_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 *thread_and_node_name;
|
||||
u8 **counter_names;
|
||||
u64 *counter_values;
|
||||
u64 *vectors_this_counter;
|
||||
} perfmon_capture_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 cpuid;
|
||||
const char **table;
|
||||
} perfmon_cpuid_and_table_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 *name;
|
||||
u8 *value;
|
||||
} name_value_pair_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* API message ID base */
|
||||
u16 msg_id_base;
|
||||
|
||||
/* on/off switch for the periodic function */
|
||||
volatile u8 state;
|
||||
|
||||
/* capture pool, hash table */
|
||||
perfmon_capture_t *capture_pool;
|
||||
uword *capture_by_thread_and_node_name;
|
||||
|
||||
/* CPU-specific event tables, hash table of selected table (if any) */
|
||||
perfmon_cpuid_and_table_t *perfmon_tables;
|
||||
uword *perfmon_table;
|
||||
|
||||
/* vector of events to collect */
|
||||
perfmon_event_config_t *events_to_collect;
|
||||
|
||||
/* Base indices of synthetic event tuples */
|
||||
u32 ipc_event_index;
|
||||
u32 mispredict_event_index;
|
||||
|
||||
/* Length of time to capture a single event */
|
||||
f64 timeout_interval;
|
||||
|
||||
/* Current event (index) being collected */
|
||||
u32 current_event;
|
||||
u32 *rdpmc_indices;
|
||||
/* mmap base / size of (mapped) struct perf_event_mmap_page */
|
||||
u8 **perf_event_pages;
|
||||
u32 page_size;
|
||||
|
||||
/* Current perf_event file descriptors, per thread */
|
||||
int *pm_fds;
|
||||
|
||||
/* Logging */
|
||||
vlib_log_class_t log_class;
|
||||
|
||||
/* convenience */
|
||||
vlib_main_t *vlib_main;
|
||||
vnet_main_t *vnet_main;
|
||||
ethernet_main_t *ethernet_main;
|
||||
} perfmon_main_t;
|
||||
|
||||
extern perfmon_main_t perfmon_main;
|
||||
|
||||
extern vlib_node_registration_t perfmon_periodic_node;
|
||||
uword *perfmon_parse_table (perfmon_main_t * pm, char *path, char *filename);
|
||||
|
||||
/* Periodic function events */
|
||||
#define PERFMON_START 1
|
||||
|
||||
#endif /* __included_perfmon_h__ */
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
* Local Variables:
|
||||
* eval: (c-set-style "gnu")
|
||||
* End:
|
||||
*/
|
433
src/plugins/perfmon/perfmon_periodic.c
Normal file
433
src/plugins/perfmon/perfmon_periodic.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -540,29 +540,38 @@ vlib_put_next_frame (vlib_main_t * vm,
|
||||
never_inline void
|
||||
vlib_node_runtime_sync_stats (vlib_main_t * vm,
|
||||
vlib_node_runtime_t * r,
|
||||
uword n_calls, uword n_vectors, uword n_clocks)
|
||||
uword n_calls, uword n_vectors, uword n_clocks,
|
||||
uword n_ticks)
|
||||
{
|
||||
vlib_node_t *n = vlib_get_node (vm, r->node_index);
|
||||
|
||||
n->stats_total.calls += n_calls + r->calls_since_last_overflow;
|
||||
n->stats_total.vectors += n_vectors + r->vectors_since_last_overflow;
|
||||
n->stats_total.clocks += n_clocks + r->clocks_since_last_overflow;
|
||||
n->stats_total.perf_counter_ticks += n_ticks +
|
||||
r->perf_counter_ticks_since_last_overflow;
|
||||
n->stats_total.perf_counter_vectors += n_vectors +
|
||||
r->perf_counter_vectors_since_last_overflow;
|
||||
n->stats_total.max_clock = r->max_clock;
|
||||
n->stats_total.max_clock_n = r->max_clock_n;
|
||||
|
||||
r->calls_since_last_overflow = 0;
|
||||
r->vectors_since_last_overflow = 0;
|
||||
r->clocks_since_last_overflow = 0;
|
||||
r->perf_counter_ticks_since_last_overflow = 0ULL;
|
||||
r->perf_counter_vectors_since_last_overflow = 0ULL;
|
||||
}
|
||||
|
||||
always_inline void __attribute__ ((unused))
|
||||
vlib_process_sync_stats (vlib_main_t * vm,
|
||||
vlib_process_t * p,
|
||||
uword n_calls, uword n_vectors, uword n_clocks)
|
||||
uword n_calls, uword n_vectors, uword n_clocks,
|
||||
uword n_ticks)
|
||||
{
|
||||
vlib_node_runtime_t *rt = &p->node_runtime;
|
||||
vlib_node_t *n = vlib_get_node (vm, rt->node_index);
|
||||
vlib_node_runtime_sync_stats (vm, rt, n_calls, n_vectors, n_clocks);
|
||||
vlib_node_runtime_sync_stats (vm, rt, n_calls, n_vectors, n_clocks,
|
||||
n_ticks);
|
||||
n->stats_total.suspends += p->n_suspends;
|
||||
p->n_suspends = 0;
|
||||
}
|
||||
@ -588,7 +597,7 @@ vlib_node_sync_stats (vlib_main_t * vm, vlib_node_t * n)
|
||||
vec_elt_at_index (vm->node_main.nodes_by_type[n->type],
|
||||
n->runtime_index);
|
||||
|
||||
vlib_node_runtime_sync_stats (vm, rt, 0, 0, 0);
|
||||
vlib_node_runtime_sync_stats (vm, rt, 0, 0, 0, 0);
|
||||
|
||||
/* Sync up runtime next frame vector counters with main node structure. */
|
||||
{
|
||||
@ -608,45 +617,68 @@ always_inline u32
|
||||
vlib_node_runtime_update_stats (vlib_main_t * vm,
|
||||
vlib_node_runtime_t * node,
|
||||
uword n_calls,
|
||||
uword n_vectors, uword n_clocks)
|
||||
uword n_vectors, uword n_clocks,
|
||||
uword n_ticks)
|
||||
{
|
||||
u32 ca0, ca1, v0, v1, cl0, cl1, r;
|
||||
u32 ptick0, ptick1, pvec0, pvec1;
|
||||
|
||||
cl0 = cl1 = node->clocks_since_last_overflow;
|
||||
ca0 = ca1 = node->calls_since_last_overflow;
|
||||
v0 = v1 = node->vectors_since_last_overflow;
|
||||
ptick0 = ptick1 = node->perf_counter_ticks_since_last_overflow;
|
||||
pvec0 = pvec1 = node->perf_counter_vectors_since_last_overflow;
|
||||
|
||||
ca1 = ca0 + n_calls;
|
||||
v1 = v0 + n_vectors;
|
||||
cl1 = cl0 + n_clocks;
|
||||
ptick1 = ptick0 + n_ticks;
|
||||
pvec1 = pvec0 + n_vectors;
|
||||
|
||||
node->calls_since_last_overflow = ca1;
|
||||
node->clocks_since_last_overflow = cl1;
|
||||
node->vectors_since_last_overflow = v1;
|
||||
node->perf_counter_ticks_since_last_overflow = ptick1;
|
||||
node->perf_counter_vectors_since_last_overflow = pvec1;
|
||||
|
||||
node->max_clock_n = node->max_clock > n_clocks ?
|
||||
node->max_clock_n : n_vectors;
|
||||
node->max_clock = node->max_clock > n_clocks ? node->max_clock : n_clocks;
|
||||
|
||||
r = vlib_node_runtime_update_main_loop_vector_stats (vm, node, n_vectors);
|
||||
|
||||
if (PREDICT_FALSE (ca1 < ca0 || v1 < v0 || cl1 < cl0))
|
||||
if (PREDICT_FALSE (ca1 < ca0 || v1 < v0 || cl1 < cl0) || (ptick1 < ptick0)
|
||||
|| (pvec1 < pvec0))
|
||||
{
|
||||
node->calls_since_last_overflow = ca0;
|
||||
node->clocks_since_last_overflow = cl0;
|
||||
node->vectors_since_last_overflow = v0;
|
||||
vlib_node_runtime_sync_stats (vm, node, n_calls, n_vectors, n_clocks);
|
||||
node->perf_counter_ticks_since_last_overflow = ptick0;
|
||||
node->perf_counter_vectors_since_last_overflow = pvec0;
|
||||
|
||||
vlib_node_runtime_sync_stats (vm, node, n_calls, n_vectors, n_clocks,
|
||||
n_ticks);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline u64
|
||||
vlib_node_runtime_perf_counter (vlib_main_t * vm)
|
||||
{
|
||||
if (PREDICT_FALSE (vm->vlib_node_runtime_perf_counter_cb != 0))
|
||||
return ((*vm->vlib_node_runtime_perf_counter_cb) (vm));
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
always_inline void
|
||||
vlib_process_update_stats (vlib_main_t * vm,
|
||||
vlib_process_t * p,
|
||||
uword n_calls, uword n_vectors, uword n_clocks)
|
||||
uword n_calls, uword n_vectors, uword n_clocks,
|
||||
uword n_ticks)
|
||||
{
|
||||
vlib_node_runtime_update_stats (vm, &p->node_runtime,
|
||||
n_calls, n_vectors, n_clocks);
|
||||
n_calls, n_vectors, n_clocks, n_ticks);
|
||||
}
|
||||
|
||||
static clib_error_t *
|
||||
@ -959,15 +991,19 @@ dispatch_node (vlib_main_t * vm,
|
||||
|
||||
if (1 /* || vm->thread_index == node->thread_index */ )
|
||||
{
|
||||
vlib_main_t *stat_vm;
|
||||
|
||||
stat_vm = /* vlib_mains ? vlib_mains[0] : */ vm;
|
||||
u64 pmc_before, pmc_delta;
|
||||
|
||||
vlib_elog_main_loop_event (vm, node->node_index,
|
||||
last_time_stamp,
|
||||
frame ? frame->n_vectors : 0,
|
||||
/* is_after */ 0);
|
||||
|
||||
/*
|
||||
* To validate accounting: pmc_before = last_time_stamp
|
||||
* perf ticks should equal clocks/pkt...
|
||||
*/
|
||||
pmc_before = vlib_node_runtime_perf_counter (vm);
|
||||
|
||||
/*
|
||||
* Turn this on if you run into
|
||||
* "bad monkey" contexts, and you want to know exactly
|
||||
@ -990,16 +1026,23 @@ dispatch_node (vlib_main_t * vm,
|
||||
|
||||
t = clib_cpu_time_now ();
|
||||
|
||||
/*
|
||||
* To validate accounting: pmc_delta = t - pmc_before;
|
||||
* perf ticks should equal clocks/pkt...
|
||||
*/
|
||||
pmc_delta = vlib_node_runtime_perf_counter (vm) - pmc_before;
|
||||
|
||||
vlib_elog_main_loop_event (vm, node->node_index, t, n, /* is_after */
|
||||
1);
|
||||
|
||||
vm->main_loop_vectors_processed += n;
|
||||
vm->main_loop_nodes_processed += n > 0;
|
||||
|
||||
v = vlib_node_runtime_update_stats (stat_vm, node,
|
||||
v = vlib_node_runtime_update_stats (vm, node,
|
||||
/* n_calls */ 1,
|
||||
/* n_vectors */ n,
|
||||
/* n_clocks */ t - last_time_stamp);
|
||||
/* n_clocks */ t - last_time_stamp,
|
||||
pmc_delta /* PMC ticks */ );
|
||||
|
||||
/* When in interrupt mode and vector rate crosses threshold switch to
|
||||
polling mode. */
|
||||
@ -1338,7 +1381,8 @@ dispatch_process (vlib_main_t * vm,
|
||||
vlib_process_update_stats (vm, p,
|
||||
/* n_calls */ !is_suspend,
|
||||
/* n_vectors */ n_vectors,
|
||||
/* n_clocks */ t - last_time_stamp);
|
||||
/* n_clocks */ t - last_time_stamp,
|
||||
/* pmc_ticks */ 0ULL);
|
||||
|
||||
return t;
|
||||
}
|
||||
@ -1421,7 +1465,8 @@ dispatch_suspended_process (vlib_main_t * vm,
|
||||
vlib_process_update_stats (vm, p,
|
||||
/* n_calls */ !is_suspend,
|
||||
/* n_vectors */ n_vectors,
|
||||
/* n_clocks */ t - last_time_stamp);
|
||||
/* n_clocks */ t - last_time_stamp,
|
||||
/* pmc_ticks */ 0ULL);
|
||||
|
||||
return t;
|
||||
}
|
||||
@ -1471,6 +1516,9 @@ vlib_main_or_worker_loop (vlib_main_t * vm, int is_main)
|
||||
if (!nm->interrupt_threshold_vector_length)
|
||||
nm->interrupt_threshold_vector_length = 5;
|
||||
|
||||
/* Make sure the performance monitor counter is disabled */
|
||||
vm->perf_counter_id = ~0;
|
||||
|
||||
/* Start all processes. */
|
||||
if (is_main)
|
||||
{
|
||||
@ -1493,6 +1541,9 @@ vlib_main_or_worker_loop (vlib_main_t * vm, int is_main)
|
||||
vlib_worker_thread_barrier_check ();
|
||||
vec_foreach (fqm, tm->frame_queue_mains)
|
||||
vlib_frame_queue_dequeue (vm, fqm);
|
||||
if (PREDICT_FALSE (vm->worker_thread_main_loop_callback != 0))
|
||||
((void (*)(vlib_main_t *)) vm->worker_thread_main_loop_callback)
|
||||
(vm);
|
||||
}
|
||||
|
||||
/* Process pre-input nodes. */
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include <vppinfra/pool.h>
|
||||
#include <vppinfra/random_buffer.h>
|
||||
#include <vppinfra/time.h>
|
||||
#include <vppinfra/pmc.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
@ -81,6 +82,11 @@ typedef struct vlib_main_t
|
||||
u32 vector_counts_per_main_loop[2];
|
||||
u32 node_counts_per_main_loop[2];
|
||||
|
||||
/* Main loop hw / sw performance counters */
|
||||
u64 (*vlib_node_runtime_perf_counter_cb) (struct vlib_main_t *);
|
||||
int perf_counter_id;
|
||||
int perf_counter_fd;
|
||||
|
||||
/* Every so often we switch to the next counter. */
|
||||
#define VLIB_LOG2_MAIN_LOOPS_PER_STATS_UPDATE 7
|
||||
|
||||
@ -192,6 +198,9 @@ typedef struct vlib_main_t
|
||||
void (*queue_signal_callback) (struct vlib_main_t *);
|
||||
u8 **argv;
|
||||
|
||||
/* Top of (worker) dispatch loop callback */
|
||||
volatile void (*worker_thread_main_loop_callback) (struct vlib_main_t *);
|
||||
|
||||
/* debugging */
|
||||
volatile int parked_at_barrier;
|
||||
|
||||
|
@ -244,6 +244,8 @@ typedef struct
|
||||
u64 calls, vectors, clocks, suspends;
|
||||
u64 max_clock;
|
||||
u64 max_clock_n;
|
||||
u64 perf_counter_ticks;
|
||||
u64 perf_counter_vectors;
|
||||
} vlib_node_stats_t;
|
||||
|
||||
#define foreach_vlib_node_state \
|
||||
@ -488,6 +490,9 @@ typedef struct vlib_node_runtime_t
|
||||
u32 vectors_since_last_overflow; /**< Number of vector elements
|
||||
processed by this node. */
|
||||
|
||||
u32 perf_counter_ticks_since_last_overflow; /**< Perf counter ticks */
|
||||
u32 perf_counter_vectors_since_last_overflow; /**< Perf counter vectors */
|
||||
|
||||
u32 next_frame_index; /**< Start of next frames for this
|
||||
node. */
|
||||
|
||||
|
@ -148,19 +148,25 @@ format_vlib_node_stats (u8 * s, va_list * va)
|
||||
f64 maxc, maxcn;
|
||||
u32 maxn;
|
||||
u32 indent;
|
||||
u64 pmc_ticks;
|
||||
f64 pmc_ticks_per_packet;
|
||||
|
||||
if (!n)
|
||||
{
|
||||
if (max)
|
||||
return format (s,
|
||||
"%=30s%=17s%=16s%=16s%=16s%=16s",
|
||||
"Name", "Max Node Clocks", "Vectors at Max",
|
||||
"Max Clocks", "Avg Clocks", "Avg Vectors/Call");
|
||||
s = format (s,
|
||||
"%=30s%=17s%=16s%=16s%=16s%=16s",
|
||||
"Name", "Max Node Clocks", "Vectors at Max",
|
||||
"Max Clocks", "Avg Clocks", "Avg Vectors/Call");
|
||||
else
|
||||
return format (s,
|
||||
"%=30s%=12s%=16s%=16s%=16s%=16s%=16s",
|
||||
"Name", "State", "Calls", "Vectors", "Suspends",
|
||||
"Clocks", "Vectors/Call");
|
||||
s = format (s,
|
||||
"%=30s%=12s%=16s%=16s%=16s%=16s%=16s",
|
||||
"Name", "State", "Calls", "Vectors", "Suspends",
|
||||
"Clocks", "Vectors/Call");
|
||||
if (vm->perf_counter_id)
|
||||
s = format (s, "%=16s", "Perf Ticks");
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
indent = format_get_indent (s);
|
||||
@ -176,6 +182,13 @@ format_vlib_node_stats (u8 * s, va_list * va)
|
||||
else
|
||||
maxcn = 0.0;
|
||||
|
||||
pmc_ticks = n->stats_total.perf_counter_ticks -
|
||||
n->stats_last_clear.perf_counter_ticks;
|
||||
if (p > 0)
|
||||
pmc_ticks_per_packet = (f64) pmc_ticks / (f64) p;
|
||||
else
|
||||
pmc_ticks_per_packet = 0.0;
|
||||
|
||||
/* Clocks per packet, per call or per suspend. */
|
||||
x = 0;
|
||||
if (p > 0)
|
||||
@ -208,6 +221,9 @@ format_vlib_node_stats (u8 * s, va_list * va)
|
||||
s = format (s, "%-30v%=12U%16Ld%16Ld%16Ld%16.2e%16.2f", ns,
|
||||
format_vlib_node_state, vm, n, c, p, d, x, v);
|
||||
|
||||
if (pmc_ticks_per_packet > 0.0)
|
||||
s = format (s, "%16.2e", pmc_ticks_per_packet);
|
||||
|
||||
if (ns != n->name)
|
||||
vec_free (ns);
|
||||
|
||||
|
@ -57,7 +57,7 @@ vlib_node_serialize (vlib_main_t * vm, vlib_node_t *** node_dups, u8 * vector,
|
||||
u8 *namep;
|
||||
u32 name_bytes;
|
||||
uword i, j, k;
|
||||
u64 l, v, c, d;
|
||||
u64 l, v, c, d, pmc;
|
||||
state_string_enum_t state_code;
|
||||
|
||||
serialize_open_vector (sm, vector);
|
||||
@ -77,6 +77,8 @@ vlib_node_serialize (vlib_main_t * vm, vlib_node_t *** node_dups, u8 * vector,
|
||||
v = n->stats_total.vectors - n->stats_last_clear.vectors;
|
||||
c = n->stats_total.calls - n->stats_last_clear.calls;
|
||||
d = n->stats_total.suspends - n->stats_last_clear.suspends;
|
||||
pmc = n->stats_total.perf_counter_ticks
|
||||
- n->stats_last_clear.perf_counter_ticks;
|
||||
|
||||
state_code = STATE_INTERNAL;
|
||||
|
||||
@ -149,6 +151,8 @@ vlib_node_serialize (vlib_main_t * vm, vlib_node_t *** node_dups, u8 * vector,
|
||||
serialize_integer (sm, v, 8);
|
||||
/* Total suspends */
|
||||
serialize_integer (sm, d, 8);
|
||||
/* PMC counter */
|
||||
serialize_integer (sm, pmc, 8);
|
||||
}
|
||||
else /* no stats */
|
||||
serialize_likely_small_unsigned_integer (sm, 0);
|
||||
@ -167,7 +171,7 @@ vlib_node_unserialize (u8 * vector)
|
||||
vlib_node_t **nodes;
|
||||
vlib_node_t ***nodes_by_thread = 0;
|
||||
int i, j, k;
|
||||
u64 l, v, c, d;
|
||||
u64 l, v, c, d, pmc;
|
||||
state_string_enum_t state_code;
|
||||
int stats_present;
|
||||
|
||||
@ -225,6 +229,9 @@ vlib_node_unserialize (u8 * vector)
|
||||
/* Total suspends */
|
||||
unserialize_integer (sm, &d, 8);
|
||||
node->stats_total.suspends = d;
|
||||
/* PMC counter */
|
||||
unserialize_integer (sm, &pmc, 8);
|
||||
node->stats_total.perf_counter_ticks = pmc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -136,6 +136,7 @@ set(VPPINFRA_HEADERS
|
||||
os.h
|
||||
pipeline.h
|
||||
pool.h
|
||||
pmc.h
|
||||
ptclosure.h
|
||||
random_buffer.h
|
||||
random.h
|
||||
|
46
src/vppinfra/pmc.h
Normal file
46
src/vppinfra/pmc.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Cisco and/or its 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.
|
||||
*/
|
||||
|
||||
#ifndef included_clib_pmc_h
|
||||
#define included_clib_pmc_h
|
||||
|
||||
#if defined (__x86_64__)
|
||||
|
||||
always_inline u64
|
||||
clib_rdpmc (int counter_id)
|
||||
{
|
||||
u32 a, d;
|
||||
|
||||
asm volatile ("rdpmc":"=a" (a), "=d" (d):"c" (counter_id));
|
||||
return (u64) a + ((u64) d << (u64) 32);
|
||||
}
|
||||
|
||||
#else
|
||||
always_inline u64
|
||||
clib_rdpmc (int counter_id)
|
||||
{
|
||||
return 0ULL;
|
||||
}
|
||||
#endif /* __aarch64__ */
|
||||
|
||||
#endif /* included_clib_pmc_h */
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
* Local Variables:
|
||||
* eval: (c-set-style "gnu")
|
||||
* End:
|
||||
*/
|
Reference in New Issue
Block a user