1 /* SPDX-License-Identifier: Apache-2.0
2 * Copyright(c) 2021 Cisco Systems, Inc.
5 #include <vnet/plugin/plugin.h>
6 #include <vpp/app/version.h>
11 } dispatch_trace_thread_t;
16 pcap_main_t dispatch_pcap_main;
17 u32 *dispatch_buffer_trace_nodes;
18 dispatch_trace_thread_t *threads;
19 u32 epoll_input_node_index;
20 } dispatch_trace_main_t;
22 dispatch_trace_main_t dispatch_trace_main;
24 #define VLIB_PCAP_MAJOR_VERSION 1
25 #define VLIB_PCAP_MINOR_VERSION 0
33 u32 packets_to_capture;
34 u32 buffer_trace_node_index;
35 u32 buffer_traces_to_capture;
36 } vlib_pcap_dispatch_trace_args_t;
39 format_buffer_metadata (u8 *s, va_list *args)
41 vlib_buffer_t *b = va_arg (*args, vlib_buffer_t *);
43 s = format (s, "flags: %U\n", format_vnet_buffer_flags, b);
44 s = format (s, "current_data: %d, current_length: %d\n",
45 (i32) (b->current_data), (i32) (b->current_length));
47 s, "current_config_index/punt_reason: %d, flow_id: %x, next_buffer: %x\n",
48 b->current_config_index, b->flow_id, b->next_buffer);
49 s = format (s, "error: %d, ref_count: %d, buffer_pool_index: %d\n",
50 (u32) (b->error), (u32) (b->ref_count),
51 (u32) (b->buffer_pool_index));
52 s = format (s, "trace_handle: 0x%x, len_not_first_buf: %d\n",
53 b->trace_handle, b->total_length_not_including_first_buffer);
57 #define A(x) vec_add1 (dtt->pcap_buffer, (x))
60 dispatch_pcap_trace (vlib_main_t *vm, vlib_node_runtime_t *node,
64 vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **bufp, *b;
65 dispatch_trace_main_t *dtm = &dispatch_trace_main;
66 pcap_main_t *pm = &dtm->dispatch_pcap_main;
67 dispatch_trace_thread_t *dtt =
68 vec_elt_at_index (dtm->threads, vm->thread_index);
69 vlib_trace_main_t *tm = &vm->trace_main;
73 f64 time_now = vlib_time_now (vm);
78 /* Input nodes don't have frames yet */
79 if (frame == 0 || frame->n_vectors == 0)
82 from = vlib_frame_vector_args (frame);
83 vlib_get_buffers (vm, from, bufs, frame->n_vectors);
86 n = vlib_get_node (vm, node->node_index);
88 for (i = 0; i < frame->n_vectors; i++)
90 if (PREDICT_TRUE (pm->n_packets_captured < pm->n_packets_to_capture))
94 vec_reset_length (dtt->pcap_buffer);
98 A ((u8) VLIB_PCAP_MAJOR_VERSION);
99 A ((u8) VLIB_PCAP_MINOR_VERSION);
100 A (0 /* string_count */);
101 A (n->protocol_hint);
103 /* Buffer index (big endian) */
104 A ((from[i] >> 24) & 0xff);
105 A ((from[i] >> 16) & 0xff);
106 A ((from[i] >> 8) & 0xff);
107 A ((from[i] >> 0) & 0xff);
109 /* Node name, NULL-terminated ASCII */
110 dtt->pcap_buffer = format (dtt->pcap_buffer, "%v%c", n->name, 0);
114 format (dtt->pcap_buffer, "%U%c", format_buffer_metadata, b, 0);
117 format (dtt->pcap_buffer, "%U%c", format_vnet_buffer_opaque, b, 0);
119 dtt->pcap_buffer = format (dtt->pcap_buffer, "%U%c",
120 format_vnet_buffer_opaque2, b, 0);
123 /* Is this packet traced? */
124 if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
126 vlib_trace_header_t **h = pool_elt_at_index (
127 tm->trace_buffer_pool, vlib_buffer_get_trace_index (b));
129 dtt->pcap_buffer = format (dtt->pcap_buffer, "%U%c",
130 format_vlib_trace, vm, h[0], 0);
134 /* Save the string count */
135 dtt->pcap_buffer[2] = string_count;
137 /* Figure out how many bytes in the pcap trace */
139 vec_len (dtt->pcap_buffer) + +vlib_buffer_length_in_chain (vm, b);
141 clib_spinlock_lock_if_init (&pm->lock);
142 n_left = clib_min (capture_size, 16384);
143 d = pcap_add_packet (pm, time_now, n_left, capture_size);
145 /* Copy the header */
146 clib_memcpy_fast (d, dtt->pcap_buffer, vec_len (dtt->pcap_buffer));
147 d += vec_len (dtt->pcap_buffer);
149 n_left = clib_min (vlib_buffer_length_in_chain (vm, b),
150 (16384 - vec_len (dtt->pcap_buffer)));
151 /* Copy the packet data */
154 u32 copy_length = clib_min ((u32) n_left, b->current_length);
155 clib_memcpy_fast (d, b->data + b->current_data, copy_length);
156 n_left -= b->current_length;
159 d += b->current_length;
160 ASSERT (b->flags & VLIB_BUFFER_NEXT_PRESENT);
161 b = vlib_get_buffer (vm, b->next_buffer);
163 clib_spinlock_unlock_if_init (&pm->lock);
167 return node->function (vm, node, frame);
171 pcap_postmortem_reset (vlib_main_t *vm)
173 dispatch_trace_main_t *dtm = &dispatch_trace_main;
174 pcap_main_t *pm = &dtm->dispatch_pcap_main;
176 /* Reset the trace buffer and capture count */
177 clib_spinlock_lock_if_init (&pm->lock);
178 vec_reset_length (pm->pcap_data);
179 pm->n_packets_captured = 0;
180 if (vec_len (vlib_worker_threads) == 1 && dtm->epoll_input_node_index)
182 vlib_node_runtime_t *epoll_input_rt =
183 vlib_node_get_runtime (vm, dtm->epoll_input_node_index);
184 epoll_input_rt->input_main_loops_per_call = 0;
186 clib_spinlock_unlock_if_init (&pm->lock);
190 pcap_postmortem_dump (void)
192 dispatch_trace_main_t *dtm = &dispatch_trace_main;
193 pcap_main_t *pm = &dtm->dispatch_pcap_main;
196 pm->n_packets_to_capture = pm->n_packets_captured;
198 (char *) format (0, "/tmp/dispatch_post_mortem.%d%c", getpid (), 0);
199 error = pcap_write (pm);
202 clib_error_report (error);
204 * We're in the middle of crashing. Don't try to free the filename.
208 vlib_pcap_dispatch_trace_configure (vlib_pcap_dispatch_trace_args_t *a)
210 vlib_main_t *vm = vlib_get_main ();
211 vlib_thread_main_t *vtm = vlib_get_thread_main ();
212 dispatch_trace_main_t *dtm = &dispatch_trace_main;
213 pcap_main_t *pm = &dtm->dispatch_pcap_main;
214 vlib_trace_main_t *tm;
215 vlib_trace_node_t *tn;
217 vec_validate (dtm->threads, vtm->n_vlib_mains);
225 "pcap dispatch capture enabled: %d of %d pkts...",
226 pm->n_packets_captured, pm->n_packets_to_capture);
227 vlib_cli_output (vm, "capture to file %s", pm->file_name);
229 for (i = 0; i < vec_len (dtm->dispatch_buffer_trace_nodes); i++)
232 vm, "Buffer trace of %d pkts from %U enabled...",
233 a->buffer_traces_to_capture, format_vlib_node_name, vm,
234 dtm->dispatch_buffer_trace_nodes[i]);
238 vlib_cli_output (vm, "pcap dispatch capture disabled");
242 /* Consistency checks */
244 /* Enable w/ capture already enabled not allowed */
245 if (dtm->enable && a->enable)
246 return VNET_API_ERROR_INVALID_VALUE;
248 /* Disable capture with capture already disabled, not interesting */
249 if (dtm->enable == 0 && a->enable == 0)
250 return VNET_API_ERROR_VALUE_EXIST;
252 /* Change number of packets to capture while capturing */
253 if (dtm->enable && a->enable &&
254 (pm->n_packets_to_capture != a->packets_to_capture))
255 return VNET_API_ERROR_INVALID_VALUE_2;
257 /* Independent of enable/disable, to allow buffer trace multi nodes */
258 if (a->buffer_trace_node_index != ~0)
260 foreach_vlib_main (({
261 tm = &this_vlib_main->trace_main;
262 tm->verbose = 0; /* not sure this ever did anything... */
263 vec_validate (tm->nodes, a->buffer_trace_node_index);
264 tn = tm->nodes + a->buffer_trace_node_index;
265 tn->limit += a->buffer_traces_to_capture;
268 tm->filter_flag = FILTER_FLAG_POST_MORTEM;
269 tm->filter_count = ~0;
271 tm->trace_enable = 1;
272 if (vlib_node_set_dispatch_wrapper (this_vlib_main,
273 dispatch_pcap_trace))
274 clib_warning (0, "Dispatch wrapper already in use on thread %u",
275 this_vlib_main->thread_index);
277 vec_add1 (dtm->dispatch_buffer_trace_nodes, a->buffer_trace_node_index);
282 /* Clean up from previous run, if any */
283 vec_free (pm->file_name);
284 vec_free (pm->pcap_data);
285 memset (pm, 0, sizeof (*pm));
287 vec_validate_aligned (vnet_trace_placeholder, 2048,
288 CLIB_CACHE_LINE_BYTES);
290 clib_spinlock_init (&(pm->lock));
292 if (a->filename == 0)
293 a->filename = format (0, "/tmp/dispatch.pcap%c", 0);
295 pm->file_name = (char *) a->filename;
296 pm->n_packets_captured = 0;
297 pm->packet_type = PCAP_PACKET_TYPE_vpp;
298 pm->n_packets_to_capture = a->packets_to_capture;
304 foreach_vlib_main (({
305 tm = &this_vlib_main->trace_main;
307 tm->filter_count = 0;
308 vlib_node_set_dispatch_wrapper (this_vlib_main, 0);
310 vec_reset_length (dtm->dispatch_buffer_trace_nodes);
311 if (pm->n_packets_captured)
314 pm->n_packets_to_capture = pm->n_packets_captured;
315 vlib_cli_output (vm, "Write %d packets to %s, and stop capture...",
316 pm->n_packets_captured, pm->file_name);
317 error = pcap_write (pm);
318 if (pm->flags & PCAP_MAIN_INIT_DONE)
320 /* Report I/O errors... */
323 clib_error_report (error);
324 return VNET_API_ERROR_SYSCALL_ERROR_1;
329 return VNET_API_ERROR_NO_SUCH_ENTRY;
332 vlib_node_t *epoll_input_node =
333 vlib_get_node_by_name (vm, (u8 *) "unix-epoll-input");
335 /* Save the input node index, see the post-mortem callback */
336 if (epoll_input_node)
337 dtm->epoll_input_node_index = epoll_input_node->index;
339 /* main thread only */
340 clib_callback_enable_disable (vm->worker_thread_main_loop_callbacks,
341 vm->worker_thread_main_loop_callback_tmp,
342 vm->worker_thread_main_loop_callback_lock,
343 pcap_postmortem_reset, a->post_mortem);
344 vlib_add_del_post_mortem_callback (pcap_postmortem_dump, a->post_mortem);
349 static clib_error_t *
350 dispatch_trace_command_fn (vlib_main_t *vm, unformat_input_t *input,
351 vlib_cli_command_t *cmd)
353 unformat_input_t _line_input, *line_input = &_line_input;
354 vlib_pcap_dispatch_trace_args_t _a, *a = &_a;
361 u32 node_index = ~0, buffer_traces_to_capture = 100;
363 /* Get a line of input. */
364 if (!unformat_user (input, unformat_line_input, line_input))
367 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
369 if (unformat (line_input, "on %=", &enable, 1))
371 else if (unformat (line_input, "enable %=", &enable, 1))
373 else if (unformat (line_input, "off %=", &enable, 0))
375 else if (unformat (line_input, "disable %=", &enable, 0))
377 else if (unformat (line_input, "max %d", &max))
379 else if (unformat (line_input, "packets-to-capture %d", &max))
381 else if (unformat (line_input, "file %U", unformat_vlib_tmpfile,
384 else if (unformat (line_input, "status %=", &status, 1))
386 else if (unformat (line_input, "buffer-trace %U %d", unformat_vlib_node,
387 vm, &node_index, &buffer_traces_to_capture))
389 else if (unformat (line_input, "post-mortem %=", &post_mortem, 1))
393 return clib_error_return (0, "unknown input `%U'",
394 format_unformat_error, line_input);
398 unformat_free (line_input);
400 /* no need for memset (a, 0, sizeof (*a)), set all fields here. */
401 a->filename = filename;
404 a->packets_to_capture = max;
405 a->buffer_trace_node_index = node_index;
406 a->buffer_traces_to_capture = buffer_traces_to_capture;
407 a->post_mortem = post_mortem;
409 rv = vlib_pcap_dispatch_trace_configure (a);
417 return clib_error_return (0, "dispatch trace already enabled...");
420 return clib_error_return (0, "dispatch trace already disabled...");
423 return clib_error_return (
424 0, "can't change number of records to capture while tracing...");
427 return clib_error_return (0, "I/O writing trace capture...");
430 return clib_error_return (0, "No packets captured...");
433 vlib_cli_output (vm, "WARNING: trace configure returned %d", rv);
440 * This command is used to start or stop pcap dispatch trace capture, or show
441 * the capture status.
443 * This command has the following optional parameters:
445 * - <b>on|off</b> - Used to start or stop capture.
447 * - <b>max <nn></b> - Depth of local buffer. Once '<em>nn</em>' number
448 * of packets have been received, buffer is flushed to file. Once another
449 * '<em>nn</em>' number of packets have been received, buffer is flushed
450 * to file, overwriting previous write. If not entered, value defaults
451 * to 100. Can only be updated if packet capture is off.
453 * - <b>file <name></b> - Used to specify the output filename. The file will
454 * be placed in the '<em>/tmp</em>' directory, so only the filename is
455 * supported. Directory should not be entered. If file already exists, file
456 * will be overwritten. If no filename is provided, '<em>/tmp/vpe.pcap</em>'
457 * will be used. Can only be updated if packet capture is off.
459 * - <b>status</b> - Displays the current status and configured attributes
460 * associated with a packet capture. If packet capture is in progress,
461 * '<em>status</em>' also will return the number of packets currently in
462 * the local buffer. All additional attributes entered on command line
463 * with '<em>status</em>' will be ignored and not applied.
466 * Example of how to display the status of capture when off:
467 * @cliexstart{pcap dispatch trace status}
468 * max is 100, for any interface to file /tmp/vpe.pcap
469 * pcap dispatch capture is off...
471 * Example of how to start a dispatch trace capture:
472 * @cliexstart{pcap dispatch trace on max 35 file dispatchTrace.pcap}
473 * pcap dispatch capture on...
475 * Example of how to start a dispatch trace capture with buffer tracing
476 * @cliexstart{pcap dispatch trace on max 10000 file dispatchTrace.pcap
477 * buffer-trace dpdk-input 1000}
478 * pcap dispatch capture on...
480 * Example of how to display the status of a tx packet capture in progress:
481 * @cliexstart{pcap tx trace status}
482 * max is 35, dispatch trace to file /tmp/vppTest.pcap
483 * pcap tx capture is on: 20 of 35 pkts...
485 * Example of how to stop a tx packet capture:
486 * @cliexstart{vppctl pcap dispatch trace off}
487 * captured 21 pkts...
488 * saved to /tmp/dispatchTrace.pcap...
489 * Example of how to start a post-mortem dispatch trace:
490 * pcap dispatch trace on max 20000 buffer-trace
491 * dpdk-input 3000000000 post-mortem
495 VLIB_CLI_COMMAND (pcap_dispatch_trace_command, static) = {
496 .path = "pcap dispatch trace",
498 "pcap dispatch trace [on|off] [max <nn>] [file <name>] [status]\n"
499 " [buffer-trace <input-node-name> <nn>][post-mortem]",
500 .function = dispatch_trace_command_fn,
503 VLIB_PLUGIN_REGISTER () = {
504 .version = VPP_BUILD_VER,
505 .description = "Dispatch Trace",