LCOV - code coverage report
Current view: top level - plugins/prom - prom.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 9 211 4.3 %
Date: 2023-10-26 01:39:38 Functions: 5 26 19.2 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2022 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             : #include <vnet/plugin/plugin.h>
      16             : #include <vpp/app/version.h>
      17             : 
      18             : #include <prom/prom.h>
      19             : #include <vpp-api/client/stat_client.h>
      20             : #include <vlib/stats/stats.h>
      21             : #include <ctype.h>
      22             : 
      23             : static prom_main_t prom_main;
      24             : 
      25             : static u8 *
      26           0 : make_stat_name (char *name)
      27             : {
      28           0 :   prom_main_t *pm = &prom_main;
      29           0 :   char *p = name;
      30             : 
      31           0 :   while (*p)
      32             :     {
      33           0 :       if (!isalnum (*p))
      34           0 :         *p = '_';
      35           0 :       p++;
      36             :     }
      37             : 
      38             :   /* Reuse vector, instead of always allocating, when building a name. */
      39           0 :   vec_reset_length (pm->name_scratch_pad);
      40           0 :   pm->name_scratch_pad =
      41           0 :     format (pm->name_scratch_pad, "%v%s", pm->stat_name_prefix, name);
      42           0 :   return pm->name_scratch_pad;
      43             : }
      44             : 
      45             : static u8 *
      46           0 : dump_counter_vector_simple (stat_segment_data_t *res, u8 *s, u8 used_only)
      47             : {
      48           0 :   u8 need_header = 1;
      49             :   int j, k;
      50             :   u8 *name;
      51             : 
      52           0 :   name = make_stat_name (res->name);
      53             : 
      54           0 :   for (k = 0; k < vec_len (res->simple_counter_vec); k++)
      55           0 :     for (j = 0; j < vec_len (res->simple_counter_vec[k]); j++)
      56             :       {
      57           0 :         if (used_only && !res->simple_counter_vec[k][j])
      58           0 :           continue;
      59           0 :         if (need_header)
      60             :           {
      61           0 :             s = format (s, "# TYPE %v counter\n", name);
      62           0 :             need_header = 0;
      63             :           }
      64           0 :         s = format (s, "%v{thread=\"%d\",interface=\"%d\"} %lld\n", name, k, j,
      65           0 :                     res->simple_counter_vec[k][j]);
      66             :       }
      67             : 
      68           0 :   return s;
      69             : }
      70             : 
      71             : static u8 *
      72           0 : dump_counter_vector_combined (stat_segment_data_t *res, u8 *s, u8 used_only)
      73             : {
      74           0 :   u8 need_header = 1;
      75             :   int j, k;
      76             :   u8 *name;
      77             : 
      78           0 :   name = make_stat_name (res->name);
      79             : 
      80           0 :   for (k = 0; k < vec_len (res->simple_counter_vec); k++)
      81           0 :     for (j = 0; j < vec_len (res->combined_counter_vec[k]); j++)
      82             :       {
      83           0 :         if (used_only && !res->combined_counter_vec[k][j].packets)
      84           0 :           continue;
      85           0 :         if (need_header)
      86             :           {
      87           0 :             s = format (s, "# TYPE %v_packets counter\n", name);
      88           0 :             s = format (s, "# TYPE %v_bytes counter\n", name);
      89           0 :             need_header = 0;
      90             :           }
      91           0 :         s = format (s, "%v_packets{thread=\"%d\",interface=\"%d\"} %lld\n",
      92           0 :                     name, k, j, res->combined_counter_vec[k][j].packets);
      93           0 :         s = format (s, "%v_bytes{thread=\"%d\",interface=\"%d\"} %lld\n", name,
      94           0 :                     k, j, res->combined_counter_vec[k][j].bytes);
      95             :       }
      96             : 
      97           0 :   return s;
      98             : }
      99             : 
     100             : static u8 *
     101           0 : dump_scalar_index (stat_segment_data_t *res, u8 *s, u8 used_only)
     102             : {
     103             :   u8 *name;
     104             : 
     105           0 :   if (used_only && !res->scalar_value)
     106           0 :     return s;
     107             : 
     108           0 :   name = make_stat_name (res->name);
     109             : 
     110           0 :   s = format (s, "# TYPE %v counter\n", name);
     111           0 :   s = format (s, "%v %.2f\n", name, res->scalar_value);
     112             : 
     113           0 :   return s;
     114             : }
     115             : 
     116             : static u8 *
     117           0 : dump_name_vector (stat_segment_data_t *res, u8 *s, u8 used_only)
     118             : {
     119             :   u8 *name;
     120             :   int k;
     121             : 
     122           0 :   name = make_stat_name (res->name);
     123             : 
     124           0 :   s = format (s, "# TYPE %v_info gauge\n", name);
     125           0 :   for (k = 0; k < vec_len (res->name_vector); k++)
     126           0 :     s = format (s, "%v_info{index=\"%d\",name=\"%s\"} 1\n", name, k,
     127           0 :                 res->name_vector[k]);
     128             : 
     129           0 :   return s;
     130             : }
     131             : 
     132             : static u8 *
     133           0 : scrape_stats_segment (u8 *s, u8 **patterns, u8 used_only)
     134             : {
     135             :   stat_segment_data_t *res;
     136             :   static u32 *stats = 0;
     137             :   int i;
     138             : 
     139           0 :   stats = stat_segment_ls (patterns);
     140             : 
     141           0 : retry:
     142           0 :   res = stat_segment_dump (stats);
     143           0 :   if (res == 0)
     144             :     { /* Memory layout has changed */
     145           0 :       if (stats)
     146           0 :         vec_free (stats);
     147           0 :       stats = stat_segment_ls (patterns);
     148           0 :       goto retry;
     149             :     }
     150             : 
     151           0 :   for (i = 0; i < vec_len (res); i++)
     152             :     {
     153           0 :       switch (res[i].type)
     154             :         {
     155           0 :         case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
     156           0 :           s = dump_counter_vector_simple (&res[i], s, used_only);
     157           0 :           break;
     158             : 
     159           0 :         case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
     160           0 :           s = dump_counter_vector_combined (&res[i], s, used_only);
     161           0 :           break;
     162             : 
     163           0 :         case STAT_DIR_TYPE_SCALAR_INDEX:
     164           0 :           s = dump_scalar_index (&res[i], s, used_only);
     165           0 :           break;
     166             : 
     167           0 :         case STAT_DIR_TYPE_NAME_VECTOR:
     168           0 :           s = dump_name_vector (&res[i], s, used_only);
     169           0 :           break;
     170             : 
     171           0 :         case STAT_DIR_TYPE_EMPTY:
     172           0 :           break;
     173             : 
     174           0 :         default:
     175           0 :           clib_warning ("Unknown value %d\n", res[i].type);
     176             :           ;
     177             :         }
     178             :     }
     179           0 :   stat_segment_data_free (res);
     180           0 :   vec_free (stats);
     181             : 
     182           0 :   return s;
     183             : }
     184             : 
     185             : static void
     186           0 : send_data_to_hss (hss_session_handle_t sh)
     187             : {
     188           0 :   hss_url_handler_args_t args = {};
     189           0 :   prom_main_t *pm = &prom_main;
     190             : 
     191           0 :   args.sh = sh;
     192           0 :   args.data = vec_dup (pm->stats);
     193           0 :   args.data_len = vec_len (pm->stats);
     194           0 :   args.sc = HTTP_STATUS_OK;
     195           0 :   args.free_vec_data = 1;
     196             : 
     197           0 :   pm->send_data (&args);
     198           0 : }
     199             : 
     200             : static void
     201           0 : send_data_to_hss_rpc (void *rpc_args)
     202             : {
     203           0 :   send_data_to_hss (*(hss_session_handle_t *) rpc_args);
     204           0 : }
     205             : 
     206             : static uword
     207           0 : prom_scraper_process (vlib_main_t *vm, vlib_node_runtime_t *rt,
     208             :                       vlib_frame_t *f)
     209             : {
     210           0 :   uword *event_data = 0, event_type;
     211           0 :   prom_main_t *pm = &prom_main;
     212             :   hss_session_handle_t sh;
     213           0 :   f64 timeout = 10000.0;
     214             : 
     215             :   while (1)
     216             :     {
     217           0 :       vlib_process_wait_for_event_or_clock (vm, timeout);
     218           0 :       event_type = vlib_process_get_events (vm, (uword **) &event_data);
     219           0 :       switch (event_type)
     220             :         {
     221           0 :         case ~0:
     222             :           /* timeout, do nothing */
     223           0 :           break;
     224           0 :         case PROM_SCRAPER_EVT_RUN:
     225           0 :           sh.as_u64 = event_data[0];
     226           0 :           vec_reset_length (pm->stats);
     227           0 :           pm->stats = scrape_stats_segment (pm->stats, pm->stats_patterns,
     228           0 :                                             pm->used_only);
     229           0 :           session_send_rpc_evt_to_thread_force (sh.thread_index,
     230             :                                                 send_data_to_hss_rpc, &sh);
     231           0 :           pm->last_scrape = vlib_time_now (vm);
     232           0 :           break;
     233           0 :         default:
     234           0 :           clib_warning ("unexpected event %u", event_type);
     235           0 :           break;
     236             :         }
     237             : 
     238           0 :       vec_reset_length (event_data);
     239             :     }
     240             :   return 0;
     241             : }
     242             : 
     243       44952 : VLIB_REGISTER_NODE (prom_scraper_process_node) = {
     244             :   .function = prom_scraper_process,
     245             :   .type = VLIB_NODE_TYPE_PROCESS,
     246             :   .name = "prom-scraper-process",
     247             :   .state = VLIB_NODE_STATE_DISABLED,
     248             : };
     249             : 
     250             : static void
     251           0 : prom_scraper_process_enable (vlib_main_t *vm)
     252             : {
     253           0 :   prom_main_t *pm = &prom_main;
     254             :   vlib_node_t *n;
     255             : 
     256           0 :   vlib_node_set_state (vm, prom_scraper_process_node.index,
     257             :                        VLIB_NODE_STATE_POLLING);
     258           0 :   n = vlib_get_node (vm, prom_scraper_process_node.index);
     259           0 :   vlib_start_process (vm, n->runtime_index);
     260             : 
     261           0 :   pm->scraper_node_index = n->index;
     262           0 : }
     263             : 
     264             : static void
     265           0 : signal_run_to_scraper (uword *args)
     266             : {
     267           0 :   prom_main_t *pm = &prom_main;
     268           0 :   ASSERT (vlib_get_thread_index () == 0);
     269           0 :   vlib_process_signal_event (pm->vm, pm->scraper_node_index,
     270             :                              PROM_SCRAPER_EVT_RUN, *args);
     271           0 : }
     272             : 
     273             : hss_url_handler_rc_t
     274           0 : prom_stats_dump (hss_url_handler_args_t *args)
     275             : {
     276           0 :   vlib_main_t *vm = vlib_get_main ();
     277           0 :   f64 now = vlib_time_now (vm);
     278           0 :   prom_main_t *pm = &prom_main;
     279             : 
     280             :   /* If we've recently scraped stats, return data */
     281           0 :   if ((now - pm->last_scrape) < pm->min_scrape_interval)
     282             :     {
     283           0 :       send_data_to_hss (args->sh);
     284           0 :       return HSS_URL_HANDLER_ASYNC;
     285             :     }
     286             : 
     287           0 :   if (vm->thread_index != 0)
     288           0 :     vl_api_rpc_call_main_thread (signal_run_to_scraper, (u8 *) &args->sh,
     289             :                                  sizeof (args->sh));
     290             :   else
     291           0 :     signal_run_to_scraper (&args->sh.as_u64);
     292             : 
     293           0 :   return HSS_URL_HANDLER_ASYNC;
     294             : }
     295             : 
     296             : void
     297           0 : prom_stat_patterns_add (u8 **patterns)
     298             : {
     299           0 :   prom_main_t *pm = &prom_main;
     300             : 
     301             :   u8 **pattern, **existing;
     302             :   u8 found;
     303             :   u32 len;
     304             : 
     305           0 :   vec_foreach (pattern, patterns)
     306             :     {
     307           0 :       found = 0;
     308           0 :       len = vec_len (*pattern);
     309           0 :       if (len == 0)
     310           0 :         continue;
     311           0 :       vec_foreach (existing, pm->stats_patterns)
     312             :         {
     313           0 :           if (vec_len (*existing) != len)
     314           0 :             continue;
     315           0 :           if (!memcmp (*existing, *pattern, len - 1))
     316             :             {
     317           0 :               found = 1;
     318           0 :               break;
     319             :             }
     320             :         }
     321           0 :       if (!found)
     322           0 :         vec_add1 (pm->stats_patterns, *pattern);
     323             :     }
     324           0 : }
     325             : 
     326             : void
     327           0 : prom_stat_patterns_free (void)
     328             : {
     329           0 :   prom_main_t *pm = &prom_main;
     330             :   u8 **pattern;
     331             : 
     332           0 :   vec_foreach (pattern, pm->stats_patterns)
     333           0 :     vec_free (*pattern);
     334           0 :   vec_free (pm->stats_patterns);
     335           0 : }
     336             : 
     337             : void
     338           0 : prom_stat_patterns_set (u8 **patterns)
     339             : {
     340           0 :   prom_stat_patterns_free ();
     341           0 :   prom_stat_patterns_add (patterns);
     342           0 : }
     343             : 
     344             : u8 **
     345           0 : prom_stat_patterns_get (void)
     346             : {
     347           0 :   return prom_main.stats_patterns;
     348             : }
     349             : 
     350             : void
     351           0 : prom_stat_name_prefix_set (u8 *prefix)
     352             : {
     353           0 :   prom_main_t *pm = &prom_main;
     354             : 
     355           0 :   vec_free (pm->stat_name_prefix);
     356           0 :   pm->stat_name_prefix = prefix;
     357           0 : }
     358             : 
     359             : void
     360           0 : prom_report_used_only (u8 used_only)
     361             : {
     362           0 :   prom_main_t *pm = &prom_main;
     363             : 
     364           0 :   pm->used_only = used_only;
     365           0 : }
     366             : 
     367             : static void
     368           0 : prom_stat_segment_client_init (void)
     369             : {
     370           0 :   stat_client_main_t *scm = &stat_client_main;
     371           0 :   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
     372             :   uword size;
     373             : 
     374           0 :   size = sm->memory_size ? sm->memory_size : STAT_SEGMENT_DEFAULT_SIZE;
     375           0 :   scm->memory_size = size;
     376           0 :   scm->shared_header = sm->shared_header;
     377           0 :   scm->directory_vector =
     378           0 :     stat_segment_adjust (scm, (void *) scm->shared_header->directory_vector);
     379           0 : }
     380             : 
     381             : void
     382           0 : prom_enable (vlib_main_t *vm)
     383             : {
     384           0 :   prom_main_t *pm = &prom_main;
     385             : 
     386           0 :   pm->register_url = vlib_get_plugin_symbol ("http_static_plugin.so",
     387             :                                              "hss_register_url_handler");
     388           0 :   pm->send_data =
     389           0 :     vlib_get_plugin_symbol ("http_static_plugin.so", "hss_session_send_data");
     390           0 :   pm->register_url (prom_stats_dump, "stats.prom", HTTP_REQ_GET);
     391             : 
     392           0 :   pm->is_enabled = 1;
     393           0 :   pm->vm = vm;
     394           0 :   if (!pm->stat_name_prefix)
     395           0 :     pm->stat_name_prefix = format (0, "vpp");
     396             : 
     397           0 :   prom_scraper_process_enable (vm);
     398           0 :   prom_stat_segment_client_init ();
     399           0 : }
     400             : 
     401             : static clib_error_t *
     402         575 : prom_init (vlib_main_t *vm)
     403             : {
     404         575 :   prom_main_t *pm = &prom_main;
     405             : 
     406         575 :   pm->is_enabled = 0;
     407         575 :   pm->min_scrape_interval = 1;
     408         575 :   pm->used_only = 0;
     409         575 :   pm->stat_name_prefix = 0;
     410             : 
     411         575 :   return 0;
     412             : }
     413             : 
     414             : prom_main_t *
     415           0 : prom_get_main (void)
     416             : {
     417           0 :   return &prom_main;
     418             : }
     419             : 
     420        1151 : VLIB_INIT_FUNCTION (prom_init) = {
     421             :   .runs_after = VLIB_INITS ("hss_main_init"),
     422             : };
     423             : 
     424             : VLIB_PLUGIN_REGISTER () = {
     425             :   .version = VPP_BUILD_VER,
     426             :   .description = "Prometheus Stats Exporter",
     427             :   .default_disabled = 0,
     428             : };
     429             : 
     430             : /*
     431             :  * fd.io coding-style-patch-verification: ON
     432             :  *
     433             :  * Local Variables:
     434             :  * eval: (c-set-style "gnu")
     435             :  * End:
     436             :  */

Generated by: LCOV version 1.14