Add 'show vlib graphviz' command

Add a new command to dump vlib graph as graphviz/dot file

Change-Id: I43fc072cff8153ac500e5fbc6641a3705c2e995e
Signed-off-by: Benoît Ganne <bganne@cisco.com>
diff --git a/src/vlib/format.c b/src/vlib/format.c
index 79a4d68..ee730bd 100644
--- a/src/vlib/format.c
+++ b/src/vlib/format.c
@@ -187,6 +187,30 @@
   return p != 0;
 }
 
+/* Parse a filename to dump debug info */
+uword
+unformat_vlib_tmpfile (unformat_input_t * input, va_list * args)
+{
+  u8 **chroot_filename = va_arg (*args, u8 **);
+  u8 *filename;
+
+  if (!unformat (input, "%s", &filename))
+    return 0;
+
+  /* Brain-police user path input */
+  if (strstr ((char *) filename, "..") || index ((char *) filename, '/'))
+    {
+      vec_free (filename);
+      return 0;
+    }
+
+  *chroot_filename = format (0, "/tmp/%s%c", filename, 0);
+  vec_free (filename);
+
+  return 1;
+}
+
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
diff --git a/src/vlib/format_funcs.h b/src/vlib/format_funcs.h
index f60b894..30e919d 100644
--- a/src/vlib/format_funcs.h
+++ b/src/vlib/format_funcs.h
@@ -59,6 +59,9 @@
 /* Parse an int either %d or 0x%x. */
 uword unformat_vlib_number (unformat_input_t * input, va_list * args);
 
+/* Parse a filename to dump debug info */
+uword unformat_vlib_tmpfile (unformat_input_t * input, va_list * args);
+
 /* Flag to format_vlib_*_header functions to tell them not to recurse
    into the next layer's header.  For example, tells format_vlib_ethernet_header
    not to format ip header. */
diff --git a/src/vlib/main.c b/src/vlib/main.c
index c0ab8e1..c5153ab 100644
--- a/src/vlib/main.c
+++ b/src/vlib/main.c
@@ -2126,7 +2126,9 @@
 	    }
 	  pm->n_packets_to_capture = max;
 	}
-      else if (unformat (line_input, "file %s", &filename))
+      else
+	if (unformat
+	    (line_input, "file %U", unformat_vlib_tmpfile, &filename))
 	{
 	  if (vm->dispatch_pcap_enable)
 	    {
@@ -2135,21 +2137,6 @@
 	      errorFlag = 1;
 	      break;
 	    }
-
-	  /* Brain-police user path input */
-	  if (strstr ((char *) filename, "..")
-	      || index ((char *) filename, '/'))
-	    {
-	      vlib_cli_output (vm, "illegal characters in filename '%s'",
-			       filename);
-	      vlib_cli_output (vm, "Hint: .. and / are not allowed.");
-	      vec_free (filename);
-	      errorFlag = 1;
-	      break;
-	    }
-
-	  chroot_filename = format (0, "/tmp/%s%c", filename, 0);
-	  vec_free (filename);
 	}
       else if (unformat (line_input, "status"))
 	{
diff --git a/src/vlib/node_cli.c b/src/vlib/node_cli.c
index ad17c1d..c8e32b5 100644
--- a/src/vlib/node_cli.c
+++ b/src/vlib/node_cli.c
@@ -37,6 +37,9 @@
  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 #include <vlib/vlib.h>
 #include <vlib/threads.h>
 
@@ -88,6 +91,81 @@
 };
 /* *INDENT-ON* */
 
+static clib_error_t *
+show_node_graphviz (vlib_main_t * vm,
+		    unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  clib_error_t *error = 0;
+  vlib_node_main_t *nm = &vm->node_main;
+  u8 *chroot_filename = 0;
+  int fd;
+  vlib_node_t **nodes = 0;
+  uword i, j;
+
+  if (!unformat_user (input, unformat_vlib_tmpfile, &chroot_filename))
+    {
+      fd = -1;
+    }
+  else
+    {
+      fd =
+	open ((char *) chroot_filename, O_CREAT | O_TRUNC | O_WRONLY, 0664);
+    }
+
+#define format__(vm__, fd__, ...) \
+  if ((fd) < 0) \
+    { \
+      vlib_cli_output((vm__), ## __VA_ARGS__); \
+    } \
+  else \
+    { \
+      fdformat((fd__), ## __VA_ARGS__); \
+    }
+
+  format__ (vm, fd, "%s", "digraph {\n");
+
+  nodes = vec_dup (nm->nodes);
+  vec_sort_with_function (nodes, node_cmp);
+
+  for (i = 0; i < vec_len (nodes); i++)
+    {
+      for (j = 0; j < vec_len (nodes[i]->next_nodes); j++)
+	{
+	  vlib_node_t *x;
+
+	  if (nodes[i]->next_nodes[j] == VLIB_INVALID_NODE_INDEX)
+	    continue;
+
+	  x = vec_elt (nm->nodes, nodes[i]->next_nodes[j]);
+	  format__ (vm, fd, "  \"%v\" -> \"%v\"\n", nodes[i]->name, x->name);
+	}
+    }
+
+  format__ (vm, fd, "%s", "}");
+
+  if (fd >= 0)
+    {
+      vlib_cli_output (vm,
+		       "vlib graph dumped into `%s'. Run eg. `fdp -Tsvg -O %s'.",
+		       chroot_filename, chroot_filename);
+    }
+
+  vec_free (nodes);
+  vec_free (chroot_filename);
+  vec_free (nodes);
+  if (fd >= 0)
+    close (fd);
+  return error;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (show_node_graphviz_command, static) = {
+  .path = "show vlib graphviz",
+  .short_help = "Dump packet processing node graph as a graphviz dotfile",
+  .function = show_node_graphviz,
+};
+/* *INDENT-ON* */
+
 static u8 *
 format_vlib_node_state (u8 * s, va_list * va)
 {