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>
diff --git a/src/plugins/tracedump/CMakeLists.txt b/src/plugins/tracedump/CMakeLists.txt
index d0491ca..7860d95 100644
--- a/src/plugins/tracedump/CMakeLists.txt
+++ b/src/plugins/tracedump/CMakeLists.txt
@@ -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
)
diff --git a/src/plugins/tracedump/graph.api b/src/plugins/tracedump/graph.api
new file mode 100644
index 0000000..8019647
--- /dev/null
+++ b/src/plugins/tracedump/graph.api
@@ -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:
+ */
diff --git a/src/plugins/tracedump/graph.h b/src/plugins/tracedump/graph.h
new file mode 100644
index 0000000..65953af
--- /dev/null
+++ b/src/plugins/tracedump/graph.h
@@ -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:
+ */
diff --git a/src/plugins/tracedump/graph_api.c b/src/plugins/tracedump/graph_api.c
new file mode 100644
index 0000000..19b336c
--- /dev/null
+++ b/src/plugins/tracedump/graph_api.c
@@ -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:
+ */
diff --git a/src/plugins/tracedump/graph_cli.c b/src/plugins/tracedump/graph_cli.c
new file mode 100644
index 0000000..2440295
--- /dev/null
+++ b/src/plugins/tracedump/graph_cli.c
@@ -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:
+ */
diff --git a/src/plugins/tracedump/graph_test.c b/src/plugins/tracedump/graph_test.c
new file mode 100644
index 0000000..79e1df6
--- /dev/null
+++ b/src/plugins/tracedump/graph_test.c
@@ -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:
+ */
diff --git a/src/plugins/tracedump/tracedump.api b/src/plugins/tracedump/tracedump.api
index 81e6725..540b066 100644
--- a/src/plugins/tracedump/tracedump.api
+++ b/src/plugins/tracedump/tracedump.api
@@ -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 @@
/* 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 @@
/* Needed when set ends in the middle of a batch */
u8 done;
+ u32 packet_number;
string trace_data[];
};
diff --git a/src/plugins/tracedump/tracedump.c b/src/plugins/tracedump/tracedump.c
index 21a0c3d..5347f82 100644
--- a/src/plugins/tracedump/tracedump.c
+++ b/src/plugins/tracedump/tracedump.c
@@ -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 @@
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 @@
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 @@
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;
diff --git a/src/plugins/tracedump/tracedump_test.c b/src/plugins/tracedump/tracedump_test.c
index ba811b5..3bf50ef 100644
--- a/src/plugins/tracedump/tracedump_test.c
+++ b/src/plugins/tracedump/tracedump_test.c
@@ -37,19 +37,122 @@
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 @@
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>
diff --git a/src/tools/vppapigen/vppapigen.py b/src/tools/vppapigen/vppapigen.py
index bfc77ca..fbb0f27 100755
--- a/src/tools/vppapigen/vppapigen.py
+++ b/src/tools/vppapigen/vppapigen.py
@@ -894,10 +894,16 @@
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
diff --git a/src/vat/main.c b/src/vat/main.c
index aaedf6c..3e63aea 100644
--- a/src/vat/main.c
+++ b/src/vat/main.c
@@ -42,15 +42,31 @@
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 @@
}
}
+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 @@
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 @@
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");
diff --git a/src/vlib/trace.c b/src/vlib/trace.c
index 384885a..abd1166 100644
--- a/src/vlib/trace.c
+++ b/src/vlib/trace.c
@@ -122,6 +122,12 @@
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 @@
};
/* *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 @@
}
/* 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 @@
/* *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 @@
}
}
- /* *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 @@
/* *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 @@
* 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 @@
("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 @@
/* *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 @@
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;
}
diff --git a/src/vlib/trace.h b/src/vlib/trace.h
index 233c0ea..54b7c29 100644
--- a/src/vlib/trace.h
+++ b/src/vlib/trace.h
@@ -113,6 +113,12 @@
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 */
diff --git a/src/vlibapi/api_helper_macros.h b/src/vlibapi/api_helper_macros.h
index 8fc0bd5..983b688 100644
--- a/src/vlibapi/api_helper_macros.h
+++ b/src/vlibapi/api_helper_macros.h
@@ -229,6 +229,35 @@
})); \
} 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