
Type: refactor Change-Id: I5235bf3e9aff58af6ba2c14e8c6529c4fc9ec86c Signed-off-by: Damjan Marion <damarion@cisco.com>
752 lines
19 KiB
C
752 lines
19 KiB
C
/*
|
|
* Copyright (c) 2015 Cisco and/or its affiliates.
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/*
|
|
* trace.c: VLIB trace buffer.
|
|
*
|
|
* Copyright (c) 2008 Eliot Dresselhaus
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include <vlib/vlib.h>
|
|
#include <vlib/threads.h>
|
|
#include <vnet/classify/vnet_classify.h>
|
|
|
|
u8 *vnet_trace_placeholder;
|
|
|
|
/* Helper function for nodes which only trace buffer data. */
|
|
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)
|
|
{
|
|
u32 n_left, *from;
|
|
|
|
n_left = n_buffers;
|
|
from = buffers;
|
|
|
|
while (n_left >= 4)
|
|
{
|
|
u32 bi0, bi1;
|
|
vlib_buffer_t *b0, *b1;
|
|
u8 *t0, *t1;
|
|
|
|
/* Prefetch next iteration. */
|
|
vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
|
|
vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
|
|
|
|
bi0 = from[0];
|
|
bi1 = from[1];
|
|
|
|
b0 = vlib_get_buffer (vm, bi0);
|
|
b1 = vlib_get_buffer (vm, bi1);
|
|
|
|
if (b0->flags & VLIB_BUFFER_IS_TRACED)
|
|
{
|
|
t0 = vlib_add_trace (vm, node, b0, n_buffer_data_bytes_in_trace);
|
|
clib_memcpy_fast (t0, b0->data + b0->current_data,
|
|
n_buffer_data_bytes_in_trace);
|
|
}
|
|
if (b1->flags & VLIB_BUFFER_IS_TRACED)
|
|
{
|
|
t1 = vlib_add_trace (vm, node, b1, n_buffer_data_bytes_in_trace);
|
|
clib_memcpy_fast (t1, b1->data + b1->current_data,
|
|
n_buffer_data_bytes_in_trace);
|
|
}
|
|
from += 2;
|
|
n_left -= 2;
|
|
}
|
|
|
|
while (n_left >= 1)
|
|
{
|
|
u32 bi0;
|
|
vlib_buffer_t *b0;
|
|
u8 *t0;
|
|
|
|
bi0 = from[0];
|
|
|
|
b0 = vlib_get_buffer (vm, bi0);
|
|
|
|
if (b0->flags & VLIB_BUFFER_IS_TRACED)
|
|
{
|
|
t0 = vlib_add_trace (vm, node, b0, n_buffer_data_bytes_in_trace);
|
|
clib_memcpy_fast (t0, b0->data + b0->current_data,
|
|
n_buffer_data_bytes_in_trace);
|
|
}
|
|
from += 1;
|
|
n_left -= 1;
|
|
}
|
|
}
|
|
|
|
/* Free up all trace buffer memory. */
|
|
void
|
|
clear_trace_buffer (void)
|
|
{
|
|
int i;
|
|
vlib_trace_main_t *tm;
|
|
|
|
foreach_vlib_main ()
|
|
{
|
|
tm = &this_vlib_main->trace_main;
|
|
|
|
tm->trace_enable = 0;
|
|
vec_free (tm->nodes);
|
|
}
|
|
|
|
foreach_vlib_main ()
|
|
{
|
|
tm = &this_vlib_main->trace_main;
|
|
|
|
for (i = 0; i < vec_len (tm->trace_buffer_pool); i++)
|
|
if (!pool_is_free_index (tm->trace_buffer_pool, i))
|
|
vec_free (tm->trace_buffer_pool[i]);
|
|
pool_free (tm->trace_buffer_pool);
|
|
}
|
|
}
|
|
|
|
u8 *
|
|
format_vlib_trace (u8 * s, va_list * va)
|
|
{
|
|
vlib_main_t *vm = va_arg (*va, vlib_main_t *);
|
|
vlib_trace_header_t *h = va_arg (*va, vlib_trace_header_t *);
|
|
vlib_trace_header_t *e = vec_end (h);
|
|
vlib_node_t *node, *prev_node;
|
|
clib_time_t *ct = &vm->clib_time;
|
|
f64 t;
|
|
|
|
prev_node = 0;
|
|
while (h < e)
|
|
{
|
|
node = vlib_get_node (vm, h->node_index);
|
|
|
|
if (node != prev_node)
|
|
{
|
|
t =
|
|
(h->time - vm->cpu_time_main_loop_start) * ct->seconds_per_clock;
|
|
s =
|
|
format (s, "\n%U: %v", format_time_interval, "h:m:s:u", t,
|
|
node->name);
|
|
}
|
|
prev_node = node;
|
|
|
|
if (node->format_trace)
|
|
s = format (s, "\n %U", node->format_trace, vm, node, h->data);
|
|
else
|
|
s = format (s, "\n %U", node->format_buffer, h->data);
|
|
|
|
h = vlib_trace_header_next (h);
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/* Root of all trace cli commands. */
|
|
VLIB_CLI_COMMAND (trace_cli_command,static) = {
|
|
.path = "trace",
|
|
.short_help = "Packet tracer commands",
|
|
};
|
|
|
|
int
|
|
trace_time_cmp (void *a1, void *a2)
|
|
{
|
|
vlib_trace_header_t **t1 = a1;
|
|
vlib_trace_header_t **t2 = a2;
|
|
i64 dt = t1[0]->time - t2[0]->time;
|
|
return dt < 0 ? -1 : (dt > 0 ? +1 : 0);
|
|
}
|
|
|
|
/*
|
|
* Return 1 if this packet passes the trace filter, or 0 otherwise
|
|
*/
|
|
u32
|
|
filter_accept (vlib_trace_main_t * tm, vlib_trace_header_t * h)
|
|
{
|
|
vlib_trace_header_t *e = vec_end (h);
|
|
|
|
if (tm->filter_flag == 0)
|
|
return 1;
|
|
|
|
/*
|
|
* When capturing a post-mortem dispatch trace,
|
|
* toss all existing traces once per dispatch cycle.
|
|
* So we can trace 4 billion pkts without running out of
|
|
* memory...
|
|
*/
|
|
if (tm->filter_flag == FILTER_FLAG_POST_MORTEM)
|
|
return 0;
|
|
|
|
if (tm->filter_flag == FILTER_FLAG_INCLUDE)
|
|
{
|
|
while (h < e)
|
|
{
|
|
if (h->node_index == tm->filter_node_index)
|
|
return 1;
|
|
h = vlib_trace_header_next (h);
|
|
}
|
|
return 0;
|
|
}
|
|
else /* FILTER_FLAG_EXCLUDE */
|
|
{
|
|
while (h < e)
|
|
{
|
|
if (h->node_index == tm->filter_node_index)
|
|
return 0;
|
|
h = vlib_trace_header_next (h);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Remove traces from the trace buffer pool that don't pass the filter
|
|
*/
|
|
void
|
|
trace_apply_filter (vlib_main_t * vm)
|
|
{
|
|
vlib_trace_main_t *tm = &vm->trace_main;
|
|
vlib_trace_header_t **h;
|
|
vlib_trace_header_t ***traces_to_remove = 0;
|
|
u32 index;
|
|
u32 trace_index;
|
|
u32 n_accepted;
|
|
|
|
u32 accept;
|
|
|
|
if (tm->filter_flag == FILTER_FLAG_NONE)
|
|
return;
|
|
|
|
/*
|
|
* Ideally we would retain the first N traces that pass the filter instead
|
|
* of any N traces.
|
|
*/
|
|
n_accepted = 0;
|
|
pool_foreach (h, tm->trace_buffer_pool)
|
|
{
|
|
accept = filter_accept(tm, h[0]);
|
|
|
|
if ((n_accepted == tm->filter_count) || !accept)
|
|
vec_add1 (traces_to_remove, h);
|
|
else
|
|
n_accepted++;
|
|
}
|
|
|
|
/* remove all traces that we don't want to keep */
|
|
for (index = 0; index < vec_len (traces_to_remove); index++)
|
|
{
|
|
trace_index = traces_to_remove[index] - tm->trace_buffer_pool;
|
|
vec_set_len (tm->trace_buffer_pool[trace_index], 0);
|
|
pool_put_index (tm->trace_buffer_pool, trace_index);
|
|
}
|
|
|
|
vec_free (traces_to_remove);
|
|
}
|
|
|
|
static clib_error_t *
|
|
cli_show_trace_buffer (vlib_main_t * vm,
|
|
unformat_input_t * input, vlib_cli_command_t * cmd)
|
|
{
|
|
vlib_trace_main_t *tm;
|
|
vlib_trace_header_t **h, **traces;
|
|
u32 i, index = 0;
|
|
char *fmt;
|
|
u8 *s = 0;
|
|
u32 max;
|
|
|
|
/*
|
|
* By default display only this many traces. To display more, explicitly
|
|
* specify a max. This prevents unexpectedly huge outputs.
|
|
*/
|
|
max = 50;
|
|
while (unformat_check_input (input) != (uword) UNFORMAT_END_OF_INPUT)
|
|
{
|
|
if (unformat (input, "max %d", &max))
|
|
;
|
|
else
|
|
return clib_error_create ("expected 'max COUNT', got `%U'",
|
|
format_unformat_error, input);
|
|
}
|
|
|
|
|
|
/* Get active traces from pool. */
|
|
|
|
foreach_vlib_main ()
|
|
{
|
|
fmt = "------------------- Start of thread %d %s -------------------\n";
|
|
s = format (s, fmt, index, vlib_worker_threads[index].name);
|
|
|
|
tm = &this_vlib_main->trace_main;
|
|
|
|
trace_apply_filter (this_vlib_main);
|
|
|
|
traces = 0;
|
|
pool_foreach (h, tm->trace_buffer_pool)
|
|
{
|
|
vec_add1 (traces, h[0]);
|
|
}
|
|
|
|
if (vec_len (traces) == 0)
|
|
{
|
|
s = format (s, "No packets in trace buffer\n");
|
|
goto done;
|
|
}
|
|
|
|
/* Sort them by increasing time. */
|
|
vec_sort_with_function (traces, trace_time_cmp);
|
|
|
|
for (i = 0; i < vec_len (traces); i++)
|
|
{
|
|
if (i == max)
|
|
{
|
|
char *warn = "Limiting display to %d packets."
|
|
" To display more specify max.";
|
|
vlib_cli_output (vm, warn, max);
|
|
s = format (s, warn, max);
|
|
goto done;
|
|
}
|
|
|
|
s = format (s, "Packet %d\n%U\n\n", i + 1, format_vlib_trace, vm,
|
|
traces[i]);
|
|
}
|
|
|
|
done:
|
|
vec_free (traces);
|
|
|
|
index++;
|
|
}
|
|
|
|
vlib_cli_output (vm, "%v", s);
|
|
vec_free (s);
|
|
return 0;
|
|
}
|
|
|
|
VLIB_CLI_COMMAND (show_trace_cli,static) = {
|
|
.path = "show trace",
|
|
.short_help = "Show trace buffer [max COUNT]",
|
|
.function = cli_show_trace_buffer,
|
|
};
|
|
|
|
int vlib_enable_disable_pkt_trace_filter (int enable) __attribute__ ((weak));
|
|
|
|
int
|
|
vlib_enable_disable_pkt_trace_filter (int enable)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
vlib_trace_stop_and_clear (void)
|
|
{
|
|
vlib_enable_disable_pkt_trace_filter (0); /* disble tracing */
|
|
clear_trace_buffer ();
|
|
}
|
|
|
|
|
|
void
|
|
trace_update_capture_options (u32 add, u32 node_index, u32 filter, u8 verbose)
|
|
{
|
|
vlib_trace_main_t *tm;
|
|
vlib_trace_node_t *tn;
|
|
|
|
if (add == ~0)
|
|
add = 50;
|
|
|
|
foreach_vlib_main ()
|
|
{
|
|
tm = &this_vlib_main->trace_main;
|
|
tm->verbose = verbose;
|
|
vec_validate (tm->nodes, node_index);
|
|
tn = tm->nodes + node_index;
|
|
|
|
/*
|
|
* Adding 0 makes no real sense, and there wa no other way
|
|
* to explicilty zero-out the limits and count, so make
|
|
* an "add 0" request really be "set to 0".
|
|
*/
|
|
if (add == 0)
|
|
tn->limit = tn->count = 0;
|
|
else
|
|
tn->limit += add;
|
|
}
|
|
|
|
foreach_vlib_main ()
|
|
{
|
|
tm = &this_vlib_main->trace_main;
|
|
tm->trace_enable = 1;
|
|
}
|
|
|
|
vlib_enable_disable_pkt_trace_filter (! !filter);
|
|
}
|
|
|
|
static clib_error_t *
|
|
cli_add_trace_buffer (vlib_main_t * vm,
|
|
unformat_input_t * input, vlib_cli_command_t * cmd)
|
|
{
|
|
unformat_input_t _line_input, *line_input = &_line_input;
|
|
vlib_node_t *node;
|
|
u32 node_index, add;
|
|
u8 verbose = 0;
|
|
int filter = 0;
|
|
clib_error_t *error = 0;
|
|
|
|
if (!unformat_user (input, unformat_line_input, line_input))
|
|
return 0;
|
|
|
|
if (vnet_trace_placeholder == 0)
|
|
vec_validate_aligned (vnet_trace_placeholder, 2048,
|
|
CLIB_CACHE_LINE_BYTES);
|
|
|
|
while (unformat_check_input (line_input) != (uword) UNFORMAT_END_OF_INPUT)
|
|
{
|
|
if (unformat (line_input, "%U %d",
|
|
unformat_vlib_node, vm, &node_index, &add))
|
|
;
|
|
else if (unformat (line_input, "verbose"))
|
|
verbose = 1;
|
|
else if (unformat (line_input, "filter"))
|
|
filter = 1;
|
|
else
|
|
{
|
|
error = clib_error_create ("expected NODE COUNT, got `%U'",
|
|
format_unformat_error, line_input);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
node = vlib_get_node (vm, node_index);
|
|
|
|
if ((node->flags & VLIB_NODE_FLAG_TRACE_SUPPORTED) == 0)
|
|
{
|
|
error = clib_error_create ("node '%U' doesn't support per-node "
|
|
"tracing. There may be another way to "
|
|
"initiate trace on this node.",
|
|
format_vlib_node_name, vm, node_index);
|
|
goto done;
|
|
}
|
|
|
|
trace_update_capture_options (add, node_index, filter, verbose);
|
|
|
|
done:
|
|
unformat_free (line_input);
|
|
|
|
return error;
|
|
}
|
|
|
|
VLIB_CLI_COMMAND (add_trace_cli,static) = {
|
|
.path = "trace add",
|
|
.short_help = "trace add <input-graph-node> <add'l-pkts-for-node-> [filter] [verbose]",
|
|
.function = cli_add_trace_buffer,
|
|
};
|
|
|
|
/*
|
|
* Configure a filter for packet traces.
|
|
*
|
|
* This supplements the packet trace feature so that only packets matching
|
|
* the filter are included in the trace. Currently the only filter is to
|
|
* keep packets that include a certain node in the trace or exclude a certain
|
|
* node in the trace.
|
|
*
|
|
* The count of traced packets in the "trace add" command is still used to
|
|
* create a certain number of traces. The "trace filter" command specifies
|
|
* how many of those packets should be retained in the trace.
|
|
*
|
|
* For example, 1Mpps of traffic is arriving and one of those packets is being
|
|
* dropped. To capture the trace for only that dropped packet, you can do:
|
|
* trace filter include error-drop 1
|
|
* trace add dpdk-input 1000000
|
|
* <wait one second>
|
|
* show trace
|
|
*
|
|
* Note that the filter could be implemented by capturing all traces and just
|
|
* reducing traces displayed by the "show trace" function. But that would
|
|
* require a lot of memory for storing the traces, making that infeasible.
|
|
*
|
|
* To remove traces from the trace pool that do not include a certain node
|
|
* requires that the trace be "complete" before applying the filter. To
|
|
* accomplish this, the trace pool is filtered upon each iteraction of the
|
|
* main vlib loop. Doing so keeps the number of allocated traces down to a
|
|
* reasonably low number. This requires that tracing for a buffer is not
|
|
* performed after the vlib main loop interation completes. i.e. you can't
|
|
* save away a buffer temporarily then inject it back into the graph and
|
|
* expect that the trace_index is still valid (such as a traffic manager might
|
|
* do). A new trace buffer should be allocated for those types of packets.
|
|
*
|
|
* The filter can be extended to support multiple nodes and other match
|
|
* criteria (e.g. input sw_if_index, mac address) but for now just checks if
|
|
* a specified node is in the trace or not in the trace.
|
|
*/
|
|
|
|
void
|
|
trace_filter_set (u32 node_index, u32 flag, u32 count)
|
|
{
|
|
foreach_vlib_main ()
|
|
{
|
|
vlib_trace_main_t *tm;
|
|
|
|
tm = &this_vlib_main->trace_main;
|
|
tm->filter_node_index = node_index;
|
|
tm->filter_flag = flag;
|
|
tm->filter_count = count;
|
|
|
|
/*
|
|
* Clear the trace limits to stop any in-progress tracing
|
|
* Prevents runaway trace allocations when the filter changes
|
|
* (or is removed)
|
|
*/
|
|
vec_free (tm->nodes);
|
|
}
|
|
}
|
|
|
|
|
|
static clib_error_t *
|
|
cli_filter_trace (vlib_main_t * vm,
|
|
unformat_input_t * input, vlib_cli_command_t * cmd)
|
|
{
|
|
u32 filter_node_index;
|
|
u32 filter_flag;
|
|
u32 filter_count;
|
|
|
|
if (unformat (input, "include %U %d",
|
|
unformat_vlib_node, vm, &filter_node_index, &filter_count))
|
|
{
|
|
filter_flag = FILTER_FLAG_INCLUDE;
|
|
}
|
|
else if (unformat (input, "exclude %U %d",
|
|
unformat_vlib_node, vm, &filter_node_index,
|
|
&filter_count))
|
|
{
|
|
filter_flag = FILTER_FLAG_EXCLUDE;
|
|
}
|
|
else if (unformat (input, "none"))
|
|
{
|
|
filter_flag = FILTER_FLAG_NONE;
|
|
filter_node_index = 0;
|
|
filter_count = 0;
|
|
}
|
|
else
|
|
return
|
|
clib_error_create
|
|
("expected 'include NODE COUNT' or 'exclude NODE COUNT' or 'none', got `%U'",
|
|
format_unformat_error, input);
|
|
|
|
trace_filter_set (filter_node_index, filter_flag, filter_count);
|
|
|
|
return 0;
|
|
}
|
|
|
|
VLIB_CLI_COMMAND (filter_trace_cli,static) = {
|
|
.path = "trace filter",
|
|
.short_help = "trace filter none | [include|exclude] NODE COUNT",
|
|
.function = cli_filter_trace,
|
|
};
|
|
|
|
static clib_error_t *
|
|
cli_clear_trace_buffer (vlib_main_t * vm,
|
|
unformat_input_t * input, vlib_cli_command_t * cmd)
|
|
{
|
|
vlib_trace_stop_and_clear ();
|
|
return 0;
|
|
}
|
|
|
|
VLIB_CLI_COMMAND (clear_trace_cli,static) = {
|
|
.path = "clear trace",
|
|
.short_help = "Clear trace buffer and free memory",
|
|
.function = cli_clear_trace_buffer,
|
|
};
|
|
|
|
/* Placeholder function to get us linked in. */
|
|
void
|
|
vlib_trace_cli_reference (void)
|
|
{
|
|
}
|
|
|
|
void *
|
|
vlib_add_trace (vlib_main_t * vm,
|
|
vlib_node_runtime_t * r, vlib_buffer_t * b, u32 n_data_bytes)
|
|
{
|
|
return vlib_add_trace_inline (vm, r, b, n_data_bytes);
|
|
}
|
|
|
|
vlib_is_packet_traced_fn_t *
|
|
vlib_is_packet_traced_function_from_name (const char *name)
|
|
{
|
|
vlib_trace_filter_function_registration_t *reg =
|
|
vlib_trace_filter_main.trace_filter_registration;
|
|
while (reg)
|
|
{
|
|
if (clib_strcmp (reg->name, name) == 0)
|
|
break;
|
|
reg = reg->next;
|
|
}
|
|
if (!reg)
|
|
return 0;
|
|
return reg->function;
|
|
}
|
|
|
|
vlib_is_packet_traced_fn_t *
|
|
vlib_is_packet_traced_default_function ()
|
|
{
|
|
vlib_trace_filter_function_registration_t *reg =
|
|
vlib_trace_filter_main.trace_filter_registration;
|
|
vlib_trace_filter_function_registration_t *tmp_reg = reg;
|
|
while (reg)
|
|
{
|
|
if (reg->priority > tmp_reg->priority)
|
|
tmp_reg = reg;
|
|
reg = reg->next;
|
|
}
|
|
return tmp_reg->function;
|
|
}
|
|
|
|
static clib_error_t *
|
|
vlib_trace_filter_function_init (vlib_main_t *vm)
|
|
{
|
|
vlib_is_packet_traced_fn_t *default_fn =
|
|
vlib_is_packet_traced_default_function ();
|
|
foreach_vlib_main ()
|
|
{
|
|
vlib_trace_main_t *tm = &this_vlib_main->trace_main;
|
|
tm->current_trace_filter_function = default_fn;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
vlib_trace_filter_main_t vlib_trace_filter_main;
|
|
|
|
VLIB_INIT_FUNCTION (vlib_trace_filter_function_init);
|
|
|
|
static clib_error_t *
|
|
show_trace_filter_function (vlib_main_t *vm, unformat_input_t *input,
|
|
vlib_cli_command_t *cmd)
|
|
{
|
|
vlib_trace_filter_main_t *tfm = &vlib_trace_filter_main;
|
|
vlib_trace_main_t *tm = &vm->trace_main;
|
|
vlib_is_packet_traced_fn_t *current_trace_filter_fn =
|
|
tm->current_trace_filter_function;
|
|
vlib_trace_filter_function_registration_t *reg =
|
|
tfm->trace_filter_registration;
|
|
|
|
while (reg)
|
|
{
|
|
vlib_cli_output (vm, "%sname:%s description: %s priority: %u",
|
|
reg->function == current_trace_filter_fn ? "(*) " : "",
|
|
reg->name, reg->description, reg->priority);
|
|
reg = reg->next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
VLIB_CLI_COMMAND (show_trace_filter_function_cli, static) = {
|
|
.path = "show trace filter function",
|
|
.short_help = "show trace filter function",
|
|
.function = show_trace_filter_function,
|
|
};
|
|
|
|
uword
|
|
unformat_vlib_trace_filter_function (unformat_input_t *input, va_list *args)
|
|
{
|
|
vlib_is_packet_traced_fn_t **res =
|
|
va_arg (*args, vlib_is_packet_traced_fn_t **);
|
|
vlib_trace_filter_main_t *tfm = &vlib_trace_filter_main;
|
|
|
|
vlib_trace_filter_function_registration_t *reg =
|
|
tfm->trace_filter_registration;
|
|
while (reg)
|
|
{
|
|
if (unformat (input, reg->name))
|
|
{
|
|
*res = reg->function;
|
|
return 1;
|
|
}
|
|
reg = reg->next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
vlib_set_trace_filter_function (vlib_is_packet_traced_fn_t *x)
|
|
{
|
|
foreach_vlib_main ()
|
|
{
|
|
this_vlib_main->trace_main.current_trace_filter_function = x;
|
|
}
|
|
}
|
|
|
|
static clib_error_t *
|
|
set_trace_filter_function (vlib_main_t *vm, unformat_input_t *input,
|
|
vlib_cli_command_t *cmd)
|
|
{
|
|
unformat_input_t _line_input, *line_input = &_line_input;
|
|
vlib_is_packet_traced_fn_t *res = 0;
|
|
clib_error_t *error = 0;
|
|
|
|
if (!unformat_user (input, unformat_line_input, line_input))
|
|
return 0;
|
|
|
|
while (unformat_check_input (line_input) != (uword) UNFORMAT_END_OF_INPUT)
|
|
{
|
|
if (unformat (line_input, "%U", unformat_vlib_trace_filter_function,
|
|
&res))
|
|
;
|
|
else
|
|
{
|
|
error = clib_error_create (
|
|
"expected valid trace filter function, got `%U'",
|
|
format_unformat_error, line_input);
|
|
goto done;
|
|
}
|
|
}
|
|
vlib_set_trace_filter_function (res);
|
|
|
|
done:
|
|
unformat_free (line_input);
|
|
|
|
return error;
|
|
}
|
|
|
|
VLIB_CLI_COMMAND (set_trace_filter_function_cli, static) = {
|
|
.path = "set trace filter function",
|
|
.short_help = "set trace filter function <func_name>",
|
|
.function = set_trace_filter_function,
|
|
};
|
|
/*
|
|
* fd.io coding-style-patch-verification: ON
|
|
*
|
|
* Local Variables:
|
|
* eval: (c-set-style "gnu")
|
|
* End:
|
|
*/
|