STATS: Separate socket for fd exchange.
[vpp.git] / src / vpp / app / stat_client.c
1 /*
2  *------------------------------------------------------------------
3  * stat_client.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 stat_client_main_t stat_client_main;
23
24 static int
25 stat_segment_connect (stat_client_main_t * sm)
26 {
27   ssvm_private_t *ssvmp = &sm->stat_segment;
28   ssvm_shared_header_t *shared_header;
29   clib_socket_t s = { 0 };
30   clib_error_t *err;
31   int fd = -1, retval;
32
33   s.config = (char *) sm->socket_name;
34   s.flags = CLIB_SOCKET_F_IS_CLIENT | CLIB_SOCKET_F_SEQPACKET;
35   err = clib_socket_init (&s);
36   if (err)
37     {
38       clib_error_report (err);
39       exit (1);
40     }
41   err = clib_socket_recvmsg (&s, 0, 0, &fd, 1);
42   if (err)
43     {
44       clib_error_report (err);
45       return -1;
46     }
47   clib_socket_close (&s);
48
49   memset (ssvmp, 0, sizeof (*ssvmp));
50   ssvmp->fd = fd;
51
52   /* Note: this closes memfd.fd */
53   retval = ssvm_slave_init_memfd (ssvmp);
54   if (retval)
55     {
56       clib_warning ("WARNING: segment map returned %d", retval);
57       return -1;
58     }
59
60   fformat (stdout, "Stat segment mapped OK...\n");
61
62   ASSERT (ssvmp && ssvmp->sh);
63
64   /* Pick up the segment lock from the shared memory header */
65   shared_header = ssvmp->sh;
66   sm->stat_segment_lockp = (clib_spinlock_t *) (shared_header->opaque[0]);
67   sm->segment_ready = 1;
68
69   return 0;
70 }
71
72 #define foreach_cached_pointer                                          \
73 _(/sys/vector_rate, SCALAR_POINTER, &stat_client_main.vector_rate_ptr)  \
74 _(/sys/input_rate, SCALAR_POINTER, &stat_client_main.input_rate_ptr)    \
75 _(/sys/last_update, SCALAR_POINTER, &stat_client_main.last_runtime_ptr) \
76 _(/sys/last_stats_clear, SCALAR_POINTER,                                \
77   &stat_client_main.last_runtime_stats_clear_ptr)                       \
78 _(/if/rx, COUNTER_VECTOR, &stat_client_main.intfc_rx_counters)          \
79 _(/if/tx, COUNTER_VECTOR, &stat_client_main.intfc_tx_counters)          \
80 _(/err/0/counter_vector, VECTOR_POINTER,                                \
81   &stat_client_main.thread_0_error_counts)                              \
82 _(serialized_nodes, SERIALIZED_NODES,                                   \
83   &stat_client_main.serialized_nodes)
84
85 typedef struct
86 {
87   char *name;
88   stat_directory_type_t type;
89   void *valuep;
90 } cached_pointer_t;
91
92 cached_pointer_t cached_pointers[] = {
93 #define _(n,t,p) {#n, STAT_DIR_TYPE_##t, (void *)p},
94   foreach_cached_pointer
95 #undef _
96 };
97
98 static void
99 maybe_update_cached_pointers (stat_client_main_t * sm,
100                               ssvm_shared_header_t * shared_header)
101 {
102   uword *p, *counter_vector_by_name;
103   int i;
104   stat_segment_directory_entry_t *ep;
105   cached_pointer_t *cp;
106   u64 *valuep;
107
108   /* Cached pointers OK? */
109   if (sm->current_epoch ==
110       (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH])
111     return;
112
113   fformat (stdout, "Updating cached pointers...\n");
114
115   /* Nope, fix them... */
116   counter_vector_by_name = (uword *)
117     shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR];
118
119   for (i = 0; i < ARRAY_LEN (cached_pointers); i++)
120     {
121       cp = &cached_pointers[i];
122
123       p = hash_get_mem (counter_vector_by_name, cp->name);
124
125       if (p == 0)
126         {
127           clib_warning ("WARN: %s not in directory!", cp->name);
128           continue;
129         }
130       ep = (stat_segment_directory_entry_t *) (p[0]);
131       ASSERT (ep->type == cp->type);
132       valuep = (u64 *) cp->valuep;
133       *valuep = (u64) ep->value;
134     }
135
136   /* And remember that we did... */
137   sm->current_epoch = (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH];
138 }
139
140 static void
141 stat_poll_loop (stat_client_main_t * sm)
142 {
143   struct timespec ts, tsrem;
144   ssvm_private_t *ssvmp = &sm->stat_segment;
145   ssvm_shared_header_t *shared_header;
146   vlib_counter_t *thread0_rx_counters = 0, *thread0_tx_counters = 0;
147   vlib_node_t ***nodes_by_thread;
148   vlib_node_t **nodes;
149   vlib_node_t *n;
150   f64 vector_rate, input_rate;
151   u32 len;
152   int i, j;
153   u32 source_address_match_errors;
154
155   /* Wait until the stats segment is mapped */
156   while (!sm->segment_ready)
157     {
158       ts.tv_sec = 0;
159       ts.tv_nsec = 100000000;
160       while (nanosleep (&ts, &tsrem) < 0)
161         ts = tsrem;
162     }
163
164   shared_header = ssvmp->sh;
165   ASSERT (ssvmp->sh);
166
167   while (1)
168     {
169       /* Scrape stats every 5 seconds */
170       ts.tv_sec = 5;
171       ts.tv_nsec = 0;
172       while (nanosleep (&ts, &tsrem) < 0)
173         ts = tsrem;
174
175       vec_reset_length (thread0_rx_counters);
176       vec_reset_length (thread0_tx_counters);
177
178       /* Grab the stats segment lock */
179       clib_spinlock_lock (sm->stat_segment_lockp);
180
181       /* see if we need to update cached pointers */
182       maybe_update_cached_pointers (sm, shared_header);
183
184       ASSERT (sm->vector_rate_ptr);
185       ASSERT (sm->intfc_rx_counters);
186       ASSERT (sm->intfc_tx_counters);
187
188       /* Read data from the segment */
189       vector_rate = *sm->vector_rate_ptr;
190       input_rate = *sm->input_rate_ptr;
191
192       len = vec_len (sm->intfc_rx_counters[0]);
193
194       ASSERT (len);
195
196       vec_validate (thread0_rx_counters, len - 1);
197       vec_validate (thread0_tx_counters, len - 1);
198
199       clib_memcpy (thread0_rx_counters, sm->intfc_rx_counters[0],
200                    len * sizeof (vlib_counter_t));
201       clib_memcpy (thread0_tx_counters, sm->intfc_tx_counters[0],
202                    len * sizeof (vlib_counter_t));
203
204       source_address_match_errors =
205         sm->thread_0_error_counts[sm->source_address_match_error_index];
206
207       /* Drop the lock */
208       clib_spinlock_unlock (sm->stat_segment_lockp);
209
210       /* And print results... */
211
212       fformat (stdout, "vector_rate %.2f input_rate %.2f\n",
213                vector_rate, input_rate);
214
215       for (i = 0; i < vec_len (thread0_rx_counters); i++)
216         {
217           fformat (stdout, "[%d]: %lld rx packets, %lld rx bytes\n",
218                    i, thread0_rx_counters[i].packets,
219                    thread0_rx_counters[i].bytes);
220           fformat (stdout, "[%d]: %lld tx packets, %lld tx bytes\n",
221                    i, thread0_tx_counters[i].packets,
222                    thread0_tx_counters[i].bytes);
223         }
224
225       fformat (stdout, "%lld source address match errors\n",
226                source_address_match_errors);
227
228       if (sm->serialized_nodes)
229         {
230           nodes_by_thread = vlib_node_unserialize (sm->serialized_nodes);
231
232           /* Across all threads... */
233           for (i = 0; i < vec_len (nodes_by_thread); i++)
234             {
235               u64 n_input, n_output, n_drop, n_punt;
236               u64 n_internal_vectors, n_internal_calls;
237               u64 n_clocks, l, v, c;
238               f64 dt;
239
240               nodes = nodes_by_thread[i];
241
242               fformat (stdout, "Thread %d -------------------------\n", i);
243
244               n_input = n_output = n_drop = n_punt = n_clocks = 0;
245               n_internal_vectors = n_internal_calls = 0;
246
247               /* Across all nodes */
248               for (j = 0; j < vec_len (nodes); j++)
249                 {
250                   n = nodes[j];
251
252                   /* Exactly stolen from node_cli.c... */
253                   l = n->stats_total.clocks - n->stats_last_clear.clocks;
254                   n_clocks += l;
255
256                   v = n->stats_total.vectors - n->stats_last_clear.vectors;
257                   c = n->stats_total.calls - n->stats_last_clear.calls;
258
259                   switch (n->type)
260                     {
261                     default:
262                       continue;
263
264                     case VLIB_NODE_TYPE_INTERNAL:
265                       n_output +=
266                         (n->flags & VLIB_NODE_FLAG_IS_OUTPUT) ? v : 0;
267                       n_drop += (n->flags & VLIB_NODE_FLAG_IS_DROP) ? v : 0;
268                       n_punt += (n->flags & VLIB_NODE_FLAG_IS_PUNT) ? v : 0;
269                       if (!(n->flags & VLIB_NODE_FLAG_IS_OUTPUT))
270                         {
271                           n_internal_vectors += v;
272                           n_internal_calls += c;
273                         }
274                       if (n->flags & VLIB_NODE_FLAG_IS_HANDOFF)
275                         n_input += v;
276                       break;
277
278                     case VLIB_NODE_TYPE_INPUT:
279                       n_input += v;
280                       break;
281                     }
282
283                   if (n->stats_total.calls)
284                     {
285                       fformat (stdout,
286                                "%s (%s): clocks %lld calls %lld vectors %lld ",
287                                n->name,
288                                n->state_string,
289                                n->stats_total.clocks,
290                                n->stats_total.calls, n->stats_total.vectors);
291                       if (n->stats_total.vectors)
292                         fformat (stdout, "clocks/pkt %.2f\n",
293                                  (f64) n->stats_total.clocks /
294                                  (f64) n->stats_total.vectors);
295                       else
296                         fformat (stdout, "\n");
297                     }
298                   vec_free (n->name);
299                   vec_free (n->next_nodes);
300                   vec_free (n);
301                 }
302
303               fformat (stdout, "average vectors/node %.2f\n",
304                        (n_internal_calls > 0
305                         ? (f64) n_internal_vectors / (f64) n_internal_calls
306                         : 0));
307
308
309               dt = *sm->last_runtime_ptr - *sm->last_runtime_stats_clear_ptr;
310
311               fformat (stdout,
312                        " vectors rates in %.4e, out %.4e, drop %.4e, "
313                        "punt %.4e\n",
314                        (f64) n_input / dt,
315                        (f64) n_output / dt, (f64) n_drop / dt,
316                        (f64) n_punt / dt);
317
318               vec_free (nodes);
319             }
320           vec_free (nodes_by_thread);
321         }
322       else
323         {
324           fformat (stdout, "serialized nodes NULL?\n");
325         }
326
327     }
328 }
329
330 int
331 main (int argc, char **argv)
332 {
333   unformat_input_t _argv, *a = &_argv;
334   stat_client_main_t *sm = &stat_client_main;
335   u8 *stat_segment_name;
336   int rv;
337
338   clib_mem_init (0, 128 << 20);
339
340   unformat_init_command_line (a, argv);
341
342   stat_segment_name = (u8 *) STAT_SEGMENT_SOCKET_FILE;
343
344   while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
345     {
346       if (unformat (a, "socket-name %s", &stat_segment_name))
347         ;
348       else
349         {
350           fformat (stderr, "%s: usage [socket-name <name>]\n", argv[0]);
351           exit (1);
352         }
353     }
354
355   sm->socket_name = stat_segment_name;
356
357   rv = stat_segment_connect (sm);
358   if (rv)
359     {
360       fformat (stderr, "Couldn't connect to vpp, does %s exist?\n",
361                stat_segment_name);
362       exit (1);
363     }
364
365   stat_poll_loop (sm);
366   exit (0);
367 }
368
369 /*
370  * fd.io coding-style-patch-verification: ON
371  *
372  * Local Variables:
373  * eval: (c-set-style "gnu")
374  * End:
375  */