summaryrefslogtreecommitdiffstats
path: root/src/plugins/perfmon
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/perfmon')
-rw-r--r--src/plugins/perfmon/perfmon.c62
-rw-r--r--src/plugins/perfmon/perfmon.h14
-rw-r--r--src/plugins/perfmon/perfmon_periodic.c295
3 files changed, 228 insertions, 143 deletions
diff --git a/src/plugins/perfmon/perfmon.c b/src/plugins/perfmon/perfmon.c
index c6a80224e0e..359555705aa 100644
--- a/src/plugins/perfmon/perfmon.c
+++ b/src/plugins/perfmon/perfmon.c
@@ -157,10 +157,16 @@ perfmon_init (vlib_main_t * vm)
pm->log_class = vlib_log_register_class ("perfmon", 0);
/* Default data collection interval */
- pm->timeout_interval = 3.0;
- vec_validate (pm->pm_fds, vec_len (vlib_mains) - 1);
- vec_validate (pm->perf_event_pages, vec_len (vlib_mains) - 1);
- vec_validate (pm->rdpmc_indices, vec_len (vlib_mains) - 1);
+ pm->timeout_interval = 2.0; /* seconds */
+ vec_validate (pm->pm_fds, 1);
+ vec_validate (pm->pm_fds[0], vec_len (vlib_mains) - 1);
+ vec_validate (pm->pm_fds[1], vec_len (vlib_mains) - 1);
+ vec_validate (pm->perf_event_pages, 1);
+ vec_validate (pm->perf_event_pages[0], vec_len (vlib_mains) - 1);
+ vec_validate (pm->perf_event_pages[1], vec_len (vlib_mains) - 1);
+ vec_validate (pm->rdpmc_indices, 1);
+ vec_validate (pm->rdpmc_indices[0], vec_len (vlib_mains) - 1);
+ vec_validate (pm->rdpmc_indices[1], vec_len (vlib_mains) - 1);
pm->page_size = getpagesize ();
ht = pm->perfmon_table = 0;
@@ -297,10 +303,12 @@ set_pmc_command_fn (vlib_main_t * vm,
perfmon_main_t *pm = &perfmon_main;
unformat_input_t _line_input, *line_input = &_line_input;
perfmon_event_config_t ec;
+ f64 delay;
u32 timeout_seconds;
u32 deadman;
- vec_reset_length (pm->events_to_collect);
+ vec_reset_length (pm->single_events_to_collect);
+ vec_reset_length (pm->paired_events_to_collect);
pm->ipc_event_index = ~0;
pm->mispredict_event_index = ~0;
@@ -316,28 +324,28 @@ set_pmc_command_fn (vlib_main_t * vm,
ec.name = "instructions";
ec.pe_type = PERF_TYPE_HARDWARE;
ec.pe_config = PERF_COUNT_HW_INSTRUCTIONS;
- pm->ipc_event_index = vec_len (pm->events_to_collect);
- vec_add1 (pm->events_to_collect, ec);
+ pm->ipc_event_index = vec_len (pm->paired_events_to_collect);
+ vec_add1 (pm->paired_events_to_collect, ec);
ec.name = "cpu-cycles";
ec.pe_type = PERF_TYPE_HARDWARE;
ec.pe_config = PERF_COUNT_HW_CPU_CYCLES;
- vec_add1 (pm->events_to_collect, ec);
+ vec_add1 (pm->paired_events_to_collect, ec);
}
else if (unformat (line_input, "branch-mispredict-rate"))
{
ec.name = "branch-misses";
ec.pe_type = PERF_TYPE_HARDWARE;
ec.pe_config = PERF_COUNT_HW_BRANCH_MISSES;
- pm->mispredict_event_index = vec_len (pm->events_to_collect);
- vec_add1 (pm->events_to_collect, ec);
+ pm->mispredict_event_index = vec_len (pm->paired_events_to_collect);
+ vec_add1 (pm->paired_events_to_collect, ec);
ec.name = "branches";
ec.pe_type = PERF_TYPE_HARDWARE;
ec.pe_config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS;
- vec_add1 (pm->events_to_collect, ec);
+ vec_add1 (pm->paired_events_to_collect, ec);
}
else if (unformat (line_input, "%U", unformat_processor_event, pm, &ec))
{
- vec_add1 (pm->events_to_collect, ec);
+ vec_add1 (pm->single_events_to_collect, ec);
}
#define _(type,event,str) \
else if (unformat (line_input, str)) \
@@ -345,7 +353,7 @@ set_pmc_command_fn (vlib_main_t * vm,
ec.name = str; \
ec.pe_type = type; \
ec.pe_config = event; \
- vec_add1 (pm->events_to_collect, ec); \
+ vec_add1 (pm->single_events_to_collect, ec); \
}
foreach_perfmon_event
#undef _
@@ -354,21 +362,33 @@ set_pmc_command_fn (vlib_main_t * vm,
format_unformat_error, line_input);
}
- if (vec_len (pm->events_to_collect) == 0)
+ /* Stick paired events at the front of the (unified) list */
+ if (vec_len (pm->paired_events_to_collect) > 0)
+ {
+ perfmon_event_config_t *tmp;
+ /* first 2n events are pairs... */
+ vec_append (pm->paired_events_to_collect, pm->single_events_to_collect);
+ tmp = pm->single_events_to_collect;
+ pm->single_events_to_collect = pm->paired_events_to_collect;
+ pm->paired_events_to_collect = tmp;
+ }
+
+ if (vec_len (pm->single_events_to_collect) == 0)
return clib_error_return (0, "no events specified...");
+ /* Figure out how long data collection will take */
+ delay =
+ ((f64) vec_len (pm->single_events_to_collect)) * pm->timeout_interval;
+ delay /= 2.0; /* collect 2 stats at once */
+
vlib_cli_output (vm, "Start collection for %d events, wait %.2f seconds",
- vec_len (pm->events_to_collect),
- (f64) (vec_len (pm->events_to_collect))
- * pm->timeout_interval);
+ vec_len (pm->single_events_to_collect), delay);
vlib_process_signal_event (pm->vlib_main, perfmon_periodic_node.index,
PERFMON_START, 0);
/* Coarse-grained wait */
- vlib_process_suspend (vm,
- ((f64) (vec_len (pm->events_to_collect)
- * pm->timeout_interval)));
+ vlib_process_suspend (vm, delay);
deadman = 0;
/* Reasonable to guess that collection may not be quite done... */
@@ -438,7 +458,7 @@ format_capture (u8 * s, va_list * args)
if (i == pm->ipc_event_index)
{
f64 ipc_rate;
- ASSERT (i + 1 < vec_len (c->counter_names));
+ ASSERT ((i + 1) < vec_len (c->counter_names));
if (c->counter_values[i + 1] > 0)
ipc_rate = (f64) c->counter_values[i]
diff --git a/src/plugins/perfmon/perfmon.h b/src/plugins/perfmon/perfmon.h
index 47ee471d5fc..9663dae36d1 100644
--- a/src/plugins/perfmon/perfmon.h
+++ b/src/plugins/perfmon/perfmon.h
@@ -97,8 +97,11 @@ typedef struct
perfmon_cpuid_and_table_t *perfmon_tables;
uword *perfmon_table;
- /* vector of events to collect */
- perfmon_event_config_t *events_to_collect;
+ /* vector of single events to collect */
+ perfmon_event_config_t *single_events_to_collect;
+
+ /* vector of paired events to collect */
+ perfmon_event_config_t *paired_events_to_collect;
/* Base indices of synthetic event tuples */
u32 ipc_event_index;
@@ -109,13 +112,14 @@ typedef struct
/* Current event (index) being collected */
u32 current_event;
- u32 *rdpmc_indices;
+ int n_active;
+ u32 **rdpmc_indices;
/* mmap base / size of (mapped) struct perf_event_mmap_page */
- u8 **perf_event_pages;
+ u8 ***perf_event_pages;
u32 page_size;
/* Current perf_event file descriptors, per thread */
- int *pm_fds;
+ int **pm_fds;
/* Logging */
vlib_log_class_t log_class;
diff --git a/src/plugins/perfmon/perfmon_periodic.c b/src/plugins/perfmon/perfmon_periodic.c
index 4e7e2378320..ae20ac4c62f 100644
--- a/src/plugins/perfmon/perfmon_periodic.c
+++ b/src/plugins/perfmon/perfmon_periodic.c
@@ -31,22 +31,34 @@ perf_event_open (struct perf_event_attr *hw_event, pid_t pid, int cpu,
return ret;
}
-static u64
-read_current_perf_counter (vlib_main_t * vm)
+static void
+read_current_perf_counters (vlib_main_t * vm, u64 * c0, u64 * c1)
{
- if (vm->perf_counter_id)
- return clib_rdpmc (vm->perf_counter_id);
- else
+ int i;
+ u64 *cc;
+ perfmon_main_t *pm = &perfmon_main;
+ uword my_thread_index = vm->thread_index;
+
+ *c0 = *c1 = 0;
+
+ for (i = 0; i < pm->n_active; i++)
{
- u64 sw_value;
- if (read (vm->perf_counter_fd, &sw_value, sizeof (sw_value)) !=
- sizeof (sw_value))
+ cc = (i == 0) ? c0 : c1;
+ if (pm->rdpmc_indices[i][my_thread_index] != ~0)
+ *cc = clib_rdpmc ((int) pm->rdpmc_indices[i][my_thread_index]);
+ else
{
- clib_unix_warning ("counter read failed, disable collection...");
- vm->vlib_node_runtime_perf_counter_cb = 0;
- return 0ULL;
+ u64 sw_value;
+ if (read (pm->pm_fds[i][my_thread_index], &sw_value,
+ sizeof (sw_value)) != sizeof (sw_value))
+ {
+ clib_unix_warning
+ ("counter read failed, disable collection...");
+ vm->vlib_node_runtime_perf_counter_cb = 0;
+ return;
+ }
+ *cc = sw_value;
}
- return sw_value;
}
}
@@ -80,9 +92,11 @@ clear_counters (perfmon_main_t * pm)
for (i = 0; i < vec_len (nm->nodes); i++)
{
n = nm->nodes[i];
- n->stats_total.perf_counter_ticks = 0;
+ n->stats_total.perf_counter0_ticks = 0;
+ n->stats_total.perf_counter1_ticks = 0;
n->stats_total.perf_counter_vectors = 0;
- n->stats_last_clear.perf_counter_ticks = 0;
+ n->stats_last_clear.perf_counter0_ticks = 0;
+ n->stats_last_clear.perf_counter1_ticks = 0;
n->stats_last_clear.perf_counter_vectors = 0;
}
}
@@ -90,7 +104,7 @@ clear_counters (perfmon_main_t * pm)
}
static void
-enable_current_event (perfmon_main_t * pm)
+enable_current_events (perfmon_main_t * pm)
{
struct perf_event_attr pe;
int fd;
@@ -98,91 +112,108 @@ enable_current_event (perfmon_main_t * pm)
perfmon_event_config_t *c;
vlib_main_t *vm = vlib_get_main ();
u32 my_thread_index = vm->thread_index;
+ u32 index;
+ int i, limit = 1;
- c = vec_elt_at_index (pm->events_to_collect, pm->current_event);
-
- memset (&pe, 0, sizeof (struct perf_event_attr));
- pe.type = c->pe_type;
- pe.size = sizeof (struct perf_event_attr);
- pe.config = c->pe_config;
- pe.disabled = 1;
- pe.pinned = 1;
- /*
- * Note: excluding the kernel makes the
- * (software) context-switch counter read 0...
- */
- if (pe.type != PERF_TYPE_SOFTWARE)
- {
- /* Exclude kernel and hypervisor */
- pe.exclude_kernel = 1;
- pe.exclude_hv = 1;
- }
+ if ((pm->current_event + 1) < vec_len (pm->single_events_to_collect))
+ limit = 2;
- fd = perf_event_open (&pe, 0, -1, -1, 0);
- if (fd == -1)
+ for (i = 0; i < limit; i++)
{
- clib_unix_warning ("event open: type %d config %d", c->pe_type,
- c->pe_config);
- return;
- }
+ c = vec_elt_at_index (pm->single_events_to_collect,
+ pm->current_event + i);
+
+ memset (&pe, 0, sizeof (struct perf_event_attr));
+ pe.type = c->pe_type;
+ pe.size = sizeof (struct perf_event_attr);
+ pe.config = c->pe_config;
+ pe.disabled = 1;
+ pe.pinned = 1;
+ /*
+ * Note: excluding the kernel makes the
+ * (software) context-switch counter read 0...
+ */
+ if (pe.type != PERF_TYPE_SOFTWARE)
+ {
+ /* Exclude kernel and hypervisor */
+ pe.exclude_kernel = 1;
+ pe.exclude_hv = 1;
+ }
- if (pe.type != PERF_TYPE_SOFTWARE)
- {
- p = mmap (0, pm->page_size, PROT_READ, MAP_SHARED, fd, 0);
- if (p == MAP_FAILED)
+ fd = perf_event_open (&pe, 0, -1, -1, 0);
+ if (fd == -1)
{
- clib_unix_warning ("mmap");
- close (fd);
+ clib_unix_warning ("event open: type %d config %d", c->pe_type,
+ c->pe_config);
return;
}
- }
- if (ioctl (fd, PERF_EVENT_IOC_RESET, 0) < 0)
- clib_unix_warning ("reset ioctl");
+ if (pe.type != PERF_TYPE_SOFTWARE)
+ {
+ p = mmap (0, pm->page_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (p == MAP_FAILED)
+ {
+ clib_unix_warning ("mmap");
+ close (fd);
+ return;
+ }
+ }
+ else
+ p = 0;
+
+ /*
+ * Software event counters - and others not capable of being
+ * read via the "rdpmc" instruction - will be read
+ * by system calls.
+ */
+ if (pe.type == PERF_TYPE_SOFTWARE || p->cap_user_rdpmc == 0)
+ index = ~0;
+ else
+ index = p->index - 1;
- if (ioctl (fd, PERF_EVENT_IOC_ENABLE, 0) < 0)
- clib_unix_warning ("enable ioctl");
+ if (ioctl (fd, PERF_EVENT_IOC_RESET, 0) < 0)
+ clib_unix_warning ("reset ioctl");
- /*
- * Software event counters - and others not capable of being
- * read via the "rdpmc" instruction - will be read
- * by system calls.
- */
- if (pe.type == PERF_TYPE_SOFTWARE || p->cap_user_rdpmc == 0)
- pm->rdpmc_indices[my_thread_index] = 0;
- else /* use rdpmc instrs */
- pm->rdpmc_indices[my_thread_index] = p->index - 1;
- pm->perf_event_pages[my_thread_index] = (void *) p;
+ if (ioctl (fd, PERF_EVENT_IOC_ENABLE, 0) < 0)
+ clib_unix_warning ("enable ioctl");
- pm->pm_fds[my_thread_index] = fd;
+ pm->rdpmc_indices[i][my_thread_index] = index;
+ pm->perf_event_pages[i][my_thread_index] = (void *) p;
+ pm->pm_fds[i][my_thread_index] = fd;
+ }
+ pm->n_active = i;
/* Enable the main loop counter snapshot mechanism */
- vm->perf_counter_id = pm->rdpmc_indices[my_thread_index];
- vm->perf_counter_fd = fd;
- vm->vlib_node_runtime_perf_counter_cb = read_current_perf_counter;
+ vm->vlib_node_runtime_perf_counter_cb = read_current_perf_counters;
}
static void
-disable_event (perfmon_main_t * pm)
+disable_events (perfmon_main_t * pm)
{
vlib_main_t *vm = vlib_get_main ();
u32 my_thread_index = vm->thread_index;
-
- if (pm->pm_fds[my_thread_index] == 0)
- return;
+ int i;
/* Stop main loop collection */
vm->vlib_node_runtime_perf_counter_cb = 0;
- if (ioctl (pm->pm_fds[my_thread_index], PERF_EVENT_IOC_DISABLE, 0) < 0)
- clib_unix_warning ("disable ioctl");
+ for (i = 0; i < pm->n_active; i++)
+ {
+ if (pm->pm_fds[i][my_thread_index] == 0)
+ continue;
+
+ if (ioctl (pm->pm_fds[i][my_thread_index], PERF_EVENT_IOC_DISABLE, 0) <
+ 0)
+ clib_unix_warning ("disable ioctl");
- if (pm->perf_event_pages[my_thread_index])
- if (munmap (pm->perf_event_pages[my_thread_index], pm->page_size) < 0)
- clib_unix_warning ("munmap");
+ if (pm->perf_event_pages[i][my_thread_index])
+ if (munmap (pm->perf_event_pages[i][my_thread_index],
+ pm->page_size) < 0)
+ clib_unix_warning ("munmap");
- (void) close (pm->pm_fds[my_thread_index]);
- pm->pm_fds[my_thread_index] = 0;
+ (void) close (pm->pm_fds[i][my_thread_index]);
+ pm->pm_fds[i][my_thread_index] = 0;
+ }
}
static void
@@ -190,7 +221,7 @@ worker_thread_start_event (vlib_main_t * vm)
{
perfmon_main_t *pm = &perfmon_main;
- enable_current_event (pm);
+ enable_current_events (pm);
vm->worker_thread_main_loop_callback = 0;
}
@@ -198,7 +229,7 @@ static void
worker_thread_stop_event (vlib_main_t * vm)
{
perfmon_main_t *pm = &perfmon_main;
- disable_event (pm);
+ disable_events (pm);
vm->worker_thread_main_loop_callback = 0;
}
@@ -207,7 +238,7 @@ start_event (perfmon_main_t * pm, f64 now, uword event_data)
{
int i;
pm->current_event = 0;
- if (vec_len (pm->events_to_collect) == 0)
+ if (vec_len (pm->single_events_to_collect) == 0)
{
pm->state = PERFMON_STATE_OFF;
return;
@@ -216,7 +247,7 @@ start_event (perfmon_main_t * pm, f64 now, uword event_data)
clear_counters (pm);
/* Start collection on this thread */
- enable_current_event (pm);
+ enable_current_events (pm);
/* And also on worker threads */
for (i = 1; i < vec_len (vlib_mains); i++)
@@ -231,7 +262,7 @@ start_event (perfmon_main_t * pm, f64 now, uword event_data)
void
scrape_and_clear_counters (perfmon_main_t * pm)
{
- int i, j;
+ int i, j, k;
vlib_main_t *vm = pm->vlib_main;
vlib_main_t *stat_vm;
vlib_node_main_t *nm;
@@ -242,7 +273,6 @@ scrape_and_clear_counters (perfmon_main_t * pm)
perfmon_event_config_t *current_event;
uword *p;
u8 *counter_name;
- u64 counter_value;
u64 vectors_this_counter;
/* snapshoot the nodes, including pm counters */
@@ -272,17 +302,17 @@ scrape_and_clear_counters (perfmon_main_t * pm)
n = nm->nodes[i];
nodes[i] = clib_mem_alloc (sizeof (*n));
clib_memcpy_fast (nodes[i], n, sizeof (*n));
- n->stats_total.perf_counter_ticks = 0;
+ n->stats_total.perf_counter0_ticks = 0;
+ n->stats_total.perf_counter1_ticks = 0;
n->stats_total.perf_counter_vectors = 0;
- n->stats_last_clear.perf_counter_ticks = 0;
+ n->stats_last_clear.perf_counter0_ticks = 0;
+ n->stats_last_clear.perf_counter1_ticks = 0;
n->stats_last_clear.perf_counter_vectors = 0;
}
}
vlib_worker_thread_barrier_release (vm);
- current_event = pm->events_to_collect + pm->current_event;
-
for (j = 0; j < vec_len (vlib_mains); j++)
{
stat_vm = vlib_mains[j];
@@ -296,38 +326,69 @@ scrape_and_clear_counters (perfmon_main_t * pm)
u8 *capture_name;
n = nodes[i];
- if (n->stats_total.perf_counter_ticks == 0)
- {
- clib_mem_free (n);
- continue;
- }
-
- capture_name = format (0, "t%d-%v%c", j, n->name, 0);
- p = hash_get_mem (pm->capture_by_thread_and_node_name,
- capture_name);
+ if (n->stats_total.perf_counter0_ticks == 0 &&
+ n->stats_total.perf_counter1_ticks == 0)
+ goto skip_this_node;
- if (p == 0)
+ for (k = 0; k < 2; k++)
{
- pool_get (pm->capture_pool, c);
- memset (c, 0, sizeof (*c));
- c->thread_and_node_name = capture_name;
- hash_set_mem (pm->capture_by_thread_and_node_name,
- capture_name, c - pm->capture_pool);
+ u64 counter_value, counter_last_clear;
+
+ /*
+ * We collect 2 counters at once, except for the
+ * last counter when the user asks for an odd number of
+ * counters
+ */
+ if ((pm->current_event + k)
+ >= vec_len (pm->single_events_to_collect))
+ break;
+
+ if (k == 0)
+ {
+ counter_value = n->stats_total.perf_counter0_ticks;
+ counter_last_clear =
+ n->stats_last_clear.perf_counter0_ticks;
+ }
+ else
+ {
+ counter_value = n->stats_total.perf_counter1_ticks;
+ counter_last_clear =
+ n->stats_last_clear.perf_counter1_ticks;
+ }
+
+ capture_name = format (0, "t%d-%v%c", j, n->name, 0);
+
+ p = hash_get_mem (pm->capture_by_thread_and_node_name,
+ capture_name);
+
+ if (p == 0)
+ {
+ pool_get (pm->capture_pool, c);
+ memset (c, 0, sizeof (*c));
+ c->thread_and_node_name = capture_name;
+ hash_set_mem (pm->capture_by_thread_and_node_name,
+ capture_name, c - pm->capture_pool);
+ }
+ else
+ {
+ c = pool_elt_at_index (pm->capture_pool, p[0]);
+ vec_free (capture_name);
+ }
+
+ /* Snapshoot counters, etc. into the capture */
+ current_event = pm->single_events_to_collect
+ + pm->current_event + k;
+ counter_name = (u8 *) current_event->name;
+ vectors_this_counter = n->stats_total.perf_counter_vectors -
+ n->stats_last_clear.perf_counter_vectors;
+
+ vec_add1 (c->counter_names, counter_name);
+ vec_add1 (c->counter_values,
+ counter_value - counter_last_clear);
+ vec_add1 (c->vectors_this_counter, vectors_this_counter);
}
- else
- c = pool_elt_at_index (pm->capture_pool, p[0]);
-
- /* Snapshoot counters, etc. into the capture */
- counter_name = (u8 *) current_event->name;
- counter_value = n->stats_total.perf_counter_ticks -
- n->stats_last_clear.perf_counter_ticks;
- vectors_this_counter = n->stats_total.perf_counter_vectors -
- n->stats_last_clear.perf_counter_vectors;
-
- vec_add1 (c->counter_names, counter_name);
- vec_add1 (c->counter_values, counter_value);
- vec_add1 (c->vectors_this_counter, vectors_this_counter);
+ skip_this_node:
clib_mem_free (n);
}
vec_free (nodes);
@@ -339,7 +400,7 @@ static void
handle_timeout (perfmon_main_t * pm, f64 now)
{
int i;
- disable_event (pm);
+ disable_events (pm);
/* And also on worker threads */
for (i = 1; i < vec_len (vlib_mains); i++)
@@ -354,14 +415,14 @@ handle_timeout (perfmon_main_t * pm, f64 now)
if (i > 1)
vlib_process_suspend (pm->vlib_main, 1e-3);
scrape_and_clear_counters (pm);
- pm->current_event++;
- if (pm->current_event >= vec_len (pm->events_to_collect))
+ pm->current_event += pm->n_active;
+ if (pm->current_event >= vec_len (pm->single_events_to_collect))
{
pm->current_event = 0;
pm->state = PERFMON_STATE_OFF;
return;
}
- enable_current_event (pm);
+ enable_current_events (pm);
/* And also on worker threads */
for (i = 1; i < vec_len (vlib_mains); i++)