Remove c-11 memcpy checks from perf-critical code
[vpp.git] / src / plugins / perfmon / perfmon_periodic.c
1 /*
2  * perfmon_periodic.c - skeleton plug-in periodic function
3  *
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:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17
18 #include <vlib/vlib.h>
19 #include <vppinfra/error.h>
20 #include <perfmon/perfmon.h>
21 #include <asm/unistd.h>
22 #include <sys/ioctl.h>
23
24 static long
25 perf_event_open (struct perf_event_attr *hw_event, pid_t pid, int cpu,
26                  int group_fd, unsigned long flags)
27 {
28   int ret;
29
30   ret = syscall (__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
31   return ret;
32 }
33
34 static u64
35 read_current_perf_counter (vlib_main_t * vm)
36 {
37   if (vm->perf_counter_id)
38     return clib_rdpmc (vm->perf_counter_id);
39   else
40     {
41       u64 sw_value;
42       if (read (vm->perf_counter_fd, &sw_value, sizeof (sw_value)) !=
43           sizeof (sw_value))
44         {
45           clib_unix_warning ("counter read failed, disable collection...");
46           vm->vlib_node_runtime_perf_counter_cb = 0;
47           return 0ULL;
48         }
49       return sw_value;
50     }
51 }
52
53 static void
54 clear_counters (perfmon_main_t * pm)
55 {
56   int i, j;
57   vlib_main_t *vm = pm->vlib_main;
58   vlib_main_t *stat_vm;
59   vlib_node_main_t *nm;
60   vlib_node_t *n;
61
62   vlib_worker_thread_barrier_sync (vm);
63
64   for (j = 0; j < vec_len (vlib_mains); j++)
65     {
66       stat_vm = vlib_mains[j];
67       if (stat_vm == 0)
68         continue;
69
70       nm = &stat_vm->node_main;
71
72       /* Clear the node runtime perfmon counters */
73       for (i = 0; i < vec_len (nm->nodes); i++)
74         {
75           n = nm->nodes[i];
76           vlib_node_sync_stats (stat_vm, n);
77         }
78
79       /* And clear the node perfmon counters */
80       for (i = 0; i < vec_len (nm->nodes); i++)
81         {
82           n = nm->nodes[i];
83           n->stats_total.perf_counter_ticks = 0;
84           n->stats_total.perf_counter_vectors = 0;
85           n->stats_last_clear.perf_counter_ticks = 0;
86           n->stats_last_clear.perf_counter_vectors = 0;
87         }
88     }
89   vlib_worker_thread_barrier_release (vm);
90 }
91
92 static void
93 enable_current_event (perfmon_main_t * pm)
94 {
95   struct perf_event_attr pe;
96   int fd;
97   struct perf_event_mmap_page *p = 0;
98   perfmon_event_config_t *c;
99   vlib_main_t *vm = vlib_get_main ();
100   u32 my_thread_index = vm->thread_index;
101
102   c = vec_elt_at_index (pm->events_to_collect, pm->current_event);
103
104   memset (&pe, 0, sizeof (struct perf_event_attr));
105   pe.type = c->pe_type;
106   pe.size = sizeof (struct perf_event_attr);
107   pe.config = c->pe_config;
108   pe.disabled = 1;
109   pe.pinned = 1;
110   /*
111    * Note: excluding the kernel makes the
112    * (software) context-switch counter read 0...
113    */
114   if (pe.type != PERF_TYPE_SOFTWARE)
115     {
116       /* Exclude kernel and hypervisor */
117       pe.exclude_kernel = 1;
118       pe.exclude_hv = 1;
119     }
120
121   fd = perf_event_open (&pe, 0, -1, -1, 0);
122   if (fd == -1)
123     {
124       clib_unix_warning ("event open: type %d config %d", c->pe_type,
125                          c->pe_config);
126       return;
127     }
128
129   if (pe.type != PERF_TYPE_SOFTWARE)
130     {
131       p = mmap (0, pm->page_size, PROT_READ, MAP_SHARED, fd, 0);
132       if (p == MAP_FAILED)
133         {
134           clib_unix_warning ("mmap");
135           close (fd);
136           return;
137         }
138     }
139
140   if (ioctl (fd, PERF_EVENT_IOC_RESET, 0) < 0)
141     clib_unix_warning ("reset ioctl");
142
143   if (ioctl (fd, PERF_EVENT_IOC_ENABLE, 0) < 0)
144     clib_unix_warning ("enable ioctl");
145
146   /*
147    * Software event counters - and others not capable of being
148    * read via the "rdpmc" instruction - will be read
149    * by system calls.
150    */
151   if (pe.type == PERF_TYPE_SOFTWARE || p->cap_user_rdpmc == 0)
152     pm->rdpmc_indices[my_thread_index] = 0;
153   else                          /* use rdpmc instrs */
154     pm->rdpmc_indices[my_thread_index] = p->index - 1;
155   pm->perf_event_pages[my_thread_index] = (void *) p;
156
157   pm->pm_fds[my_thread_index] = fd;
158
159   /* Enable the main loop counter snapshot mechanism */
160   vm->perf_counter_id = pm->rdpmc_indices[my_thread_index];
161   vm->perf_counter_fd = fd;
162   vm->vlib_node_runtime_perf_counter_cb = read_current_perf_counter;
163 }
164
165 static void
166 disable_event (perfmon_main_t * pm)
167 {
168   vlib_main_t *vm = vlib_get_main ();
169   u32 my_thread_index = vm->thread_index;
170
171   if (pm->pm_fds[my_thread_index] == 0)
172     return;
173
174   /* Stop main loop collection */
175   vm->vlib_node_runtime_perf_counter_cb = 0;
176
177   if (ioctl (pm->pm_fds[my_thread_index], PERF_EVENT_IOC_DISABLE, 0) < 0)
178     clib_unix_warning ("disable ioctl");
179
180   if (pm->perf_event_pages[my_thread_index])
181     if (munmap (pm->perf_event_pages[my_thread_index], pm->page_size) < 0)
182       clib_unix_warning ("munmap");
183
184   (void) close (pm->pm_fds[my_thread_index]);
185   pm->pm_fds[my_thread_index] = 0;
186 }
187
188 static void
189 worker_thread_start_event (vlib_main_t * vm)
190 {
191   perfmon_main_t *pm = &perfmon_main;
192
193   enable_current_event (pm);
194   vm->worker_thread_main_loop_callback = 0;
195 }
196
197 static void
198 worker_thread_stop_event (vlib_main_t * vm)
199 {
200   perfmon_main_t *pm = &perfmon_main;
201   disable_event (pm);
202   vm->worker_thread_main_loop_callback = 0;
203 }
204
205 static void
206 start_event (perfmon_main_t * pm, f64 now, uword event_data)
207 {
208   int i;
209   pm->current_event = 0;
210   if (vec_len (pm->events_to_collect) == 0)
211     {
212       pm->state = PERFMON_STATE_OFF;
213       return;
214     }
215   pm->state = PERFMON_STATE_RUNNING;
216   clear_counters (pm);
217
218   /* Start collection on this thread */
219   enable_current_event (pm);
220
221   /* And also on worker threads */
222   for (i = 1; i < vec_len (vlib_mains); i++)
223     {
224       if (vlib_mains[i] == 0)
225         continue;
226       vlib_mains[i]->worker_thread_main_loop_callback = (void *)
227         worker_thread_start_event;
228     }
229 }
230
231 void
232 scrape_and_clear_counters (perfmon_main_t * pm)
233 {
234   int i, j;
235   vlib_main_t *vm = pm->vlib_main;
236   vlib_main_t *stat_vm;
237   vlib_node_main_t *nm;
238   vlib_node_t ***node_dups = 0;
239   vlib_node_t **nodes;
240   vlib_node_t *n;
241   perfmon_capture_t *c;
242   perfmon_event_config_t *current_event;
243   uword *p;
244   u8 *counter_name;
245   u64 counter_value;
246   u64 vectors_this_counter;
247
248   /* snapshoot the nodes, including pm counters */
249   vlib_worker_thread_barrier_sync (vm);
250
251   for (j = 0; j < vec_len (vlib_mains); j++)
252     {
253       stat_vm = vlib_mains[j];
254       if (stat_vm == 0)
255         continue;
256
257       nm = &stat_vm->node_main;
258
259       for (i = 0; i < vec_len (nm->nodes); i++)
260         {
261           n = nm->nodes[i];
262           vlib_node_sync_stats (stat_vm, n);
263         }
264
265       nodes = 0;
266       vec_validate (nodes, vec_len (nm->nodes) - 1);
267       vec_add1 (node_dups, nodes);
268
269       /* Snapshoot and clear the per-node perfmon counters */
270       for (i = 0; i < vec_len (nm->nodes); i++)
271         {
272           n = nm->nodes[i];
273           nodes[i] = clib_mem_alloc (sizeof (*n));
274           clib_memcpy_fast (nodes[i], n, sizeof (*n));
275           n->stats_total.perf_counter_ticks = 0;
276           n->stats_total.perf_counter_vectors = 0;
277           n->stats_last_clear.perf_counter_ticks = 0;
278           n->stats_last_clear.perf_counter_vectors = 0;
279         }
280     }
281
282   vlib_worker_thread_barrier_release (vm);
283
284   current_event = pm->events_to_collect + pm->current_event;
285
286   for (j = 0; j < vec_len (vlib_mains); j++)
287     {
288       stat_vm = vlib_mains[j];
289       if (stat_vm == 0)
290         continue;
291
292       nodes = node_dups[j];
293
294       for (i = 0; i < vec_len (nodes); i++)
295         {
296           u8 *capture_name;
297
298           n = nodes[i];
299           if (n->stats_total.perf_counter_ticks == 0)
300             {
301               clib_mem_free (n);
302               continue;
303             }
304
305           capture_name = format (0, "t%d-%v%c", j, n->name, 0);
306
307           p = hash_get_mem (pm->capture_by_thread_and_node_name,
308                             capture_name);
309
310           if (p == 0)
311             {
312               pool_get (pm->capture_pool, c);
313               memset (c, 0, sizeof (*c));
314               c->thread_and_node_name = capture_name;
315               hash_set_mem (pm->capture_by_thread_and_node_name,
316                             capture_name, c - pm->capture_pool);
317             }
318           else
319             c = pool_elt_at_index (pm->capture_pool, p[0]);
320
321           /* Snapshoot counters, etc. into the capture */
322           counter_name = (u8 *) current_event->name;
323           counter_value = n->stats_total.perf_counter_ticks -
324             n->stats_last_clear.perf_counter_ticks;
325           vectors_this_counter = n->stats_total.perf_counter_vectors -
326             n->stats_last_clear.perf_counter_vectors;
327
328           vec_add1 (c->counter_names, counter_name);
329           vec_add1 (c->counter_values, counter_value);
330           vec_add1 (c->vectors_this_counter, vectors_this_counter);
331           clib_mem_free (n);
332         }
333       vec_free (nodes);
334     }
335   vec_free (node_dups);
336 }
337
338 static void
339 handle_timeout (perfmon_main_t * pm, f64 now)
340 {
341   int i;
342   disable_event (pm);
343
344   /* And also on worker threads */
345   for (i = 1; i < vec_len (vlib_mains); i++)
346     {
347       if (vlib_mains[i] == 0)
348         continue;
349       vlib_mains[i]->worker_thread_main_loop_callback = (void *)
350         worker_thread_stop_event;
351     }
352
353   /* Short delay to make sure workers have stopped collection */
354   if (i > 1)
355     vlib_process_suspend (pm->vlib_main, 1e-3);
356   scrape_and_clear_counters (pm);
357   pm->current_event++;
358   if (pm->current_event >= vec_len (pm->events_to_collect))
359     {
360       pm->current_event = 0;
361       pm->state = PERFMON_STATE_OFF;
362       return;
363     }
364   enable_current_event (pm);
365
366   /* And also on worker threads */
367   for (i = 1; i < vec_len (vlib_mains); i++)
368     {
369       if (vlib_mains[i] == 0)
370         continue;
371       vlib_mains[i]->worker_thread_main_loop_callback = (void *)
372         worker_thread_start_event;
373     }
374 }
375
376 static uword
377 perfmon_periodic_process (vlib_main_t * vm,
378                           vlib_node_runtime_t * rt, vlib_frame_t * f)
379 {
380   perfmon_main_t *pm = &perfmon_main;
381   f64 now;
382   uword *event_data = 0;
383   uword event_type;
384   int i;
385
386   while (1)
387     {
388       if (pm->state == PERFMON_STATE_RUNNING)
389         vlib_process_wait_for_event_or_clock (vm, pm->timeout_interval);
390       else
391         vlib_process_wait_for_event (vm);
392
393       now = vlib_time_now (vm);
394
395       event_type = vlib_process_get_events (vm, (uword **) & event_data);
396
397       switch (event_type)
398         {
399         case PERFMON_START:
400           for (i = 0; i < vec_len (event_data); i++)
401             start_event (pm, now, event_data[i]);
402           break;
403
404           /* Handle timeout */
405         case ~0:
406           handle_timeout (pm, now);
407           break;
408
409         default:
410           clib_warning ("Unexpected event %d", event_type);
411           break;
412         }
413       vec_reset_length (event_data);
414     }
415   return 0;                     /* or not */
416 }
417
418 /* *INDENT-OFF* */
419 VLIB_REGISTER_NODE (perfmon_periodic_node) =
420 {
421   .function = perfmon_periodic_process,
422   .type = VLIB_NODE_TYPE_PROCESS,
423   .name = "perfmon-periodic-process",
424 };
425 /* *INDENT-ON* */
426
427 /*
428  * fd.io coding-style-patch-verification: ON
429  *
430  * Local Variables:
431  * eval: (c-set-style "gnu")
432  * End:
433  */