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