Stat segment / client: show run" works now
[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 _(vector_rate, SCALAR_POINTER, &stat_client_main.vector_rate_ptr)       \
146 _(input_rate, SCALAR_POINTER, &stat_client_main.input_rate_ptr)         \
147 _(last_update, SCALAR_POINTER, &stat_client_main.last_runtime_ptr)      \
148 _(last_stats_clear, SCALAR_POINTER,                                     \
149   &stat_client_main.last_runtime_stats_clear_ptr)                       \
150 _(rx, COUNTER_VECTOR, &stat_client_main.intfc_rx_counters)              \
151 _(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 _(/err/IP4 source address matches local interface, ERROR_INDEX,         \
155   &stat_client_main.source_address_match_error_index)                   \
156 _(serialized_nodes, SERIALIZED_NODES,                                   \
157   &stat_client_main.serialized_nodes)
158
159 typedef struct
160 {
161   char *name;
162   stat_directory_type_t type;
163   void *valuep;
164 } cached_pointer_t;
165
166 cached_pointer_t cached_pointers[] = {
167 #define _(n,t,p) {#n, STAT_DIR_TYPE_##t, (void *)p},
168   foreach_cached_pointer
169 #undef _
170 };
171
172 static void
173 maybe_update_cached_pointers (stat_client_main_t * sm,
174                               ssvm_shared_header_t * shared_header)
175 {
176   uword *p, *counter_vector_by_name;
177   int i;
178   stat_segment_directory_entry_t *ep;
179   cached_pointer_t *cp;
180   u64 *valuep;
181
182   /* Cached pointers OK? */
183   if (sm->current_epoch ==
184       (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH])
185     return;
186
187   fformat (stdout, "Updating cached pointers...\n");
188
189   /* Nope, fix them... */
190   counter_vector_by_name = (uword *)
191     shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR];
192
193   for (i = 0; i < ARRAY_LEN (cached_pointers); i++)
194     {
195       cp = &cached_pointers[i];
196
197       p = hash_get_mem (counter_vector_by_name, cp->name);
198
199       if (p == 0)
200         {
201           clib_warning ("WARN: %s not in directory!", cp->name);
202           continue;
203         }
204       ep = (stat_segment_directory_entry_t *) (p[0]);
205       ASSERT (ep->type == cp->type);
206       valuep = (u64 *) cp->valuep;
207       *valuep = (u64) ep->value;
208     }
209
210   /* And remember that we did... */
211   sm->current_epoch = (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH];
212 }
213
214 static void
215 stat_poll_loop (stat_client_main_t * sm)
216 {
217   struct timespec ts, tsrem;
218   ssvm_private_t *ssvmp = &sm->stat_segment;
219   ssvm_shared_header_t *shared_header;
220   vlib_counter_t *thread0_rx_counters = 0, *thread0_tx_counters = 0;
221   vlib_node_t ***nodes_by_thread;
222   vlib_node_t **nodes;
223   vlib_node_t *n;
224   f64 vector_rate, input_rate;
225   u32 len;
226   int i, j;
227   u32 source_address_match_errors;
228
229   /* Wait until the stats segment is mapped */
230   while (!sm->segment_ready)
231     {
232       ts.tv_sec = 0;
233       ts.tv_nsec = 100000000;
234       while (nanosleep (&ts, &tsrem) < 0)
235         ts = tsrem;
236     }
237
238   shared_header = ssvmp->sh;
239   ASSERT (ssvmp->sh);
240
241   while (1)
242     {
243       /* Scrape stats every 5 seconds */
244       ts.tv_sec = 5;
245       ts.tv_nsec = 0;
246       while (nanosleep (&ts, &tsrem) < 0)
247         ts = tsrem;
248
249       vec_reset_length (thread0_rx_counters);
250       vec_reset_length (thread0_tx_counters);
251
252       /* Grab the stats segment lock */
253       clib_spinlock_lock (sm->stat_segment_lockp);
254
255       /* see if we need to update cached pointers */
256       maybe_update_cached_pointers (sm, shared_header);
257
258       ASSERT (sm->vector_rate_ptr);
259       ASSERT (sm->intfc_rx_counters);
260       ASSERT (sm->intfc_tx_counters);
261
262       /* Read data from the segment */
263       vector_rate = *sm->vector_rate_ptr;
264       input_rate = *sm->input_rate_ptr;
265
266       len = vec_len (sm->intfc_rx_counters[0]);
267
268       ASSERT (len);
269
270       vec_validate (thread0_rx_counters, len - 1);
271       vec_validate (thread0_tx_counters, len - 1);
272
273       clib_memcpy (thread0_rx_counters, sm->intfc_rx_counters[0],
274                    len * sizeof (vlib_counter_t));
275       clib_memcpy (thread0_tx_counters, sm->intfc_tx_counters[0],
276                    len * sizeof (vlib_counter_t));
277
278       source_address_match_errors =
279         sm->thread_0_error_counts[sm->source_address_match_error_index];
280
281       /* Drop the lock */
282       clib_spinlock_unlock (sm->stat_segment_lockp);
283
284       /* And print results... */
285
286       fformat (stdout, "vector_rate %.2f input_rate %.2f\n",
287                vector_rate, input_rate);
288
289       for (i = 0; i < vec_len (thread0_rx_counters); i++)
290         {
291           fformat (stdout, "[%d]: %lld rx packets, %lld rx bytes\n",
292                    i, thread0_rx_counters[i].packets,
293                    thread0_rx_counters[i].bytes);
294           fformat (stdout, "[%d]: %lld tx packets, %lld tx bytes\n",
295                    i, thread0_tx_counters[i].packets,
296                    thread0_tx_counters[i].bytes);
297         }
298
299       fformat (stdout, "%lld source address match errors\n",
300                source_address_match_errors);
301
302       if (sm->serialized_nodes)
303         {
304           nodes_by_thread = vlib_node_unserialize (sm->serialized_nodes);
305
306           /* Across all threads... */
307           for (i = 0; i < vec_len (nodes_by_thread); i++)
308             {
309               u64 n_input, n_output, n_drop, n_punt;
310               u64 n_internal_vectors, n_internal_calls;
311               u64 n_clocks, l, v, c;
312               f64 dt;
313
314               nodes = nodes_by_thread[i];
315
316               fformat (stdout, "Thread %d -------------------------\n", i);
317
318               n_input = n_output = n_drop = n_punt = n_clocks = 0;
319               n_internal_vectors = n_internal_calls = 0;
320
321               /* Across all nodes */
322               for (j = 0; j < vec_len (nodes); j++)
323                 {
324                   n = nodes[j];
325
326                   /* Exactly stolen from node_cli.c... */
327                   l = n->stats_total.clocks - n->stats_last_clear.clocks;
328                   n_clocks += l;
329
330                   v = n->stats_total.vectors - n->stats_last_clear.vectors;
331                   c = n->stats_total.calls - n->stats_last_clear.calls;
332
333                   switch (n->type)
334                     {
335                     default:
336                       continue;
337
338                     case VLIB_NODE_TYPE_INTERNAL:
339                       n_output +=
340                         (n->flags & VLIB_NODE_FLAG_IS_OUTPUT) ? v : 0;
341                       n_drop += (n->flags & VLIB_NODE_FLAG_IS_DROP) ? v : 0;
342                       n_punt += (n->flags & VLIB_NODE_FLAG_IS_PUNT) ? v : 0;
343                       if (!(n->flags & VLIB_NODE_FLAG_IS_OUTPUT))
344                         {
345                           n_internal_vectors += v;
346                           n_internal_calls += c;
347                         }
348                       if (n->flags & VLIB_NODE_FLAG_IS_HANDOFF)
349                         n_input += v;
350                       break;
351
352                     case VLIB_NODE_TYPE_INPUT:
353                       n_input += v;
354                       break;
355                     }
356
357                   if (n->stats_total.calls)
358                     {
359                       fformat (stdout,
360                                "%s (%s): clocks %lld calls %lld vectors %lld ",
361                                n->name,
362                                n->state_string,
363                                n->stats_total.clocks,
364                                n->stats_total.calls, n->stats_total.vectors);
365                       if (n->stats_total.vectors)
366                         fformat (stdout, "clocks/pkt %.2f\n",
367                                  (f64) n->stats_total.clocks /
368                                  (f64) n->stats_total.vectors);
369                       else
370                         fformat (stdout, "\n");
371                     }
372                   vec_free (n->name);
373                   vec_free (n->next_nodes);
374                   vec_free (n);
375                 }
376
377               fformat (stdout, "average vectors/node %.2f\n",
378                        (n_internal_calls > 0
379                         ? (f64) n_internal_vectors / (f64) n_internal_calls
380                         : 0));
381
382
383               dt = *sm->last_runtime_ptr - *sm->last_runtime_stats_clear_ptr;
384
385               fformat (stdout,
386                        " vectors rates in %.4e, out %.4e, drop %.4e, "
387                        "punt %.4e\n",
388                        (f64) n_input / dt,
389                        (f64) n_output / dt, (f64) n_drop / dt,
390                        (f64) n_punt / dt);
391
392               vec_free (nodes);
393             }
394           vec_free (nodes_by_thread);
395         }
396       else
397         {
398           fformat (stdout, "serialized nodes NULL?\n");
399         }
400
401     }
402 }
403
404
405 int
406 main (int argc, char **argv)
407 {
408   unformat_input_t _argv, *a = &_argv;
409   stat_client_main_t *sm = &stat_client_main;
410   u8 *socket_name;
411   int rv;
412
413   clib_mem_init (0, 128 << 20);
414
415   unformat_init_command_line (a, argv);
416
417   socket_name = (u8 *) API_SOCKET_FILE;
418
419   while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
420     {
421       if (unformat (a, "socket-name %s", &socket_name))
422         ;
423       else
424         {
425           fformat (stderr, "%s: usage [socket-name <name>]\n", argv[0]);
426           exit (1);
427         }
428     }
429
430   sm->socket_name = socket_name;
431
432   rv = connect_to_vpp (sm);
433
434   if (rv)
435     {
436       fformat (stderr, "Couldn't connect to vpp, does %s exist?\n",
437                socket_name);
438       exit (1);
439     }
440
441   stat_poll_loop (sm);
442   exit (0);
443 }
444
445 /*
446  * fd.io coding-style-patch-verification: ON
447  *
448  * Local Variables:
449  * eval: (c-set-style "gnu")
450  * End:
451  */