FD.io VPP  v16.06
Vector Packet Processing
trace.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*
16  * trace.c: VLIB trace buffer.
17  *
18  * Copyright (c) 2008 Eliot Dresselhaus
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining
21  * a copy of this software and associated documentation files (the
22  * "Software"), to deal in the Software without restriction, including
23  * without limitation the rights to use, copy, modify, merge, publish,
24  * distribute, sublicense, and/or sell copies of the Software, and to
25  * permit persons to whom the Software is furnished to do so, subject to
26  * the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
31  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38  */
39 
40 #include <vlib/vlib.h>
41 #include <vlib/threads.h>
42 
43 /* Helper function for nodes which only trace buffer data. */
44 void
46  vlib_node_runtime_t * node,
47  u32 * buffers,
48  uword n_buffers,
49  uword next_buffer_stride,
50  uword n_buffer_data_bytes_in_trace)
51 {
52  u32 n_left, * from;
53 
54  n_left = n_buffers;
55  from = buffers;
56 
57  while (n_left >= 4)
58  {
59  u32 bi0, bi1;
60  vlib_buffer_t * b0, * b1;
61  u8 * t0, * t1;
62 
63  /* Prefetch next iteration. */
64  vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
65  vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
66 
67  bi0 = from[0];
68  bi1 = from[1];
69 
70  b0 = vlib_get_buffer (vm, bi0);
71  b1 = vlib_get_buffer (vm, bi1);
72 
73  if (b0->flags & VLIB_BUFFER_IS_TRACED)
74  {
75  t0 = vlib_add_trace (vm, node, b0, n_buffer_data_bytes_in_trace);
76  clib_memcpy (t0, b0->data + b0->current_data,
77  n_buffer_data_bytes_in_trace);
78  }
79  if (b1->flags & VLIB_BUFFER_IS_TRACED)
80  {
81  t1 = vlib_add_trace (vm, node, b1, n_buffer_data_bytes_in_trace);
82  clib_memcpy (t1, b1->data + b1->current_data,
83  n_buffer_data_bytes_in_trace);
84  }
85  from += 2;
86  n_left -= 2;
87  }
88 
89  while (n_left >= 1)
90  {
91  u32 bi0;
92  vlib_buffer_t * b0;
93  u8 * t0;
94 
95  bi0 = from[0];
96 
97  b0 = vlib_get_buffer (vm, bi0);
98 
99  if (b0->flags & VLIB_BUFFER_IS_TRACED)
100  {
101  t0 = vlib_add_trace (vm, node, b0, n_buffer_data_bytes_in_trace);
102  clib_memcpy (t0, b0->data + b0->current_data,
103  n_buffer_data_bytes_in_trace);
104  }
105  from += 1;
106  n_left -= 1;
107  }
108 }
109 
110 /* Free up all trace buffer memory. */
111 always_inline void
113 {
114  int i;
115  vlib_trace_main_t * tm;
116 
118  ({
119  void *mainheap;
120 
121  tm = &this_vlib_main->trace_main;
122  mainheap = clib_mem_set_heap (this_vlib_main->heap_base);
123 
124  tm->trace_active_hint = 0;
125 
126  for (i = 0; i < vec_len (tm->trace_buffer_pool); i++)
127  if (! pool_is_free_index (tm->trace_buffer_pool, i))
128  vec_free (tm->trace_buffer_pool[i]);
130  clib_mem_set_heap (mainheap);
131  }));
132 }
133 
134 static u8 * format_vlib_trace (u8 * s, va_list * va)
135 {
136  vlib_main_t * vm = va_arg (*va, vlib_main_t *);
137  vlib_trace_header_t * h = va_arg (*va, vlib_trace_header_t *);
138  vlib_trace_header_t * e = vec_end (h);
139  vlib_node_t * node, * prev_node;
140  clib_time_t * ct = &vm->clib_time;
141  f64 t;
142 
143  prev_node = 0;
144  while (h < e)
145  {
146  node = vlib_get_node (vm, h->node_index);
147 
148  if (node != prev_node)
149  {
150  t = (h->time - vm->cpu_time_main_loop_start) * ct->seconds_per_clock;
151  s = format (s, "\n%U: %v",
152  format_time_interval, "h:m:s:u", t,
153  node->name);
154  }
155  prev_node = node;
156 
157  if (node->format_trace)
158  s = format (s, "\n %U",
159  node->format_trace, vm, node, h->data);
160  else
161  s = format (s, "\n %U",
162  node->format_buffer, h->data);
163 
164  h = vlib_trace_header_next (h);
165  }
166 
167  return s;
168 }
169 
170 /* Root of all trace cli commands. */
171 VLIB_CLI_COMMAND (trace_cli_command,static) = {
172  .path = "trace",
173  .short_help = "Packet tracer commands",
174 };
175 
176 static int
177 trace_cmp (void * a1, void * a2)
178 {
179  vlib_trace_header_t ** t1 = a1;
180  vlib_trace_header_t ** t2 = a2;
181  i64 dt = t1[0]->time - t2[0]->time;
182  return dt < 0 ? -1 : (dt > 0 ? +1 : 0);
183 }
184 
185 /*
186  * Return 1 if this packet passes the trace filter, or 0 otherwise
187  */
189 {
190  vlib_trace_header_t * e = vec_end (h);
191 
192  if (tm->filter_flag == 0) return 1;
193 
194  if (tm->filter_flag == FILTER_FLAG_INCLUDE)
195  {
196  while (h < e)
197  {
198  if (h->node_index == tm->filter_node_index)
199  return 1;
200  h = vlib_trace_header_next (h);
201  }
202  return 0;
203  }
204  else /* FILTER_FLAG_EXCLUDE */
205  {
206  while (h < e)
207  {
208  if (h->node_index == tm->filter_node_index)
209  return 0;
210  h = vlib_trace_header_next (h);
211  }
212  return 1;
213  }
214 
215  return 0;
216 }
217 
218 /*
219  * Remove traces from the trace buffer pool that don't pass the filter
220  */
222 {
223  vlib_trace_main_t * tm = &vm->trace_main;
224  vlib_trace_header_t ** h;
225  vlib_trace_header_t *** traces_to_remove = 0;
226  u32 index;
227  u32 trace_index;
228  u32 n_accepted;
229 
230  u32 accept;
231 
232  if (tm->filter_flag == FILTER_FLAG_NONE)
233  return;
234 
235  /*
236  * Ideally we would retain the first N traces that pass the filter instead
237  * of any N traces.
238  */
239  n_accepted = 0;
241  ({
242  accept = filter_accept(tm, h[0]);
243 
244  if ((n_accepted == tm->filter_count) || !accept)
245  vec_add1 (traces_to_remove, h);
246  else
247  n_accepted++;
248  }));
249 
250  /* remove all traces that we don't want to keep */
251  for (index=0; index<vec_len(traces_to_remove); index++)
252  {
253  trace_index = traces_to_remove[index] - tm->trace_buffer_pool;
254  _vec_len (tm->trace_buffer_pool[trace_index]) = 0;
255  pool_put_index (tm->trace_buffer_pool, trace_index);
256  }
257 
258  vec_free (traces_to_remove);
259 }
260 
261 static clib_error_t *
263  unformat_input_t * input,
264  vlib_cli_command_t * cmd)
265 {
266  vlib_trace_main_t * tm;
267  vlib_trace_header_t ** h, ** traces;
268  u32 i, index = 0;
269  char * fmt;
270  u8 * s = 0;
271  u32 max;
272 
273  /*
274  * By default display only this many traces. To display more, explicitly
275  * specify a max. This prevents unexpectedly huge outputs.
276  */
277  max = 50;
279  {
280  if (unformat (input, "max %d", &max))
281  ;
282  else
283  return clib_error_create ("expected 'max COUNT', got `%U'",
284  format_unformat_error, input);
285  }
286 
287 
288  /* Get active traces from pool. */
289 
291  ({
292  void *mainheap;
293 
294  fmt = "------------------- Start of thread %d %s -------------------\n";
295  s = format (s, fmt, index, vlib_worker_threads[index].name);
296 
297  tm = &this_vlib_main->trace_main;
298 
299  mainheap = clib_mem_set_heap (this_vlib_main->heap_base);
300 
301  trace_apply_filter(this_vlib_main);
302 
303  traces = 0;
305  ({
306  vec_add1 (traces, h[0]);
307  }));
308 
309  if (vec_len (traces) == 0)
310  {
311  clib_mem_set_heap (mainheap);
312  s = format (s, "No packets in trace buffer\n");
313  goto done;
314  }
315 
316  /* Sort them by increasing time. */
318 
319  for (i = 0; i < vec_len (traces); i++)
320  {
321  if (i == max)
322  {
323  vlib_cli_output (vm, "Limiting display to %d packets."
324  " To display more specify max.", max);
325  goto done;
326  }
327 
328  clib_mem_set_heap (mainheap);
329 
330  s = format (s, "Packet %d\n%U\n\n", i + 1,
331  format_vlib_trace, vm, traces[i]);
332 
333  mainheap = clib_mem_set_heap (this_vlib_main->heap_base);
334  }
335 
336  done:
337  vec_free (traces);
338  clib_mem_set_heap (mainheap);
339 
340  index++;
341  }));
342 
343  vlib_cli_output (vm, (char *) s);
344  vec_free (s);
345  return 0;
346 }
347 
348 VLIB_CLI_COMMAND (show_trace_cli,static) = {
349  .path = "show trace",
350  .short_help = "Show trace buffer [max COUNT]",
351  .function = cli_show_trace_buffer,
352 };
353 
354 static clib_error_t *
356  unformat_input_t * input,
357  vlib_cli_command_t * cmd)
358 {
359  vlib_trace_main_t * tm;
360  vlib_trace_node_t * tn;
361  u32 node_index, add;
362 
363  if (unformat (input, "%U %d", unformat_vlib_node, vm, &node_index, &add))
364  ;
365  else
366  return clib_error_create ("expected NODE COUNT, got `%U'",
367  format_unformat_error, input);
368 
370  ({
371  void *oldheap;
372  tm = &this_vlib_main->trace_main;
373 
374  tm->trace_active_hint = 1;
375 
376  oldheap = clib_mem_set_heap (this_vlib_main->heap_base);
377 
378  vec_validate (tm->nodes, node_index);
379  tn = tm->nodes + node_index;
380  tn->limit += add;
381  clib_mem_set_heap (oldheap);
382  }));
383 
384  return 0;
385 }
386 
387 VLIB_CLI_COMMAND (add_trace_cli,static) = {
388  .path = "trace add",
389  .short_help = "Trace given number of packets",
390  .function = cli_add_trace_buffer,
391 };
392 
393 
394 /*
395  * Configure a filter for packet traces.
396  *
397  * This supplements the packet trace feature so that only packets matching
398  * the filter are included in the trace. Currently the only filter is to
399  * keep packets that include a certain node in the trace or exclude a certain
400  * node in the trace.
401  *
402  * The count of traced packets in the "trace add" command is still used to
403  * create a certain number of traces. The "trace filter" command specifies
404  * how many of those packets should be retained in the trace.
405  *
406  * For example, 1Mpps of traffic is arriving and one of those packets is being
407  * dropped. To capture the trace for only that dropped packet, you can do:
408  * trace filter include error-drop 1
409  * trace add dpdk-input 1000000
410  * <wait one second>
411  * show trace
412  *
413  * Note that the filter could be implemented by capturing all traces and just
414  * reducing traces displayed by the "show trace" function. But that would
415  * require a lot of memory for storing the traces, making that infeasible.
416  *
417  * To remove traces from the trace pool that do not include a certain node
418  * requires that the trace be "complete" before applying the filter. To
419  * accomplish this, the trace pool is filtered upon each iteraction of the
420  * main vlib loop. Doing so keeps the number of allocated traces down to a
421  * reasonably low number. This requires that tracing for a buffer is not
422  * performed after the vlib main loop interation completes. i.e. you can't
423  * save away a buffer temporarily then inject it back into the graph and
424  * expect that the trace_index is still valid (such as a traffic manager might
425  * do). A new trace buffer should be allocated for those types of packets.
426  *
427  * The filter can be extended to support multiple nodes and other match
428  * criteria (e.g. input sw_if_index, mac address) but for now just checks if
429  * a specified node is in the trace or not in the trace.
430  */
431 static clib_error_t *
433  unformat_input_t * input,
434  vlib_cli_command_t * cmd)
435 {
436  vlib_trace_main_t * tm = &vm->trace_main;
437  u32 filter_node_index;
438  u32 filter_flag;
439  u32 filter_count;
440  void *mainheap;
441 
442  if (unformat (input, "include %U %d",
443  unformat_vlib_node, vm, &filter_node_index, &filter_count))
444  {
445  filter_flag = FILTER_FLAG_INCLUDE;
446  }
447  else if (unformat (input, "exclude %U %d",
448  unformat_vlib_node, vm, &filter_node_index, &filter_count))
449  {
450  filter_flag = FILTER_FLAG_EXCLUDE;
451  }
452  else if (unformat (input, "none"))
453  {
454  filter_flag = FILTER_FLAG_NONE;
455  filter_node_index = 0;
456  filter_count = 0;
457  }
458  else
459  return clib_error_create ("expected 'include NODE COUNT' or 'exclude NODE COUNT' or 'none', got `%U'",
460  format_unformat_error, input);
461 
463  ({
464  tm = &this_vlib_main->trace_main;
465  tm->filter_node_index = filter_node_index;
466  tm->filter_flag = filter_flag;
467  tm->filter_count = filter_count;
468 
469  /*
470  * Clear the trace limits to stop any in-progress tracing
471  * Prevents runaway trace allocations when the filter changes (or is removed)
472  */
473  mainheap = clib_mem_set_heap (this_vlib_main->heap_base);
474  vec_free (tm->nodes);
475  clib_mem_set_heap (mainheap);
476  }));
477 
478  return 0;
479 }
480 
481 VLIB_CLI_COMMAND (filter_trace_cli,static) = {
482  .path = "trace filter",
483  .short_help = "filter trace output - include NODE COUNT | exclude NODE COUNT | none",
484  .function = cli_filter_trace,
485 };
486 
487 static clib_error_t *
489  unformat_input_t * input,
490  vlib_cli_command_t * cmd)
491 {
493  return 0;
494 }
495 
496 VLIB_CLI_COMMAND (clear_trace_cli,static) = {
497  .path = "clear trace",
498  .short_help = "Clear trace buffer and free memory",
499  .function = cli_clear_trace_buffer,
500 };
501 
502 /* Dummy function to get us linked in. */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:394
u32 filter_flag
Definition: trace.h:73
sll srl srl sll sra u16x4 i
Definition: vector_sse2.h:267
u32 trace_active_hint
Definition: trace.h:80
#define foreach_vlib_main(body)
Definition: threads.h:212
uword unformat(unformat_input_t *i, char *fmt,...)
Definition: unformat.c:942
void vlib_trace_cli_reference(void)
Definition: trace.c:503
always_inline vlib_node_t * vlib_get_node(vlib_main_t *vm, u32 i)
Definition: node_funcs.h:46
vlib_trace_node_t * nodes
Definition: trace.h:83
static clib_error_t * cli_clear_trace_buffer(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: trace.c:488
#define UNFORMAT_END_OF_INPUT
Definition: format.h:142
format_function_t * format_trace
Definition: node.h:281
always_inline void clear_trace_buffer(void)
Definition: trace.c:112
clib_time_t clib_time
Definition: main.h:61
u32 filter_node_index
Definition: trace.h:72
always_inline uword unformat_check_input(unformat_input_t *i)
Definition: format.h:168
static clib_error_t * cli_show_trace_buffer(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: trace.c:262
vlib_trace_header_t ** trace_buffer_pool
Definition: trace.h:69
i16 current_data
signed offset in data[], pre_data[] that we are currently processing.
Definition: buffer.h:77
#define pool_foreach(VAR, POOL, BODY)
Definition: pool.h:328
#define vlib_prefetch_buffer_with_index(vm, bi, type)
Prefetch buffer metadata by buffer index The first 64 bytes of buffer contains most header informatio...
Definition: buffer_funcs.h:181
#define always_inline
Definition: clib.h:84
#define vec_end(v)
End (last data address) of vector.
u64 cpu_time_main_loop_start
Definition: main.h:67
u32 filter_accept(vlib_trace_main_t *tm, vlib_trace_header_t *h)
Definition: trace.c:188
add
Definition: vector_sse2.h:213
#define FILTER_FLAG_EXCLUDE
Definition: trace.h:76
#define FILTER_FLAG_NONE
Definition: trace.h:74
static int trace_cmp(void *a1, void *a2)
Definition: trace.c:177
#define clib_error_create(args...)
Definition: error.h:109
always_inline void * clib_mem_set_heap(void *heap)
Definition: mem.h:190
f64 seconds_per_clock
Definition: time.h:56
format_function_t * format_buffer
Definition: node.h:277
long i64
Definition: types.h:82
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:538
u8 * name
Definition: node.h:187
u32 filter_count
Definition: trace.h:77
#define pool_free(p)
Definition: pool.h:248
static clib_error_t * cli_filter_trace(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: trace.c:432
static clib_error_t * cli_add_trace_buffer(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: trace.c:355
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:298
always_inline vlib_trace_header_t * vlib_trace_header_next(vlib_trace_header_t *h)
Definition: trace_funcs.h:80
#define clib_memcpy(a, b, c)
Definition: string.h:63
#define pool_is_free_index(P, I)
Definition: pool.h:197
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:150
vlib_worker_thread_t * vlib_worker_threads
Definition: threads.h:106
#define pool_put_index(p, i)
Definition: pool.h:214
unsigned int u32
Definition: types.h:88
u8 * format_unformat_error(u8 *s, va_list *va)
Definition: unformat.c:87
u8 * format(u8 *s, char *fmt,...)
Definition: format.c:405
vlib_trace_main_t trace_main
Definition: main.h:121
#define VLIB_BUFFER_IS_TRACED
Definition: buffer.h:91
u64 uword
Definition: types.h:112
void vlib_trace_frame_buffers_only(vlib_main_t *vm, vlib_node_runtime_t *node, u32 *buffers, uword n_buffers, uword next_buffer_stride, uword n_buffer_data_bytes_in_trace)
Definition: trace.c:45
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
double f64
Definition: types.h:140
unsigned char u8
Definition: types.h:56
#define vec_sort_with_function(vec, f)
Sort a vector using the supplied element comparison function.
Definition: vec.h:898
u8 * format_time_interval(u8 *s, va_list *args)
Definition: std-formats.c:115
unformat_function_t unformat_vlib_node
Definition: node_funcs.h:967
always_inline void * vlib_add_trace(vlib_main_t *vm, vlib_node_runtime_t *r, vlib_buffer_t *b, u32 n_data_bytes)
Definition: trace_funcs.h:55
u8 data[0]
Packet data.
Definition: buffer.h:150
struct _unformat_input_t unformat_input_t
u32 flags
buffer flags: VLIB_BUFFER_IS_TRACED: trace this buffer.
Definition: buffer.h:84
always_inline vlib_buffer_t * vlib_get_buffer(vlib_main_t *vm, u32 buffer_index)
Translate buffer index into buffer pointer.
Definition: buffer_funcs.h:69
void trace_apply_filter(vlib_main_t *vm)
Definition: trace.c:221
static u8 * format_vlib_trace(u8 *s, va_list *va)
Definition: trace.c:134
#define FILTER_FLAG_INCLUDE
Definition: trace.h:75