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:
committed by
Dave Barach
parent
d20bc1d30a
commit
c0b195450b
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -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:
|
||||
*/
|
||||
@@ -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:
|
||||
*/
|
||||
@@ -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:
|
||||
*/
|
||||
@@ -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:
|
||||
*/
|
||||
@@ -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:
|
||||
*/
|
||||
@@ -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[];
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user