text: /*
   * Copyright (c) 2015 Cisco and/or its affiliates.
   * 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.
   */
  /*
   * node_cli.c: node CLI
   *
   * Copyright (c) 2008 Eliot Dresselhaus
   *
   * Permission is hereby granted, free of charge, to any person obtaining
   * a copy of this software and associated documentation files (the
   * "Software"), to deal in the Software without restriction, including
   * without limitation the rights to use, copy, modify, merge, publish,
   * distribute, sublicense, and/or sell copies of the Software, and to
   * permit persons to whom the Software is furnished to do so, subject to
   * the following conditions:
   *
   * The above copyright notice and this permission notice shall be
   * included in all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
   *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
   *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  
  #include <vlib/vlib.h>
  #include <vlib/threads.h>
  
  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 *
  show_node_graph (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 node_index;
  
    vlib_cli_output (vm, "%U\n", format_vlib_node_graph, nm, 0);
  
    if (unformat (input, "%U", unformat_vlib_node, vm, &node_index))
      {
        n = vlib_get_node (vm, node_index);
        vlib_cli_output (vm, "%U\n", format_vlib_node_graph, nm, n);
      }
    else
      {
        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++)
  	vlib_cli_output (vm, "%U\n\n", format_vlib_node_graph, nm, nodes[i]);
  
        vec_free (nodes);
      }
  
    return 0;
  }
  
  /* *INDENT-OFF* */
  VLIB_CLI_COMMAND (show_node_graph_command, static) = {
    .path = "show vlib graph",
    .short_help = "Show packet processing node graph",
    .function = show_node_graph,
  };
  /* *INDENT-ON* */
  
  static u8 *
  format_vlib_node_state (u8 * s, va_list * va)
  {
    vlib_main_t *vm = va_arg (*va, vlib_main_t *);
    vlib_node_t *n = va_arg (*va, vlib_node_t *);
    char *state;
  
    state = "active";
    if (n->type == VLIB_NODE_TYPE_PROCESS)
      {
        vlib_process_t *p = vlib_get_process_from_node (vm, n);
  
        switch (p->flags & (VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_CLOCK
  			  | VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_EVENT))
  	{
  	default:
  	  if (!(p->flags & VLIB_PROCESS_IS_RUNNING))
  	    state = "done";
  	  break;
  
  	case VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_CLOCK:
  	  state = "time wait";
  	  break;
  
  	case VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_EVENT:
  	  state = "event wait";
  	  break;
  
  	case (VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_EVENT | VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_CLOCK):
  	  state =
  	    "any wait";
  	  break;
  	}
      }
    else if (n->type != VLIB_NODE_TYPE_INTERNAL)
      {
        state = "polling";
        if (n->state == VLIB_NODE_STATE_DISABLED)
  	state = "disabled";
        else if (n->state == VLIB_NODE_STATE_INTERRUPT)
  	state = "interrupt wait";
      }
  
    return format (s, "%s", state);
  }
  
  static u8 *
  format_vlib_node_stats (u8 * s, va_list * va)
  {
    vlib_main_t *vm = va_arg (*va, vlib_main_t *);
    vlib_node_t *n = va_arg (*va, vlib_node_t *);
    int max = va_arg (*va, int);
    f64 v;
    u8 *ns;
    u8 *misc_info = 0;
    u64 c, p, l, d;
    f64 x;
    f64 maxc, maxcn;
    u32 maxn;
    u32 indent;
    u64 pmc_ticks;
    f64 pmc_ticks_per_packet;
  
    if (!n)
      {
        if (max)
  	s = format (s,
  		    "%=30s%=17s%=16s%=16s%=16s%=16s",
  		    "Name", "Max Node Clocks", "Vectors at Max",
  		    "Max Clocks", "Avg Clocks", "Avg Vectors/Call");
        else
  	s = format (s,
  		    "%=30s%=12s%=16s%=16s%=16s%=16s%=16s",
  		    "Name", "State", "Calls", "Vectors", "Suspends",
  		    "Clocks", "Vectors/Call");
        if (vm->perf_counter_id)
  	s = format (s, "%=16s", "Perf Ticks");
  
        return s;
      }
  
    indent = format_get_indent (s);
  
    l = n->stats_total.clocks - n->stats_last_clear.clocks;
    c = n->stats_total.calls - n->stats_last_clear.calls;
    p = n->stats_total.vectors - n->stats_last_clear.vectors;
    d = n->stats_total.suspends - n->stats_last_clear.suspends;
    maxc = (f64) n->stats_total.max_clock;
    maxn = n->stats_total.max_clock_n;
    if (n->stats_total.max_clock_n)
      maxcn = (f64) n->stats_total.max_clock / (f64) maxn;
    else
      maxcn = 0.0;
  
    pmc_ticks = n->stats_total.perf_counter_ticks -
      n->stats_last_clear.perf_counter_ticks;
    if (p > 0)
      pmc_ticks_per_packet = (f64) pmc_ticks / (f64) p;
    else
      pmc_ticks_per_packet = 0.0;
  
    /* Clocks per packet, per call or per suspend. */
    x = 0;
    if (p > 0)
      x = (f64) l / (f64) p;
    else if (c > 0)
      x = (f64) l / (f64) c;
    else if (d > 0)
      x = (f64) l / (f64) d;
  
    if (c > 0)
      v = (double) p / (double) c;
    else
      v = 0;
  
    if (n->type == VLIB_NODE_TYPE_PROCESS)
      {
        vlib_process_t *p = vlib_get_process_from_node (vm, n);
  
        /* Show processes with events pending.  This helps spot bugs where events are not
           being handled. */
        if (!clib_bitmap_is_zero (p->non_empty_event_type_bitmap))
  	misc_info = format (misc_info, "events pending, ");
      }
    ns = n->name;
  
    if (max)
      s = format (s, "%-30v%=17.2e%=16d%=16.2e%=16.2e%=16.2e",
  		ns, maxc, maxn, maxcn, x, v);
    else
      s = format (s, "%-30v%=12U%16Ld%16Ld%16Ld%16.2e%16.2f", ns,
  		format_vlib_node_state, vm, n, c, p, d, x, v);
  
    if (pmc_ticks_per_packet > 0.0)
      s = format (s, "%16.2e", pmc_ticks_per_packet);
  
    if (ns != n->name)
      vec_free (ns);
  
    if (misc_info)
      {
        s = format (s, "\n%U%v", format_white_space, indent + 4, misc_info);
        vec_free (misc_info);
      }
  
    return s;
  }
  
  static clib_error_t *
  show_node_runtime (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;
    f64 time_now;
    u32 node_index;
    vlib_node_t ***node_dups = 0;
    f64 *vectors_per_main_loop = 0;
    f64 *last_vector_length_per_node = 0;
  
    time_now = vlib_time_now (vm);
  
    if (unformat (input, "%U", unformat_vlib_node, vm, &node_index))
      {
        n = vlib_get_node (vm, node_index);
        vlib_node_sync_stats (vm, n);
        vlib_cli_output (vm, "%U\n", format_vlib_node_stats, vm, 0, 0);
        vlib_cli_output (vm, "%U\n", format_vlib_node_stats, vm, n, 0);
      }
    else
      {
        vlib_node_t **nodes;
        uword i, j;
        f64 dt;
        u64 n_input, n_output, n_drop, n_punt;
        u64 n_internal_vectors, n_internal_calls;
        u64 n_clocks, l, v, c, d;
        int brief = 1;
        int max = 0;
        vlib_main_t **stat_vms = 0, *stat_vm;
  
        /* Suppress nodes with zero calls since last clear */
        if (unformat (input, "brief") || unformat (input, "b"))
  	brief = 1;
        if (unformat (input, "verbose") || unformat (input, "v"))
  	brief = 0;
        if (unformat (input, "max") || unformat (input, "m"))
  	max = 1;
  
        for (i = 0; i < vec_len (vlib_mains); i++)
  	{
  	  stat_vm = vlib_mains[i];
  	  if (stat_vm)
  	    vec_add1 (stat_vms, stat_vm);
  	}
  
        /*
         * Barrier sync across stats scraping.
         * Otherwise, the counts will be grossly inaccurate.
         */
        vlib_worker_thread_barrier_sync (vm);
  
        for (j = 0; j < vec_len (stat_vms); j++)
  	{
  	  stat_vm = stat_vms[j];
  	  nm = &stat_vm->node_main;
  
  	  for (i = 0; i < vec_len (nm->nodes); i++)
  	    {
  	      n = nm->nodes[i];
  	      vlib_node_sync_stats (stat_vm, n);
  	    }
  
  	  nodes = vec_dup (nm->nodes);
  
  	  vec_add1 (node_dups, nodes);
  	  vec_add1 (vectors_per_main_loop,
  		    vlib_last_vectors_per_main_loop_as_f64 (stat_vm));
  	  vec_add1 (last_vector_length_per_node,
  		    vlib_last_vector_length_per_node (stat_vm));
  	}
        vlib_worker_thread_barrier_release (vm);
  
  
        for (j = 0; j < vec_len (stat_vms); j++)
  	{
  	  stat_vm = stat_vms[j];
  	  nodes = node_dups[j];
  
  	  vec_sort_with_function (nodes, node_cmp);
  
  	  n_input = n_output = n_drop = n_punt = n_clocks = 0;
  	  n_internal_vectors = n_internal_calls = 0;
  	  for (i = 0; i < vec_len (nodes); i++)
  	    {
  	      n = nodes[i];
  
  	      l = n->stats_total.clocks - n->stats_last_clear.clocks;
  	      n_clocks += l;
  
  	      v = n->stats_total.vectors - n->stats_last_clear.vectors;
  	      c = n->stats_total.calls - n->stats_last_clear.calls;
  
  	      switch (n->type)
  		{
  		default:
  		  continue;
  
  		case VLIB_NODE_TYPE_INTERNAL:
  		  n_output += (n->flags & VLIB_NODE_FLAG_IS_OUTPUT) ? v : 0;
  		  n_drop += (n->flags & VLIB_NODE_FLAG_IS_DROP) ? v : 0;
  		  n_punt += (n->flags & VLIB_NODE_FLAG_IS_PUNT) ? v : 0;
  		  if (!(n->flags & VLIB_NODE_FLAG_IS_OUTPUT))
  		    {
  		      n_internal_vectors += v;
  		      n_internal_calls += c;
  		    }
  		  if (n->flags & VLIB_NODE_FLAG_IS_HANDOFF)
  		    n_input += v;
  		  break;
  
  		case VLIB_NODE_TYPE_INPUT:
  		  n_input += v;
  		  break;
  		}
  	    }
  
  	  if (vec_len (vlib_mains) > 1)
  	    {
  	      vlib_worker_thread_t *w = vlib_worker_threads + j;
  	      if (j > 0)
  		vlib_cli_output (vm, "---------------");
  
  	      if (w->cpu_id > -1)
  		vlib_cli_output (vm, "Thread %d %s (lcore %u)", j, w->name,
  				 w->cpu_id);
  	      else
  		vlib_cli_output (vm, "Thread %d %s", j, w->name);
  	    }
  
  	  dt = time_now - nm->time_last_runtime_stats_clear;
  	  vlib_cli_output
  	    (vm,
  	     "Time %.1f, average vectors/node %.2f, last %d main loops %.2f per node %.2f"
  	     "\n  vector rates in %.4e, out %.4e, drop %.4e, punt %.4e",
  	     dt,
  	     (n_internal_calls > 0
  	      ? (f64) n_internal_vectors / (f64) n_internal_calls
  	      : 0),
  	     1 << VLIB_LOG2_MAIN_LOOPS_PER_STATS_UPDATE,
  	     vectors_per_main_loop[j],
  	     last_vector_length_per_node[j],
  	     (f64) n_input / dt,
  	     (f64) n_output / dt, (f64) n_drop / dt, (f64) n_punt / dt);
  
  	  vlib_cli_output (vm, "%U", format_vlib_node_stats, stat_vm, 0, max);
  	  for (i = 0; i < vec_len (nodes); i++)
  	    {
  	      c =
  		nodes[i]->stats_total.calls -
  		nodes[i]->stats_last_clear.calls;
  	      d =
  		nodes[i]->stats_total.suspends -
  		nodes[i]->stats_last_clear.suspends;
  	      if (c || d || !brief)
  		{
  		  vlib_cli_output (vm, "%U", format_vlib_node_stats, stat_vm,
  				   nodes[i], max);
  		}
  	    }
  	  vec_free (nodes);
  	}
        vec_free (stat_vms);
        vec_free (node_dups);
        vec_free (vectors_per_main_loop);
        vec_free (last_vector_length_per_node);
      }
  
    return 0;
  }
  
  /* *INDENT-OFF* */
  VLIB_CLI_COMMAND (show_node_runtime_command, static) = {
    .path = "show runtime",
    .short_help = "Show packet processing runtime",
    .function = show_node_runtime,
    .is_mp_safe = 1,
  };
  /* *INDENT-ON* */
  
  static clib_error_t *
  clear_node_runtime (vlib_main_t * vm,
  		    unformat_input_t * input, vlib_cli_command_t * cmd)
  {
    vlib_node_main_t *nm;
    vlib_node_t *n;
    int i, j;
    vlib_main_t **stat_vms = 0, *stat_vm;
    vlib_node_runtime_t *r;
  
    for (i = 0; i < vec_len (vlib_mains); i++)
      {
        stat_vm = vlib_mains[i];
        if (stat_vm)
  	vec_add1 (stat_vms, stat_vm);
      }
  
    vlib_worker_thread_barrier_sync (vm);
  
    for (j = 0; j < vec_len (stat_vms); j++)
      {
        stat_vm = stat_vms[j];
        nm = &stat_vm->node_main;
  
        for (i = 0; i < vec_len (nm->nodes); i++)
  	{
  	  n = nm->nodes[i];
  	  vlib_node_sync_stats (stat_vm, n);
  	  n->stats_last_clear = n->stats_total;
  
  	  r = vlib_node_get_runtime (stat_vm, n->index);
  	  r->max_clock = 0;
  	}
        /* Note: input/output rates computed using vlib_global_main */
        nm->time_last_runtime_stats_clear = vlib_time_now (vm);
      }
  
    vlib_worker_thread_barrier_release (vm);
  
    vec_free (stat_vms);
  
    return 0;
  }
  
  /* *INDENT-OFF* */
  VLIB_CLI_COMMAND (clear_node_runtime_command, static) = {
    .path = "clear runtime",
    .short_help = "Clear packet processing runtime statistics",
    .function = clear_node_runtime,
  };
  /* *INDENT-ON* */
  
  static clib_error_t *
  show_node (vlib_main_t * vm, unformat_input_t * input,
  	   vlib_cli_command_t * cmd)
  {
    unformat_input_t _line_input, *line_input = &_line_input;
    clib_error_t *error = 0;
    vlib_node_main_t *nm = &vm->node_main;
    vlib_node_t *n;
    u8 *s = 0, *s2 = 0;
    u32 i, node_index = ~0;
    char *type_str;
    u8 valid_node_name = 0;
  
    if (!unformat_user (input, unformat_line_input, line_input))
      return 0;
  
    while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
      {
        if (unformat (line_input, "index %u", &node_index))
  	;
        else
  	if (unformat (line_input, "%U", unformat_vlib_node, vm, &node_index))
  	valid_node_name = 1;
        else if (!valid_node_name)
  	error = clib_error_return (0, "unknown node name: '%U'",
  				   format_unformat_error, line_input);
        else
  	error = clib_error_return (0, "unknown input '%U'",
  				   format_unformat_error, line_input);
      }
  
    unformat_free (line_input);
  
    if (error)
      return error;
  
    if (node_index >= vec_len (vm->node_main.nodes))
      return clib_error_return (0, "please specify valid node");
  
    n = vlib_get_node (vm, node_index);
    vlib_node_sync_stats (vm, n);
  
    switch (n->type)
      {
      case VLIB_NODE_TYPE_INTERNAL:
        type_str = "internal";
        break;
      case VLIB_NODE_TYPE_INPUT:
        type_str = "input";
        break;
      case VLIB_NODE_TYPE_PRE_INPUT:
        type_str = "pre-input";
        break;
      case VLIB_NODE_TYPE_PROCESS:
        type_str = "process";
        break;
      default:
        type_str = "unknown";
      }
  
    if (n->sibling_of)
      s = format (s, ", sibling-of %s", n->sibling_of);
  
    vlib_cli_output (vm, "node %s, type %s, state %U, index %d%v\n",
  		   n->name, type_str, format_vlib_node_state, vm, n,
  		   n->index, s);
    vec_reset_length (s);
  
    if (n->node_fn_registrations)
      {
        vlib_node_fn_registration_t *fnr = n->node_fn_registrations;
        while (fnr)
  	{
  	  if (vec_len (s) == 0)
  	    s = format (s, "\n    %-15s  %=8s  %6s",
  			"Name", "Priority", "Active");
  	  s = format (s, "\n    %-15s  %=8u  %=6s", fnr->name, fnr->priority,
  		      fnr->function == n->function ? "yes" : "");
  	  fnr = fnr->next_registration;
  	}
      }
    else
      s = format (s, "\n    default only");
    vlib_cli_output (vm, "  node function variants:%v\n", s);
    vec_reset_length (s);
  
    for (i = 0; i < vec_len (n->next_nodes); i++)
      {
        vlib_node_t *pn;
        if (n->next_nodes[i] == VLIB_INVALID_NODE_INDEX)
  	continue;
  
        pn = vec_elt (nm->nodes, n->next_nodes[i]);
  
        if (vec_len (s) == 0)
  	s = format (s, "\n    %10s  %10s  %=30s %8s",
  		    "next-index", "node-index", "Node", "Vectors");
  
        s = format (s, "\n    %=10u  %=10u  %-30v %=8llu", i, n->next_nodes[i],
  		  pn->name, vec_elt (n->n_vectors_by_next_node, i));
      }
  
    if (vec_len (s) == 0)
      s = format (s, "\n    none");
    vlib_cli_output (vm, "\n  next nodes:%v\n", s);
    vec_reset_length (s);
  
    if (n->type == VLIB_NODE_TYPE_INTERNAL)
      {
        int j = 0;
        /* *INDENT-OFF* */
        clib_bitmap_foreach (i, n->prev_node_bitmap, ({
  	    vlib_node_t *pn = vlib_get_node (vm, i);
  	    if (j++ % 3 == 0)
  	      s = format (s, "\n    ");
  	    s2 = format (s2, "%v (%u)", pn->name, i);
  	    s = format (s, "%-35v", s2);
  	    vec_reset_length (s2);
  	  }));
        /* *INDENT-ON* */
  
        if (vec_len (s) == 0)
  	s = format (s, "\n    none");
        vlib_cli_output (vm, "\n  known previous nodes:%v\n", s);
        vec_reset_length (s);
      }
  
    vec_free (s);
    vec_free (s2);
    return 0;
  }
  
  /* *INDENT-OFF* */
  VLIB_CLI_COMMAND (show_node_command, static) = {
    .path = "show node",
    .short_help = "show node [index] <node-name | node-index>",
    .function = show_node,
  };
  
  static clib_error_t *
  set_node_fn(vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
  {
    unformat_input_t _line_input, *line_input = &_line_input;
    u32 node_index;
    vlib_node_t *n;
    clib_error_t *err = 0;
    vlib_node_fn_registration_t *fnr;
    u8 *variant = 0;
  
    if (!unformat_user (input, unformat_line_input, line_input))
      return 0;
  
    if (!unformat (line_input, "%U", unformat_vlib_node, vm, &node_index))
      {
        err = clib_error_return (0, "please specify valid node name");
        goto done;
      }
  
    if (!unformat (line_input, "%s", &variant))
      {
        err = clib_error_return (0, "please specify node functional variant");
        goto done;
      }
  
    n = vlib_get_node (vm, node_index);
  
    if (n->node_fn_registrations == 0)
      {
        err = clib_error_return (0, "node doesn't have functional variants");
        goto done;
      }
  
    fnr = n->node_fn_registrations;
    vec_add1 (variant, 0);
  
    while (fnr)
      {
        if (!strncmp (fnr->name, (char *) variant, vec_len (variant) - 1))
  	{
  	  int i;
  
  	  n->function = fnr->function;
  
  	  for (i = 0; i < vec_len (vlib_mains); i++)
  	    {
  	      vlib_node_runtime_t *nrt;
  	      nrt = vlib_node_get_runtime (vlib_mains[i], n->index);
  	      nrt->function = fnr->function;
  	    }
  	  goto done;
  	}
        fnr = fnr->next_registration;
      }
  
    err = clib_error_return (0, "node functional variant '%s' not found", variant);
  
  done:
    vec_free (variant);
    unformat_free (line_input);
    return err;
  }
  
  /* *INDENT-OFF* */
  VLIB_CLI_COMMAND (set_node_fn_command, static) = {
    .path = "set node function",
    .short_help = "set node function <node-name> <variant-name>",
    .function = set_node_fn,
  };
  /* *INDENT-ON* */
  
  /* Dummy function to get us linked in. */
  void
  vlib_node_cli_reference (void)
  {
  }
  
  /*
   * fd.io coding-style-patch-verification: ON
   *
   * Local Variables:
   * eval: (c-set-style "gnu")
   * End:
   */
  