2 * perfmon.c - skeleton vpp engine plug-in
4 * Copyright (c) <current-year> <your-organization>
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 #include <vnet/vnet.h>
19 #include <vnet/plugin/plugin.h>
20 #include <perfmon/perfmon.h>
21 #include <perfmon/perfmon_intel.h>
23 #include <vlibapi/api.h>
24 #include <vlibmemory/api.h>
25 #include <vpp/app/version.h>
26 #include <linux/limits.h>
28 perfmon_main_t perfmon_main;
31 perfmon_register_intel_pmc (perfmon_intel_pmc_cpu_model_t * m, int n_models,
32 perfmon_intel_pmc_event_t * e, int n_events)
34 perfmon_main_t *pm = &perfmon_main;
35 perfmon_intel_pmc_registration_t r;
39 r.n_events = n_events;
40 r.n_models = n_models;
42 vec_add1 (pm->perfmon_tables, r);
48 #if defined(__x86_64__)
50 asm volatile ("mov $1, %%eax; cpuid; mov %%eax, %0":"=r" (cpuid)::"%eax",
51 "%edx", "%ecx", "%rbx");
59 perfmon_cpu_model_matches (perfmon_intel_pmc_cpu_model_t * mt,
60 u32 n_models, u8 model, u8 stepping)
63 for (i = 0; i < n_models; i++)
65 if (mt[i].model != model)
68 if (mt[i].has_stepping)
70 if (mt[i].stepping != stepping)
79 static perfmon_intel_pmc_event_t *
80 perfmon_find_table_by_model_stepping (perfmon_main_t * pm,
81 u8 model, u8 stepping)
83 perfmon_intel_pmc_registration_t *rt;
85 vec_foreach (rt, pm->perfmon_tables)
87 if (perfmon_cpu_model_matches (rt->models, rt->n_models, model, stepping))
94 perfmon_init (vlib_main_t * vm)
96 perfmon_main_t *pm = &perfmon_main;
97 clib_error_t *error = 0;
100 perfmon_intel_pmc_event_t *ev;
103 pm->vnet_main = vnet_get_main ();
105 pm->capture_by_thread_and_node_name =
106 hash_create_string (0, sizeof (uword));
108 pm->log_class = vlib_log_register_class ("perfmon", 0);
110 /* Default data collection interval */
111 pm->timeout_interval = 2.0; /* seconds */
112 vec_validate (pm->pm_fds, 1);
113 vec_validate (pm->pm_fds[0], vec_len (vlib_mains) - 1);
114 vec_validate (pm->pm_fds[1], vec_len (vlib_mains) - 1);
115 vec_validate (pm->perf_event_pages, 1);
116 vec_validate (pm->perf_event_pages[0], vec_len (vlib_mains) - 1);
117 vec_validate (pm->perf_event_pages[1], vec_len (vlib_mains) - 1);
118 vec_validate (pm->rdpmc_indices, 1);
119 vec_validate (pm->rdpmc_indices[0], vec_len (vlib_mains) - 1);
120 vec_validate (pm->rdpmc_indices[1], vec_len (vlib_mains) - 1);
121 pm->page_size = getpagesize ();
123 pm->perfmon_table = 0;
124 pm->pmc_event_by_name = 0;
126 cpuid = get_cpuid ();
127 model = ((cpuid >> 12) & 0xf0) | ((cpuid >> 4) & 0xf);
128 stepping = cpuid & 0xf;
130 pm->perfmon_table = perfmon_find_table_by_model_stepping (pm,
133 if (pm->perfmon_table == 0)
135 vlib_log_err (pm->log_class, "No table for cpuid %x", cpuid);
136 vlib_log_err (pm->log_class, " model %x, stepping %x",
141 pm->pmc_event_by_name = hash_create_string (0, sizeof (u32));
142 ev = pm->perfmon_table;
144 for (; ev->event_name; ev++)
146 hash_set_mem (pm->pmc_event_by_name, ev->event_name,
147 ev - pm->perfmon_table);
154 VLIB_INIT_FUNCTION (perfmon_init);
157 VLIB_PLUGIN_REGISTER () =
159 .version = VPP_BUILD_VER,
160 .description = "Performance Monitor",
161 #if !defined(__x86_64__)
162 .default_disabled = 1,
168 unformat_processor_event (unformat_input_t * input, va_list * args)
170 perfmon_main_t *pm = va_arg (*args, perfmon_main_t *);
171 perfmon_event_config_t *ep = va_arg (*args, perfmon_event_config_t *);
177 if (pm->perfmon_table == 0 || pm->pmc_event_by_name == 0)
180 if (!unformat (input, "%s", &s))
183 hp = hash_get_pair_mem (pm->pmc_event_by_name, s);
190 idx = (u32) (hp->value[0]);
192 pe_config |= pm->perfmon_table[idx].event_code[0];
193 pe_config |= pm->perfmon_table[idx].umask << 8;
195 ep->name = (char *) hp->key;
196 ep->pe_type = PERF_TYPE_RAW;
197 ep->pe_config = pe_config;
201 static clib_error_t *
202 set_pmc_command_fn (vlib_main_t * vm,
203 unformat_input_t * input, vlib_cli_command_t * cmd)
205 perfmon_main_t *pm = &perfmon_main;
206 vlib_thread_main_t *vtm = vlib_get_thread_main ();
207 int num_threads = 1 + vtm->n_threads;
208 unformat_input_t _line_input, *line_input = &_line_input;
209 perfmon_event_config_t ec;
216 vec_reset_length (pm->single_events_to_collect);
217 vec_reset_length (pm->paired_events_to_collect);
218 pm->ipc_event_index = ~0;
219 pm->mispredict_event_index = ~0;
221 if (!unformat_user (input, unformat_line_input, line_input))
222 return clib_error_return (0, "counter names required...");
224 clib_bitmap_zero (pm->thread_bitmap);
226 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
228 if (unformat (line_input, "timeout %u", &timeout_seconds))
229 pm->timeout_interval = (f64) timeout_seconds;
230 else if (unformat (line_input, "instructions-per-clock"))
232 ec.name = "instructions";
233 ec.pe_type = PERF_TYPE_HARDWARE;
234 ec.pe_config = PERF_COUNT_HW_INSTRUCTIONS;
235 pm->ipc_event_index = vec_len (pm->paired_events_to_collect);
236 vec_add1 (pm->paired_events_to_collect, ec);
237 ec.name = "cpu-cycles";
238 ec.pe_type = PERF_TYPE_HARDWARE;
239 ec.pe_config = PERF_COUNT_HW_CPU_CYCLES;
240 vec_add1 (pm->paired_events_to_collect, ec);
242 else if (unformat (line_input, "branch-mispredict-rate"))
244 ec.name = "branch-misses";
245 ec.pe_type = PERF_TYPE_HARDWARE;
246 ec.pe_config = PERF_COUNT_HW_BRANCH_MISSES;
247 pm->mispredict_event_index = vec_len (pm->paired_events_to_collect);
248 vec_add1 (pm->paired_events_to_collect, ec);
249 ec.name = "branches";
250 ec.pe_type = PERF_TYPE_HARDWARE;
251 ec.pe_config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS;
252 vec_add1 (pm->paired_events_to_collect, ec);
254 else if (unformat (line_input, "threads %U",
255 unformat_bitmap_list, &pm->thread_bitmap))
257 else if (unformat (line_input, "thread %U",
258 unformat_bitmap_list, &pm->thread_bitmap))
260 else if (unformat (line_input, "%U", unformat_processor_event, pm, &ec))
262 vec_add1 (pm->single_events_to_collect, ec);
264 #define _(type,event,str) \
265 else if (unformat (line_input, str)) \
269 ec.pe_config = event; \
270 vec_add1 (pm->single_events_to_collect, ec); \
272 foreach_perfmon_event
276 error = clib_error_return (0, "unknown input '%U'",
277 format_unformat_error, line_input);
278 unformat_free (line_input);
283 unformat_free (line_input);
285 last_set = clib_bitmap_last_set (pm->thread_bitmap);
286 if (last_set != ~0 && last_set >= num_threads)
287 return clib_error_return (0, "thread %d does not exist", last_set);
289 /* Stick paired events at the front of the (unified) list */
290 if (vec_len (pm->paired_events_to_collect) > 0)
292 perfmon_event_config_t *tmp;
293 /* first 2n events are pairs... */
294 vec_append (pm->paired_events_to_collect, pm->single_events_to_collect);
295 tmp = pm->single_events_to_collect;
296 pm->single_events_to_collect = pm->paired_events_to_collect;
297 pm->paired_events_to_collect = tmp;
300 if (vec_len (pm->single_events_to_collect) == 0)
301 return clib_error_return (0, "no events specified...");
303 /* Figure out how long data collection will take */
305 ((f64) vec_len (pm->single_events_to_collect)) * pm->timeout_interval;
306 delay /= 2.0; /* collect 2 stats at once */
308 vlib_cli_output (vm, "Start collection for %d events, wait %.2f seconds",
309 vec_len (pm->single_events_to_collect), delay);
311 vlib_process_signal_event (pm->vlib_main, perfmon_periodic_node.index,
314 /* Coarse-grained wait */
315 vlib_process_suspend (vm, delay);
318 /* Reasonable to guess that collection may not be quite done... */
319 while (pm->state == PERFMON_STATE_RUNNING)
321 vlib_process_suspend (vm, 10e-3);
324 vlib_cli_output (vm, "DEADMAN: collection still running...");
329 vlib_cli_output (vm, "Data collection complete...");
334 VLIB_CLI_COMMAND (set_pmc_command, static) =
337 .short_help = "set pmc [threads n,n1-n2] c1... [see \"show pmc events\"]",
338 .function = set_pmc_command_fn,
344 capture_name_sort (void *a1, void *a2)
346 perfmon_capture_t *c1 = a1;
347 perfmon_capture_t *c2 = a2;
349 return strcmp ((char *) c1->thread_and_node_name,
350 (char *) c2->thread_and_node_name);
354 format_capture (u8 * s, va_list * args)
356 perfmon_main_t *pm = va_arg (*args, perfmon_main_t *);
357 perfmon_capture_t *c = va_arg (*args, perfmon_capture_t *);
358 int verbose __attribute__ ((unused)) = va_arg (*args, int);
364 s = format (s, "%=40s%=20s%=16s%=16s%=16s",
365 "Name", "Counter", "Count", "Pkts", "Counts/Pkt");
369 for (i = 0; i < vec_len (c->counter_names); i++)
374 name = c->thread_and_node_name;
381 /* Deal with synthetic events right here */
382 if (i == pm->ipc_event_index)
385 ASSERT ((i + 1) < vec_len (c->counter_names));
387 if (c->counter_values[i + 1] > 0)
388 ipc_rate = (f64) c->counter_values[i]
389 / (f64) c->counter_values[i + 1];
393 s = format (s, "%-40s%+20s%+16llu%+16llu%+16.2e\n",
394 name, "instructions-per-clock",
395 c->counter_values[i],
396 c->counter_values[i + 1], ipc_rate);
400 if (i == pm->mispredict_event_index)
403 ASSERT (i + 1 < vec_len (c->counter_names));
405 if (c->counter_values[i + 1] > 0)
406 mispredict_rate = (f64) c->counter_values[i]
407 / (f64) c->counter_values[i + 1];
409 mispredict_rate = 0.0;
411 s = format (s, "%-40s%+20s%+16llu%+16llu%+16.2e\n",
412 name, "branch-mispredict-rate",
413 c->counter_values[i],
414 c->counter_values[i + 1], mispredict_rate);
418 if (c->vectors_this_counter[i])
420 ((f64) c->counter_values[i]) / ((f64) c->vectors_this_counter[i]);
424 s = format (s, "%-40s%+20s%+16llu%+16llu%+16.2e",
425 name, c->counter_names[i],
426 c->counter_values[i],
427 c->vectors_this_counter[i], ticks_per_pkt);
433 format_generic_events (u8 * s, va_list * args)
435 int verbose = va_arg (*args, int);
437 #define _(type,config,name) \
439 s = format (s, "\n %s", name); \
441 s = format (s, "\n %s (%d, %d)", name, type, config);
442 foreach_perfmon_event;
454 sort_nvps_by_name (void *a1, void *a2)
456 sort_nvp_t *nvp1 = a1;
457 sort_nvp_t *nvp2 = a2;
459 return strcmp ((char *) nvp1->name, (char *) nvp2->name);
463 format_pmc_event (u8 * s, va_list * args)
465 perfmon_intel_pmc_event_t *ev = va_arg (*args, perfmon_intel_pmc_event_t *);
467 s = format (s, "%s\n", ev->event_name);
468 s = format (s, " umask: 0x%x\n", ev->umask);
469 s = format (s, " code: 0x%x", ev->event_code[0]);
471 if (ev->event_code[1])
472 s = format (s, " , 0x%x\n", ev->event_code[1]);
474 s = format (s, "\n");
480 format_processor_events (u8 * s, va_list * args)
482 perfmon_main_t *pm = va_arg (*args, perfmon_main_t *);
483 int verbose = va_arg (*args, int);
484 sort_nvp_t *sort_nvps = 0;
490 hash_foreach_mem (key, value, pm->pmc_event_by_name,
492 vec_add2 (sort_nvps, sn, 1);
497 vec_sort_with_function (sort_nvps, sort_nvps_by_name);
501 vec_foreach (sn, sort_nvps)
502 s = format (s, "\n %s ", sn->name);
506 vec_foreach (sn, sort_nvps)
507 s = format(s, "%U", format_pmc_event, &pm->perfmon_table[sn->index]);
509 vec_free (sort_nvps);
514 static clib_error_t *
515 show_pmc_command_fn (vlib_main_t * vm,
516 unformat_input_t * input, vlib_cli_command_t * cmd)
518 perfmon_main_t *pm = &perfmon_main;
522 perfmon_capture_t *c;
523 perfmon_capture_t *captures = 0;
525 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
527 if (unformat (input, "events"))
529 else if (unformat (input, "verbose"))
537 vlib_cli_output (vm, "Generic Events %U",
538 format_generic_events, verbose);
539 vlib_cli_output (vm, "Synthetic Events");
540 vlib_cli_output (vm, " instructions-per-clock");
541 vlib_cli_output (vm, " branch-mispredict-rate");
542 if (pm->perfmon_table)
543 vlib_cli_output (vm, "Processor Events %U",
544 format_processor_events, pm, verbose);
548 if (pm->state == PERFMON_STATE_RUNNING)
550 vlib_cli_output (vm, "Data collection in progress...");
554 if (pool_elts (pm->capture_pool) == 0)
556 vlib_cli_output (vm, "No data...");
561 pool_foreach (c, pm->capture_pool,
563 vec_add1 (captures, *c);
567 vec_sort_with_function (captures, capture_name_sort);
569 vlib_cli_output (vm, "%U", format_capture, pm, 0 /* header */ ,
572 for (i = 0; i < vec_len (captures); i++)
576 vlib_cli_output (vm, "%U", format_capture, pm, c, verbose);
585 VLIB_CLI_COMMAND (show_pmc_command, static) =
588 .short_help = "show pmc [verbose]",
589 .function = show_pmc_command_fn,
594 static clib_error_t *
595 clear_pmc_command_fn (vlib_main_t * vm,
596 unformat_input_t * input, vlib_cli_command_t * cmd)
598 perfmon_main_t *pm = &perfmon_main;
602 if (pm->state == PERFMON_STATE_RUNNING)
604 vlib_cli_output (vm, "Performance monitor is still running...");
608 pool_free (pm->capture_pool);
611 hash_foreach_mem (key, value, pm->capture_by_thread_and_node_name,
616 hash_free (pm->capture_by_thread_and_node_name);
617 pm->capture_by_thread_and_node_name =
618 hash_create_string (0, sizeof (uword));
623 VLIB_CLI_COMMAND (clear_pmc_command, static) =
626 .short_help = "clear the performance monitor counters",
627 .function = clear_pmc_command_fn,
633 * fd.io coding-style-patch-verification: ON
636 * eval: (c-set-style "gnu")