2 *------------------------------------------------------------------
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:
10 * http://www.apache.org/licenses/LICENSE-2.0
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 *------------------------------------------------------------------
20 #include <vpp/app/stat_client.h>
22 #include <vpp/api/vpe_msg_enum.h>
24 #define vl_typedefs /* define message structures */
25 #include <vpp/api/vpe_all_api_h.h>
28 #define vl_endianfun /* define endian fcns */
29 #include <vpp/api/vpe_all_api_h.h>
32 /* instantiate all the print functions we know about */
33 #define vl_print(handle, ...) fformat (handle, __VA_ARGS__)
35 #include <vpp/api/vpe_all_api_h.h>
38 stat_client_main_t stat_client_main;
40 static void vl_api_map_stats_segment_reply_t_handler
41 (vl_api_map_stats_segment_reply_t * mp)
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);
53 fformat (stderr, "ERROR mapping stats segment: %d", rv);
58 * Check the socket for the magic fd
60 error = vl_sock_api_recv_fd_msg (scm->socket_fd, &my_fd, 5);
63 clib_error_report (error);
67 memset (ssvmp, 0, sizeof (*ssvmp));
70 /* Note: this closes memfd.fd */
71 retval = ssvm_slave_init_memfd (ssvmp);
74 clib_warning ("WARNING: segment map returned %d", retval);
78 fformat (stdout, "Stat segment mapped OK...\n");
80 ASSERT (ssvmp && ssvmp->sh);
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;
87 /* No need to keep the socket API connection open */
88 close (sm->socket_client_main->socket_fd);
91 #define foreach_api_reply_msg \
92 _(MAP_STATS_SEGMENT_REPLY, map_stats_segment_reply)
98 vl_msg_api_set_handlers(VL_API_##N, #n, \
99 vl_api_##n##_t_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;
109 connect_to_vpp (stat_client_main_t * sm)
112 vl_api_map_stats_segment_t *mp;
113 api_main_t *am = &api_main;
115 sm->socket_client_main = &socket_client_main;
117 rv = vl_socket_client_connect ((char *) sm->socket_name,
119 0 /* default socket rx, tx buffer */ );
122 fformat (stderr, "Error connecting to vpp...\n");
126 /* Hook up reply handler */
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;
135 /* Send the message */
136 vl_socket_client_write ();
138 /* Wait for a reply, process it.. */
139 vl_socket_client_read (5 /* timeout in seconds */ );
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)
162 stat_directory_type_t type;
166 cached_pointer_t cached_pointers[] = {
167 #define _(n,t,p) {#n, STAT_DIR_TYPE_##t, (void *)p},
168 foreach_cached_pointer
173 maybe_update_cached_pointers (stat_client_main_t * sm,
174 ssvm_shared_header_t * shared_header)
176 uword *p, *counter_vector_by_name;
178 stat_segment_directory_entry_t *ep;
179 cached_pointer_t *cp;
182 /* Cached pointers OK? */
183 if (sm->current_epoch ==
184 (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH])
187 fformat (stdout, "Updating cached pointers...\n");
189 /* Nope, fix them... */
190 counter_vector_by_name = (uword *)
191 shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR];
193 for (i = 0; i < ARRAY_LEN (cached_pointers); i++)
195 cp = &cached_pointers[i];
197 p = hash_get_mem (counter_vector_by_name, cp->name);
201 clib_warning ("WARN: %s not in directory!", cp->name);
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;
210 /* And remember that we did... */
211 sm->current_epoch = (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH];
215 stat_poll_loop (stat_client_main_t * sm)
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;
224 f64 vector_rate, input_rate;
227 u32 source_address_match_errors;
229 /* Wait until the stats segment is mapped */
230 while (!sm->segment_ready)
233 ts.tv_nsec = 100000000;
234 while (nanosleep (&ts, &tsrem) < 0)
238 shared_header = ssvmp->sh;
243 /* Scrape stats every 5 seconds */
246 while (nanosleep (&ts, &tsrem) < 0)
249 vec_reset_length (thread0_rx_counters);
250 vec_reset_length (thread0_tx_counters);
252 /* Grab the stats segment lock */
253 clib_spinlock_lock (sm->stat_segment_lockp);
255 /* see if we need to update cached pointers */
256 maybe_update_cached_pointers (sm, shared_header);
258 ASSERT (sm->vector_rate_ptr);
259 ASSERT (sm->intfc_rx_counters);
260 ASSERT (sm->intfc_tx_counters);
262 /* Read data from the segment */
263 vector_rate = *sm->vector_rate_ptr;
264 input_rate = *sm->input_rate_ptr;
266 len = vec_len (sm->intfc_rx_counters[0]);
270 vec_validate (thread0_rx_counters, len - 1);
271 vec_validate (thread0_tx_counters, len - 1);
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));
278 source_address_match_errors =
279 sm->thread_0_error_counts[sm->source_address_match_error_index];
282 clib_spinlock_unlock (sm->stat_segment_lockp);
284 /* And print results... */
286 fformat (stdout, "vector_rate %.2f input_rate %.2f\n",
287 vector_rate, input_rate);
289 for (i = 0; i < vec_len (thread0_rx_counters); i++)
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);
299 fformat (stdout, "%lld source address match errors\n",
300 source_address_match_errors);
302 if (sm->serialized_nodes)
304 nodes_by_thread = vlib_node_unserialize (sm->serialized_nodes);
306 /* Across all threads... */
307 for (i = 0; i < vec_len (nodes_by_thread); i++)
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;
314 nodes = nodes_by_thread[i];
316 fformat (stdout, "Thread %d -------------------------\n", i);
318 n_input = n_output = n_drop = n_punt = n_clocks = 0;
319 n_internal_vectors = n_internal_calls = 0;
321 /* Across all nodes */
322 for (j = 0; j < vec_len (nodes); j++)
326 /* Exactly stolen from node_cli.c... */
327 l = n->stats_total.clocks - n->stats_last_clear.clocks;
330 v = n->stats_total.vectors - n->stats_last_clear.vectors;
331 c = n->stats_total.calls - n->stats_last_clear.calls;
338 case VLIB_NODE_TYPE_INTERNAL:
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))
345 n_internal_vectors += v;
346 n_internal_calls += c;
348 if (n->flags & VLIB_NODE_FLAG_IS_HANDOFF)
352 case VLIB_NODE_TYPE_INPUT:
357 if (n->stats_total.calls)
360 "%s (%s): clocks %lld calls %lld vectors %lld ",
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);
370 fformat (stdout, "\n");
373 vec_free (n->next_nodes);
377 fformat (stdout, "average vectors/node %.2f\n",
378 (n_internal_calls > 0
379 ? (f64) n_internal_vectors / (f64) n_internal_calls
383 dt = *sm->last_runtime_ptr - *sm->last_runtime_stats_clear_ptr;
386 " vectors rates in %.4e, out %.4e, drop %.4e, "
389 (f64) n_output / dt, (f64) n_drop / dt,
394 vec_free (nodes_by_thread);
398 fformat (stdout, "serialized nodes NULL?\n");
406 main (int argc, char **argv)
408 unformat_input_t _argv, *a = &_argv;
409 stat_client_main_t *sm = &stat_client_main;
413 clib_mem_init (0, 128 << 20);
415 unformat_init_command_line (a, argv);
417 socket_name = (u8 *) API_SOCKET_FILE;
419 while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
421 if (unformat (a, "socket-name %s", &socket_name))
425 fformat (stderr, "%s: usage [socket-name <name>]\n", argv[0]);
430 sm->socket_name = socket_name;
432 rv = connect_to_vpp (sm);
436 fformat (stderr, "Couldn't connect to vpp, does %s exist?\n",
446 * fd.io coding-style-patch-verification: ON
449 * eval: (c-set-style "gnu")