b3dee5331ca35775f3201fd466c11cb86f261b02
[vpp.git] / src / vpp / app / stat_client.c
1 /*
2  *------------------------------------------------------------------
3  * api_format.c
4  *
5  * Copyright (c) 2018 Cisco and/or its affiliates.
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at:
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *------------------------------------------------------------------
18  */
19
20 #include <vpp/app/stat_client.h>
21
22 #include <vpp/api/vpe_msg_enum.h>
23
24 #define vl_typedefs             /* define message structures */
25 #include <vpp/api/vpe_all_api_h.h>
26 #undef vl_typedefs
27
28 #define vl_endianfun            /* define endian fcns */
29 #include <vpp/api/vpe_all_api_h.h>
30 #undef vl_endianfun
31
32 /* instantiate all the print functions we know about */
33 #define vl_print(handle, ...) fformat (handle, __VA_ARGS__)
34 #define vl_printfun
35 #include <vpp/api/vpe_all_api_h.h>
36 #undef vl_printfun
37
38 stat_client_main_t stat_client_main;
39
40 static void vl_api_map_stats_segment_reply_t_handler
41   (vl_api_map_stats_segment_reply_t * mp)
42 {
43   stat_client_main_t *sm = &stat_client_main;
44   ssvm_private_t *ssvmp = &sm->stat_segment;
45   ssvm_shared_header_t *shared_header;
46   socket_client_main_t *scm = sm->socket_client_main;
47   int rv = ntohl (mp->retval);
48   int my_fd, retval;
49   clib_error_t *error;
50
51   if (rv != 0)
52     {
53       fformat (stderr, "ERROR mapping stats segment: %d", rv);
54       exit (1);
55     }
56
57   /*
58    * Check the socket for the magic fd
59    */
60   error = vl_sock_api_recv_fd_msg (scm->socket_fd, &my_fd, 5);
61   if (error)
62     {
63       clib_error_report (error);
64       exit (1);
65     }
66
67   memset (ssvmp, 0, sizeof (*ssvmp));
68   ssvmp->fd = my_fd;
69
70   /* Note: this closes memfd.fd */
71   retval = ssvm_slave_init_memfd (ssvmp);
72   if (retval)
73     {
74       clib_warning ("WARNING: segment map returned %d", retval);
75       exit (1);
76     }
77
78   fformat (stdout, "Stat segment mapped OK...\n");
79
80   ASSERT (ssvmp && ssvmp->sh);
81
82   /* Pick up the segment lock from the shared memory header */
83   shared_header = ssvmp->sh;
84   sm->stat_segment_lockp = (clib_spinlock_t *) (shared_header->opaque[0]);
85   sm->segment_ready = 1;
86
87   /* No need to keep the socket API connection open */
88   close (sm->socket_client_main->socket_fd);
89 }
90
91 #define foreach_api_reply_msg \
92 _(MAP_STATS_SEGMENT_REPLY, map_stats_segment_reply)
93
94 static void
95 vpp_api_hookup (void)
96 {
97 #define _(N,n)                                                  \
98     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
99                            vl_api_##n##_t_handler,              \
100                            vl_noop_handler,                     \
101                            vl_api_##n##_t_endian,               \
102                            vl_api_##n##_t_print,                \
103                            sizeof(vl_api_##n##_t), 1);
104   foreach_api_reply_msg;
105 #undef _
106 }
107
108 static int
109 connect_to_vpp (stat_client_main_t * sm)
110 {
111   int rv;
112   vl_api_map_stats_segment_t *mp;
113   api_main_t *am = &api_main;
114
115   sm->socket_client_main = &socket_client_main;
116
117   rv = vl_socket_client_connect ((char *) sm->socket_name,
118                                  "stat_client",
119                                  0 /* default socket rx, tx buffer */ );
120   if (rv)
121     {
122       fformat (stderr, "Error connecting to vpp...\n");
123       exit (1);
124     }
125
126   /* Hook up reply handler */
127   vpp_api_hookup ();
128
129   /* Map the stats segment */
130   mp = vl_socket_client_msg_alloc (sizeof (*mp));
131   mp->_vl_msg_id = ntohs (VL_API_MAP_STATS_SEGMENT);
132   mp->client_index = am->my_client_index;
133   mp->context = 0xdeaddabe;
134
135   /* Send the message */
136   vl_socket_client_write ();
137
138   /* Wait for a reply, process it.. */
139   vl_socket_client_read (5 /* timeout in seconds */ );
140
141   return 0;
142 }
143
144 #define foreach_cached_pointer                                          \
145 _(/sys/vector_rate, SCALAR_POINTER, &stat_client_main.vector_rate_ptr)  \
146 _(/sys/input_rate, SCALAR_POINTER, &stat_client_main.input_rate_ptr)    \
147 _(/sys/last_update, SCALAR_POINTER, &stat_client_main.last_runtime_ptr) \
148 _(/sys/last_stats_clear, SCALAR_POINTER,                                \
149   &stat_client_main.last_runtime_stats_clear_ptr)                       \
150 _(/if/rx, COUNTER_VECTOR, &stat_client_main.intfc_rx_counters)          \
151 _(/if/tx, COUNTER_VECTOR, &stat_client_main.intfc_tx_counters)          \
152 _(/err/0/counter_vector, VECTOR_POINTER,                                \
153   &stat_client_main.thread_0_error_counts)                              \
154 _(serialized_nodes, SERIALIZED_NODES,                                   \
155   &stat_client_main.serialized_nodes)
156
157 typedef struct
158 {
159   char *name;
160   stat_directory_type_t type;
161   void *valuep;
162 } cached_pointer_t;
163
164 cached_pointer_t cached_pointers[] = {
165 #define _(n,t,p) {#n, STAT_DIR_TYPE_##t, (void *)p},
166   foreach_cached_pointer
167 #undef _
168 };
169
170 static void
171 maybe_update_cached_pointers (stat_client_main_t * sm,
172                               ssvm_shared_header_t * shared_header)
173 {
174   uword *p, *counter_vector_by_name;
175   int i;
176   stat_segment_directory_entry_t *ep;
177   cached_pointer_t *cp;
178   u64 *valuep;
179
180   /* Cached pointers OK? */
181   if (sm->current_epoch ==
182       (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH])
183     return;
184
185   fformat (stdout, "Updating cached pointers...\n");
186
187   /* Nope, fix them... */
188   counter_vector_by_name = (uword *)
189     shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR];
190
191   for (i = 0; i < ARRAY_LEN (cached_pointers); i++)
192     {
193       cp = &cached_pointers[i];
194
195       p = hash_get_mem (counter_vector_by_name, cp->name);
196
197       if (p == 0)
198         {
199           clib_warning ("WARN: %s not in directory!", cp->name);
200           continue;
201         }
202       ep = (stat_segment_directory_entry_t *) (p[0]);
203       ASSERT (ep->type == cp->type);
204       valuep = (u64 *) cp->valuep;
205       *valuep = (u64) ep->value;
206     }
207
208   /* And remember that we did... */
209   sm->current_epoch = (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH];
210 }
211
212 static void
213 stat_poll_loop (stat_client_main_t * sm)
214 {
215   struct timespec ts, tsrem;
216   ssvm_private_t *ssvmp = &sm->stat_segment;
217   ssvm_shared_header_t *shared_header;
218   vlib_counter_t *thread0_rx_counters = 0, *thread0_tx_counters = 0;
219   vlib_node_t ***nodes_by_thread;
220   vlib_node_t **nodes;
221   vlib_node_t *n;
222   f64 vector_rate, input_rate;
223   u32 len;
224   int i, j;
225   u32 source_address_match_errors;
226
227   /* Wait until the stats segment is mapped */
228   while (!sm->segment_ready)
229     {
230       ts.tv_sec = 0;
231       ts.tv_nsec = 100000000;
232       while (nanosleep (&ts, &tsrem) < 0)
233         ts = tsrem;
234     }
235
236   shared_header = ssvmp->sh;
237   ASSERT (ssvmp->sh);
238
239   while (1)
240     {
241       /* Scrape stats every 5 seconds */
242       ts.tv_sec = 5;
243       ts.tv_nsec = 0;
244       while (nanosleep (&ts, &tsrem) < 0)
245         ts = tsrem;
246
247       vec_reset_length (thread0_rx_counters);
248       vec_reset_length (thread0_tx_counters);
249
250       /* Grab the stats segment lock */
251       clib_spinlock_lock (sm->stat_segment_lockp);
252
253       /* see if we need to update cached pointers */
254       maybe_update_cached_pointers (sm, shared_header);
255
256       ASSERT (sm->vector_rate_ptr);
257       ASSERT (sm->intfc_rx_counters);
258       ASSERT (sm->intfc_tx_counters);
259
260       /* Read data from the segment */
261       vector_rate = *sm->vector_rate_ptr;
262       input_rate = *sm->input_rate_ptr;
263
264       len = vec_len (sm->intfc_rx_counters[0]);
265
266       ASSERT (len);
267
268       vec_validate (thread0_rx_counters, len - 1);
269       vec_validate (thread0_tx_counters, len - 1);
270
271       clib_memcpy (thread0_rx_counters, sm->intfc_rx_counters[0],
272                    len * sizeof (vlib_counter_t));
273       clib_memcpy (thread0_tx_counters, sm->intfc_tx_counters[0],
274                    len * sizeof (vlib_counter_t));
275
276       source_address_match_errors =
277         sm->thread_0_error_counts[sm->source_address_match_error_index];
278
279       /* Drop the lock */
280       clib_spinlock_unlock (sm->stat_segment_lockp);
281
282       /* And print results... */
283
284       fformat (stdout, "vector_rate %.2f input_rate %.2f\n",
285                vector_rate, input_rate);
286
287       for (i = 0; i < vec_len (thread0_rx_counters); i++)
288         {
289           fformat (stdout, "[%d]: %lld rx packets, %lld rx bytes\n",
290                    i, thread0_rx_counters[i].packets,
291                    thread0_rx_counters[i].bytes);
292           fformat (stdout, "[%d]: %lld tx packets, %lld tx bytes\n",
293                    i, thread0_tx_counters[i].packets,
294                    thread0_tx_counters[i].bytes);
295         }
296
297       fformat (stdout, "%lld source address match errors\n",
298                source_address_match_errors);
299
300       if (sm->serialized_nodes)
301         {
302           nodes_by_thread = vlib_node_unserialize (sm->serialized_nodes);
303
304           /* Across all threads... */
305           for (i = 0; i < vec_len (nodes_by_thread); i++)
306             {
307               u64 n_input, n_output, n_drop, n_punt;
308               u64 n_internal_vectors, n_internal_calls;
309               u64 n_clocks, l, v, c;
310               f64 dt;
311
312               nodes = nodes_by_thread[i];
313
314               fformat (stdout, "Thread %d -------------------------\n", i);
315
316               n_input = n_output = n_drop = n_punt = n_clocks = 0;
317               n_internal_vectors = n_internal_calls = 0;
318
319               /* Across all nodes */
320               for (j = 0; j < vec_len (nodes); j++)
321                 {
322                   n = nodes[j];
323
324                   /* Exactly stolen from node_cli.c... */
325                   l = n->stats_total.clocks - n->stats_last_clear.clocks;
326                   n_clocks += l;
327
328                   v = n->stats_total.vectors - n->stats_last_clear.vectors;
329                   c = n->stats_total.calls - n->stats_last_clear.calls;
330
331                   switch (n->type)
332                     {
333                     default:
334                       continue;
335
336                     case VLIB_NODE_TYPE_INTERNAL:
337                       n_output +=
338                         (n->flags & VLIB_NODE_FLAG_IS_OUTPUT) ? v : 0;
339                       n_drop += (n->flags & VLIB_NODE_FLAG_IS_DROP) ? v : 0;
340                       n_punt += (n->flags & VLIB_NODE_FLAG_IS_PUNT) ? v : 0;
341                       if (!(n->flags & VLIB_NODE_FLAG_IS_OUTPUT))
342                         {
343                           n_internal_vectors += v;
344                           n_internal_calls += c;
345                         }
346                       if (n->flags & VLIB_NODE_FLAG_IS_HANDOFF)
347                         n_input += v;
348                       break;
349
350                     case VLIB_NODE_TYPE_INPUT:
351                       n_input += v;
352                       break;
353                     }
354
355                   if (n->stats_total.calls)
356                     {
357                       fformat (stdout,
358                                "%s (%s): clocks %lld calls %lld vectors %lld ",
359                                n->name,
360                                n->state_string,
361                                n->stats_total.clocks,
362                                n->stats_total.calls, n->stats_total.vectors);
363                       if (n->stats_total.vectors)
364                         fformat (stdout, "clocks/pkt %.2f\n",
365                                  (f64) n->stats_total.clocks /
366                                  (f64) n->stats_total.vectors);
367                       else
368                         fformat (stdout, "\n");
369                     }
370                   vec_free (n->name);
371                   vec_free (n->next_nodes);
372                   vec_free (n);
373                 }
374
375               fformat (stdout, "average vectors/node %.2f\n",
376                        (n_internal_calls > 0
377                         ? (f64) n_internal_vectors / (f64) n_internal_calls
378                         : 0));
379
380
381               dt = *sm->last_runtime_ptr - *sm->last_runtime_stats_clear_ptr;
382
383               fformat (stdout,
384                        " vectors rates in %.4e, out %.4e, drop %.4e, "
385                        "punt %.4e\n",
386                        (f64) n_input / dt,
387                        (f64) n_output / dt, (f64) n_drop / dt,
388                        (f64) n_punt / dt);
389
390               vec_free (nodes);
391             }
392           vec_free (nodes_by_thread);
393         }
394       else
395         {
396           fformat (stdout, "serialized nodes NULL?\n");
397         }
398
399     }
400 }
401
402
403 int
404 main (int argc, char **argv)
405 {
406   unformat_input_t _argv, *a = &_argv;
407   stat_client_main_t *sm = &stat_client_main;
408   u8 *socket_name;
409   int rv;
410
411   clib_mem_init (0, 128 << 20);
412
413   unformat_init_command_line (a, argv);
414
415   socket_name = (u8 *) API_SOCKET_FILE;
416
417   while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
418     {
419       if (unformat (a, "socket-name %s", &socket_name))
420         ;
421       else
422         {
423           fformat (stderr, "%s: usage [socket-name <name>]\n", argv[0]);
424           exit (1);
425         }
426     }
427
428   sm->socket_name = socket_name;
429
430   rv = connect_to_vpp (sm);
431
432   if (rv)
433     {
434       fformat (stderr, "Couldn't connect to vpp, does %s exist?\n",
435                socket_name);
436       exit (1);
437     }
438
439   stat_poll_loop (sm);
440   exit (0);
441 }
442
443 /*
444  * fd.io coding-style-patch-verification: ON
445  *
446  * Local Variables:
447  * eval: (c-set-style "gnu")
448  * End:
449  */