From c0b195450b31f7092834c0f14a27ca929faf8bca Mon Sep 17 00:00:00 2001 From: Jon Loeliger Date: Mon, 11 May 2020 08:43:51 -0500 Subject: [PATCH] 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 Signed-off-by: Ole Troan Signed-off-by: Dave Barach --- src/plugins/tracedump/CMakeLists.txt | 4 + src/plugins/tracedump/graph.api | 95 ++++++++++++ src/plugins/tracedump/graph.h | 35 +++++ src/plugins/tracedump/graph_api.c | 270 +++++++++++++++++++++++++++++++++ src/plugins/tracedump/graph_cli.c | 148 ++++++++++++++++++ src/plugins/tracedump/graph_test.c | 185 ++++++++++++++++++++++ src/plugins/tracedump/tracedump.api | 69 ++++++++- src/plugins/tracedump/tracedump.c | 114 +++++++++++++- src/plugins/tracedump/tracedump_test.c | 120 ++++++++++++++- src/tools/vppapigen/vppapigen.py | 14 +- src/vat/main.c | 77 +++++++++- src/vlib/trace.c | 121 ++++++++++----- src/vlib/trace.h | 6 + src/vlibapi/api_helper_macros.h | 29 ++++ 14 files changed, 1240 insertions(+), 47 deletions(-) create mode 100644 src/plugins/tracedump/graph.api create mode 100644 src/plugins/tracedump/graph.h create mode 100644 src/plugins/tracedump/graph_api.c create mode 100644 src/plugins/tracedump/graph_cli.c create mode 100644 src/plugins/tracedump/graph_test.c 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 ] [node_index ]|[node_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 +#include + +#include +#include + +#define REPLY_MSG_ID_BASE gmp->msg_id_base +#include + +#include + + +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 +#include + +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 +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +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 |] [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 +#include +#include +#include +#include +#include +#include + +#define __plugin_msg_base graph_test_main.msg_id_base +#include + +#include +#include +#include +#include + +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 + +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) ] | [(include_classifier|exclude_classifier) ] [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 ] [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 ] [position ] [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 diff --git a/src/tools/vppapigen/vppapigen.py b/src/tools/vppapigen/vppapigen.py index bfc77ca7019..fbb0f27cc34 100755 --- a/src/tools/vppapigen/vppapigen.py +++ b/src/tools/vppapigen/vppapigen.py @@ -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 diff --git a/src/vat/main.c b/src/vat/main.c index aaedf6c450b..3e63aea260f 100644 --- a/src/vat/main.c +++ b/src/vat/main.c @@ -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"); diff --git a/src/vlib/trace.c b/src/vlib/trace.c index 384885a1a53..abd116622c7 100644 --- a/src/vlib/trace.c +++ b/src/vlib/trace.c @@ -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 [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; } diff --git a/src/vlib/trace.h b/src/vlib/trace.h index 233c0ea0b56..54b7c294569 100644 --- a/src/vlib/trace.h +++ b/src/vlib/trace.h @@ -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 */ diff --git a/src/vlibapi/api_helper_macros.h b/src/vlibapi/api_helper_macros.h index 8fc0bd54c76..983b688c02d 100644 --- a/src/vlibapi/api_helper_macros.h +++ b/src/vlibapi/api_helper_macros.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 -- 2.16.6