LCOV - code coverage report
Current view: top level - plugins/perfmon - perfmon.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 27 185 14.6 %
Date: 2023-10-26 01:39:38 Functions: 4 8 50.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2020 Cisco and/or its affiliates.
       3             :  * Licensed under the Apache License, Version 2.0 (the "License");
       4             :  * you may not use this file except in compliance with the License.
       5             :  * You may obtain a copy of the License at:
       6             :  *
       7             :  *     http://www.apache.org/licenses/LICENSE-2.0
       8             :  *
       9             :  * Unless required by applicable law or agreed to in writing, software
      10             :  * distributed under the License is distributed on an "AS IS" BASIS,
      11             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             :  * See the License for the specific language governing permissions and
      13             :  * limitations under the License.
      14             :  */
      15             : 
      16             : #include <vnet/vnet.h>
      17             : 
      18             : #include <vlibapi/api.h>
      19             : #include <vlibmemory/api.h>
      20             : #include <vnet/plugin/plugin.h>
      21             : #include <vpp/app/version.h>
      22             : #include <linux/limits.h>
      23             : #include <sys/ioctl.h>
      24             : 
      25             : #include <perfmon/perfmon.h>
      26             : 
      27             : perfmon_main_t perfmon_main;
      28             : 
      29             : VLIB_PLUGIN_REGISTER () = {
      30             :   .version = VPP_BUILD_VER,
      31             :   .description = "Performance Monitor",
      32             : };
      33             : 
      34         575 : VLIB_REGISTER_LOG_CLASS (if_default_log, static) = {
      35             :   .class_name = "perfmon",
      36             : };
      37             : 
      38             : #define log_debug(fmt, ...)                                                   \
      39             :   vlib_log_debug (if_default_log.class, fmt, __VA_ARGS__)
      40             : #define log_warn(fmt, ...)                                                    \
      41             :   vlib_log_warn (if_default_log.class, fmt, __VA_ARGS__)
      42             : #define log_err(fmt, ...) vlib_log_err (if_default_log.class, fmt, __VA_ARGS__)
      43             : 
      44             : void
      45           0 : perfmon_reset (vlib_main_t *vm)
      46             : {
      47           0 :   perfmon_main_t *pm = &perfmon_main;
      48           0 :   uword page_size = clib_mem_get_page_size ();
      49             : 
      50           0 :   if (pm->is_running)
      51           0 :     for (int i = 0; i < vlib_get_n_threads (); i++)
      52           0 :       vlib_node_set_dispatch_wrapper (vlib_get_main_by_index (i), 0);
      53             : 
      54           0 :   for (int i = 0; i < vec_len (pm->fds_to_close); i++)
      55           0 :     close (pm->fds_to_close[i]);
      56           0 :   vec_free (pm->fds_to_close);
      57           0 :   vec_free (pm->group_fds);
      58           0 :   if (pm->default_instance_type)
      59             :     {
      60           0 :       perfmon_instance_type_t *it = pm->default_instance_type;
      61           0 :       for (int i = 0; i < vec_len (it->instances); i++)
      62           0 :         vec_free (it->instances[i].name);
      63           0 :       vec_free (it->instances);
      64           0 :       vec_free (pm->default_instance_type);
      65             :     }
      66             : 
      67           0 :   for (int i = 0; i < vec_len (pm->thread_runtimes); i++)
      68             :     {
      69           0 :       perfmon_thread_runtime_t *tr = vec_elt_at_index (pm->thread_runtimes, i);
      70           0 :       vec_free (tr->node_stats);
      71           0 :       for (int j = 0; j < PERF_MAX_EVENTS; j++)
      72           0 :         if (tr->mmap_pages[j])
      73           0 :           munmap (tr->mmap_pages[j], page_size);
      74             :     }
      75           0 :   vec_free (pm->thread_runtimes);
      76             : 
      77           0 :   pm->is_running = 0;
      78           0 :   pm->active_instance_type = 0;
      79           0 :   pm->active_bundle = 0;
      80           0 : }
      81             : 
      82             : static clib_error_t *
      83           0 : perfmon_set (vlib_main_t *vm, perfmon_bundle_t *b)
      84             : {
      85           0 :   clib_error_t *err = 0;
      86           0 :   perfmon_main_t *pm = &perfmon_main;
      87             :   perfmon_source_t *s;
      88           0 :   int is_node = 0;
      89           0 :   int n_nodes = vec_len (vm->node_main.nodes);
      90           0 :   uword page_size = clib_mem_get_page_size ();
      91           0 :   u32 instance_type = 0;
      92             :   perfmon_event_t *e;
      93           0 :   perfmon_instance_type_t *it = 0;
      94             : 
      95           0 :   perfmon_reset (vm);
      96             : 
      97           0 :   s = b->src;
      98           0 :   ASSERT (b->n_events);
      99             : 
     100           0 :   if (b->active_type == PERFMON_BUNDLE_TYPE_NODE)
     101           0 :     is_node = 1;
     102             : 
     103           0 :   if (s->instances_by_type == 0)
     104             :     {
     105           0 :       vec_add2 (pm->default_instance_type, it, 1);
     106           0 :       it->name = is_node ? "Thread/Node" : "Thread";
     107           0 :       for (int i = 0; i < vlib_get_n_threads (); i++)
     108             :         {
     109           0 :           vlib_worker_thread_t *w = vlib_worker_threads + i;
     110             :           perfmon_instance_t *in;
     111           0 :           vec_add2 (it->instances, in, 1);
     112           0 :           in->cpu = w->cpu_id;
     113           0 :           in->pid = w->lwp;
     114           0 :           in->name = (char *) format (0, "%s (%u)%c", w->name, i, 0);
     115             :         }
     116           0 :       if (is_node)
     117           0 :         vec_validate (pm->thread_runtimes, vlib_get_n_threads () - 1);
     118             :     }
     119             :   else
     120             :     {
     121           0 :       e = s->events + b->events[0];
     122             : 
     123           0 :       if (e->type_from_instance)
     124             :         {
     125           0 :           instance_type = e->instance_type;
     126           0 :           for (int i = 1; i < b->n_events; i++)
     127             :             {
     128           0 :               e = s->events + b->events[i];
     129           0 :               ASSERT (e->type_from_instance == 1 &&
     130             :                       e->instance_type == instance_type);
     131             :             }
     132             :         }
     133           0 :       it = vec_elt_at_index (s->instances_by_type, instance_type);
     134             :     }
     135             : 
     136           0 :   pm->active_instance_type = it;
     137             : 
     138           0 :   for (int i = 0; i < vec_len (it->instances); i++)
     139             :     {
     140           0 :       perfmon_instance_t *in = vec_elt_at_index (it->instances, i);
     141             : 
     142           0 :       vec_validate (pm->group_fds, i);
     143           0 :       pm->group_fds[i] = -1;
     144           0 :       u8 n_events_opened = 0;
     145             : 
     146           0 :       for (int j = 0; j < b->n_events; j++)
     147             :         {
     148             :           int fd;
     149           0 :           perfmon_event_t *e = s->events + b->events[j];
     150           0 :           if (!e->implemented)
     151           0 :             continue;
     152           0 :           struct perf_event_attr pe = {
     153             :             .size = sizeof (struct perf_event_attr),
     154           0 :             .type = e->type_from_instance ? in->type : e->type,
     155           0 :             .config = e->config,
     156           0 :             .config1 = e->config1,
     157           0 :             .exclude_kernel = e->exclude_kernel,
     158             :             .read_format =
     159             :               (PERF_FORMAT_GROUP | PERF_FORMAT_TOTAL_TIME_ENABLED |
     160             :                PERF_FORMAT_TOTAL_TIME_RUNNING),
     161             :             .disabled = 1,
     162             :           };
     163             : 
     164           0 :         perf_event_open:
     165           0 :           log_debug ("perf_event_open pe.type=%u pe.config=0x%x pid=%d "
     166             :                      "cpu=%d group_fd=%d",
     167             :                      pe.type, pe.config, in->pid, in->cpu, pm->group_fds[i]);
     168           0 :           fd = syscall (__NR_perf_event_open, &pe, in->pid, in->cpu,
     169           0 :                         pm->group_fds[i], 0);
     170             : 
     171           0 :           if (fd == -1)
     172             :             {
     173           0 :               if (errno ==
     174             :                   EOPNOTSUPP) /* 64b counters not supported on aarch64 */
     175             :                 {
     176           0 :                   pe.config1 = 2; /* retry with 32b counter width */
     177           0 :                   goto perf_event_open;
     178             :                 }
     179             :               else
     180             :                 {
     181           0 :                   err = clib_error_return_unix (0, "perf_event_open");
     182           0 :                   goto error;
     183             :                 }
     184             :             }
     185             : 
     186           0 :           vec_add1 (pm->fds_to_close, fd);
     187             : 
     188           0 :           if (pm->group_fds[i] == -1)
     189           0 :             pm->group_fds[i] = fd;
     190             : 
     191           0 :           if (is_node)
     192             :             {
     193             :               perfmon_thread_runtime_t *tr;
     194           0 :               tr = vec_elt_at_index (pm->thread_runtimes, i);
     195           0 :               tr->mmap_pages[n_events_opened] =
     196           0 :                 mmap (0, page_size, PROT_READ, MAP_SHARED, fd, 0);
     197             : 
     198           0 :               if (tr->mmap_pages[n_events_opened] == MAP_FAILED)
     199             :                 {
     200           0 :                   err = clib_error_return_unix (0, "mmap");
     201           0 :                   goto error;
     202             :                 }
     203             :             }
     204           0 :           n_events_opened++;
     205             :         }
     206             : 
     207           0 :       if (is_node && n_events_opened)
     208             :         {
     209             :           perfmon_thread_runtime_t *rt;
     210           0 :           rt = vec_elt_at_index (pm->thread_runtimes, i);
     211           0 :           rt->bundle = b;
     212           0 :           rt->n_events = n_events_opened;
     213           0 :           rt->n_nodes = n_nodes;
     214           0 :           rt->preserve_samples = b->preserve_samples;
     215           0 :           vec_validate_aligned (rt->node_stats, n_nodes - 1,
     216             :                                 CLIB_CACHE_LINE_BYTES);
     217             :         }
     218             :     }
     219             : 
     220           0 :   pm->active_bundle = b;
     221             : 
     222           0 : error:
     223           0 :   if (err)
     224             :     {
     225           0 :       log_err ("%U", format_clib_error, err);
     226           0 :       perfmon_reset (vm);
     227             :     }
     228           0 :   return err;
     229             : }
     230             : 
     231             : clib_error_t *
     232           0 : perfmon_start (vlib_main_t *vm, perfmon_bundle_t *b)
     233             : {
     234           0 :   clib_error_t *err = 0;
     235           0 :   perfmon_main_t *pm = &perfmon_main;
     236             :   int n_groups;
     237             : 
     238           0 :   if (pm->is_running == 1)
     239           0 :     return clib_error_return (0, "already running");
     240             : 
     241           0 :   if ((err = perfmon_set (vm, b)) != 0)
     242           0 :     return err;
     243             : 
     244           0 :   n_groups = vec_len (pm->group_fds);
     245             : 
     246           0 :   for (int i = 0; i < n_groups; i++)
     247             :     {
     248           0 :       if (ioctl (pm->group_fds[i], PERF_EVENT_IOC_ENABLE,
     249             :                  PERF_IOC_FLAG_GROUP) == -1)
     250             :         {
     251           0 :           perfmon_reset (vm);
     252           0 :           return clib_error_return_unix (0, "ioctl(PERF_EVENT_IOC_ENABLE)");
     253             :         }
     254             :     }
     255           0 :   if (b->active_type == PERFMON_BUNDLE_TYPE_NODE)
     256             :     {
     257           0 :       vlib_node_function_t *dispatch_wrapper = NULL;
     258           0 :       err = b->src->config_dispatch_wrapper (b, &dispatch_wrapper);
     259           0 :       if (err || !dispatch_wrapper)
     260             :         {
     261           0 :           perfmon_reset (vm);
     262           0 :           return err;
     263             :         }
     264             : 
     265           0 :       for (int i = 0; i < vlib_get_n_threads (); i++)
     266           0 :         vlib_node_set_dispatch_wrapper (vlib_get_main_by_index (i),
     267             :                                         dispatch_wrapper);
     268             :     }
     269           0 :   pm->sample_time = vlib_time_now (vm);
     270           0 :   pm->is_running = 1;
     271             : 
     272           0 :   return 0;
     273             : }
     274             : 
     275             : clib_error_t *
     276           0 : perfmon_stop (vlib_main_t *vm)
     277             : {
     278           0 :   perfmon_main_t *pm = &perfmon_main;
     279           0 :   int n_groups = vec_len (pm->group_fds);
     280             : 
     281           0 :   if (pm->is_running != 1)
     282           0 :     return clib_error_return (0, "not running");
     283             : 
     284           0 :   if (pm->active_bundle->active_type == PERFMON_BUNDLE_TYPE_NODE)
     285             :     {
     286           0 :       for (int i = 0; i < vlib_get_n_threads (); i++)
     287           0 :         vlib_node_set_dispatch_wrapper (vlib_get_main_by_index (i), 0);
     288             :     }
     289             : 
     290           0 :   for (int i = 0; i < n_groups; i++)
     291             :     {
     292           0 :       if (ioctl (pm->group_fds[i], PERF_EVENT_IOC_DISABLE,
     293             :                  PERF_IOC_FLAG_GROUP) == -1)
     294             :         {
     295           0 :           perfmon_reset (vm);
     296           0 :           return clib_error_return_unix (0, "ioctl(PERF_EVENT_IOC_DISABLE)");
     297             :         }
     298             :     }
     299             : 
     300           0 :   pm->is_running = 0;
     301           0 :   pm->sample_time = vlib_time_now (vm) - pm->sample_time;
     302           0 :   return 0;
     303             : }
     304             : 
     305             : static clib_error_t *
     306         575 : perfmon_init (vlib_main_t *vm)
     307             : {
     308         575 :   perfmon_main_t *pm = &perfmon_main;
     309         575 :   perfmon_source_t *s = pm->sources;
     310         575 :   perfmon_bundle_t *b = pm->bundles;
     311             : 
     312         575 :   pm->source_by_name = hash_create_string (0, sizeof (uword));
     313        2300 :   while (s)
     314             :     {
     315             :       clib_error_t *err;
     316        3450 :       if (hash_get_mem (pm->source_by_name, s->name) != 0)
     317           0 :         clib_panic ("duplicate source name '%s'", s->name);
     318        1725 :       if (s->init_fn && ((err = (s->init_fn) (vm, s))))
     319             :         {
     320           0 :           log_warn ("skipping source '%s' - %U", s->name, format_clib_error,
     321             :                     err);
     322           0 :           clib_error_free (err);
     323           0 :           s = s->next;
     324           0 :           continue;
     325             :         }
     326             : 
     327        3450 :       hash_set_mem (pm->source_by_name, s->name, s);
     328        1725 :       log_debug ("source '%s' registered", s->name);
     329        1725 :       s = s->next;
     330             :     }
     331             : 
     332         575 :   pm->bundle_by_name = hash_create_string (0, sizeof (uword));
     333       10925 :   while (b)
     334             :     {
     335             :       clib_error_t *err;
     336             :       uword *p;
     337             : 
     338       20700 :       if ((p = hash_get_mem (pm->source_by_name, b->source)) == 0)
     339             :         {
     340           0 :           log_debug ("missing source '%s', skipping bundle '%s'", b->source,
     341             :                      b->name);
     342           0 :           b = b->next;
     343           0 :           continue;
     344             :         }
     345             : 
     346       10350 :       b->src = (perfmon_source_t *) p[0];
     347       10350 :       if (b->src->bundle_support && !b->src->bundle_support (b))
     348             :         {
     349        6325 :           log_debug ("skipping bundle '%s' - not supported", b->name);
     350        6325 :           b = b->next;
     351        6325 :           continue;
     352             :         }
     353             : 
     354        4025 :       if (b->init_fn && ((err = (b->init_fn) (vm, b))))
     355             :         {
     356           0 :           log_warn ("skipping bundle '%s' - %U", b->name, format_clib_error,
     357             :                     err);
     358           0 :           clib_error_free (err);
     359           0 :           b = b->next;
     360           0 :           continue;
     361             :         }
     362             : 
     363        8050 :       if (hash_get_mem (pm->bundle_by_name, b->name) != 0)
     364           0 :         clib_panic ("duplicate bundle name '%s'", b->name);
     365             : 
     366        8050 :       hash_set_mem (pm->bundle_by_name, b->name, b);
     367        4025 :       log_debug ("bundle '%s' registered", b->name);
     368             : 
     369        4025 :       b = b->next;
     370             :     }
     371             : 
     372         575 :   return 0;
     373             : }
     374             : 
     375        1151 : VLIB_INIT_FUNCTION (perfmon_init);

Generated by: LCOV version 1.14