Clean up gcc-5.2 warnings
[vpp.git] / vlib / vlib / trace.c
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
45 vlib_trace_frame_buffers_only (vlib_main_t * vm,
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           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           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           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
112 clear_trace_buffer (void)
113 {
114   int i;
115   vlib_trace_main_t * tm;
116
117   foreach_vlib_main (
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     for (i = 0; i < vec_len (tm->trace_buffer_pool); i++)
125       if (! pool_is_free_index (tm->trace_buffer_pool, i))
126         vec_free (tm->trace_buffer_pool[i]);
127     pool_free (tm->trace_buffer_pool);
128     clib_mem_set_heap (mainheap);
129   }));
130 }
131
132 static u8 * format_vlib_trace (u8 * s, va_list * va)
133 {
134   vlib_main_t * vm = va_arg (*va, vlib_main_t *);
135   vlib_trace_header_t * h = va_arg (*va, vlib_trace_header_t *);
136   vlib_trace_header_t * e = vec_end (h);
137   vlib_node_t * node, * prev_node;
138   clib_time_t * ct = &vm->clib_time;
139   f64 t;
140   
141   prev_node = 0;
142   while (h < e)
143     {
144       node = vlib_get_node (vm, h->node_index);
145
146       if (node != prev_node)
147         {
148           t = (h->time - vm->cpu_time_main_loop_start) * ct->seconds_per_clock;
149           s = format (s, "\n%U: %v",
150                       format_time_interval, "h:m:s:u", t,
151                       node->name);
152         }
153       prev_node = node;
154
155       if (node->format_trace)
156         s = format (s, "\n  %U",
157                     node->format_trace, vm, node, h->data);
158       else
159         s = format (s, "\n  %U",
160                     node->format_buffer, h->data);
161
162       h = vlib_trace_header_next (h);
163     }
164
165   return s;
166 }
167
168 /* Root of all trace cli commands. */
169 VLIB_CLI_COMMAND (trace_cli_command,static) = {
170   .path = "trace",
171   .short_help = "Packet tracer commands",
172 };
173
174 static int
175 trace_cmp (void * a1, void * a2)
176 {
177   vlib_trace_header_t ** t1 = a1;
178   vlib_trace_header_t ** t2 = a2;
179   i64 dt = t1[0]->time - t2[0]->time;
180   return dt < 0 ? -1 : (dt > 0 ? +1 : 0);
181 }
182
183 /*
184  * Return 1 if this packet passes the trace filter, or 0 otherwise
185  */
186 u32 filter_accept (vlib_trace_main_t * tm, vlib_trace_header_t * h)
187 {
188   vlib_trace_header_t * e = vec_end (h);
189
190   if (tm->filter_flag == 0) return 1;
191
192   if (tm->filter_flag == FILTER_FLAG_INCLUDE)
193     {
194       while (h < e)
195         {
196           if (h->node_index == tm->filter_node_index)
197             return 1;
198           h = vlib_trace_header_next (h);
199         }
200       return 0;
201    }
202   else /* FILTER_FLAG_EXCLUDE */
203     {
204       while (h < e)
205         {
206           if (h->node_index == tm->filter_node_index)
207             return 0;
208           h = vlib_trace_header_next (h);
209         }
210       return 1;
211    }
212
213   return 0;
214 }
215
216 /*
217  * Remove traces from the trace buffer pool that don't pass the filter
218  */
219 void trace_apply_filter (vlib_main_t * vm)
220 {
221   vlib_trace_main_t * tm = &vm->trace_main;
222   vlib_trace_header_t ** h;
223   vlib_trace_header_t *** traces_to_remove = 0;
224   u32 index;
225   u32 trace_index;
226   u32 n_accepted;
227
228   u32 accept;
229
230   if (tm->filter_flag == FILTER_FLAG_NONE)
231     return;
232
233   /*
234    * Ideally we would retain the first N traces that pass the filter instead
235    * of any N traces.
236    */
237   n_accepted = 0;
238   pool_foreach (h, tm->trace_buffer_pool,
239    ({
240       accept = filter_accept(tm, h[0]);
241
242       if ((n_accepted == tm->filter_count) || !accept)
243           vec_add1 (traces_to_remove, h);
244       else
245           n_accepted++;
246   }));
247
248   /* remove all traces that we don't want to keep */
249   for (index=0; index<vec_len(traces_to_remove); index++)
250     {
251       trace_index = traces_to_remove[index] - tm->trace_buffer_pool;
252       _vec_len (tm->trace_buffer_pool[trace_index]) = 0;
253       pool_put_index (tm->trace_buffer_pool, trace_index);
254     }
255
256   vec_free (traces_to_remove);
257 }
258
259 static clib_error_t *
260 cli_show_trace_buffer (vlib_main_t * vm,
261                        unformat_input_t * input,
262                        vlib_cli_command_t * cmd)
263 {
264   vlib_trace_main_t * tm;
265   vlib_trace_header_t ** h, ** traces;
266   u32 i, index = 0;
267   char * fmt;
268   u8 * s = 0;
269   u32 max;
270
271   /*
272    * By default display only this many traces. To display more, explicitly
273    * specify a max. This prevents unexpectedly huge outputs.
274    */
275   max = 50;
276   while (unformat_check_input(input) != (uword)UNFORMAT_END_OF_INPUT)
277     {
278       if (unformat (input, "max %d", &max))
279         ;
280       else
281         return clib_error_create ("expected 'max COUNT', got `%U'",
282                                   format_unformat_error, input);
283     }
284
285
286   /* Get active traces from pool. */
287
288   foreach_vlib_main (
289   ({
290     void *mainheap;
291
292     fmt = "------------------- Start of thread %d %s -------------------\n";
293     s = format (s, fmt, index, vlib_worker_threads[index].name);
294
295     tm = &this_vlib_main->trace_main;
296
297     mainheap = clib_mem_set_heap (this_vlib_main->heap_base);
298
299     trace_apply_filter(this_vlib_main);
300
301     traces = 0;
302     pool_foreach (h, tm->trace_buffer_pool, 
303     ({
304       vec_add1 (traces, h[0]);
305     }));
306     
307     if (vec_len (traces) == 0)
308       {
309         clib_mem_set_heap (mainheap);
310         s = format (s, "No packets in trace buffer\n");
311         goto done;
312       }
313     
314     /* Sort them by increasing time. */
315     vec_sort_with_function (traces, trace_cmp);
316     
317     for (i = 0; i < vec_len (traces); i++)
318       {
319         if (i == max)
320           {
321             vlib_cli_output (vm, "Limiting display to %d packets."
322                                  " To display more specify max.", max);
323             goto done;
324           }
325
326         clib_mem_set_heap (mainheap);
327         
328         s = format (s, "Packet %d\n%U\n\n", i + 1,
329                          format_vlib_trace, vm, traces[i]);
330
331         mainheap = clib_mem_set_heap (this_vlib_main->heap_base);
332       }
333     
334   done:
335     vec_free (traces);
336     clib_mem_set_heap (mainheap);
337
338     index++;
339   }));
340
341   vlib_cli_output (vm, (char *) s);
342   vec_free (s);
343   return 0;
344 }
345
346 VLIB_CLI_COMMAND (show_trace_cli,static) = {
347   .path = "show trace",
348   .short_help = "Show trace buffer [max COUNT]",
349   .function = cli_show_trace_buffer,
350 };
351
352 static clib_error_t *
353 cli_add_trace_buffer (vlib_main_t * vm,
354                       unformat_input_t * input,
355                       vlib_cli_command_t * cmd)
356 {
357   vlib_trace_main_t * tm;
358   vlib_trace_node_t * tn;
359   u32 node_index, add;
360
361   if (unformat (input, "%U %d", unformat_vlib_node, vm, &node_index, &add))
362     ;
363   else
364     return clib_error_create ("expected NODE COUNT, got `%U'",
365                               format_unformat_error, input);
366
367   foreach_vlib_main (
368   ({
369     void *oldheap;
370     tm = &this_vlib_main->trace_main;
371
372     oldheap = clib_mem_set_heap (this_vlib_main->heap_base);
373
374     vec_validate (tm->nodes, node_index);
375     tn = tm->nodes + node_index;
376     tn->limit += add;
377     clib_mem_set_heap (oldheap);
378   }));
379
380   return 0;
381 }
382
383 VLIB_CLI_COMMAND (add_trace_cli,static) = {
384   .path = "trace add",
385   .short_help = "Trace given number of packets",
386   .function = cli_add_trace_buffer,
387 };
388
389
390 /*
391  * Configure a filter for packet traces.
392  *
393  * This supplements the packet trace feature so that only packets matching
394  * the filter are included in the trace. Currently the only filter is to
395  * keep packets that include a certain node in the trace or exclude a certain
396  * node in the trace.
397  *
398  * The count of traced packets in the "trace add" command is still used to
399  * create a certain number of traces. The "trace filter" command specifies
400  * how many of those packets should be retained in the trace.
401  *
402  * For example, 1Mpps of traffic is arriving and one of those packets is being
403  * dropped. To capture the trace for only that dropped packet, you can do:
404  *     trace filter include error-drop 1
405  *     trace add dpdk-input 1000000
406  *     <wait one second>
407  *     show trace
408  *
409  * Note that the filter could be implemented by capturing all traces and just
410  * reducing traces displayed by the "show trace" function. But that would
411  * require a lot of memory for storing the traces, making that infeasible.
412  *
413  * To remove traces from the trace pool that do not include a certain node
414  * requires that the trace be "complete" before applying the filter. To
415  * accomplish this, the trace pool is filtered upon each iteraction of the
416  * main vlib loop. Doing so keeps the number of allocated traces down to a
417  * reasonably low number. This requires that tracing for a buffer is not
418  * performed after the vlib main loop interation completes. i.e. you can't
419  * save away a buffer temporarily then inject it back into the graph and
420  * expect that the trace_index is still valid (such as a traffic manager might
421  * do). A new trace buffer should be allocated for those types of packets.
422  *
423  * The filter can be extended to support multiple nodes and other match
424  * criteria (e.g. input sw_if_index, mac address) but for now just checks if
425  * a specified node is in the trace or not in the trace.
426  */
427 static clib_error_t *
428 cli_filter_trace (vlib_main_t * vm,
429                   unformat_input_t * input,
430                   vlib_cli_command_t * cmd)
431 {
432   vlib_trace_main_t * tm = &vm->trace_main;
433   u32 filter_node_index;
434   u32 filter_flag;
435   u32 filter_count;
436   void *mainheap;
437
438   if (unformat (input, "include %U %d",
439       unformat_vlib_node, vm, &filter_node_index, &filter_count))
440     {
441       filter_flag = FILTER_FLAG_INCLUDE;
442     }
443   else if (unformat (input, "exclude %U %d",
444       unformat_vlib_node, vm, &filter_node_index, &filter_count))
445     {
446       filter_flag = FILTER_FLAG_EXCLUDE;
447     }
448   else if (unformat (input, "none"))
449     {
450       filter_flag = FILTER_FLAG_NONE;
451       filter_node_index = 0;
452       filter_count = 0;
453     }
454   else
455     return clib_error_create ("expected 'include NODE COUNT' or 'exclude NODE COUNT' or 'none', got `%U'",
456                               format_unformat_error, input);
457
458   foreach_vlib_main (
459   ({
460     tm = &this_vlib_main->trace_main;
461     tm->filter_node_index = filter_node_index;
462     tm->filter_flag = filter_flag;
463     tm->filter_count = filter_count;
464
465     /*
466      * Clear the trace limits to stop any in-progress tracing
467      * Prevents runaway trace allocations when the filter changes (or is removed)
468      */
469     mainheap = clib_mem_set_heap (this_vlib_main->heap_base);
470     vec_free (tm->nodes);
471     clib_mem_set_heap (mainheap);
472   }));
473
474   return 0;
475 }
476
477 VLIB_CLI_COMMAND (filter_trace_cli,static) = {
478   .path = "trace filter",
479   .short_help = "filter trace output - include NODE COUNT | exclude NODE COUNT | none",
480   .function = cli_filter_trace,
481 };
482
483 static clib_error_t *
484 cli_clear_trace_buffer (vlib_main_t * vm,
485                         unformat_input_t * input,
486                         vlib_cli_command_t * cmd)
487 {
488   clear_trace_buffer ();
489   return 0;
490 }
491
492 VLIB_CLI_COMMAND (clear_trace_cli,static) = {
493   .path = "clear trace",
494   .short_help = "Clear trace buffer and free memory",
495   .function = cli_clear_trace_buffer,
496 };
497
498 /* Dummy function to get us linked in. */
499 void vlib_trace_cli_reference (void) {}