2 * tracedump.c - skeleton vpp engine plug-in
4 * Copyright (c) <current-year> <your-organization>
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 #include <vnet/vnet.h>
19 #include <vnet/plugin/plugin.h>
20 #include <tracedump/tracedump.h>
21 #include <vlib/trace.h>
23 #include <vlibapi/api.h>
24 #include <vlibmemory/api.h>
25 #include <vpp/app/version.h>
28 #include <tracedump/tracedump.api_enum.h>
29 #include <tracedump/tracedump.api_types.h>
31 #define REPLY_MSG_ID_BASE tdmp->msg_id_base
32 #include <vlibapi/api_helper_macros.h>
34 tracedump_main_t tracedump_main;
38 vl_api_trace_set_filters_t_handler (vl_api_trace_set_filters_t * mp)
40 vlib_main_t *vm = vlib_get_main ();
41 tracedump_main_t *tdmp = &tracedump_main;
42 u32 node_index = clib_net_to_host_u32 (mp->node_index);
43 u32 flag = clib_net_to_host_u32 (mp->flag);
44 u32 count = clib_net_to_host_u32 (mp->count);
45 vl_api_trace_set_filters_reply_t *rmp;
48 if (flag == TRACE_FF_NONE)
50 count = node_index = 0;
52 else if (flag != TRACE_FF_INCLUDE_NODE && flag != TRACE_FF_EXCLUDE_NODE)
54 rv = VNET_API_ERROR_INVALID_VALUE;
59 node = vlib_get_node (vm, node_index);
62 rv = VNET_API_ERROR_NO_SUCH_NODE;
66 trace_filter_set (node_index, flag, count);
69 REPLY_MACRO (VL_API_TRACE_SET_FILTERS_REPLY);
74 vl_api_trace_capture_packets_t_handler (vl_api_trace_capture_packets_t * mp)
76 vlib_main_t *vm = vlib_get_main ();
77 tracedump_main_t *tdmp = &tracedump_main;
78 u32 add = clib_net_to_host_u32 (mp->max_packets);
79 u32 node_index = clib_net_to_host_u32 (mp->node_index);
80 u8 filter = mp->use_filter;
81 u8 verbose = mp->verbose;
82 u8 pre_clear = mp->pre_capture_clear;
83 vl_api_trace_capture_packets_reply_t *rmp;
86 if (!vnet_trace_placeholder)
87 vec_validate_aligned (vnet_trace_placeholder, 2048,
88 CLIB_CACHE_LINE_BYTES);
91 node = vlib_get_node (vm, node_index);
94 rv = VNET_API_ERROR_NO_SUCH_NODE;
98 if ((node->flags & VLIB_NODE_FLAG_TRACE_SUPPORTED) == 0)
100 /* FIXME: Make a new, better error like "UNSUPPORTED_NODE_OPERATION"? */
101 rv = VNET_API_ERROR_NO_SUCH_NODE;
106 vlib_trace_stop_and_clear ();
108 trace_update_capture_options (add, node_index, filter, verbose);
111 REPLY_MACRO (VL_API_TRACE_CAPTURE_PACKETS_REPLY);
116 vl_api_trace_clear_capture_t_handler (vl_api_trace_clear_capture_t * mp)
118 vl_api_trace_clear_capture_reply_t *rmp;
119 tracedump_main_t *tdmp = &tracedump_main;
121 vlib_trace_stop_and_clear ();
124 REPLY_MACRO (VL_API_TRACE_CLEAR_CAPTURE_REPLY);
130 trace_cmp (void *a1, void *a2)
132 vlib_trace_header_t **t1 = a1;
133 vlib_trace_header_t **t2 = a2;
134 i64 dt = t1[0]->time - t2[0]->time;
135 return dt < 0 ? -1 : (dt > 0 ? +1 : 0);
139 toss_client_cache (tracedump_main_t * tdmp, u32 client_index,
140 vlib_trace_header_t *** client_trace_cache)
142 vlib_trace_header_t **th;
145 /* Across each vlib main... */
146 for (i = 0; i < vec_len (client_trace_cache); i++)
148 th = client_trace_cache[i];
149 /* Toss the thread's cached data */
152 /* And toss the vector of threads */
153 vec_free (client_trace_cache);
154 tdmp->traces[client_index] = client_trace_cache;
157 static clib_error_t *
158 tracedump_cache_reaper (u32 client_index)
160 tracedump_main_t *tdmp = &tracedump_main;
161 vlib_trace_header_t ***client_trace_cache;
163 /* Its likely that we won't have a cache entry */
164 if (client_index >= vec_len (tdmp->traces))
167 client_trace_cache = tdmp->traces[client_index];
168 toss_client_cache (tdmp, client_index, client_trace_cache);
172 VL_MSG_API_REAPER_FUNCTION (tracedump_cache_reaper);
174 /* API message handler */
176 vl_api_trace_dump_t_handler (vl_api_trace_dump_t * mp)
178 vl_api_registration_t *rp;
179 vl_api_trace_dump_reply_t *rmp;
180 vl_api_trace_details_t *dmp;
181 tracedump_main_t *tdmp = &tracedump_main;
182 vlib_trace_header_t ***client_trace_cache, **th;
185 u32 iterator_thread_id, iterator_position, max_records;
186 i32 retval = VNET_API_ERROR_NO_SUCH_ENTRY;
187 u32 last_thread_id = ~0, last_position = ~0;
189 u8 last_more_this_thread = 0;
190 u8 last_more_threads = 0;
193 rp = vl_api_client_index_to_registration (mp->client_index);
197 /* Use the registration pool index... */
198 client_index = rp->vl_api_registration_pool_index;
200 vec_validate_init_empty (tdmp->traces, client_index, 0);
202 client_trace_cache = tdmp->traces[client_index];
204 /* Clear the per-client cache if requested */
207 toss_client_cache (tdmp, client_index, client_trace_cache);
208 client_trace_cache = 0;
211 /* Now, where were we? */
212 iterator_thread_id = clib_net_to_host_u32 (mp->thread_id);
213 iterator_position = clib_net_to_host_u32 (mp->position);
214 max_records = clib_net_to_host_u32 (mp->max_records);
216 /* Don't overflow the existing queue space for shared memory API clients. */
217 if (rp->vl_input_queue)
219 svm_queue_t *q = rp->vl_input_queue;
220 u32 queue_slots_available = q->maxsize - q->cursize;
221 int chunk = (queue_slots_available > 0) ? queue_slots_available - 1 : 0;
222 if (chunk < max_records)
226 /* Need a fresh cache for this client? */
227 if (vec_len (client_trace_cache) == 0
228 && (iterator_thread_id != ~0 || iterator_position != ~0))
230 vlib_worker_thread_barrier_sync (vlib_get_first_main ());
232 /* Make a slot for each worker thread */
233 vec_validate (client_trace_cache, vlib_get_n_threads () - 1);
238 vlib_trace_main_t *tm = &this_vlib_main->trace_main;
240 /* Filter as directed */
241 trace_apply_filter (this_vlib_main);
243 pool_foreach (th, tm->trace_buffer_pool)
245 vec_add1 (client_trace_cache[i], th[0]);
248 /* Sort them by increasing time. */
249 if (vec_len (client_trace_cache[i]))
250 vec_sort_with_function (client_trace_cache[i], trace_cmp);
254 vlib_worker_thread_barrier_release (vlib_get_first_main ());
257 /* Save the cache, one way or the other */
258 tdmp->traces[client_index] = client_trace_cache;
260 for (i = iterator_thread_id; i < vec_len (client_trace_cache); i++)
262 for (j = iterator_position; j < vec_len (client_trace_cache[i]); j++)
264 if (max_records == 0)
268 th = &client_trace_cache[i][j];
270 vec_reset_length (s);
273 format (s, "%U", format_vlib_trace, vlib_get_first_main (), th[0]);
275 dmp = vl_msg_api_alloc (sizeof (*dmp) + vec_len (s));
277 htons (VL_API_TRACE_DETAILS + (tdmp->msg_id_base));
278 dmp->context = mp->context;
279 last_thread_id = dmp->thread_id = ntohl (i);
280 last_position = dmp->position = ntohl (j);
281 vl_api_vec_to_api_string (s, &dmp->trace_data);
282 dmp->packet_number = htonl (j);
283 dmp->more_threads = 0;
284 dmp->more_this_thread = 0;
286 /* Last record in the batch? */
287 if (max_records == 1)
289 /* More threads, but not more in this thread? */
290 if (j == (vec_len (client_trace_cache[i]) - 1))
291 dmp->more_threads = 1;
293 dmp->more_this_thread = 1;
295 /* Done, may or may not be at the end of a batch. */
297 if (i == (vec_len (client_trace_cache) - 1) &&
298 j == (vec_len (client_trace_cache[i]) - 1))
300 last_done = dmp->done = 1;
301 last_more_threads = dmp->more_threads = 0;
302 last_more_this_thread = dmp->more_this_thread = 0;
303 vl_api_send_msg (rp, (u8 *) dmp);
306 last_done = dmp->done;
307 vl_api_send_msg (rp, (u8 *) dmp);
311 iterator_position = 0;
316 rmp = vl_msg_api_alloc (sizeof (*rmp));
317 rmp->_vl_msg_id = htons (VL_API_TRACE_DUMP_REPLY + (tdmp->msg_id_base));
318 rmp->context = mp->context;
319 rmp->retval = clib_host_to_net_u32 (retval);
320 rmp->last_thread_id = last_thread_id;
321 rmp->last_position = last_position;
322 rmp->done = last_done;
323 rmp->more_this_thread = last_more_this_thread;
324 rmp->more_threads = last_more_threads;
326 /* Tag cleanup flushes to make life easy for the client */
327 if (iterator_thread_id == ~0 && iterator_position == ~0)
333 vl_api_send_msg (rp, (u8 *) rmp);
338 /* API message handler */
340 vl_api_trace_v2_dump_t_handler (vl_api_trace_v2_dump_t *mp)
342 vl_api_registration_t *rp;
343 vl_api_trace_v2_details_t *dmp;
344 tracedump_main_t *tdmp = &tracedump_main;
345 vlib_trace_header_t ***client_trace_cache, **th;
348 u32 first_position, max, first_thread_id, last_thread_id;
349 u32 n_threads = vlib_get_n_threads ();
352 rp = vl_api_client_index_to_registration (mp->client_index);
356 client_index = rp->vl_api_registration_pool_index;
358 vec_validate_init_empty (tdmp->traces, client_index, 0);
360 client_trace_cache = tdmp->traces[client_index];
364 toss_client_cache (tdmp, client_index, client_trace_cache);
365 client_trace_cache = 0;
368 /* Now, where were we? */
369 first_thread_id = last_thread_id = clib_net_to_host_u32 (mp->thread_id);
370 first_position = clib_net_to_host_u32 (mp->position);
371 max = clib_net_to_host_u32 (mp->max);
373 if (first_thread_id == ~0)
376 last_thread_id = n_threads - 1;
379 /* Don't overflow the existing queue space for shared memory API clients. */
380 if (rp->vl_input_queue)
382 svm_queue_t *q = rp->vl_input_queue;
383 u32 queue_slots_available = q->maxsize - q->cursize;
384 int chunk = (queue_slots_available > 0) ? queue_slots_available - 1 : 0;
385 /* split available slots among requested threads */
386 if (chunk < max * (last_thread_id - first_thread_id + 1))
387 max = chunk / (last_thread_id - first_thread_id + 1);
390 /* Need a fresh cache for this client? */
391 if (vec_len (client_trace_cache) == 0 && first_position != ~0)
393 vlib_worker_thread_barrier_sync (vlib_get_first_main ());
395 /* Make a slot for each worker thread */
396 vec_validate (client_trace_cache, n_threads - 1);
401 vlib_trace_main_t *tm = &this_vlib_main->trace_main;
403 /* Filter as directed */
404 trace_apply_filter (this_vlib_main);
406 pool_foreach (th, tm->trace_buffer_pool)
408 vec_add1 (client_trace_cache[i], th[0]);
411 /* Sort them by increasing time. */
412 if (vec_len (client_trace_cache[i]))
413 vec_sort_with_function (client_trace_cache[i], trace_cmp);
417 vlib_worker_thread_barrier_release (vlib_get_first_main ());
420 /* Save the cache, one way or the other */
421 tdmp->traces[client_index] = client_trace_cache;
423 for (i = first_thread_id;
424 i <= last_thread_id && i < vec_len (client_trace_cache); i++)
426 // dump a number of 'max' packets per thead
427 for (j = first_position;
428 j < vec_len (client_trace_cache[i]) && j < first_position + max;
431 th = &client_trace_cache[i][j];
433 vec_reset_length (s);
436 format (s, "%U", format_vlib_trace, vlib_get_first_main (), th[0]);
438 dmp = vl_msg_api_alloc (sizeof (*dmp) + vec_len (s));
440 htons (VL_API_TRACE_V2_DETAILS + (tdmp->msg_id_base));
441 dmp->context = mp->context;
442 dmp->thread_id = ntohl (i);
443 dmp->position = ntohl (j);
444 dmp->more = j < vec_len (client_trace_cache[i]) - 1;
445 vl_api_vec_to_api_string (s, &dmp->trace_data);
447 vl_api_send_msg (rp, (u8 *) dmp);
455 vl_api_trace_clear_cache_t_handler (vl_api_trace_clear_cache_t *mp)
457 vl_api_registration_t *rp;
458 tracedump_main_t *tdmp = &tracedump_main;
459 vlib_trace_header_t ***client_trace_cache;
460 vl_api_trace_clear_cache_reply_t *rmp;
463 rp = vl_api_client_index_to_registration (mp->client_index);
467 client_index = rp->vl_api_registration_pool_index;
468 vec_validate_init_empty (tdmp->traces, client_index, 0);
469 client_trace_cache = tdmp->traces[client_index];
470 toss_client_cache (tdmp, client_index, client_trace_cache);
473 REPLY_MACRO (VL_API_TRACE_CLEAR_CACHE_REPLY);
476 /* API definitions */
477 #include <tracedump/tracedump.api.c>
479 static clib_error_t *
480 tracedump_init (vlib_main_t * vm)
482 tracedump_main_t *tdmp = &tracedump_main;
483 api_main_t *am = vlibapi_get_main ();
485 clib_error_t *error = 0;
487 tdmp->vlib_main = vm;
488 tdmp->vnet_main = vnet_get_main ();
490 /* Add our API messages to the global name_crc hash table */
491 tdmp->msg_id_base = setup_message_id_table ();
493 vl_api_set_msg_thread_safe (am, tdmp->msg_id_base + VL_API_TRACE_DUMP, 1);
494 vl_api_set_msg_thread_safe (am, tdmp->msg_id_base + VL_API_TRACE_V2_DUMP, 1);
499 VLIB_INIT_FUNCTION (tracedump_init);
501 VLIB_PLUGIN_REGISTER () =
503 .version = VPP_BUILD_VER,
504 .description = "Streaming packet trace dump plugin",
509 * fd.io coding-style-patch-verification: ON
512 * eval: (c-set-style "gnu")