2bedf6f2d7da1838a53b1156c00d5d6d64ca3042
[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                       vec_free (n->name);
263                       vec_free (n->next_nodes);
264                       vec_free (n);
265                       continue;
266
267                     case VLIB_NODE_TYPE_INTERNAL:
268                       n_output +=
269                         (n->flags & VLIB_NODE_FLAG_IS_OUTPUT) ? v : 0;
270                       n_drop += (n->flags & VLIB_NODE_FLAG_IS_DROP) ? v : 0;
271                       n_punt += (n->flags & VLIB_NODE_FLAG_IS_PUNT) ? v : 0;
272                       if (!(n->flags & VLIB_NODE_FLAG_IS_OUTPUT))
273                         {
274                           n_internal_vectors += v;
275                           n_internal_calls += c;
276                         }
277                       if (n->flags & VLIB_NODE_FLAG_IS_HANDOFF)
278                         n_input += v;
279                       break;
280
281                     case VLIB_NODE_TYPE_INPUT:
282                       n_input += v;
283                       break;
284                     }
285
286                   if (n->stats_total.calls)
287                     {
288                       fformat (stdout,
289                                "%s (%s): clocks %lld calls %lld vectors %lld ",
290                                n->name,
291                                n->state_string,
292                                n->stats_total.clocks,
293                                n->stats_total.calls, n->stats_total.vectors);
294                       if (n->stats_total.vectors)
295                         fformat (stdout, "clocks/pkt %.2f\n",
296                                  (f64) n->stats_total.clocks /
297                                  (f64) n->stats_total.vectors);
298                       else
299                         fformat (stdout, "\n");
300                     }
301                   vec_free (n->name);
302                   vec_free (n->next_nodes);
303                   vec_free (n);
304                 }
305
306               fformat (stdout, "average vectors/node %.2f\n",
307                        (n_internal_calls > 0
308                         ? (f64) n_internal_vectors / (f64) n_internal_calls
309                         : 0));
310
311
312               dt = *sm->last_runtime_ptr - *sm->last_runtime_stats_clear_ptr;
313
314               fformat (stdout,
315                        " vectors rates in %.4e, out %.4e, drop %.4e, "
316                        "punt %.4e\n",
317                        (f64) n_input / dt,
318                        (f64) n_output / dt, (f64) n_drop / dt,
319                        (f64) n_punt / dt);
320
321               vec_free (nodes);
322             }
323           vec_free (nodes_by_thread);
324         }
325       else
326         {
327           fformat (stdout, "serialized nodes NULL?\n");
328         }
329
330     }
331 }
332
333 int
334 main (int argc, char **argv)
335 {
336   unformat_input_t _argv, *a = &_argv;
337   stat_client_main_t *sm = &stat_client_main;
338   u8 *stat_segment_name;
339   int rv;
340
341   clib_mem_init (0, 128 << 20);
342
343   unformat_init_command_line (a, argv);
344
345   stat_segment_name = (u8 *) STAT_SEGMENT_SOCKET_FILE;
346
347   while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
348     {
349       if (unformat (a, "socket-name %s", &stat_segment_name))
350         ;
351       else
352         {
353           fformat (stderr, "%s: usage [socket-name <name>]\n", argv[0]);
354           exit (1);
355         }
356     }
357
358   sm->socket_name = stat_segment_name;
359
360   rv = stat_segment_connect (sm);
361   if (rv)
362     {
363       fformat (stderr, "Couldn't connect to vpp, does %s exist?\n",
364                stat_segment_name);
365       exit (1);
366     }
367
368   stat_poll_loop (sm);
369   exit (0);
370 }
371
372 /*
373  * fd.io coding-style-patch-verification: ON
374  *
375  * Local Variables:
376  * eval: (c-set-style "gnu")
377  * End:
378  */