aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/tracedump
diff options
context:
space:
mode:
authorJon Loeliger <jdl@netgate.com>2020-05-11 08:43:51 -0500
committerDave Barach <openvpp@barachs.net>2020-10-06 10:49:27 +0000
commitc0b195450b31f7092834c0f14a27ca929faf8bca (patch)
tree0129c95d2dcd184db08eb88243769d1beb74e273 /src/plugins/tracedump
parentd20bc1d30a913e783a39919268c9870cbfe8817f (diff)
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>
Diffstat (limited to 'src/plugins/tracedump')
-rw-r--r--src/plugins/tracedump/CMakeLists.txt4
-rw-r--r--src/plugins/tracedump/graph.api95
-rw-r--r--src/plugins/tracedump/graph.h35
-rw-r--r--src/plugins/tracedump/graph_api.c270
-rw-r--r--src/plugins/tracedump/graph_cli.c148
-rw-r--r--src/plugins/tracedump/graph_test.c185
-rw-r--r--src/plugins/tracedump/tracedump.api69
-rw-r--r--src/plugins/tracedump/tracedump.c114
-rw-r--r--src/plugins/tracedump/tracedump_test.c120
9 files changed, 1036 insertions, 4 deletions
diff --git a/src/plugins/tracedump/CMakeLists.txt b/src/plugins/tracedump/CMakeLists.txt
index d0491caf677..7860d95bc11 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 00000000000..8019647040a
--- /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 00000000000..65953af819e
--- /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 00000000000..19b336ccb04
--- /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 00000000000..2440295a1a7
--- /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 00000000000..79e1df61c5f
--- /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 81e6725abc8..540b0664074 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 @@ 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[];
};
diff --git a/src/plugins/tracedump/tracedump.c b/src/plugins/tracedump/tracedump.c
index 21a0c3d0379..5347f82d612 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 @@ 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;
diff --git a/src/plugins/tracedump/tracedump_test.c b/src/plugins/tracedump/tracedump_test.c
index ba811b5d38c..3bf50efb4ac 100644
--- a/src/plugins/tracedump/tracedump_test.c
+++ b/src/plugins/tracedump/tracedump_test.c
@@ -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>