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 _(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)
157 stat_directory_type_t type;
161 cached_pointer_t cached_pointers[] = {
162 #define _(n,t,p) {#n, STAT_DIR_TYPE_##t, (void *)p},
163 foreach_cached_pointer
168 maybe_update_cached_pointers (stat_client_main_t * sm,
169 ssvm_shared_header_t * shared_header)
171 uword *p, *counter_vector_by_name;
173 stat_segment_directory_entry_t *ep;
174 cached_pointer_t *cp;
177 /* Cached pointers OK? */
178 if (sm->current_epoch ==
179 (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH])
182 fformat (stdout, "Updating cached pointers...\n");
184 /* Nope, fix them... */
185 counter_vector_by_name = (uword *)
186 shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR];
188 for (i = 0; i < ARRAY_LEN (cached_pointers); i++)
190 cp = &cached_pointers[i];
192 p = hash_get_mem (counter_vector_by_name, cp->name);
196 clib_warning ("WARN: %s not in directory!", cp->name);
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;
205 /* And remember that we did... */
206 sm->current_epoch = (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH];
210 stat_poll_loop (stat_client_main_t * sm)
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;
219 u32 source_address_match_errors;
221 /* Wait until the stats segment is mapped */
222 while (!sm->segment_ready)
225 ts.tv_nsec = 100000000;
226 while (nanosleep (&ts, &tsrem) < 0)
230 shared_header = ssvmp->sh;
235 /* Scrape stats every 5 seconds */
238 while (nanosleep (&ts, &tsrem) < 0)
241 vec_reset_length (thread0_rx_counters);
242 vec_reset_length (thread0_tx_counters);
244 /* Grab the stats segment lock */
245 clib_spinlock_lock (sm->stat_segment_lockp);
247 /* see if we need to update cached pointers */
248 maybe_update_cached_pointers (sm, shared_header);
250 ASSERT (sm->vector_rate_ptr);
251 ASSERT (sm->intfc_rx_counters);
252 ASSERT (sm->intfc_tx_counters);
254 /* Read data from the segment */
255 vector_rate = *sm->vector_rate_ptr;
256 input_rate = *sm->input_rate_ptr;
258 len = vec_len (sm->intfc_rx_counters[0]);
262 vec_validate (thread0_rx_counters, len - 1);
263 vec_validate (thread0_tx_counters, len - 1);
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));
270 source_address_match_errors =
271 sm->thread_0_error_counts[sm->source_address_match_error_index];
274 clib_spinlock_unlock (sm->stat_segment_lockp);
276 /* And print results... */
278 fformat (stdout, "vector_rate %.2f input_rate %.2f\n",
279 vector_rate, input_rate);
281 for (i = 0; i < vec_len (thread0_rx_counters); i++)
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);
291 fformat (stdout, "%lld source address match errors\n",
292 source_address_match_errors);
298 main (int argc, char **argv)
300 unformat_input_t _argv, *a = &_argv;
301 stat_client_main_t *sm = &stat_client_main;
305 clib_mem_init (0, 128 << 20);
307 unformat_init_command_line (a, argv);
309 socket_name = (u8 *) API_SOCKET_FILE;
311 while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
313 if (unformat (a, "socket-name %s", &socket_name))
317 fformat (stderr, "%s: usage [socket-name <name>]\n", argv[0]);
322 sm->socket_name = socket_name;
324 rv = connect_to_vpp (sm);
328 fformat (stderr, "Couldn't connect to vpp, does %s exist?\n",
338 * fd.io coding-style-patch-verification: ON
341 * eval: (c-set-style "gnu")