misc: asan: do not poison memory after munmap()
[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 /* "not in glibc" */
25 static long
26 perf_event_open (struct perf_event_attr *hw_event, pid_t pid, int cpu,
27                  int group_fd, unsigned long flags)
28 {
29   int ret;
30
31   ret = syscall (__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
32   return ret;
33 }
34
35 static void
36 read_current_perf_counters (vlib_main_t * vm, u64 * c0, u64 * c1,
37                             vlib_node_runtime_t * node,
38                             vlib_frame_t * frame, int before_or_after)
39 {
40   int i;
41   u64 *cc;
42   perfmon_main_t *pm = &perfmon_main;
43   uword my_thread_index = vm->thread_index;
44
45   *c0 = *c1 = 0;
46
47   for (i = 0; i < pm->n_active; i++)
48     {
49       cc = (i == 0) ? c0 : c1;
50       if (pm->rdpmc_indices[i][my_thread_index] != ~0)
51         *cc = clib_rdpmc ((int) pm->rdpmc_indices[i][my_thread_index]);
52       else
53         {
54           u64 sw_value;
55           int read_result;
56           if ((read_result = read (pm->pm_fds[i][my_thread_index], &sw_value,
57                                    sizeof (sw_value)) != sizeof (sw_value)))
58             {
59               clib_unix_warning
60                 ("counter read returned %d, expected %d",
61                  read_result, sizeof (sw_value));
62               clib_callback_enable_disable
63                 (vm->vlib_node_runtime_perf_counter_cbs,
64                  vm->vlib_node_runtime_perf_counter_cb_tmp,
65                  vm->worker_thread_main_loop_callback_lock,
66                  read_current_perf_counters, 0 /* enable */ );
67               return;
68             }
69           *cc = sw_value;
70         }
71     }
72 }
73
74 static void
75 clear_counters (perfmon_main_t * pm)
76 {
77   int i, j;
78   vlib_main_t *vm = pm->vlib_main;
79   vlib_main_t *stat_vm;
80   vlib_node_main_t *nm;
81   vlib_node_t *n;
82
83   vlib_worker_thread_barrier_sync (vm);
84
85   for (j = 0; j < vec_len (vlib_mains); j++)
86     {
87       stat_vm = vlib_mains[j];
88       if (stat_vm == 0)
89         continue;
90
91       nm = &stat_vm->node_main;
92
93       /* Clear the node runtime perfmon counters */
94       for (i = 0; i < vec_len (nm->nodes); i++)
95         {
96           n = nm->nodes[i];
97           vlib_node_sync_stats (stat_vm, n);
98         }
99
100       /* And clear the node perfmon counters */
101       for (i = 0; i < vec_len (nm->nodes); i++)
102         {
103           n = nm->nodes[i];
104           n->stats_total.perf_counter0_ticks = 0;
105           n->stats_total.perf_counter1_ticks = 0;
106           n->stats_total.perf_counter_vectors = 0;
107           n->stats_last_clear.perf_counter0_ticks = 0;
108           n->stats_last_clear.perf_counter1_ticks = 0;
109           n->stats_last_clear.perf_counter_vectors = 0;
110         }
111     }
112   vlib_worker_thread_barrier_release (vm);
113 }
114
115 static void
116 enable_current_events (perfmon_main_t * pm)
117 {
118   struct perf_event_attr pe;
119   int fd;
120   struct perf_event_mmap_page *p = 0;
121   perfmon_event_config_t *c;
122   vlib_main_t *vm = vlib_get_main ();
123   u32 my_thread_index = vm->thread_index;
124   u32 index;
125   int i, limit = 1;
126   int cpu;
127
128   if ((pm->current_event + 1) < vec_len (pm->single_events_to_collect))
129     limit = 2;
130
131   for (i = 0; i < limit; i++)
132     {
133       vec_validate (pm->pm_fds[i], vec_len (vlib_mains) - 1);
134       vec_validate (pm->perf_event_pages[i], vec_len (vlib_mains) - 1);
135       vec_validate (pm->rdpmc_indices[i], vec_len (vlib_mains) - 1);
136
137       c = vec_elt_at_index (pm->single_events_to_collect,
138                             pm->current_event + i);
139
140       memset (&pe, 0, sizeof (struct perf_event_attr));
141       pe.type = c->pe_type;
142       pe.size = sizeof (struct perf_event_attr);
143       pe.config = c->pe_config;
144       pe.disabled = 1;
145       pe.pinned = 1;
146       /*
147        * Note: excluding the kernel makes the
148        * (software) context-switch counter read 0...
149        */
150       if (pe.type != PERF_TYPE_SOFTWARE)
151         {
152           /* Exclude kernel and hypervisor */
153           pe.exclude_kernel = 1;
154           pe.exclude_hv = 1;
155         }
156
157       cpu = vm->cpu_id;
158
159       fd = perf_event_open (&pe, 0, cpu, -1, 0);
160       if (fd == -1)
161         {
162           clib_unix_warning ("event open: type %d config %d", c->pe_type,
163                              c->pe_config);
164           return;
165         }
166
167       if (pe.type != PERF_TYPE_SOFTWARE)
168         {
169           p = mmap (0, pm->page_size, PROT_READ, MAP_SHARED, fd, 0);
170           if (p == MAP_FAILED)
171             {
172               clib_unix_warning ("mmap");
173               close (fd);
174               return;
175             }
176           CLIB_MEM_UNPOISON (p, pm->page_size);
177         }
178       else
179         p = 0;
180
181       if (ioctl (fd, PERF_EVENT_IOC_RESET, 0) < 0)
182         clib_unix_warning ("reset ioctl");
183
184       if (ioctl (fd, PERF_EVENT_IOC_ENABLE, 0) < 0)
185         clib_unix_warning ("enable ioctl");
186
187       pm->perf_event_pages[i][my_thread_index] = (void *) p;
188       pm->pm_fds[i][my_thread_index] = fd;
189     }
190
191   /*
192    * Hardware events must be all opened and enabled before aquiring
193    * pmc indices, otherwise the pmc indices might be out-dated.
194    */
195   for (i = 0; i < limit; i++)
196     {
197       p =
198         (struct perf_event_mmap_page *)
199         pm->perf_event_pages[i][my_thread_index];
200
201       /*
202        * Software event counters - and others not capable of being
203        * read via the "rdpmc" instruction - will be read
204        * by system calls.
205        */
206       if (p == 0 || p->cap_user_rdpmc == 0)
207         index = ~0;
208       else
209         index = p->index - 1;
210
211       pm->rdpmc_indices[i][my_thread_index] = index;
212     }
213
214   pm->n_active = i;
215   /* Enable the main loop counter snapshot mechanism */
216   clib_callback_enable_disable
217     (vm->vlib_node_runtime_perf_counter_cbs,
218      vm->vlib_node_runtime_perf_counter_cb_tmp,
219      vm->worker_thread_main_loop_callback_lock,
220      read_current_perf_counters, 1 /* enable */ );
221 }
222
223 static void
224 disable_events (perfmon_main_t * pm)
225 {
226   vlib_main_t *vm = vlib_get_main ();
227   u32 my_thread_index = vm->thread_index;
228   int i;
229
230   /* Stop main loop collection */
231   clib_callback_enable_disable
232     (vm->vlib_node_runtime_perf_counter_cbs,
233      vm->vlib_node_runtime_perf_counter_cb_tmp,
234      vm->worker_thread_main_loop_callback_lock,
235      read_current_perf_counters, 0 /* enable */ );
236
237   for (i = 0; i < pm->n_active; i++)
238     {
239       if (pm->pm_fds[i][my_thread_index] == 0)
240         continue;
241
242       if (ioctl (pm->pm_fds[i][my_thread_index], PERF_EVENT_IOC_DISABLE, 0) <
243           0)
244         clib_unix_warning ("disable ioctl");
245
246       if (pm->perf_event_pages[i][my_thread_index])
247         {
248           if (munmap (pm->perf_event_pages[i][my_thread_index],
249                       pm->page_size) < 0)
250             clib_unix_warning ("munmap");
251           pm->perf_event_pages[i][my_thread_index] = 0;
252         }
253
254       (void) close (pm->pm_fds[i][my_thread_index]);
255       pm->pm_fds[i][my_thread_index] = 0;
256
257     }
258 }
259
260 static void
261 worker_thread_start_event (vlib_main_t * vm)
262 {
263   perfmon_main_t *pm = &perfmon_main;
264
265   clib_callback_enable_disable (vm->worker_thread_main_loop_callbacks,
266                                 vm->worker_thread_main_loop_callback_tmp,
267                                 vm->worker_thread_main_loop_callback_lock,
268                                 worker_thread_start_event, 0 /* enable */ );
269   enable_current_events (pm);
270 }
271
272 static void
273 worker_thread_stop_event (vlib_main_t * vm)
274 {
275   perfmon_main_t *pm = &perfmon_main;
276   clib_callback_enable_disable (vm->worker_thread_main_loop_callbacks,
277                                 vm->worker_thread_main_loop_callback_tmp,
278                                 vm->worker_thread_main_loop_callback_lock,
279                                 worker_thread_stop_event, 0 /* enable */ );
280   disable_events (pm);
281 }
282
283 static void
284 start_event (perfmon_main_t * pm, f64 now, uword event_data)
285 {
286   int i;
287   int last_set;
288   int all = 0;
289   pm->current_event = 0;
290
291   if (vec_len (pm->single_events_to_collect) == 0)
292     {
293       pm->state = PERFMON_STATE_OFF;
294       return;
295     }
296
297   last_set = clib_bitmap_last_set (pm->thread_bitmap);
298   all = (last_set == ~0);
299
300   pm->state = PERFMON_STATE_RUNNING;
301   clear_counters (pm);
302
303   /* Start collection on thread 0? */
304   if (all || clib_bitmap_get (pm->thread_bitmap, 0))
305     {
306       /* Start collection on this thread */
307       enable_current_events (pm);
308     }
309
310   /* And also on worker threads */
311   for (i = 1; i < vec_len (vlib_mains); i++)
312     {
313       if (vlib_mains[i] == 0)
314         continue;
315
316       if (all || clib_bitmap_get (pm->thread_bitmap, i))
317         clib_callback_enable_disable
318           (vlib_mains[i]->worker_thread_main_loop_callbacks,
319            vlib_mains[i]->worker_thread_main_loop_callback_tmp,
320            vlib_mains[i]->worker_thread_main_loop_callback_lock,
321            (void *) worker_thread_start_event, 1 /* enable */ );
322     }
323 }
324
325 void
326 scrape_and_clear_counters (perfmon_main_t * pm)
327 {
328   int i, j, k;
329   vlib_main_t *vm = pm->vlib_main;
330   vlib_main_t *stat_vm;
331   vlib_node_main_t *nm;
332   vlib_node_t ***node_dups = 0;
333   vlib_node_t **nodes;
334   vlib_node_t *n;
335   perfmon_capture_t *c;
336   perfmon_event_config_t *current_event;
337   uword *p;
338   u8 *counter_name;
339   u64 vectors_this_counter;
340
341   /* snapshoot the nodes, including pm counters */
342   vlib_worker_thread_barrier_sync (vm);
343
344   for (j = 0; j < vec_len (vlib_mains); j++)
345     {
346       stat_vm = vlib_mains[j];
347       if (stat_vm == 0)
348         continue;
349
350       nm = &stat_vm->node_main;
351
352       for (i = 0; i < vec_len (nm->nodes); i++)
353         {
354           n = nm->nodes[i];
355           vlib_node_sync_stats (stat_vm, n);
356         }
357
358       nodes = 0;
359       vec_validate (nodes, vec_len (nm->nodes) - 1);
360       vec_add1 (node_dups, nodes);
361
362       /* Snapshoot and clear the per-node perfmon counters */
363       for (i = 0; i < vec_len (nm->nodes); i++)
364         {
365           n = nm->nodes[i];
366           nodes[i] = clib_mem_alloc (sizeof (*n));
367           clib_memcpy_fast (nodes[i], n, sizeof (*n));
368           n->stats_total.perf_counter0_ticks = 0;
369           n->stats_total.perf_counter1_ticks = 0;
370           n->stats_total.perf_counter_vectors = 0;
371           n->stats_last_clear.perf_counter0_ticks = 0;
372           n->stats_last_clear.perf_counter1_ticks = 0;
373           n->stats_last_clear.perf_counter_vectors = 0;
374         }
375     }
376
377   vlib_worker_thread_barrier_release (vm);
378
379   for (j = 0; j < vec_len (vlib_mains); j++)
380     {
381       stat_vm = vlib_mains[j];
382       if (stat_vm == 0)
383         continue;
384
385       nodes = node_dups[j];
386
387       for (i = 0; i < vec_len (nodes); i++)
388         {
389           u8 *capture_name;
390
391           n = nodes[i];
392
393           if (n->stats_total.perf_counter0_ticks == 0 &&
394               n->stats_total.perf_counter1_ticks == 0)
395             goto skip_this_node;
396
397           for (k = 0; k < 2; k++)
398             {
399               u64 counter_value, counter_last_clear;
400
401               /*
402                * We collect 2 counters at once, except for the
403                * last counter when the user asks for an odd number of
404                * counters
405                */
406               if ((pm->current_event + k)
407                   >= vec_len (pm->single_events_to_collect))
408                 break;
409
410               if (k == 0)
411                 {
412                   counter_value = n->stats_total.perf_counter0_ticks;
413                   counter_last_clear =
414                     n->stats_last_clear.perf_counter0_ticks;
415                 }
416               else
417                 {
418                   counter_value = n->stats_total.perf_counter1_ticks;
419                   counter_last_clear =
420                     n->stats_last_clear.perf_counter1_ticks;
421                 }
422
423               capture_name = format (0, "t%d-%v%c", j, n->name, 0);
424
425               p = hash_get_mem (pm->capture_by_thread_and_node_name,
426                                 capture_name);
427
428               if (p == 0)
429                 {
430                   pool_get (pm->capture_pool, c);
431                   memset (c, 0, sizeof (*c));
432                   c->thread_and_node_name = capture_name;
433                   hash_set_mem (pm->capture_by_thread_and_node_name,
434                                 capture_name, c - pm->capture_pool);
435                 }
436               else
437                 {
438                   c = pool_elt_at_index (pm->capture_pool, p[0]);
439                   vec_free (capture_name);
440                 }
441
442               /* Snapshoot counters, etc. into the capture */
443               current_event = pm->single_events_to_collect
444                 + pm->current_event + k;
445               counter_name = (u8 *) current_event->name;
446               vectors_this_counter = n->stats_total.perf_counter_vectors -
447                 n->stats_last_clear.perf_counter_vectors;
448
449               vec_add1 (c->counter_names, counter_name);
450               vec_add1 (c->counter_values,
451                         counter_value - counter_last_clear);
452               vec_add1 (c->vectors_this_counter, vectors_this_counter);
453             }
454         skip_this_node:
455           clib_mem_free (n);
456         }
457       vec_free (nodes);
458     }
459   vec_free (node_dups);
460 }
461
462 static void
463 handle_timeout (vlib_main_t * vm, perfmon_main_t * pm, f64 now)
464 {
465   int i;
466   int last_set, all;
467
468   last_set = clib_bitmap_last_set (pm->thread_bitmap);
469   all = (last_set == ~0);
470
471   if (all || clib_bitmap_get (pm->thread_bitmap, 0))
472     disable_events (pm);
473
474   /* And also on worker threads */
475   for (i = 1; i < vec_len (vlib_mains); i++)
476     {
477       if (vlib_mains[i] == 0)
478         continue;
479       if (all || clib_bitmap_get (pm->thread_bitmap, i))
480         clib_callback_enable_disable
481           (vlib_mains[i]->worker_thread_main_loop_callbacks,
482            vlib_mains[i]->worker_thread_main_loop_callback_tmp,
483            vlib_mains[i]->worker_thread_main_loop_callback_lock,
484            (void *) worker_thread_stop_event, 1 /* enable */ );
485     }
486
487   /* Make sure workers have stopped collection */
488   if (i > 1)
489     {
490       f64 deadman = vlib_time_now (vm) + 1.0;
491
492       for (i = 1; i < vec_len (vlib_mains); i++)
493         {
494           /* Has the worker actually stopped collecting data? */
495           while (clib_callback_is_set
496                  (vlib_mains[i]->worker_thread_main_loop_callbacks,
497                   vlib_mains[i]->worker_thread_main_loop_callback_lock,
498                   read_current_perf_counters))
499             {
500               if (vlib_time_now (vm) > deadman)
501                 {
502                   clib_warning ("Thread %d deadman timeout!", i);
503                   break;
504                 }
505               vlib_process_suspend (pm->vlib_main, 1e-3);
506             }
507         }
508     }
509   scrape_and_clear_counters (pm);
510   pm->current_event += pm->n_active;
511   if (pm->current_event >= vec_len (pm->single_events_to_collect))
512     {
513       pm->current_event = 0;
514       pm->state = PERFMON_STATE_OFF;
515       return;
516     }
517
518   if (all || clib_bitmap_get (pm->thread_bitmap, 0))
519     enable_current_events (pm);
520
521   /* And also on worker threads */
522   for (i = 1; i < vec_len (vlib_mains); i++)
523     {
524       if (vlib_mains[i] == 0)
525         continue;
526       if (all || clib_bitmap_get (pm->thread_bitmap, i))
527         clib_callback_enable_disable
528           (vlib_mains[i]->worker_thread_main_loop_callbacks,
529            vlib_mains[i]->worker_thread_main_loop_callback_tmp,
530            vlib_mains[i]->worker_thread_main_loop_callback_lock,
531            worker_thread_start_event, 1 /* enable */ );
532     }
533 }
534
535 static uword
536 perfmon_periodic_process (vlib_main_t * vm,
537                           vlib_node_runtime_t * rt, vlib_frame_t * f)
538 {
539   perfmon_main_t *pm = &perfmon_main;
540   f64 now;
541   uword *event_data = 0;
542   uword event_type;
543   int i;
544
545   while (1)
546     {
547       if (pm->state == PERFMON_STATE_RUNNING)
548         vlib_process_wait_for_event_or_clock (vm, pm->timeout_interval);
549       else
550         vlib_process_wait_for_event (vm);
551
552       now = vlib_time_now (vm);
553
554       event_type = vlib_process_get_events (vm, (uword **) & event_data);
555
556       switch (event_type)
557         {
558         case PERFMON_START:
559           for (i = 0; i < vec_len (event_data); i++)
560             start_event (pm, now, event_data[i]);
561           break;
562
563           /* Handle timeout */
564         case ~0:
565           handle_timeout (vm, pm, now);
566           break;
567
568         default:
569           clib_warning ("Unexpected event %d", event_type);
570           break;
571         }
572       vec_reset_length (event_data);
573     }
574   return 0;                     /* or not */
575 }
576
577 /* *INDENT-OFF* */
578 VLIB_REGISTER_NODE (perfmon_periodic_node) =
579 {
580   .function = perfmon_periodic_process,
581   .type = VLIB_NODE_TYPE_PROCESS,
582   .name = "perfmon-periodic-process",
583 };
584 /* *INDENT-ON* */
585
586 /*
587  * fd.io coding-style-patch-verification: ON
588  *
589  * Local Variables:
590  * eval: (c-set-style "gnu")
591  * End:
592  */