api: API trace improvements
[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), #n,                \
148                            vl_api_##n##_t_handler, vl_noop_handler,           \
149                            vl_api_##n##_t_endian, vl_api_##n##_t_print,       \
150                            sizeof (vl_api_##n##_t), 1, vl_api_##n##_t_tojson, \
151                            vl_api_##n##_t_fromjson);
152   foreach_mactime_api_msg;
153 #undef _
154
155   return 0;
156 }
157
158 static void
159 dump_mactime_table (mt_main_t * mm)
160 {
161   vl_api_mactime_dump_t *mp;
162   u32 deadman_counter = 1000;
163
164   /* Send the dump request */
165   mp = vl_msg_api_alloc (sizeof (*mp));
166   memset (mp, 0, sizeof (*mp));
167   mp->_vl_msg_id =
168     clib_host_to_net_u16 (VL_API_MACTIME_DUMP + mm->msg_id_base);
169   mp->client_index = mm->my_client_index;
170   mp->my_table_epoch = mm->my_table_epoch;
171   vl_msg_api_send_shmem (mm->vl_input_queue, (u8 *) & mp);
172
173   /* Wait up to 1 second for vpp to reply */
174   while (deadman_counter-- && mm->result_ready == 0)
175     unix_sleep (1e-3);
176
177   if (mm->retval && (mm->retval != VNET_API_ERROR_NO_CHANGE))
178     clib_warning ("dump reply %d", mm->retval);
179
180 }
181
182 static void
183 scrape_stats_segment (mt_main_t * mm)
184 {
185   vlib_counter_t **counters_by_thread;
186   vlib_counter_t *counters;
187   mactime_device_t *dev;
188   stat_segment_access_t sa;
189   stat_client_main_t *sm = mm->stat_client_main;
190   stat_segment_directory_entry_t *ep;
191   int need_update2 = 0;
192   static u32 *pool_indices;
193   int i, j;
194
195   vec_reset_length (pool_indices);
196   /* *INDENT-OFF* */
197   pool_foreach (dev, mm->devices)
198    {
199     vec_add1 (pool_indices, dev->pool_index);
200   }
201   /* *INDENT-ON* */
202
203   /* Nothing to do... */
204   if (vec_len (pool_indices) == 0)
205     return;
206
207 again1:
208
209   /* Has directory been updated? */
210   if (mm->ls_result1 == 0 || (sm->shared_header->epoch != sm->current_epoch))
211     {
212       need_update2 = 1;
213       vec_free (mm->ls_result1);
214       mm->ls_result1 = stat_segment_ls (mm->pattern1);
215     }
216
217   stat_segment_access_start (&sa, sm);
218
219   ep = vec_elt_at_index (sm->directory_vector, mm->ls_result1[0]);
220   counters_by_thread = stat_segment_adjust (sm, ep->data);
221
222   for (i = 0; i < vec_len (pool_indices); i++)
223     {
224       u32 index = pool_indices[i];
225
226       vec_validate (mm->allow_counters, index);
227       mm->allow_counters[index].packets = 0;
228       mm->allow_counters[index].bytes = 0;
229
230       for (j = 0; j < vec_len (counters_by_thread); j++)
231         {
232           counters = stat_segment_adjust (sm, counters_by_thread[j]);
233           mm->allow_counters[index].packets += counters[index].packets;
234           mm->allow_counters[index].bytes += counters[index].bytes;
235         }
236     }
237
238   /* Ugh, segment changed during access. Try again */
239   if (stat_segment_access_end (&sa, sm))
240     goto again1;
241
242   /* Has directory been updated? */
243   if (mm->ls_result2 == 0 || need_update2)
244     {
245       vec_free (mm->ls_result2);
246       mm->ls_result2 = stat_segment_ls (mm->pattern2);
247     }
248
249 again2:
250   stat_segment_access_start (&sa, sm);
251
252   ep = vec_elt_at_index (sm->directory_vector, mm->ls_result2[0]);
253   counters_by_thread = stat_segment_adjust (sm, ep->data);
254
255   for (i = 0; i < vec_len (pool_indices); i++)
256     {
257       u32 index = pool_indices[i];
258
259       vec_validate (mm->drop_counters, index);
260       mm->drop_counters[index].packets = 0;
261       mm->drop_counters[index].bytes = 0;
262
263       for (j = 0; j < vec_len (counters_by_thread); j++)
264         {
265           counters = stat_segment_adjust (sm, counters_by_thread[j]);
266           mm->drop_counters[index].packets += counters[index].packets;
267           mm->drop_counters[index].bytes += counters[index].bytes;
268         }
269     }
270   /* Ugh, segment changed during access. Try again */
271   if (stat_segment_access_end (&sa, sm))
272     goto again2;
273 }
274
275 static u8 *
276 format_mac_address (u8 * s, va_list * args)
277 {
278   u8 *a = va_arg (*args, u8 *);
279
280   return format (s, "%02x:%02x:%02x:%02x:%02x:%02x",
281                  a[0], a[1], a[2], a[3], a[4], a[5]);
282 }
283
284 static u8 *
285 format_bytes_with_width (u8 * s, va_list * va)
286 {
287   uword nbytes = va_arg (*va, u64);
288   int width = va_arg (*va, int);
289   f64 nbytes_f64;
290   u8 *fmt;
291   char *suffix = "";
292
293   if (width > 0)
294     fmt = format (0, "%%%d.3f%%s%c", width, 0);
295   else
296     fmt = format (0, "%%.3f%%s%c", 0);
297
298   if (nbytes > (1024ULL * 1024ULL * 1024ULL))
299     {
300       nbytes_f64 = ((f64) nbytes) / (1024.0 * 1024.0 * 1024.0);
301       suffix = "G";
302     }
303   else if (nbytes > (1024ULL * 1024ULL))
304     {
305       nbytes_f64 = ((f64) nbytes) / (1024.0 * 1024.0);
306       suffix = "M";
307     }
308   else if (nbytes > 1024ULL)
309     {
310       nbytes_f64 = ((f64) nbytes) / (1024.0);
311       suffix = "K";
312     }
313   else
314     {
315       nbytes_f64 = (f64) nbytes;
316       suffix = "B";
317     }
318
319   s = format (s, (char *) fmt, nbytes_f64, suffix);
320   vec_free (fmt);
321   return s;
322 }
323
324 static u8 *
325 format_device (u8 * s, va_list * args)
326 {
327   mactime_device_t *dp = va_arg (*args, mactime_device_t *);
328   mt_main_t *mm = &mt_main;
329   int verbose = va_arg (*args, int);
330   int current_status = 99;
331   char *status_string;
332   u8 *macstring = 0;
333   f64 now;
334   int j;
335
336   if (dp == 0)
337     {
338       s = format (s, "%-15s %5s %18s %14s %10s %11s %13s",
339                   "Device Name", "Index", "Addresses", "Status",
340                   "AllowPkt", "AllowByte", "DropPkt");
341       vec_add1 (s, '\n');
342       return s;
343     }
344
345   now = clib_timebase_now (&mm->timebase);
346
347   if (PREDICT_FALSE ((now - mm->sunday_midnight) > 86400.0 * 7.0))
348     mm->sunday_midnight = clib_timebase_find_sunday_midnight (now);
349
350   /* Check dynamic ranges */
351   for (j = 0; j < vec_len (dp->ranges); j++)
352     {
353       clib_timebase_range_t *r = dp->ranges + j;
354       f64 start0, end0;
355
356       start0 = r->start + mm->sunday_midnight;
357       end0 = r->end + mm->sunday_midnight;
358       if (verbose)
359         s = format (s, "  Range %d: %U - %U\n", j,
360                     format_clib_timebase_time, start0,
361                     format_clib_timebase_time, end0);
362
363       if (now >= start0 && now <= end0)
364         {
365           if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW)
366             current_status = 3;
367           else if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW_QUOTA)
368             current_status = 5;
369           else
370             current_status = 2;
371           if (verbose)
372             {
373               s = format (s, "  Time in range %d:", j);
374               s = format (s, "     %U - %U\n",
375                           format_clib_timebase_time, start0,
376                           format_clib_timebase_time, end0);
377             }
378           goto print;
379         }
380     }
381   if (verbose && j)
382     s = format (s, "  No range match.\n");
383   if (dp->flags & MACTIME_DEVICE_FLAG_STATIC_DROP)
384     current_status = 0;
385   if (dp->flags & MACTIME_DEVICE_FLAG_STATIC_ALLOW)
386     current_status = 1;
387   if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW)
388     current_status = 2;
389   if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_DROP)
390     current_status = 3;
391   if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW_QUOTA)
392     current_status = 4;
393
394 print:
395   macstring = format (0, "%U", format_mac_address, dp->mac_address);
396   switch (current_status)
397     {
398     case 0:
399       status_string = "static drop";
400       break;
401     case 1:
402       status_string = "static allow";
403       break;
404     case 2:
405       status_string = "dynamic drop";
406       break;
407     case 3:
408       status_string = "dynamic allow";
409       break;
410     case 4:
411       status_string = "d-quota inact";
412       break;
413     case 5:
414       status_string = "d-quota activ";
415       break;
416     default:
417       status_string = "code bug!";
418       break;
419     }
420
421   s = format (s, "%-15s %5d %18s %14s %10lld %U %13lld\n",
422               dp->device_name, dp->pool_index, macstring, status_string,
423               mm->allow_counters[dp->pool_index].packets,
424               format_bytes_with_width,
425               mm->allow_counters[dp->pool_index].bytes, 10,
426               mm->drop_counters[dp->pool_index].packets);
427   vec_free (macstring);
428
429   if (dp->data_quota > 0)
430     {
431       s = format (s, "%-59s %s%U %s%U", " ", "Quota ",
432                   format_bytes_with_width, dp->data_quota, 10,
433                   "Use ", format_bytes_with_width, dp->data_used_in_range, 8);
434       vec_add1 (s, '\n');
435     }
436   return s;
437 }
438
439 static void
440 print_device_table (mt_main_t * mm)
441 {
442   mactime_device_t *dev;
443
444   fformat (stdout, "%U", format_device, 0 /* header */ , 0 /* verbose */ );
445   /* *INDENT-OFF* */
446   pool_foreach (dev, mm->devices)
447    {
448     fformat (stdout, "%U", format_device, dev, 0 /* verbose */);
449   }
450   /* *INDENT-ON* */
451 }
452
453 int
454 main (int argc, char **argv)
455 {
456   mt_main_t *mm = &mt_main;
457   extern stat_client_main_t stat_client_main;
458
459   clib_mem_init (0, 64 << 20);
460
461   if (connect_to_vpp ("mactime_top") < 0)
462     {
463       fformat (stderr, "vpp api client connect error\n");
464       exit (1);
465     }
466
467   if (stat_segment_connect (argv[1]) < 0)
468     {
469       fformat (stderr, "stat segment connect error");
470       exit (1);
471     }
472
473   mm->stat_client_main = (stat_client_main_t *) & stat_client_main;
474
475   /* US EDT - $$$ FIXME */
476   clib_time_init (&mm->clib_time);
477   mm->timezone_offset = -5.0;
478   clib_timebase_init (&mm->timebase, mm->timezone_offset,
479                       CLIB_TIMEBASE_DAYLIGHT_USA,
480                       0 /* allocate a clib_time_t */ );
481
482   vec_add1 (mm->pattern1, (u8 *) "^/mactime/allow");
483   vec_add1 (mm->pattern2, (u8 *) "^/mactime/drop");
484
485   while (1)
486     {
487       dump_mactime_table (mm);
488       scrape_stats_segment (mm);
489       print_device_table (mm);
490       unix_sleep (5.0);
491     }
492   return 0;
493 }
494
495
496 /*
497  * fd.io coding-style-patch-verification: ON
498  *
499  * Local Variables:
500  * eval: (c-set-style "gnu")
501  * End:
502  */