610a6a5b98d3c1e25be4b8296d1960744a44b051
[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 _(rx, COUNTER_VECTOR, &stat_client_main.intfc_rx_counters)              \
148 _(tx, COUNTER_VECTOR, &stat_client_main.intfc_tx_counters)              \
149 _(/err/0/counter_vector, VECTOR_POINTER,                                \
150   &stat_client_main.thread_0_error_counts)                              \
151 _(/err/IP4 source address matches local interface, ERROR_INDEX,         \
152   &stat_client_main.source_address_match_error_index)
153
154 typedef struct
155 {
156   char *name;
157   stat_directory_type_t type;
158   void *valuep;
159 } cached_pointer_t;
160
161 cached_pointer_t cached_pointers[] = {
162 #define _(n,t,p) {#n, STAT_DIR_TYPE_##t, (void *)p},
163   foreach_cached_pointer
164 #undef _
165 };
166
167 static void
168 maybe_update_cached_pointers (stat_client_main_t * sm,
169                               ssvm_shared_header_t * shared_header)
170 {
171   uword *p, *counter_vector_by_name;
172   int i;
173   stat_segment_directory_entry_t *ep;
174   cached_pointer_t *cp;
175   u64 *valuep;
176
177   /* Cached pointers OK? */
178   if (sm->current_epoch ==
179       (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH])
180     return;
181
182   fformat (stdout, "Updating cached pointers...\n");
183
184   /* Nope, fix them... */
185   counter_vector_by_name = (uword *)
186     shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR];
187
188   for (i = 0; i < ARRAY_LEN (cached_pointers); i++)
189     {
190       cp = &cached_pointers[i];
191
192       p = hash_get_mem (counter_vector_by_name, cp->name);
193
194       if (p == 0)
195         {
196           clib_warning ("WARN: %s not in directory!", cp->name);
197           continue;
198         }
199       ep = (stat_segment_directory_entry_t *) (p[0]);
200       ASSERT (ep->type == cp->type);
201       valuep = (u64 *) cp->valuep;
202       *valuep = (u64) ep->value;
203     }
204
205   /* And remember that we did... */
206   sm->current_epoch = (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH];
207 }
208
209 static void
210 stat_poll_loop (stat_client_main_t * sm)
211 {
212   struct timespec ts, tsrem;
213   ssvm_private_t *ssvmp = &sm->stat_segment;
214   ssvm_shared_header_t *shared_header;
215   vlib_counter_t *thread0_rx_counters = 0, *thread0_tx_counters = 0;
216   f64 vector_rate, input_rate;
217   u32 len;
218   int i;
219   u32 source_address_match_errors;
220
221   /* Wait until the stats segment is mapped */
222   while (!sm->segment_ready)
223     {
224       ts.tv_sec = 0;
225       ts.tv_nsec = 100000000;
226       while (nanosleep (&ts, &tsrem) < 0)
227         ts = tsrem;
228     }
229
230   shared_header = ssvmp->sh;
231   ASSERT (ssvmp->sh);
232
233   while (1)
234     {
235       /* Scrape stats every 5 seconds */
236       ts.tv_sec = 5;
237       ts.tv_nsec = 0;
238       while (nanosleep (&ts, &tsrem) < 0)
239         ts = tsrem;
240
241       vec_reset_length (thread0_rx_counters);
242       vec_reset_length (thread0_tx_counters);
243
244       /* Grab the stats segment lock */
245       clib_spinlock_lock (sm->stat_segment_lockp);
246
247       /* see if we need to update cached pointers */
248       maybe_update_cached_pointers (sm, shared_header);
249
250       ASSERT (sm->vector_rate_ptr);
251       ASSERT (sm->intfc_rx_counters);
252       ASSERT (sm->intfc_tx_counters);
253
254       /* Read data from the segment */
255       vector_rate = *sm->vector_rate_ptr;
256       input_rate = *sm->input_rate_ptr;
257
258       len = vec_len (sm->intfc_rx_counters[0]);
259
260       ASSERT (len);
261
262       vec_validate (thread0_rx_counters, len - 1);
263       vec_validate (thread0_tx_counters, len - 1);
264
265       clib_memcpy (thread0_rx_counters, sm->intfc_rx_counters[0],
266                    len * sizeof (vlib_counter_t));
267       clib_memcpy (thread0_tx_counters, sm->intfc_tx_counters[0],
268                    len * sizeof (vlib_counter_t));
269
270       source_address_match_errors =
271         sm->thread_0_error_counts[sm->source_address_match_error_index];
272
273       /* Drop the lock */
274       clib_spinlock_unlock (sm->stat_segment_lockp);
275
276       /* And print results... */
277
278       fformat (stdout, "vector_rate %.2f input_rate %.2f\n",
279                vector_rate, input_rate);
280
281       for (i = 0; i < vec_len (thread0_rx_counters); i++)
282         {
283           fformat (stdout, "[%d]: %lld rx packets, %lld rx bytes\n",
284                    i, thread0_rx_counters[i].packets,
285                    thread0_rx_counters[i].bytes);
286           fformat (stdout, "[%d]: %lld tx packets, %lld tx bytes\n",
287                    i, thread0_tx_counters[i].packets,
288                    thread0_tx_counters[i].bytes);
289         }
290
291       fformat (stdout, "%lld source address match errors\n",
292                source_address_match_errors);
293     }
294 }
295
296
297 int
298 main (int argc, char **argv)
299 {
300   unformat_input_t _argv, *a = &_argv;
301   stat_client_main_t *sm = &stat_client_main;
302   u8 *socket_name;
303   int rv;
304
305   clib_mem_init (0, 128 << 20);
306
307   unformat_init_command_line (a, argv);
308
309   socket_name = (u8 *) API_SOCKET_FILE;
310
311   while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
312     {
313       if (unformat (a, "socket-name %s", &socket_name))
314         ;
315       else
316         {
317           fformat (stderr, "%s: usage [socket-name <name>]\n", argv[0]);
318           exit (1);
319         }
320     }
321
322   sm->socket_name = socket_name;
323
324   rv = connect_to_vpp (sm);
325
326   if (rv)
327     {
328       fformat (stderr, "Couldn't connect to vpp, does %s exist?\n",
329                socket_name);
330       exit (1);
331     }
332
333   stat_poll_loop (sm);
334   exit (0);
335 }
336
337 /*
338  * fd.io coding-style-patch-verification: ON
339  *
340  * Local Variables:
341  * eval: (c-set-style "gnu")
342  * End:
343  */