feature: Add packet trace API

Also spiffed up the vpp_api_test plugin loader so it executes
VLIB_INIT_FUNCTIONs and VLIB_API_INIT_FUNCTIONs.

Type: feature

Change-Id: Id9a4f455d73738c41bcfea220df2112bb9679681
Signed-off-by: Jon Loeliger <jdl@netgate.com>
Signed-off-by: Ole Troan <ot@cisco.com>
Signed-off-by: Dave Barach <dave@barachs.net>
This commit is contained in:
Jon Loeliger
2020-05-11 08:43:51 -05:00
committed by Dave Barach
parent d20bc1d30a
commit c0b195450b
14 changed files with 1240 additions and 47 deletions
+4
View File
@@ -14,13 +14,17 @@
add_vpp_plugin(tracedump
SOURCES
graph_api.c
graph_cli.c
tracedump.c
tracedump.h
API_FILES
graph.api
tracedump.api
API_TEST_SOURCES
graph_test.c
tracedump_test.c
)
+95
View File
@@ -0,0 +1,95 @@
/* Hey Emacs use -*- mode: C -*- */
/*
* Copyright 2020 Rubicon Communications, LLC.
*
* 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.
*/
option version = "1.0.0";
enum node_flag : u32
{
NODE_FLAG_FRAME_NO_FREE_AFTER_DISPATCH = 0x0001,
NODE_FLAG_IS_OUTPUT = 0x0002,
NODE_FLAG_IS_DROP = 0x0004,
NODE_FLAG_IS_PUNT = 0x0008,
NODE_FLAG_IS_HANDOFF = 0x0010,
NODE_FLAG_TRACE = 0x0020,
NODE_FLAG_SWITCH_FROM_INTERRUPT_TO_POLLING_MODE=0x0040,
NODE_FLAG_SWITCH_FROM_POLLING_TO_INTERRUPT_MODE=0x0080,
NODE_FLAG_TRACE_SUPPORTED = 0x0100,
};
service {
rpc graph_node_get returns graph_node_get_reply
stream graph_node_details;
};
/** \brief graph_node_get - Get nodes of the packet processing graph
In order:
if index != ~0, dump node with given index
if index == ~0 and name[0] != 0, dump named node
if index == ~0 and name[0] == 0 and flag != 0, dump flagged nodes
otherwise when no selection criteria given, dump all nodes.
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
@param cursor - Starting iterator value, ~0 for initial request
@param index - index of a specific node, or ~0 for all
@param name - name of a specific node, or 0 for all
@param flags - NODE_FLAG_* values
@param flags - If true, dump details will contain next nodes out-arcs
*/
define graph_node_get
{
u32 client_index;
u32 context;
u32 cursor;
u32 index;
string name[64]; /* GRAPH_NODE_LEN */
vl_api_node_flag_t flags; /* NODE_FLAG_* bits */
bool want_arcs; /* Include node out-arcs? */
option vat_help = "graph_node_dump [start <cursor>] [node_index <index>]|[node_name <name>]|[flags]";
};
define graph_node_get_reply
{
u32 context;
i32 retval;
u32 cursor;
};
/** \brief Details for each graph node
@param index - index of the node
@param name - name of the node
@param flags - NODE_FLAG_* values
@param n_arcs - If requested, the number of out-arcs to other nodes
@param arcs - If requested, the set of out-arc next-node-indices
*/
define graph_node_details
{
u32 context;
u32 index;
string name[64]; /* GRAPH_NODE_LEN */
vl_api_node_flag_t flags;
u32 n_arcs;
u32 arcs_out[n_arcs];
};
/*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/
+35
View File
@@ -0,0 +1,35 @@
/* Hey Emacs use -*- mode: C -*- */
/*
* Copyright 2020 Rubicon Communications, LLC.
*
* 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.
*/
#define GRAPH_NODE_NAME_LEN 64
typedef struct
{
u16 msg_id_base;
vlib_node_t **sorted_node_vec;
} graph_main_t;
extern graph_main_t graph_main;
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/
+270
View File
@@ -0,0 +1,270 @@
/* Hey Emacs use -*- mode: C -*- */
/*
* Copyright 2020 Rubicon Communications, LLC.
*
* 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.
*/
#include <vnet/vnet.h>
#include <vlibmemory/api.h>
#include <tracedump/graph.api_enum.h>
#include <tracedump/graph.api_types.h>
#define REPLY_MSG_ID_BASE gmp->msg_id_base
#include <vlibapi/api_helper_macros.h>
#include <tracedump/graph.h>
graph_main_t graph_main;
#define MIN(x,y) (((x) < (y)) ? (x) : (y))
/*
* If ever the graph or set of nodes changes, this cache of
* nodes in sorted order should be invalidated.
*/
void
graph_node_invalid_cache (void)
{
graph_main_t *gmp = &graph_main;
vec_free (gmp->sorted_node_vec);
}
static clib_error_t *
graph_node_cache_reaper (u32 client_index)
{
graph_node_invalid_cache ();
return 0;
}
VL_MSG_API_REAPER_FUNCTION (graph_node_cache_reaper);
static void
send_graph_node_reply (vl_api_registration_t * rp,
u32 context, u32 retval, u32 cursor)
{
graph_main_t *gmp = &graph_main;
vl_api_graph_node_get_reply_t *rmp;
rmp = vl_msg_api_alloc (sizeof (*rmp));
rmp->_vl_msg_id = htons (VL_API_GRAPH_NODE_GET_REPLY + gmp->msg_id_base);
rmp->context = context;
rmp->retval = clib_host_to_net_u32 (retval);
rmp->cursor = htonl (cursor);
vl_api_send_msg (rp, (u8 *) rmp);
}
static void
send_graph_node_details (vlib_node_main_t * nm,
vl_api_registration_t * reg,
u32 context, vlib_node_t * n, bool want_arcs)
{
graph_main_t *gmp = &graph_main;
vl_api_graph_node_details_t *mp;
u32 msg_size;
msg_size = sizeof (*mp);
if (want_arcs)
msg_size += vec_len (n->next_nodes) * sizeof (*n->next_nodes);
mp = vl_msg_api_alloc (msg_size);
if (!mp)
return;
clib_memset (mp, 0, msg_size);
mp->_vl_msg_id = htons (VL_API_GRAPH_NODE_DETAILS + gmp->msg_id_base);
mp->context = context;
mp->index = htonl (n->index);
mp->flags = htonl (n->flags);
clib_strncpy ((char *) mp->name, (char *) n->name,
MIN (sizeof (mp->name) - 1, vec_len (n->name)));
if (want_arcs)
{
int i;
mp->n_arcs = htonl (vec_len (n->next_nodes));
for (i = 0; i < vec_len (n->next_nodes); ++i)
{
mp->arcs_out[i] = htonl (n->next_nodes[i]);
}
}
vl_api_send_msg (reg, (u8 *) mp);
}
static int
node_cmp (void *a1, void *a2)
{
vlib_node_t **n1 = a1;
vlib_node_t **n2 = a2;
return vec_cmp (n1[0]->name, n2[0]->name);
}
/*
* When cursor == ~0, it begins a request:
* if index != ~0, dump node with given index
* if index == ~0 and name[0] != 0, dump node with given name
* if index == ~0 and name[0] == 0, and flag != 0, dump flagged nodes
* else
* index == ~0 and name[0] == 0 and flag == 0, so dump all nodes.
*
* When cursor != ~0, it is the middle of a request:
* The same (index, name, and flag) parameters are assumed,
* The next results resume from cursor.
*/
static void
vl_api_graph_node_get_t_handler (vl_api_graph_node_get_t * mp)
{
vl_api_registration_t *rp;
rp = vl_api_client_index_to_registration (mp->client_index);
if (!rp)
return;
vlib_main_t *vm = vlib_get_main ();
vlib_node_main_t *nm = &vm->node_main;
graph_main_t *gmp = &graph_main;
vlib_node_t *n;
u32 cursor;
u32 node_index;
bool want_arcs;
want_arcs = ! !mp->want_arcs;
cursor = ntohl (mp->cursor);
n = 0;
/*
* Return details on a specific node by index?
*/
node_index = ntohl (mp->index);
if (cursor == ~0 && node_index != ~0)
{
if (node_index < vec_len (nm->nodes))
n = vlib_get_node (vm, node_index);
if (!n)
{
send_graph_node_reply (rp, mp->context,
VNET_API_ERROR_NO_SUCH_ENTRY, ~0);
return;
}
send_graph_node_details (nm, rp, mp->context, n, want_arcs);
send_graph_node_reply (rp, mp->context, 0, ~0);
return;
}
/*
* Return details on a specific node by name?
*/
if (cursor == ~0 && mp->name[0] != 0)
{
n = vlib_get_node_by_name (vm, (u8 *) mp->name);
if (!n)
{
send_graph_node_reply (rp, mp->context,
VNET_API_ERROR_NO_SUCH_ENTRY, ~0);
return;
}
send_graph_node_details (nm, rp, mp->context, n, want_arcs);
send_graph_node_reply (rp, mp->context, 0, ~0);
return;
}
/*
* Inspect all nodes, but potentially limit them by flag selection.
* As iteration my need to occur over multiple streaming API calls,
* determine the API client index and cache a sorted list of nodes.
*
* First time through, make a sorted node list and cache it.
*/
vlib_node_t **nodes = gmp->sorted_node_vec;
if (!nodes)
{
nodes = vec_dup (nm->nodes);
vec_sort_with_function (nodes, node_cmp);
gmp->sorted_node_vec = nodes;
}
u32 flags = ntohl (mp->flags);
u32 first_index = (cursor == ~0) ? 0 : cursor;
/* Don't overflow the existing queue space. */
svm_queue_t *q = rp->vl_input_queue;
u32 queue_slots_available = q->maxsize - q->cursize;
int chunk = (queue_slots_available > 0) ? queue_slots_available - 1 : 0;
u32 i;
for (i = first_index; i < vec_len (nodes); ++i)
{
if (chunk-- == 0)
{
/*
* Pick up again at cursor = i.
*/
send_graph_node_reply (rp, mp->context, VNET_API_ERROR_EAGAIN, i);
return;
}
n = nodes[i];
if (flags == 0 || (n->flags & flags))
{
send_graph_node_details (nm, rp, mp->context, n, want_arcs);
}
}
send_graph_node_reply (rp, mp->context, 0, ~0);
}
#include <vnet/format_fns.h>
#include <tracedump/graph.api.c>
static clib_error_t *
graph_api_hookup (vlib_main_t * vm)
{
api_main_t *am = vlibapi_get_main ();
graph_main_t *gmp = &graph_main;
gmp->msg_id_base = setup_message_id_table ();
am->is_mp_safe[gmp->msg_id_base + VL_API_GRAPH_NODE_GET] = 1;
am->is_autoendian[gmp->msg_id_base + VL_API_GRAPH_NODE_DETAILS] = 1;
return 0;
}
VLIB_INIT_FUNCTION (graph_api_hookup);
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/
+148
View File
@@ -0,0 +1,148 @@
/* Hey Emacs use -*- mode: C -*- */
/*
* Copyright 2020 Rubicon Communications, LLC.
*
* 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.
*/
#include <sys/socket.h>
#include <linux/if.h>
#include <vnet/vnet.h>
#include <vnet/plugin/plugin.h>
#include <vlibapi/api.h>
#include <vlibmemory/api.h>
#include <vpp/app/version.h>
#include <vnet/format_fns.h>
#include <tracedump/graph.h>
#include <tracedump/graph.api_enum.h>
#include <tracedump/graph.api_types.h>
static void graph_node_print (vlib_main_t *vm, vlib_node_t *n, bool want_arcs)
{
vlib_cli_output (vm, "Node (%4d): %v, Flags: 0x%x\n",
n->index, n->name, n->flags);
if (!want_arcs)
return;
int i;
int n_arcs = vec_len (n->next_nodes);
for (i = 0; i < n_arcs; ++i)
{
vlib_cli_output (vm, " next: %d\n", n->next_nodes[i]);
}
}
static int
node_cmp (void *a1, void *a2)
{
vlib_node_t **n1 = a1;
vlib_node_t **n2 = a2;
return vec_cmp (n1[0]->name, n2[0]->name);
}
static clib_error_t *
graph_node_show_cmd (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd)
{
vlib_node_main_t *nm = &vm->node_main;
vlib_node_t *n;
u32 index;
u8 *name;
u32 flags;
bool want_arcs;
index = ~0;
name = 0;
flags = 0;
want_arcs = false;
n = 0;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (input, "node %d", &index))
n = vlib_get_node (vm, index);
else if (unformat (input, "node %v", &name))
n = vlib_get_node_by_name (vm, name);
else if (unformat (input, "want_arcs"))
want_arcs = true;
else if (unformat (input, "trace_supported"))
flags |= NODE_FLAG_TRACE_SUPPORTED;
else if (unformat (input, "input"))
flags |= NODE_FLAG_TRACE_SUPPORTED;
else if (unformat (input, "drop"))
flags |= NODE_FLAG_IS_DROP;
else if (unformat (input, "output"))
flags |= NODE_FLAG_IS_OUTPUT;
else if (unformat (input, "punt"))
flags |= NODE_FLAG_IS_PUNT;
else if (unformat (input, "handoff"))
flags |= NODE_FLAG_IS_HANDOFF;
else if (unformat (input, "no_free"))
flags |= NODE_FLAG_FRAME_NO_FREE_AFTER_DISPATCH;
else if (unformat (input, "polling"))
flags |= NODE_FLAG_SWITCH_FROM_INTERRUPT_TO_POLLING_MODE;
else if (unformat (input, "interrupt"))
flags |= NODE_FLAG_SWITCH_FROM_POLLING_TO_INTERRUPT_MODE;
else
return clib_error_return (0, "unknown input '%U'",
format_unformat_error, input);
}
/*
* Just one node requested? Ignore flags.
*/
if (n) {
graph_node_print (vm, n, want_arcs);
return 0;
}
vlib_node_t **nodes = vec_dup (nm->nodes);
uword i;
vec_sort_with_function (nodes, node_cmp);
for (i = 0; i < vec_len (nodes); ++i)
{
if (flags == 0 || (flags & nodes[i]->flags))
{
graph_node_print (vm, nodes[i], want_arcs);
}
}
vec_free (nodes);
return 0;
}
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (graph_node_show_command, static) = {
.path = "show graph",
.short_help = "show graph [node <index>|<name>] [want_arcs] [input|trace_supported] [drop] [output] [punt] [handoff] [no_free] [polling] [interrupt]",
.function = graph_node_show_cmd,
};
/* *INDENT-ON* */
/*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/
+185
View File
@@ -0,0 +1,185 @@
/*
* Copyright (c) 2020 cisco
* 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.
*/
#include <vat/vat.h>
#include <vlibapi/api.h>
#include <vlibmemory/api.h>
#include <vppinfra/error.h>
#include <vppinfra/time_range.h>
#include <vnet/ethernet/ethernet.h>
#include <vpp-api/client/stat_client.h>
#define __plugin_msg_base graph_test_main.msg_id_base
#include <vlibapi/vat_helper_macros.h>
#include <vnet/format_fns.h>
#include <tracedump/graph.api_enum.h>
#include <tracedump/graph.api_types.h>
#include <vpp/api/vpe.api_types.h>
typedef struct
{
u16 msg_id_base;
u32 ping_id;
vat_main_t *vat_main;
} graph_test_main_t;
graph_test_main_t graph_test_main;
uword
api_unformat_node_index (unformat_input_t * input, va_list * args)
{
u32 *result = va_arg (*args, u32 *);
return unformat (input, "%u", result);
}
static void
vl_api_graph_node_get_reply_t_handler (vl_api_graph_node_get_reply_t * mp)
{
vat_main_t *vam = &vat_main;
clib_warning ("Next node index: %u\n", mp->cursor);
vam->result_ready = 1;
}
int
api_graph_node_get (vat_main_t * vam)
{
graph_test_main_t *gtm = &graph_test_main;
unformat_input_t *i = vam->input;
vl_api_graph_node_get_t *mp;
vl_api_control_ping_t *mp_ping;
u32 node_index;
char *node_name;
u32 flags;
bool want_arcs;
if (vam->json_output)
{
clib_warning ("JSON output not supported for graph_node_get");
return -99;
}
node_index = ~0;
node_name = 0;
flags = 0;
want_arcs = false;
while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
{
if (unformat (i, "node_index %u", &node_index))
;
else if (unformat (i, "node_name %s", &node_name))
;
else if (unformat (i, "want_arcs"))
want_arcs = true;
else if (unformat (i, "trace_supported"))
flags |= NODE_FLAG_TRACE_SUPPORTED;
else if (unformat (i, "input"))
flags |= NODE_FLAG_TRACE_SUPPORTED;
else if (unformat (i, "drop"))
flags |= NODE_FLAG_IS_DROP;
else if (unformat (i, "ouptput"))
flags |= NODE_FLAG_IS_OUTPUT;
else if (unformat (i, "punt"))
flags |= NODE_FLAG_IS_PUNT;
else if (unformat (i, "handoff"))
flags |= NODE_FLAG_IS_HANDOFF;
else if (unformat (i, "no_free"))
flags |= NODE_FLAG_FRAME_NO_FREE_AFTER_DISPATCH;
else if (unformat (i, "polling"))
flags |= NODE_FLAG_SWITCH_FROM_INTERRUPT_TO_POLLING_MODE;
else if (unformat (i, "interrupt"))
flags |= NODE_FLAG_SWITCH_FROM_POLLING_TO_INTERRUPT_MODE;
else
{
clib_warning ("Unknown input: %U\n", format_unformat_error, i);
return -99;
}
}
M (GRAPH_NODE_GET, mp);
mp->index = htonl (node_index);
mp->flags = htonl (flags);
mp->want_arcs = want_arcs;
if (node_name && node_name[0])
clib_strncpy ((char *) mp->name, node_name, sizeof (mp->name) - 1);
int ret = 0;
S (mp);
if (!gtm->ping_id)
gtm->ping_id =
vl_msg_api_get_msg_index ((u8 *) (VL_API_CONTROL_PING_CRC));
mp_ping = vl_msg_api_alloc_as_if_client (sizeof (*mp_ping));
mp_ping->_vl_msg_id = htons (gtm->ping_id);
mp_ping->client_index = vam->my_client_index;
S (mp_ping);
W (ret);
return ret;
}
void
vl_api_graph_node_details_t_handler (vl_api_graph_node_details_t * mp)
{
vat_main_t *vam = &vat_main;
u32 n_arcs;
int i;
fformat (vam->ofp,
"Node: %s Index:%d Flags:0x%x\n",
mp->name, ntohl (mp->index), ntohl (mp->flags));
n_arcs = ntohl (mp->n_arcs);
for (i = 0; i < n_arcs; ++i)
{
u32 node_index = ntohl (mp->arcs_out[i]);
fformat (vam->ofp, " next: %d\n", node_index);
}
}
void
vl_api_graph_node_details_t_handler_json (vl_api_graph_node_details_t * mp)
{
clib_error ("graph_node_details JSON not supported");
}
/* Override generated plugin register symbol */
#define vat_plugin_register graph_test_vat_plugin_register
#include <tracedump/graph.api_test.c>
static clib_error_t *
graph_api_hookup_shim (vlib_main_t * vm)
{
graph_test_vat_plugin_register (&vat_main);
return 0;
}
VLIB_API_INIT_FUNCTION (graph_api_hookup_shim);
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/
+68 -1
View File
@@ -1,3 +1,4 @@
/* Hey Emacs use -*- mode: C -*- */
/*
* tracedump.api - streaming packet trace dump API
*
@@ -23,10 +24,73 @@
* called through a shared memory interface.
*/
/* Version and type recitations */
option version = "0.1.0";
enum trace_filter_flag : u32
{
TRACE_FF_NONE = 0,
TRACE_FF_INCLUDE_NODE = 1,
TRACE_FF_EXCLUDE_NODE = 2,
TRACE_FF_INCLUDE_CLASSIFIER = 3,
TRACE_FF_EXCLUDE_CLASSIFIER = 4,
};
/** \brief trace_set_filters
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
@param flag - One of the trace_filter_flag values
@param node_index = The node-index to include/exclude
@param classifier_table_index = The include/exclude classifier table
@param count = The number of packets to include/exclude
*/
autoreply define trace_set_filters
{
u32 client_index;
u32 context;
vl_api_trace_filter_flag_t flag; /* TRACE_FF_* */
u32 count;
u32 node_index [default = 0xffffffff];
u32 classifier_table_index [default = 0xffffffff];
option vat_help = "trace_set_filters [none] | [(include_node|exclude_node) <node-index>] | [(include_classifier|exclude_classifier) <classifier-index>] [count <count>]";
};
/** \brief trace_capture_packets
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
@param node_index - graph input node whose packets are captured
@param max_packets - maximum number of packets to capture
@param use_filter - if true, apply filters to select/reject packets
@param verbose - if true, set verbose packet capture flag
@param pre_capture_clear - if true, clear buffer before capture begins
*/
autoreply define trace_capture_packets
{
u32 client_index;
u32 context;
u32 node_index;
u32 max_packets;
bool use_filter;
bool verbose;
bool pre_capture_clear;
option vat_help = "trace_capture_packets [node_index <index>] [max <max>] [pre_capture_clear] [use_filter] [verbose]";
};
/** \brief trace_clear_capture
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
*/
autoreply define trace_clear_capture
{
u32 client_index;
u32 context;
option vat_help = "trace_clear_capture";
};
service {
rpc trace_dump returns trace_dump_reply
stream trace_details;
@@ -48,6 +112,8 @@ define trace_dump {
/* Max number of replies per burst */
u32 max_records;
option vat_help = "trace_dump [thread_id <tid>] [position <pos>] [max <max>]";
};
define trace_dump_reply {
@@ -78,5 +144,6 @@ define trace_details {
/* Needed when set ends in the middle of a batch */
u8 done;
u32 packet_number;
string trace_data[];
};
+112 -2
View File
@@ -33,6 +33,109 @@
tracedump_main_t tracedump_main;
static void
vl_api_trace_set_filters_t_handler (vl_api_trace_set_filters_t * mp)
{
vlib_main_t *vm = vlib_get_main ();
tracedump_main_t *tdmp = &tracedump_main;
u32 node_index = clib_net_to_host_u32 (mp->node_index);
u32 flag = clib_net_to_host_u32 (mp->flag);
u32 count = clib_net_to_host_u32 (mp->count);
vl_api_trace_set_filters_reply_t *rmp;
int rv = 0;
if (flag == TRACE_FF_NONE)
{
count = node_index = 0;
}
else if (flag != TRACE_FF_INCLUDE_NODE && flag != TRACE_FF_EXCLUDE_NODE)
{
rv = VNET_API_ERROR_INVALID_VALUE;
goto done;
}
vlib_node_t *node;
node = vlib_get_node (vm, node_index);
if (!node)
{
rv = VNET_API_ERROR_NO_SUCH_NODE;
goto done;
}
trace_filter_set (node_index, flag, count);
done:
REPLY_MACRO (VL_API_TRACE_SET_FILTERS_REPLY);
}
static void
vl_api_trace_capture_packets_t_handler (vl_api_trace_capture_packets_t * mp)
{
vlib_main_t *vm = vlib_get_main ();
tracedump_main_t *tdmp = &tracedump_main;
u32 add = clib_net_to_host_u32 (mp->max_packets);
u32 node_index = clib_net_to_host_u32 (mp->node_index);
u8 filter = mp->use_filter;
u8 verbose = mp->verbose;
u8 pre_clear = mp->pre_capture_clear;
vl_api_trace_capture_packets_reply_t *rmp;
int rv = 0;
if (!vnet_trace_placeholder)
vec_validate_aligned (vnet_trace_placeholder, 2048,
CLIB_CACHE_LINE_BYTES);
vlib_node_t *node;
node = vlib_get_node (vm, node_index);
if (!node)
{
rv = VNET_API_ERROR_NO_SUCH_NODE;
goto done;
}
if ((node->flags & VLIB_NODE_FLAG_TRACE_SUPPORTED) == 0)
{
/* FIXME: Make a new, better error like "UNSUPPORTED_NODE_OPERATION"? */
rv = VNET_API_ERROR_NO_SUCH_NODE;
goto done;
}
if (filter)
{
if (vlib_enable_disable_pkt_trace_filter (1) < 0) /* enable */
{
/* FIXME: Make a new error like "UNSUPPORTED_NODE_OPERATION"? */
rv = VNET_API_ERROR_NO_SUCH_NODE;
goto done;
}
}
if (pre_clear)
vlib_trace_stop_and_clear ();
trace_update_capture_options (add, node_index, filter, verbose);
done:
REPLY_MACRO (VL_API_TRACE_CAPTURE_PACKETS_REPLY);
}
static void
vl_api_trace_clear_capture_t_handler (vl_api_trace_clear_capture_t * mp)
{
vl_api_trace_clear_capture_reply_t *rmp;
tracedump_main_t *tdmp = &tracedump_main;
vlib_trace_stop_and_clear ();
int rv = 0;
REPLY_MACRO (VL_API_TRACE_CLEAR_CAPTURE_REPLY);
}
static int
trace_cmp (void *a1, void *a2)
{
@@ -120,6 +223,13 @@ vl_api_trace_dump_t_handler (vl_api_trace_dump_t * mp)
iterator_position = clib_net_to_host_u32 (mp->position);
max_records = clib_net_to_host_u32 (mp->max_records);
/* Don't overflow the existing queue space. */
svm_queue_t *q = rp->vl_input_queue;
u32 queue_slots_available = q->maxsize - q->cursize;
int chunk = (queue_slots_available > 0) ? queue_slots_available - 1 : 0;
if (chunk < max_records)
max_records = chunk;
/* Need a fresh cache for this client? */
if (vec_len (client_trace_cache) == 0
&& (iterator_thread_id != ~0 || iterator_position != ~0))
@@ -168,8 +278,7 @@ vl_api_trace_dump_t_handler (vl_api_trace_dump_t * mp)
vec_reset_length (s);
s = format (s, "Packet %d\n%U\n\n", j + 1, format_vlib_trace,
&vlib_global_main, th[0]);
s = format (s, "%U", format_vlib_trace, &vlib_global_main, th[0]);
dmp = vl_msg_api_alloc (sizeof (*dmp) + vec_len (s));
dmp->_vl_msg_id =
@@ -178,6 +287,7 @@ vl_api_trace_dump_t_handler (vl_api_trace_dump_t * mp)
last_thread_id = dmp->thread_id = ntohl (i);
last_position = dmp->position = ntohl (j);
vl_api_vec_to_api_string (s, &dmp->trace_data);
dmp->packet_number = htonl (j);
dmp->more_threads = 0;
dmp->more_this_thread = 0;
+119 -1
View File
@@ -37,19 +37,122 @@ typedef struct
tracedump_test_main_t tracedump_test_main;
int
api_trace_set_filters (vat_main_t * vam)
{
unformat_input_t *i = vam->input;
vl_api_trace_set_filters_t *mp;
u32 flag;
u32 count;
u32 node_index;
u32 classifier;
flag = TRACE_FF_NONE;
count = 50;
node_index = ~0;
classifier = ~0;
while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
{
if (unformat (i, "none"))
flag = TRACE_FF_NONE;
else if (unformat (i, "include_node %u", &node_index))
flag = TRACE_FF_INCLUDE_NODE;
else if (unformat (i, "exclude_node %u", &node_index))
flag = TRACE_FF_EXCLUDE_NODE;
else if (unformat (i, "include_classifier %u", &classifier))
flag = TRACE_FF_INCLUDE_CLASSIFIER;
else if (unformat (i, "exclude_classifier %u", &classifier))
flag = TRACE_FF_EXCLUDE_CLASSIFIER;
else if (unformat (i, "count %u", &count))
;
else
{
clib_warning ("Unknown input: %U\n", format_unformat_error, i);
return -99;
}
}
M (TRACE_SET_FILTERS, mp);
mp->flag = htonl (flag);
mp->node_index = htonl (node_index);
mp->count = htonl (count);
mp->classifier_table_index = htonl (classifier);
int ret = 0;
S (mp);
W (ret);
return ret;
}
int
api_trace_capture_packets (vat_main_t * vam)
{
unformat_input_t *i = vam->input;
vl_api_trace_capture_packets_t *mp;
u32 node_index;
u32 max;
bool pre_capture_clear;
bool use_filter;
bool verbose;
node_index = ~0;
max = 50;
pre_capture_clear = use_filter = verbose = false;
while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
{
if (unformat (i, "node_index %u", &node_index))
;
else if (unformat (i, "max %u", &max))
;
else if (unformat (i, "pre_capture_clear"))
pre_capture_clear = false;
else if (unformat (i, "use_filter"))
use_filter = false;
else if (unformat (i, "verbose"))
verbose = false;
else
{
clib_warning ("Unknown input: %U\n", format_unformat_error, i);
return -99;
}
}
M (TRACE_CAPTURE_PACKETS, mp);
mp->node_index = htonl (node_index);
mp->max_packets = htonl (max);
mp->use_filter = use_filter;
mp->verbose = verbose;
mp->pre_capture_clear = pre_capture_clear;
int ret = 0;
S (mp);
W (ret);
return ret;
}
static void
vl_api_trace_details_t_handler (vl_api_trace_details_t * dmp)
{
u32 packet_number;
u32 thread_id, position;
thread_id = clib_net_to_host_u32 (dmp->thread_id);
position = clib_net_to_host_u32 (dmp->position);
packet_number = clib_net_to_host_u32 (dmp->packet_number);
fformat
(stdout,
"thread %d position %d more_this_thread %d more_threads %d done %d\n",
thread_id, position, (u32) dmp->more_this_thread,
(u32) dmp->more_threads, (u32) dmp->done);
fformat (stdout, " %U\n", vl_api_format_string, (&dmp->trace_data));
fformat (stdout, "Packet %d\n%U\n\n",
packet_number, vl_api_format_string, (&dmp->trace_data));
}
@@ -117,6 +220,21 @@ api_trace_dump (vat_main_t * vam)
return ret;
}
int
api_trace_clear_capture (vat_main_t * vam)
{
vl_api_trace_clear_capture_t *mp;
int ret;
M (TRACE_CLEAR_CAPTURE, mp);
S (mp);
W (ret);
return ret;
}
#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
#define vl_endianfun
#include <tracedump/tracedump.api.h>
+10 -4
View File
@@ -894,10 +894,16 @@ class VPPAPI(object):
continue
if d.endswith('_details'):
if d[:-8]+'_dump' not in msgs:
raise ValueError('{} missing dump message'
.format(d))
continue
if d[:-8]+'_get' in msgs:
if d[:-8]+'_get' in svcs:
continue
else:
raise ValueError('{} should be in a stream service'
.format(d[:-8]+'_get'))
if d[:-8]+'_dump' in msgs:
continue
raise ValueError('{} missing dump or get message'
.format(d))
if d in svcs:
continue
+75 -2
View File
@@ -42,15 +42,31 @@ connect_to_vpe (char *name)
return 0;
}
/* *INDENT-OFF* */
vlib_main_t vlib_global_main;
vlib_main_t **vlib_mains;
static struct
{
vec_header_t h;
vlib_main_t *vm;
} __attribute__ ((packed)) __bootstrap_vlib_main_vector
__attribute__ ((aligned (CLIB_CACHE_LINE_BYTES))) =
{
.h.len = 1,
.vm = &vlib_global_main,
};
/* *INDENT-ON* */
vlib_main_t **vlib_mains = &__bootstrap_vlib_main_vector.vm;
void
vlib_cli_output (struct vlib_main_t *vm, char *fmt, ...)
{
clib_warning ("BUG");
}
static u8 *
format_api_error (u8 * s, va_list * args)
{
@@ -338,6 +354,45 @@ load_features (void)
}
}
static inline clib_error_t *
call_init_exit_functions_internal (vlib_main_t * vm,
_vlib_init_function_list_elt_t ** headp,
int call_once, int do_sort)
{
clib_error_t *error = 0;
_vlib_init_function_list_elt_t *i;
#if 0
/* Not worth copying the topological sort code */
if (do_sort && (error = vlib_sort_init_exit_functions (headp)))
return (error);
#endif
i = *headp;
while (i)
{
if (call_once && !hash_get (vm->init_functions_called, i->f))
{
if (call_once)
hash_set1 (vm->init_functions_called, i->f);
error = i->f (vm);
if (error)
return error;
}
i = i->next_init_function;
}
return error;
}
clib_error_t *
vlib_call_init_exit_functions (vlib_main_t * vm,
_vlib_init_function_list_elt_t ** headp,
int call_once)
{
return call_init_exit_functions_internal (vm, headp, call_once,
1 /* do_sort */ );
}
int
main (int argc, char **argv)
{
@@ -351,6 +406,8 @@ main (int argc, char **argv)
u8 json_output = 0;
int i;
f64 timeout;
clib_error_t *error;
vlib_main_t *vm = &vlib_global_main;
clib_mem_init_thread_safe (0, 128 << 20);
@@ -447,6 +504,22 @@ main (int argc, char **argv)
vam->current_file = (u8 *) "plugin-init";
vat_plugin_init (vam);
/* Set up the init function hash table */
vm->init_functions_called = hash_create (0, 0);
/* Execute plugin init and api_init functions */
error = vlib_call_init_exit_functions
(vm, &vm->init_function_registrations, 1 /* call once */ );
if (error)
clib_error_report (error);
error = vlib_call_init_exit_functions
(vm, &vm->api_init_function_registrations, 1 /* call_once */ );
if (error)
clib_error_report (error);
for (i = 0; i < vec_len (input_files); i++)
{
vam->ifp = fopen ((char *) input_files[i], "r");
+84 -37
View File
@@ -122,6 +122,12 @@ clear_trace_buffer (void)
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))
@@ -175,8 +181,8 @@ VLIB_CLI_COMMAND (trace_cli_command,static) = {
};
/* *INDENT-ON* */
static int
trace_cmp (void *a1, void *a2)
int
trace_time_cmp (void *a1, void *a2)
{
vlib_trace_header_t **t1 = a1;
vlib_trace_header_t **t2 = a2;
@@ -316,7 +322,7 @@ cli_show_trace_buffer (vlib_main_t * vm,
}
/* Sort them by increasing time. */
vec_sort_with_function (traces, trace_cmp);
vec_sort_with_function (traces, trace_time_cmp);
for (i = 0; i < vec_len (traces); i++)
{
@@ -352,20 +358,63 @@ VLIB_CLI_COMMAND (show_trace_cli,static) = {
/* *INDENT-ON* */
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;
/* *INDENT-OFF* */
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;
}));
/* *INDENT-ON* */
}
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_trace_main_t *tm;
vlib_node_t *node;
vlib_trace_node_t *tn;
u32 node_index, add;
u8 verbose = 0;
int filter = 0;
@@ -415,17 +464,7 @@ cli_add_trace_buffer (vlib_main_t * vm,
}
}
/* *INDENT-OFF* */
foreach_vlib_main ((
{
tm = &this_vlib_main->trace_main;
tm->verbose = verbose;
vec_validate (tm->nodes, node_index);
tn = tm->nodes + node_index;
tn->limit += add;
tm->trace_enable = 1;
}));
/* *INDENT-ON* */
trace_update_capture_options (add, node_index, filter, verbose);
done:
unformat_free (line_input);
@@ -436,7 +475,7 @@ done:
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (add_trace_cli,static) = {
.path = "trace add",
.short_help = "Trace given number of packets",
.short_help = "trace add <input-graph-node> <add'l-pkts-for-node-> [filter] [verbose]",
.function = cli_add_trace_buffer,
};
/* *INDENT-ON* */
@@ -478,11 +517,35 @@ VLIB_CLI_COMMAND (add_trace_cli,static) = {
* 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)
{
/* *INDENT-OFF* */
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);
}));
/* *INDENT-ON* */
}
static clib_error_t *
cli_filter_trace (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd)
{
vlib_trace_main_t *tm = &vm->trace_main;
u32 filter_node_index;
u32 filter_flag;
u32 filter_count;
@@ -510,22 +573,7 @@ cli_filter_trace (vlib_main_t * vm,
("expected 'include NODE COUNT' or 'exclude NODE COUNT' or 'none', got `%U'",
format_unformat_error, input);
/* *INDENT-OFF* */
foreach_vlib_main (
({
tm = &this_vlib_main->trace_main;
tm->filter_node_index = filter_node_index;
tm->filter_flag = filter_flag;
tm->filter_count = filter_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);
}));
/* *INDENT-ON* */
trace_filter_set (filter_node_index, filter_flag, filter_count);
return 0;
}
@@ -533,7 +581,7 @@ cli_filter_trace (vlib_main_t * vm,
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (filter_trace_cli,static) = {
.path = "trace filter",
.short_help = "filter trace output - include NODE COUNT | exclude NODE COUNT | none",
.short_help = "trace filter none | [include|exclude] NODE COUNT",
.function = cli_filter_trace,
};
/* *INDENT-ON* */
@@ -542,8 +590,7 @@ static clib_error_t *
cli_clear_trace_buffer (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd)
{
vlib_enable_disable_pkt_trace_filter (0 /* enable */ );
clear_trace_buffer ();
vlib_trace_stop_and_clear ();
return 0;
}
+6
View File
@@ -113,6 +113,12 @@ typedef struct
format_function_t format_vlib_trace;
void trace_apply_filter (struct vlib_main_t *vm);
int trace_time_cmp (void *a1, void *a2);
void vlib_trace_stop_and_clear (void);
int vlib_enable_disable_pkt_trace_filter (int enable) __attribute__ ((weak));
void trace_update_capture_options (u32 add, u32 node_index,
u32 filter, u8 verbose);
void trace_filter_set (u32 node_index, u32 flag, u32 count);
#endif /* included_vlib_trace_h */
+29
View File
@@ -229,6 +229,35 @@ do { \
})); \
} while(0);
#define REPLY_AND_DETAILS_VEC_MACRO(t, v, mp, rmp, rv, body) \
do { \
vl_api_registration_t *rp; \
rp = vl_api_client_index_to_registration (mp->client_index); \
if (rp == 0) \
return; \
u32 cursor = clib_net_to_host_u32 (mp->cursor); \
vlib_main_t *vm = vlib_get_main (); \
f64 start = vlib_time_now (vm); \
if (!v || vec_len (v) == 0) { \
cursor = ~0; \
rv = VNET_API_ERROR_INVALID_VALUE; \
} else if (cursor == ~0) \
cursor = 0; \
while (cursor != ~0 && cursor < vec_len (v)) { \
do {body;} while (0); \
++cursor; \
if (vl_api_process_may_suspend (vm, rp, start)) { \
if (cursor < vec_len (v)) \
rv = VNET_API_ERROR_EAGAIN; \
break; \
} \
} \
REPLY_MACRO2 (t, ({ \
rmp->cursor = clib_host_to_net_u32 (cursor); \
})); \
} while(0);
/* "trust, but verify" */
static inline uword