stats: remove offsets on vpp side
[vpp.git] / src / plugins / mactime / mactime_top.c
1 #include <vppinfra/time.h>
2 #include <vppinfra/hash.h>
3 #include <vppinfra/pool.h>
4 #include <vpp-api/client/stat_client.h>
5 #include <vppinfra/vec.h>
6 #include <mactime/mactime_device.h>
7 #include <vlibapi/api_common.h>
8 #include <vlibmemory/memory_client.h>
9 #include <vlibmemory/api.h>
10 #include <vnet/api_errno.h>
11 #include <svm/queue.h>
12
13 #include <vnet/format_fns.h>
14 #include <mactime/mactime.api_types.h>
15 #include <mactime/mactime.api_enum.h>
16
17 typedef struct
18 {
19   /* device database */
20   uword *device_by_device_name;
21   mactime_device_t *devices;
22   u32 my_table_epoch;
23
24   /* Stat segment variables */
25   stat_client_main_t *stat_client_main;
26   u8 **pattern1, **pattern2;
27   u32 *ls_result1, *ls_result2;
28   vlib_counter_t *allow_counters;
29   vlib_counter_t *drop_counters;
30
31   /* Timebase */
32   clib_time_t clib_time;
33   clib_timebase_t timebase;
34   f64 timezone_offset;
35   f64 sunday_midnight;
36
37   /* API message-handling */
38   svm_queue_t *vl_input_queue;
39   u32 my_client_index;
40   u16 msg_id_base;
41   volatile u32 result_ready;
42   volatile i32 retval;
43 } mt_main_t;
44
45 mt_main_t mt_main;
46
47 /* Indispensable for debugging in gdb... */
48
49 u32
50 vl (void *x)
51 {
52   return vec_len (x);
53 }
54
55 #define foreach_mactime_api_msg                 \
56 _(MACTIME_DUMP_REPLY, mactime_dump_reply)       \
57 _(MACTIME_DETAILS, mactime_details)
58
59 static void vl_api_mactime_dump_reply_t_handler
60   (vl_api_mactime_dump_reply_t * mp)
61 {
62   mt_main_t *mm = &mt_main;
63   i32 retval = clib_net_to_host_u32 (mp->retval);
64
65   mm->retval = retval;
66   mm->result_ready = 1;
67 }
68
69 static void
70 vl_api_mactime_details_t_handler (vl_api_mactime_details_t * mp)
71 {
72   mt_main_t *mm = &mt_main;
73   mactime_device_t *dev;
74   int i;
75   clib_timebase_range_t *rp;
76   uword *p;
77
78   if (PREDICT_FALSE (mm->device_by_device_name == 0))
79     mm->device_by_device_name = hash_create_string (0, sizeof (uword));
80
81   p = hash_get_mem (mm->device_by_device_name, mp->device_name);
82   if (p)
83     dev = pool_elt_at_index (mm->devices, p[0]);
84   else
85     {
86       u8 *hash_name_copy = format (0, "%s%c", mp->device_name, 0);
87       pool_get (mm->devices, dev);
88       memset (dev, 0, sizeof (*dev));
89       dev->device_name = vec_dup (hash_name_copy);
90       hash_set_mem (mm->device_by_device_name, hash_name_copy,
91                     dev - mm->devices);
92     }
93
94   clib_memcpy_fast (dev->mac_address, mp->mac_address,
95                     sizeof (dev->mac_address));
96   dev->data_quota = clib_net_to_host_u64 (mp->data_quota);
97   dev->data_used_in_range = clib_net_to_host_u64 (mp->data_used_in_range);
98   dev->flags = clib_net_to_host_u32 (mp->flags);
99   dev->pool_index = clib_net_to_host_u32 (mp->pool_index);
100   vec_reset_length (dev->ranges);
101   for (i = 0; i < clib_net_to_host_u32 (mp->nranges); i++)
102     {
103       vec_add2 (dev->ranges, rp, 1);
104       rp->start = mp->ranges[i].start;
105       rp->end = mp->ranges[i].end;
106     }
107 }
108
109 #define vl_print(handle, ...) fformat(handle, __VA_ARGS__)
110 #define vl_endianfun            /* define message structures */
111 #include <mactime/mactime.api.h>
112 #undef vl_endianfun
113
114 /* instantiate all the print functions we know about */
115 #define vl_printfun
116 #include <mactime/mactime.api.h>
117 #undef vl_printfun
118
119 #define vl_api_version(n,v) static u32 api_version = v;
120 #include <mactime/mactime.api.h>
121 #undef vl_api_version
122
123 static int
124 connect_to_vpp (char *name)
125 {
126   api_main_t *am = vlibapi_get_main ();
127   mt_main_t *mm = &mt_main;
128   u8 *msg_base_lookup_name;
129
130   if (vl_client_connect_to_vlib ("/vpe-api", name, 32) < 0)
131     return -1;
132
133   mm->vl_input_queue = am->shmem_hdr->vl_input_queue;
134   mm->my_client_index = am->my_client_index;
135
136   msg_base_lookup_name = format (0, "mactime_%08x%c", api_version, 0);
137
138   mm->msg_id_base = vl_client_get_first_plugin_msg_id
139     ((char *) msg_base_lookup_name);
140
141   vec_free (msg_base_lookup_name);
142
143   if (mm->msg_id_base == (u16) ~ 0)
144     return -1;
145
146 #define _(N,n)                                                  \
147     vl_msg_api_set_handlers((VL_API_##N + mm->msg_id_base),     \
148                            #n,                                  \
149                            vl_api_##n##_t_handler,              \
150                            vl_noop_handler,                     \
151                            vl_api_##n##_t_endian,               \
152                            vl_api_##n##_t_print,                \
153                            sizeof(vl_api_##n##_t), 1);
154   foreach_mactime_api_msg;
155 #undef _
156
157   return 0;
158 }
159
160 static void
161 dump_mactime_table (mt_main_t * mm)
162 {
163   vl_api_mactime_dump_t *mp;
164   u32 deadman_counter = 1000;
165
166   /* Send the dump request */
167   mp = vl_msg_api_alloc (sizeof (*mp));
168   memset (mp, 0, sizeof (*mp));
169   mp->_vl_msg_id =
170     clib_host_to_net_u16 (VL_API_MACTIME_DUMP + mm->msg_id_base);
171   mp->client_index = mm->my_client_index;
172   mp->my_table_epoch = mm->my_table_epoch;
173   vl_msg_api_send_shmem (mm->vl_input_queue, (u8 *) & mp);
174
175   /* Wait up to 1 second for vpp to reply */
176   while (deadman_counter-- && mm->result_ready == 0)
177     unix_sleep (1e-3);
178
179   if (mm->retval && (mm->retval != VNET_API_ERROR_NO_CHANGE))
180     clib_warning ("dump reply %d", mm->retval);
181
182 }
183
184 static void
185 scrape_stats_segment (mt_main_t * mm)
186 {
187   vlib_counter_t **counters_by_thread;
188   vlib_counter_t *counters;
189   mactime_device_t *dev;
190   stat_segment_access_t sa;
191   stat_client_main_t *sm = mm->stat_client_main;
192   stat_segment_directory_entry_t *ep;
193   int need_update2 = 0;
194   static u32 *pool_indices;
195   int i, j;
196
197   vec_reset_length (pool_indices);
198   /* *INDENT-OFF* */
199   pool_foreach (dev, mm->devices,
200   ({
201     vec_add1 (pool_indices, dev->pool_index);
202   }));
203   /* *INDENT-ON* */
204
205   /* Nothing to do... */
206   if (vec_len (pool_indices) == 0)
207     return;
208
209 again1:
210
211   /* Has directory been updated? */
212   if (mm->ls_result1 == 0 || (sm->shared_header->epoch != sm->current_epoch))
213     {
214       need_update2 = 1;
215       vec_free (mm->ls_result1);
216       mm->ls_result1 = stat_segment_ls (mm->pattern1);
217     }
218
219   stat_segment_access_start (&sa, sm);
220
221   ep = vec_elt_at_index (sm->directory_vector, mm->ls_result1[0]);
222   counters_by_thread = stat_segment_adjust (sm, ep->data);
223
224   for (i = 0; i < vec_len (pool_indices); i++)
225     {
226       u32 index = pool_indices[i];
227
228       vec_validate (mm->allow_counters, index);
229       mm->allow_counters[index].packets = 0;
230       mm->allow_counters[index].bytes = 0;
231
232       for (j = 0; j < vec_len (counters_by_thread); j++)
233         {
234           counters = stat_segment_adjust (sm, counters_by_thread[j]);
235           mm->allow_counters[index].packets += counters[index].packets;
236           mm->allow_counters[index].bytes += counters[index].bytes;
237         }
238     }
239
240   /* Ugh, segment changed during access. Try again */
241   if (stat_segment_access_end (&sa, sm))
242     goto again1;
243
244   /* Has directory been updated? */
245   if (mm->ls_result2 == 0 || need_update2)
246     {
247       vec_free (mm->ls_result2);
248       mm->ls_result2 = stat_segment_ls (mm->pattern2);
249     }
250
251 again2:
252   stat_segment_access_start (&sa, sm);
253
254   ep = vec_elt_at_index (sm->directory_vector, mm->ls_result2[0]);
255   counters_by_thread = stat_segment_adjust (sm, ep->data);
256
257   for (i = 0; i < vec_len (pool_indices); i++)
258     {
259       u32 index = pool_indices[i];
260
261       vec_validate (mm->drop_counters, index);
262       mm->drop_counters[index].packets = 0;
263       mm->drop_counters[index].bytes = 0;
264
265       for (j = 0; j < vec_len (counters_by_thread); j++)
266         {
267           counters = stat_segment_adjust (sm, counters_by_thread[j]);
268           mm->drop_counters[index].packets += counters[index].packets;
269           mm->drop_counters[index].bytes += counters[index].bytes;
270         }
271     }
272   /* Ugh, segment changed during access. Try again */
273   if (stat_segment_access_end (&sa, sm))
274     goto again2;
275 }
276
277 static u8 *
278 format_mac_address (u8 * s, va_list * args)
279 {
280   u8 *a = va_arg (*args, u8 *);
281
282   return format (s, "%02x:%02x:%02x:%02x:%02x:%02x",
283                  a[0], a[1], a[2], a[3], a[4], a[5]);
284 }
285
286 static u8 *
287 format_bytes_with_width (u8 * s, va_list * va)
288 {
289   uword nbytes = va_arg (*va, u64);
290   int width = va_arg (*va, int);
291   f64 nbytes_f64;
292   u8 *fmt;
293   char *suffix = "";
294
295   if (width > 0)
296     fmt = format (0, "%%%d.3f%%s%c", width, 0);
297   else
298     fmt = format (0, "%%.3f%%s%c", 0);
299
300   if (nbytes > (1024ULL * 1024ULL * 1024ULL))
301     {
302       nbytes_f64 = ((f64) nbytes) / (1024.0 * 1024.0 * 1024.0);
303       suffix = "G";
304     }
305   else if (nbytes > (1024ULL * 1024ULL))
306     {
307       nbytes_f64 = ((f64) nbytes) / (1024.0 * 1024.0);
308       suffix = "M";
309     }
310   else if (nbytes > 1024ULL)
311     {
312       nbytes_f64 = ((f64) nbytes) / (1024.0);
313       suffix = "K";
314     }
315   else
316     {
317       nbytes_f64 = (f64) nbytes;
318       suffix = "B";
319     }
320
321   s = format (s, (char *) fmt, nbytes_f64, suffix);
322   vec_free (fmt);
323   return s;
324 }
325
326 static u8 *
327 format_device (u8 * s, va_list * args)
328 {
329   mactime_device_t *dp = va_arg (*args, mactime_device_t *);
330   mt_main_t *mm = &mt_main;
331   int verbose = va_arg (*args, int);
332   int current_status = 99;
333   char *status_string;
334   u8 *macstring = 0;
335   f64 now;
336   int j;
337
338   if (dp == 0)
339     {
340       s = format (s, "%-15s %5s %18s %14s %10s %11s %13s",
341                   "Device Name", "Index", "Addresses", "Status",
342                   "AllowPkt", "AllowByte", "DropPkt");
343       vec_add1 (s, '\n');
344       return s;
345     }
346
347   now = clib_timebase_now (&mm->timebase);
348
349   if (PREDICT_FALSE ((now - mm->sunday_midnight) > 86400.0 * 7.0))
350     mm->sunday_midnight = clib_timebase_find_sunday_midnight (now);
351
352   /* Check dynamic ranges */
353   for (j = 0; j < vec_len (dp->ranges); j++)
354     {
355       clib_timebase_range_t *r = dp->ranges + j;
356       f64 start0, end0;
357
358       start0 = r->start + mm->sunday_midnight;
359       end0 = r->end + mm->sunday_midnight;
360       if (verbose)
361         s = format (s, "  Range %d: %U - %U\n", j,
362                     format_clib_timebase_time, start0,
363                     format_clib_timebase_time, end0);
364
365       if (now >= start0 && now <= end0)
366         {
367           if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW)
368             current_status = 3;
369           else if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW_QUOTA)
370             current_status = 5;
371           else
372             current_status = 2;
373           if (verbose)
374             {
375               s = format (s, "  Time in range %d:", j);
376               s = format (s, "     %U - %U\n",
377                           format_clib_timebase_time, start0,
378                           format_clib_timebase_time, end0);
379             }
380           goto print;
381         }
382     }
383   if (verbose && j)
384     s = format (s, "  No range match.\n");
385   if (dp->flags & MACTIME_DEVICE_FLAG_STATIC_DROP)
386     current_status = 0;
387   if (dp->flags & MACTIME_DEVICE_FLAG_STATIC_ALLOW)
388     current_status = 1;
389   if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW)
390     current_status = 2;
391   if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_DROP)
392     current_status = 3;
393   if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW_QUOTA)
394     current_status = 4;
395
396 print:
397   macstring = format (0, "%U", format_mac_address, dp->mac_address);
398   switch (current_status)
399     {
400     case 0:
401       status_string = "static drop";
402       break;
403     case 1:
404       status_string = "static allow";
405       break;
406     case 2:
407       status_string = "dynamic drop";
408       break;
409     case 3:
410       status_string = "dynamic allow";
411       break;
412     case 4:
413       status_string = "d-quota inact";
414       break;
415     case 5:
416       status_string = "d-quota activ";
417       break;
418     default:
419       status_string = "code bug!";
420       break;
421     }
422
423   s = format (s, "%-15s %5d %18s %14s %10lld %U %13lld\n",
424               dp->device_name, dp->pool_index, macstring, status_string,
425               mm->allow_counters[dp->pool_index].packets,
426               format_bytes_with_width,
427               mm->allow_counters[dp->pool_index].bytes, 10,
428               mm->drop_counters[dp->pool_index].packets);
429   vec_free (macstring);
430
431   if (dp->data_quota > 0)
432     {
433       s = format (s, "%-59s %s%U %s%U", " ", "Quota ",
434                   format_bytes_with_width, dp->data_quota, 10,
435                   "Use ", format_bytes_with_width, dp->data_used_in_range, 8);
436       vec_add1 (s, '\n');
437     }
438   return s;
439 }
440
441 static void
442 print_device_table (mt_main_t * mm)
443 {
444   mactime_device_t *dev;
445
446   fformat (stdout, "%U", format_device, 0 /* header */ , 0 /* verbose */ );
447   /* *INDENT-OFF* */
448   pool_foreach (dev, mm->devices,
449   ({
450     fformat (stdout, "%U", format_device, dev, 0 /* verbose */);
451   }));
452   /* *INDENT-ON* */
453 }
454
455 int
456 main (int argc, char **argv)
457 {
458   mt_main_t *mm = &mt_main;
459   extern stat_client_main_t stat_client_main;
460
461   clib_mem_init (0, 64 << 20);
462
463   if (connect_to_vpp ("mactime_top") < 0)
464     {
465       fformat (stderr, "vpp api client connect error\n");
466       exit (1);
467     }
468
469   if (stat_segment_connect (argv[1]) < 0)
470     {
471       fformat (stderr, "stat segment connect error");
472       exit (1);
473     }
474
475   mm->stat_client_main = (stat_client_main_t *) & stat_client_main;
476
477   /* US EDT - $$$ FIXME */
478   clib_time_init (&mm->clib_time);
479   mm->timezone_offset = -5.0;
480   clib_timebase_init (&mm->timebase, mm->timezone_offset,
481                       CLIB_TIMEBASE_DAYLIGHT_USA,
482                       0 /* allocate a clib_time_t */ );
483
484   vec_add1 (mm->pattern1, (u8 *) "^/mactime/allow");
485   vec_add1 (mm->pattern2, (u8 *) "^/mactime/drop");
486
487   while (1)
488     {
489       dump_mactime_table (mm);
490       scrape_stats_segment (mm);
491       print_device_table (mm);
492       unix_sleep (5.0);
493     }
494   return 0;
495 }
496
497
498 /*
499  * fd.io coding-style-patch-verification: ON
500  *
501  * Local Variables:
502  * eval: (c-set-style "gnu")
503  * End:
504  */