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->perf_event_pages, 1);
114 vec_validate (pm->rdpmc_indices, 1);
115 pm->page_size = getpagesize ();
117 pm->perfmon_table = 0;
118 pm->pmc_event_by_name = 0;
120 cpuid = get_cpuid ();
121 model = ((cpuid >> 12) & 0xf0) | ((cpuid >> 4) & 0xf);
122 stepping = cpuid & 0xf;
124 pm->perfmon_table = perfmon_find_table_by_model_stepping (pm,
127 if (pm->perfmon_table == 0)
129 vlib_log_err (pm->log_class, "No table for cpuid %x", cpuid);
130 vlib_log_err (pm->log_class, " model %x, stepping %x",
135 pm->pmc_event_by_name = hash_create_string (0, sizeof (u32));
136 ev = pm->perfmon_table;
138 for (; ev->event_name; ev++)
140 hash_set_mem (pm->pmc_event_by_name, ev->event_name,
141 ev - pm->perfmon_table);
148 VLIB_INIT_FUNCTION (perfmon_init);
151 VLIB_PLUGIN_REGISTER () =
153 .version = VPP_BUILD_VER,
154 .description = "Performance Monitor",
155 #if !defined(__x86_64__)
156 .default_disabled = 1,
162 unformat_processor_event (unformat_input_t * input, va_list * args)
164 perfmon_main_t *pm = va_arg (*args, perfmon_main_t *);
165 perfmon_event_config_t *ep = va_arg (*args, perfmon_event_config_t *);
171 if (pm->perfmon_table == 0 || pm->pmc_event_by_name == 0)
174 if (!unformat (input, "%s", &s))
177 hp = hash_get_pair_mem (pm->pmc_event_by_name, s);
184 idx = (u32) (hp->value[0]);
186 pe_config |= pm->perfmon_table[idx].event_code[0];
187 pe_config |= pm->perfmon_table[idx].umask << 8;
189 ep->name = (char *) hp->key;
190 ep->pe_type = PERF_TYPE_RAW;
191 ep->pe_config = pe_config;
195 static clib_error_t *
196 set_pmc_command_fn (vlib_main_t * vm,
197 unformat_input_t * input, vlib_cli_command_t * cmd)
199 perfmon_main_t *pm = &perfmon_main;
200 vlib_thread_main_t *vtm = vlib_get_thread_main ();
201 int num_threads = 1 + vtm->n_threads;
202 unformat_input_t _line_input, *line_input = &_line_input;
203 perfmon_event_config_t ec;
210 vec_reset_length (pm->single_events_to_collect);
211 vec_reset_length (pm->paired_events_to_collect);
212 pm->ipc_event_index = ~0;
213 pm->mispredict_event_index = ~0;
215 if (!unformat_user (input, unformat_line_input, line_input))
216 return clib_error_return (0, "counter names required...");
218 clib_bitmap_zero (pm->thread_bitmap);
220 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
222 if (unformat (line_input, "timeout %u", &timeout_seconds))
223 pm->timeout_interval = (f64) timeout_seconds;
224 else if (unformat (line_input, "instructions-per-clock"))
226 ec.name = "instructions";
227 ec.pe_type = PERF_TYPE_HARDWARE;
228 ec.pe_config = PERF_COUNT_HW_INSTRUCTIONS;
229 pm->ipc_event_index = vec_len (pm->paired_events_to_collect);
230 vec_add1 (pm->paired_events_to_collect, ec);
231 ec.name = "cpu-cycles";
232 ec.pe_type = PERF_TYPE_HARDWARE;
233 ec.pe_config = PERF_COUNT_HW_CPU_CYCLES;
234 vec_add1 (pm->paired_events_to_collect, ec);
236 else if (unformat (line_input, "branch-mispredict-rate"))
238 ec.name = "branch-misses";
239 ec.pe_type = PERF_TYPE_HARDWARE;
240 ec.pe_config = PERF_COUNT_HW_BRANCH_MISSES;
241 pm->mispredict_event_index = vec_len (pm->paired_events_to_collect);
242 vec_add1 (pm->paired_events_to_collect, ec);
243 ec.name = "branches";
244 ec.pe_type = PERF_TYPE_HARDWARE;
245 ec.pe_config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS;
246 vec_add1 (pm->paired_events_to_collect, ec);
248 else if (unformat (line_input, "threads %U",
249 unformat_bitmap_list, &pm->thread_bitmap))
251 else if (unformat (line_input, "thread %U",
252 unformat_bitmap_list, &pm->thread_bitmap))
254 else if (unformat (line_input, "%U", unformat_processor_event, pm, &ec))
256 vec_add1 (pm->single_events_to_collect, ec);
258 #define _(type,event,str) \
259 else if (unformat (line_input, str)) \
263 ec.pe_config = event; \
264 vec_add1 (pm->single_events_to_collect, ec); \
266 foreach_perfmon_event
270 error = clib_error_return (0, "unknown input '%U'",
271 format_unformat_error, line_input);
272 unformat_free (line_input);
277 unformat_free (line_input);
279 last_set = clib_bitmap_last_set (pm->thread_bitmap);
280 if (last_set != ~0 && last_set >= num_threads)
281 return clib_error_return (0, "thread %d does not exist", last_set);
283 /* Stick paired events at the front of the (unified) list */
284 if (vec_len (pm->paired_events_to_collect) > 0)
286 perfmon_event_config_t *tmp;
287 /* first 2n events are pairs... */
288 vec_append (pm->paired_events_to_collect, pm->single_events_to_collect);
289 tmp = pm->single_events_to_collect;
290 pm->single_events_to_collect = pm->paired_events_to_collect;
291 pm->paired_events_to_collect = tmp;
294 if (vec_len (pm->single_events_to_collect) == 0)
295 return clib_error_return (0, "no events specified...");
297 /* Figure out how long data collection will take */
299 ((f64) vec_len (pm->single_events_to_collect)) * pm->timeout_interval;
300 delay /= 2.0; /* collect 2 stats at once */
302 vlib_cli_output (vm, "Start collection for %d events, wait %.2f seconds",
303 vec_len (pm->single_events_to_collect), delay);
305 vlib_process_signal_event (pm->vlib_main, perfmon_periodic_node.index,
308 /* Coarse-grained wait */
309 vlib_process_suspend (vm, delay);
312 /* Reasonable to guess that collection may not be quite done... */
313 while (pm->state == PERFMON_STATE_RUNNING)
315 vlib_process_suspend (vm, 10e-3);
318 vlib_cli_output (vm, "DEADMAN: collection still running...");
323 vlib_cli_output (vm, "Data collection complete...");
328 VLIB_CLI_COMMAND (set_pmc_command, static) =
331 .short_help = "set pmc [threads n,n1-n2] c1... [see \"show pmc events\"]",
332 .function = set_pmc_command_fn,
338 capture_name_sort (void *a1, void *a2)
340 perfmon_capture_t *c1 = a1;
341 perfmon_capture_t *c2 = a2;
343 return strcmp ((char *) c1->thread_and_node_name,
344 (char *) c2->thread_and_node_name);
348 format_capture (u8 * s, va_list * args)
350 perfmon_main_t *pm = va_arg (*args, perfmon_main_t *);
351 perfmon_capture_t *c = va_arg (*args, perfmon_capture_t *);
352 int verbose __attribute__ ((unused)) = va_arg (*args, int);
358 s = format (s, "%=40s%=20s%=16s%=16s%=16s",
359 "Name", "Counter", "Count", "Pkts", "Counts/Pkt");
363 for (i = 0; i < vec_len (c->counter_names); i++)
368 name = c->thread_and_node_name;
375 /* Deal with synthetic events right here */
376 if (i == pm->ipc_event_index)
379 ASSERT ((i + 1) < vec_len (c->counter_names));
381 if (c->counter_values[i + 1] > 0)
382 ipc_rate = (f64) c->counter_values[i]
383 / (f64) c->counter_values[i + 1];
387 s = format (s, "%-40s%+20s%+16llu%+16llu%+16.2e\n",
388 name, "instructions-per-clock",
389 c->counter_values[i],
390 c->counter_values[i + 1], ipc_rate);
394 if (i == pm->mispredict_event_index)
397 ASSERT (i + 1 < vec_len (c->counter_names));
399 if (c->counter_values[i + 1] > 0)
400 mispredict_rate = (f64) c->counter_values[i]
401 / (f64) c->counter_values[i + 1];
403 mispredict_rate = 0.0;
405 s = format (s, "%-40s%+20s%+16llu%+16llu%+16.2e\n",
406 name, "branch-mispredict-rate",
407 c->counter_values[i],
408 c->counter_values[i + 1], mispredict_rate);
412 if (c->vectors_this_counter[i])
414 ((f64) c->counter_values[i]) / ((f64) c->vectors_this_counter[i]);
418 s = format (s, "%-40s%+20s%+16llu%+16llu%+16.2e",
419 name, c->counter_names[i],
420 c->counter_values[i],
421 c->vectors_this_counter[i], ticks_per_pkt);
427 format_generic_events (u8 * s, va_list * args)
429 int verbose = va_arg (*args, int);
431 #define _(type,config,name) \
433 s = format (s, "\n %s", name); \
435 s = format (s, "\n %s (%d, %d)", name, type, config);
436 foreach_perfmon_event;
448 sort_nvps_by_name (void *a1, void *a2)
450 sort_nvp_t *nvp1 = a1;
451 sort_nvp_t *nvp2 = a2;
453 return strcmp ((char *) nvp1->name, (char *) nvp2->name);
457 format_pmc_event (u8 * s, va_list * args)
459 perfmon_intel_pmc_event_t *ev = va_arg (*args, perfmon_intel_pmc_event_t *);
461 s = format (s, "%s\n", ev->event_name);
462 s = format (s, " umask: 0x%x\n", ev->umask);
463 s = format (s, " code: 0x%x", ev->event_code[0]);
465 if (ev->event_code[1])
466 s = format (s, " , 0x%x\n", ev->event_code[1]);
468 s = format (s, "\n");
474 format_processor_events (u8 * s, va_list * args)
476 perfmon_main_t *pm = va_arg (*args, perfmon_main_t *);
477 int verbose = va_arg (*args, int);
478 sort_nvp_t *sort_nvps = 0;
484 hash_foreach_mem (key, value, pm->pmc_event_by_name,
486 vec_add2 (sort_nvps, sn, 1);
491 vec_sort_with_function (sort_nvps, sort_nvps_by_name);
495 vec_foreach (sn, sort_nvps)
496 s = format (s, "\n %s ", sn->name);
500 vec_foreach (sn, sort_nvps)
501 s = format(s, "%U", format_pmc_event, &pm->perfmon_table[sn->index]);
503 vec_free (sort_nvps);
508 static clib_error_t *
509 show_pmc_command_fn (vlib_main_t * vm,
510 unformat_input_t * input, vlib_cli_command_t * cmd)
512 perfmon_main_t *pm = &perfmon_main;
516 perfmon_capture_t *c;
517 perfmon_capture_t *captures = 0;
519 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
521 if (unformat (input, "events"))
523 else if (unformat (input, "verbose"))
531 vlib_cli_output (vm, "Generic Events %U",
532 format_generic_events, verbose);
533 vlib_cli_output (vm, "Synthetic Events");
534 vlib_cli_output (vm, " instructions-per-clock");
535 vlib_cli_output (vm, " branch-mispredict-rate");
536 if (pm->perfmon_table)
537 vlib_cli_output (vm, "Processor Events %U",
538 format_processor_events, pm, verbose);
542 if (pm->state == PERFMON_STATE_RUNNING)
544 vlib_cli_output (vm, "Data collection in progress...");
548 if (pool_elts (pm->capture_pool) == 0)
550 vlib_cli_output (vm, "No data...");
555 pool_foreach (c, pm->capture_pool,
557 vec_add1 (captures, *c);
561 vec_sort_with_function (captures, capture_name_sort);
563 vlib_cli_output (vm, "%U", format_capture, pm, 0 /* header */ ,
566 for (i = 0; i < vec_len (captures); i++)
570 vlib_cli_output (vm, "%U", format_capture, pm, c, verbose);
579 VLIB_CLI_COMMAND (show_pmc_command, static) =
582 .short_help = "show pmc [verbose]",
583 .function = show_pmc_command_fn,
588 static clib_error_t *
589 clear_pmc_command_fn (vlib_main_t * vm,
590 unformat_input_t * input, vlib_cli_command_t * cmd)
592 perfmon_main_t *pm = &perfmon_main;
596 if (pm->state == PERFMON_STATE_RUNNING)
598 vlib_cli_output (vm, "Performance monitor is still running...");
602 pool_free (pm->capture_pool);
605 hash_foreach_mem (key, value, pm->capture_by_thread_and_node_name,
610 hash_free (pm->capture_by_thread_and_node_name);
611 pm->capture_by_thread_and_node_name =
612 hash_create_string (0, sizeof (uword));
617 VLIB_CLI_COMMAND (clear_pmc_command, static) =
620 .short_help = "clear the performance monitor counters",
621 .function = clear_pmc_command_fn,
627 * fd.io coding-style-patch-verification: ON
630 * eval: (c-set-style "gnu")