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 _(/sys/vector_rate, SCALAR_POINTER, &stat_client_main.vector_rate_ptr) \
146 _(/sys/input_rate, SCALAR_POINTER, &stat_client_main.input_rate_ptr) \
147 _(/sys/last_update, SCALAR_POINTER, &stat_client_main.last_runtime_ptr) \
148 _(/sys/last_stats_clear, SCALAR_POINTER, \
149 &stat_client_main.last_runtime_stats_clear_ptr) \
150 _(/if/rx, COUNTER_VECTOR, &stat_client_main.intfc_rx_counters) \
151 _(/if/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 _(serialized_nodes, SERIALIZED_NODES, \
155 &stat_client_main.serialized_nodes)
160 stat_directory_type_t type;
164 cached_pointer_t cached_pointers[] = {
165 #define _(n,t,p) {#n, STAT_DIR_TYPE_##t, (void *)p},
166 foreach_cached_pointer
171 maybe_update_cached_pointers (stat_client_main_t * sm,
172 ssvm_shared_header_t * shared_header)
174 uword *p, *counter_vector_by_name;
176 stat_segment_directory_entry_t *ep;
177 cached_pointer_t *cp;
180 /* Cached pointers OK? */
181 if (sm->current_epoch ==
182 (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH])
185 fformat (stdout, "Updating cached pointers...\n");
187 /* Nope, fix them... */
188 counter_vector_by_name = (uword *)
189 shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR];
191 for (i = 0; i < ARRAY_LEN (cached_pointers); i++)
193 cp = &cached_pointers[i];
195 p = hash_get_mem (counter_vector_by_name, cp->name);
199 clib_warning ("WARN: %s not in directory!", cp->name);
202 ep = (stat_segment_directory_entry_t *) (p[0]);
203 ASSERT (ep->type == cp->type);
204 valuep = (u64 *) cp->valuep;
205 *valuep = (u64) ep->value;
208 /* And remember that we did... */
209 sm->current_epoch = (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH];
213 stat_poll_loop (stat_client_main_t * sm)
215 struct timespec ts, tsrem;
216 ssvm_private_t *ssvmp = &sm->stat_segment;
217 ssvm_shared_header_t *shared_header;
218 vlib_counter_t *thread0_rx_counters = 0, *thread0_tx_counters = 0;
219 vlib_node_t ***nodes_by_thread;
222 f64 vector_rate, input_rate;
225 u32 source_address_match_errors;
227 /* Wait until the stats segment is mapped */
228 while (!sm->segment_ready)
231 ts.tv_nsec = 100000000;
232 while (nanosleep (&ts, &tsrem) < 0)
236 shared_header = ssvmp->sh;
241 /* Scrape stats every 5 seconds */
244 while (nanosleep (&ts, &tsrem) < 0)
247 vec_reset_length (thread0_rx_counters);
248 vec_reset_length (thread0_tx_counters);
250 /* Grab the stats segment lock */
251 clib_spinlock_lock (sm->stat_segment_lockp);
253 /* see if we need to update cached pointers */
254 maybe_update_cached_pointers (sm, shared_header);
256 ASSERT (sm->vector_rate_ptr);
257 ASSERT (sm->intfc_rx_counters);
258 ASSERT (sm->intfc_tx_counters);
260 /* Read data from the segment */
261 vector_rate = *sm->vector_rate_ptr;
262 input_rate = *sm->input_rate_ptr;
264 len = vec_len (sm->intfc_rx_counters[0]);
268 vec_validate (thread0_rx_counters, len - 1);
269 vec_validate (thread0_tx_counters, len - 1);
271 clib_memcpy (thread0_rx_counters, sm->intfc_rx_counters[0],
272 len * sizeof (vlib_counter_t));
273 clib_memcpy (thread0_tx_counters, sm->intfc_tx_counters[0],
274 len * sizeof (vlib_counter_t));
276 source_address_match_errors =
277 sm->thread_0_error_counts[sm->source_address_match_error_index];
280 clib_spinlock_unlock (sm->stat_segment_lockp);
282 /* And print results... */
284 fformat (stdout, "vector_rate %.2f input_rate %.2f\n",
285 vector_rate, input_rate);
287 for (i = 0; i < vec_len (thread0_rx_counters); i++)
289 fformat (stdout, "[%d]: %lld rx packets, %lld rx bytes\n",
290 i, thread0_rx_counters[i].packets,
291 thread0_rx_counters[i].bytes);
292 fformat (stdout, "[%d]: %lld tx packets, %lld tx bytes\n",
293 i, thread0_tx_counters[i].packets,
294 thread0_tx_counters[i].bytes);
297 fformat (stdout, "%lld source address match errors\n",
298 source_address_match_errors);
300 if (sm->serialized_nodes)
302 nodes_by_thread = vlib_node_unserialize (sm->serialized_nodes);
304 /* Across all threads... */
305 for (i = 0; i < vec_len (nodes_by_thread); i++)
307 u64 n_input, n_output, n_drop, n_punt;
308 u64 n_internal_vectors, n_internal_calls;
309 u64 n_clocks, l, v, c;
312 nodes = nodes_by_thread[i];
314 fformat (stdout, "Thread %d -------------------------\n", i);
316 n_input = n_output = n_drop = n_punt = n_clocks = 0;
317 n_internal_vectors = n_internal_calls = 0;
319 /* Across all nodes */
320 for (j = 0; j < vec_len (nodes); j++)
324 /* Exactly stolen from node_cli.c... */
325 l = n->stats_total.clocks - n->stats_last_clear.clocks;
328 v = n->stats_total.vectors - n->stats_last_clear.vectors;
329 c = n->stats_total.calls - n->stats_last_clear.calls;
336 case VLIB_NODE_TYPE_INTERNAL:
338 (n->flags & VLIB_NODE_FLAG_IS_OUTPUT) ? v : 0;
339 n_drop += (n->flags & VLIB_NODE_FLAG_IS_DROP) ? v : 0;
340 n_punt += (n->flags & VLIB_NODE_FLAG_IS_PUNT) ? v : 0;
341 if (!(n->flags & VLIB_NODE_FLAG_IS_OUTPUT))
343 n_internal_vectors += v;
344 n_internal_calls += c;
346 if (n->flags & VLIB_NODE_FLAG_IS_HANDOFF)
350 case VLIB_NODE_TYPE_INPUT:
355 if (n->stats_total.calls)
358 "%s (%s): clocks %lld calls %lld vectors %lld ",
361 n->stats_total.clocks,
362 n->stats_total.calls, n->stats_total.vectors);
363 if (n->stats_total.vectors)
364 fformat (stdout, "clocks/pkt %.2f\n",
365 (f64) n->stats_total.clocks /
366 (f64) n->stats_total.vectors);
368 fformat (stdout, "\n");
371 vec_free (n->next_nodes);
375 fformat (stdout, "average vectors/node %.2f\n",
376 (n_internal_calls > 0
377 ? (f64) n_internal_vectors / (f64) n_internal_calls
381 dt = *sm->last_runtime_ptr - *sm->last_runtime_stats_clear_ptr;
384 " vectors rates in %.4e, out %.4e, drop %.4e, "
387 (f64) n_output / dt, (f64) n_drop / dt,
392 vec_free (nodes_by_thread);
396 fformat (stdout, "serialized nodes NULL?\n");
404 main (int argc, char **argv)
406 unformat_input_t _argv, *a = &_argv;
407 stat_client_main_t *sm = &stat_client_main;
411 clib_mem_init (0, 128 << 20);
413 unformat_init_command_line (a, argv);
415 socket_name = (u8 *) API_SOCKET_FILE;
417 while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
419 if (unformat (a, "socket-name %s", &socket_name))
423 fformat (stderr, "%s: usage [socket-name <name>]\n", argv[0]);
428 sm->socket_name = socket_name;
430 rv = connect_to_vpp (sm);
434 fformat (stderr, "Couldn't connect to vpp, does %s exist?\n",
444 * fd.io coding-style-patch-verification: ON
447 * eval: (c-set-style "gnu")