X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fvlib%2Fcli.c;h=bb6c574653790610eae3a9f8d05bb26f6df4a9b2;hb=5c944eef7012e7c5b363399ed92966fb659196b7;hp=9e14bee219e06d711f9f10aade9a09511df13ce7;hpb=3060e07b71fd4dd6203769d73c4f8f21709f9b9c;p=vpp.git diff --git a/src/vlib/cli.c b/src/vlib/cli.c index 9e14bee219e..bb6c5746537 100644 --- a/src/vlib/cli.c +++ b/src/vlib/cli.c @@ -38,10 +38,20 @@ */ #include +#include #include +#include #include #include +/** \file src/vlib/cli.c Debug CLI Implementation + */ + +int vl_api_set_elog_trace_api_messages (int enable); +int vl_api_get_elog_trace_api_messages (void); + +static void *current_traced_heap; + /* Root of all show commands. */ /* *INDENT-OFF* */ VLIB_CLI_COMMAND (vlib_cli_show_command, static) = { @@ -200,29 +210,6 @@ unformat_vlib_cli_sub_command (unformat_input_t * i, va_list * args) vlib_cli_main_t *cm = &vm->cli_main; uword *match_bitmap, is_unique, index; - { - vlib_cli_sub_rule_t *sr; - vlib_cli_parse_rule_t *r; - vec_foreach (sr, c->sub_rules) - { - void **d; - r = vec_elt_at_index (cm->parse_rules, sr->rule_index); - vec_add2 (cm->parse_rule_data, d, 1); - vec_reset_length (d[0]); - if (r->data_size) - d[0] = _vec_resize (d[0], - /* length increment */ 1, - r->data_size, - /* header_bytes */ 0, - /* data align */ sizeof (uword)); - if (unformat_user (i, r->unformat_function, vm, d[0])) - { - *result = vec_elt_at_index (cm->commands, sr->command_index); - return 1; - } - } - } - match_bitmap = vlib_cli_sub_command_match (c, i); is_unique = clib_bitmap_count_set_bits (match_bitmap) == 1; index = ~0; @@ -333,7 +320,8 @@ done: clib_bitmap_free (match_bitmap); unformat_free (&input); - vec_sort_with_function (result, vlib_cli_cmp_strings); + if (result) + vec_sort_with_function (result, vlib_cli_cmp_strings); return result; } @@ -351,50 +339,12 @@ format_vlib_cli_command_help (u8 * s, va_list * args) return s; } -static u8 * -format_vlib_cli_parse_rule_name (u8 * s, va_list * args) -{ - vlib_cli_parse_rule_t *r = va_arg (*args, vlib_cli_parse_rule_t *); - return format (s, "<%U>", format_c_identifier, r->name); -} - static u8 * format_vlib_cli_path (u8 * s, va_list * args) { u8 *path = va_arg (*args, u8 *); - int i, in_rule; - in_rule = 0; - for (i = 0; i < vec_len (path); i++) - { - switch (path[i]) - { - case '%': - in_rule = 1; - vec_add1 (s, '<'); /* start of */ - break; - - case '_': - /* _ -> space in rules. */ - vec_add1 (s, in_rule ? ' ' : '_'); - break; - case ' ': - if (in_rule) - { - vec_add1 (s, '>'); /* end of */ - in_rule = 0; - } - vec_add1 (s, ' '); - break; - - default: - vec_add1 (s, path[i]); - break; - } - } - - if (in_rule) - vec_add1 (s, '>'); /* terminate */ + s = format (s, "%v", path); return s; } @@ -404,13 +354,10 @@ all_subs (vlib_cli_main_t * cm, vlib_cli_command_t * subs, u32 command_index) { vlib_cli_command_t *c = vec_elt_at_index (cm->commands, command_index); vlib_cli_sub_command_t *sc; - vlib_cli_sub_rule_t *sr; if (c->function) vec_add1 (subs, c[0]); - vec_foreach (sr, c->sub_rules) - subs = all_subs (cm, subs, sr->command_index); vec_foreach (sc, c->sub_commands) subs = all_subs (cm, subs, sc->index); return subs; @@ -473,17 +420,14 @@ vlib_cli_dispatch_sub_commands (vlib_main_t * vm, vlib_cli_output (vm, "%U", format_vlib_cli_command_help, c, /* is_long */ 1); - else if (vec_len (c->sub_commands) + vec_len (c->sub_rules) == 0) + else if (vec_len (c->sub_commands) == 0) vlib_cli_output (vm, "%v: no sub-commands", c->path); else { + vlib_cli_sub_rule_t *sr, *subs = 0; vlib_cli_sub_command_t *sc; - vlib_cli_sub_rule_t *sr, *subs; - - subs = vec_dup (c->sub_rules); - /* Add in rules if any. */ vec_foreach (sc, c->sub_commands) { vec_add2 (subs, sr, 1); @@ -497,25 +441,11 @@ vlib_cli_dispatch_sub_commands (vlib_main_t * vm, for (i = 0; i < vec_len (subs); i++) { vlib_cli_command_t *d; - vlib_cli_parse_rule_t *r; d = vec_elt_at_index (cm->commands, subs[i].command_index); - r = - subs[i].rule_index != ~0 ? vec_elt_at_index (cm->parse_rules, - subs - [i].rule_index) : - 0; - - if (r) - vlib_cli_output - (vm, " %-30U %U", - format_vlib_cli_parse_rule_name, r, - format_vlib_cli_command_help, d, /* is_long */ 0); - else - vlib_cli_output - (vm, " %-30v %U", - subs[i].name, - format_vlib_cli_command_help, d, /* is_long */ 0); + vlib_cli_output + (vm, " %-30v %U", subs[i].name, + format_vlib_cli_command_help, d, /* is_long */ 0); } vec_free (subs); @@ -549,6 +479,39 @@ vlib_cli_dispatch_sub_commands (vlib_main_t * vm, parent_command_index); unformat_free (&sub_input); } + else if (unformat (input, "leak-check %U", + unformat_vlib_cli_sub_input, &sub_input)) + { + u8 *leak_report; + if (current_traced_heap) + { + void *oldheap; + oldheap = clib_mem_set_heap (current_traced_heap); + clib_mem_trace (0); + clib_mem_set_heap (oldheap); + current_traced_heap = 0; + } + clib_mem_trace (1); + error = + vlib_cli_dispatch_sub_commands (vm, cm, &sub_input, + parent_command_index); + unformat_free (&sub_input); + + /* Otherwise, the clib_error_t shows up as a leak... */ + if (error) + { + vlib_cli_output (vm, "%v", error->what); + clib_error_free (error); + error = 0; + } + + (void) clib_mem_trace_enable_disable (0); + leak_report = format (0, "%U", format_mheap, clib_mem_get_heap (), + 1 /* verbose, i.e. print leaks */ ); + clib_mem_trace (0); + vlib_cli_output (vm, "%v", leak_report); + vec_free (leak_report); + } else if (unformat_user (input, unformat_vlib_cli_sub_command, vm, parent, &c)) @@ -581,14 +544,58 @@ vlib_cli_dispatch_sub_commands (vlib_main_t * vm, } else { + if (PREDICT_FALSE (vm->elog_trace_cli_commands)) + { + /* *INDENT-OFF* */ + ELOG_TYPE_DECLARE (e) = + { + .format = "cli-cmd: %s", + .format_args = "T4", + }; + /* *INDENT-ON* */ + struct + { + u32 c; + } *ed; + ed = ELOG_DATA (&vm->elog_main, e); + ed->c = elog_string (&vm->elog_main, "%v", c->path); + } + if (!c->is_mp_safe) vlib_worker_thread_barrier_sync (vm); + c->hit_counter++; c_error = c->function (vm, si, c); if (!c->is_mp_safe) vlib_worker_thread_barrier_release (vm); + if (PREDICT_FALSE (vm->elog_trace_cli_commands)) + { + /* *INDENT-OFF* */ + ELOG_TYPE_DECLARE (e) = + { + .format = "cli-cmd: %s %s", + .format_args = "T4T4", + }; + /* *INDENT-ON* */ + struct + { + u32 c, err; + } *ed; + ed = ELOG_DATA (&vm->elog_main, e); + ed->c = elog_string (&vm->elog_main, "%v", c->path); + if (c_error) + { + vec_add1 (c_error->what, 0); + ed->err = + elog_string (&vm->elog_main, (char *) c_error->what); + _vec_len (c_error->what) -= 1; + } + else + ed->err = elog_string (&vm->elog_main, "OK"); + } + if (c_error) { error = @@ -638,16 +645,16 @@ vlib_unix_error_report (vlib_main_t * vm, clib_error_t * error) } /* Process CLI input. */ -void +int vlib_cli_input (vlib_main_t * vm, unformat_input_t * input, vlib_cli_output_function_t * function, uword function_arg) { vlib_process_t *cp = vlib_get_current_process (vm); - vlib_cli_main_t *cm = &vm->cli_main; clib_error_t *error; vlib_cli_output_function_t *save_function; uword save_function_arg; + int rv = 0; save_function = cp->output_function; save_function_arg = cp->output_function_arg; @@ -657,9 +664,8 @@ vlib_cli_input (vlib_main_t * vm, do { - vec_reset_length (cm->parse_rule_data); - error = vlib_cli_dispatch_sub_commands (vm, &vm->cli_main, input, /* parent */ - 0); + error = vlib_cli_dispatch_sub_commands (vm, &vm->cli_main, input, + /* parent */ 0); } while (!error && !unformat (input, "%U", unformat_eof)); @@ -667,11 +673,15 @@ vlib_cli_input (vlib_main_t * vm, { vlib_cli_output (vm, "%v", error->what); vlib_unix_error_report (vm, error); + /* clib_error_return is unfortunately often called with a '0' + return code */ + rv = error->code != 0 ? error->code : -1; clib_error_free (error); } cp->output_function = save_function; cp->output_function_arg = save_function_arg; + return rv; } /* Output to current CLI connection. */ @@ -698,18 +708,48 @@ vlib_cli_output (vlib_main_t * vm, char *fmt, ...) vec_free (s); } +void *vl_msg_push_heap (void) __attribute__ ((weak)); +void * +vl_msg_push_heap (void) +{ + return 0; +} + +void vl_msg_pop_heap (void *oldheap) __attribute__ ((weak)); +void +vl_msg_pop_heap (void *oldheap) +{ +} + +void *vlib_stats_push_heap (void *) __attribute__ ((weak)); +void * +vlib_stats_push_heap (void *notused) +{ + return 0; +} + static clib_error_t * show_memory_usage (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { - int verbose = 0; + int verbose __attribute__ ((unused)) = 0; + int api_segment = 0, stats_segment = 0, main_heap = 0; clib_error_t *error; u32 index = 0; + uword clib_mem_trace_enable_disable (uword enable); + uword was_enabled; + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "verbose")) verbose = 1; + else if (unformat (input, "api-segment")) + api_segment = 1; + else if (unformat (input, "stats-segment")) + stats_segment = 1; + else if (unformat (input, "main-heap")) + main_heap = 1; else { error = clib_error_return (0, "unknown input `%U'", @@ -718,21 +758,106 @@ show_memory_usage (vlib_main_t * vm, } } + if ((api_segment + stats_segment + main_heap) == 0) + return clib_error_return + (0, "Please supply one of api-segment, stats-segment or main-heap"); + + if (api_segment) + { + void *oldheap = vl_msg_push_heap (); + was_enabled = clib_mem_trace_enable_disable (0); + u8 *s_in_svm = + format (0, "%U\n", format_mheap, clib_mem_get_heap (), 1); + vl_msg_pop_heap (oldheap); + u8 *s = vec_dup (s_in_svm); + + oldheap = vl_msg_push_heap (); + vec_free (s_in_svm); + clib_mem_trace_enable_disable (was_enabled); + vl_msg_pop_heap (oldheap); + vlib_cli_output (vm, "API segment"); + vlib_cli_output (vm, "%v", s); + vec_free (s); + } + if (stats_segment) + { + void *oldheap = vlib_stats_push_heap (0); + was_enabled = clib_mem_trace_enable_disable (0); + u8 *s_in_svm = + format (0, "%U\n", format_mheap, clib_mem_get_heap (), 1); + if (oldheap) + clib_mem_set_heap (oldheap); + u8 *s = vec_dup (s_in_svm); + + oldheap = vlib_stats_push_heap (0); + vec_free (s_in_svm); + if (oldheap) + { + clib_mem_trace_enable_disable (was_enabled); + clib_mem_set_heap (oldheap); + } + vlib_cli_output (vm, "Stats segment"); + vlib_cli_output (vm, "%v", s); + vec_free (s); + } + +#if USE_DLMALLOC == 0 /* *INDENT-OFF* */ foreach_vlib_main ( ({ - vlib_cli_output (vm, "Thread %d %v\n", index, vlib_worker_threads[index].name); - vlib_cli_output (vm, "%U\n", format_mheap, clib_per_cpu_mheaps[index], verbose); + mheap_t *h = mheap_header (clib_per_cpu_mheaps[index]); + vlib_cli_output (vm, "%sThread %d %s\n", index ? "\n":"", index, + vlib_worker_threads[index].name); + vlib_cli_output (vm, " %U\n", format_page_map, pointer_to_uword (h) - + h->vm_alloc_offset_from_header, + h->vm_alloc_size); + vlib_cli_output (vm, " %U\n", format_mheap, clib_per_cpu_mheaps[index], + verbose); index++; })); /* *INDENT-ON* */ +#else + { + if (main_heap) + { + /* + * Note: the foreach_vlib_main causes allocator traffic, + * so shut off tracing before we go there... + */ + was_enabled = clib_mem_trace_enable_disable (0); + + /* *INDENT-OFF* */ + foreach_vlib_main ( + ({ + struct dlmallinfo mi; + void *mspace; + mspace = clib_per_cpu_mheaps[index]; + + mi = mspace_mallinfo (mspace); + vlib_cli_output (vm, "%sThread %d %s\n", index ? "\n":"", index, + vlib_worker_threads[index].name); + vlib_cli_output (vm, " %U\n", format_page_map, + pointer_to_uword (mspace_least_addr(mspace)), + mi.arena); + vlib_cli_output (vm, " %U\n", format_mheap, + clib_per_cpu_mheaps[index], + verbose); + index++; + })); + /* *INDENT-ON* */ + + /* Restore the trace flag */ + clib_mem_trace_enable_disable (was_enabled); + } + } +#endif /* USE_DLMALLOC */ return 0; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (show_memory_usage_command, static) = { .path = "show memory", - .short_help = "Show current memory usage", + .short_help = "show memory [api-segment][stats-segment][verbose]", .function = show_memory_usage, }; /* *INDENT-ON* */ @@ -743,7 +868,7 @@ show_cpu (vlib_main_t * vm, unformat_input_t * input, { #define _(a,b,c) vlib_cli_output (vm, "%-25s " b, a ":", c); _("Model name", "%U", format_cpu_model_name); - _("Microarchitecture", "%U", format_cpu_uarch); + _("Microarch model (family)", "%U", format_cpu_uarch); _("Flags", "%U", format_cpu_flags); _("Base frequency", "%.2f GHz", ((f64) vm->clib_time.clocks_per_second) * 1e-9); @@ -768,32 +893,95 @@ VLIB_CLI_COMMAND (show_cpu_command, static) = { .short_help = "Show cpu information", .function = show_cpu, }; - /* *INDENT-ON* */ + static clib_error_t * enable_disable_memory_trace (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { - clib_error_t *error = 0; - int enable; + unformat_input_t _line_input, *line_input = &_line_input; + int enable = 1; + int api_segment = 0; + int stats_segment = 0; + int main_heap = 0; + void *oldheap; + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; - if (!unformat_user (input, unformat_vlib_enable_disable, &enable)) + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) { - error = clib_error_return (0, "expecting enable/on or disable/off"); - goto done; + if (unformat (line_input, "%U", unformat_vlib_enable_disable, &enable)) + ; + else if (unformat (line_input, "api-segment")) + api_segment = 1; + else if (unformat (line_input, "stats-segment")) + stats_segment = 1; + else if (unformat (line_input, "main-heap")) + main_heap = 1; + else + { + unformat_free (line_input); + return clib_error_return (0, "invalid input"); + } } + unformat_free (line_input); - clib_mem_trace (enable); + if ((api_segment + stats_segment + main_heap + (enable == 0)) == 0) + { + return clib_error_return + (0, "Need one of main-heap, stats-segment or api-segment"); + } -done: - return error; + /* Turn off current trace, if any */ + if (current_traced_heap) + { + void *oldheap; + oldheap = clib_mem_set_heap (current_traced_heap); + clib_mem_trace (0); + clib_mem_set_heap (oldheap); + current_traced_heap = 0; + } + + if (enable == 0) + return 0; + + /* API segment */ + if (api_segment) + { + oldheap = vl_msg_push_heap (); + current_traced_heap = clib_mem_get_heap (); + clib_mem_trace (1); + vl_msg_pop_heap (oldheap); + + } + + /* Stats segment */ + if (stats_segment) + { + oldheap = vlib_stats_push_heap (0); + current_traced_heap = clib_mem_get_heap (); + clib_mem_trace (stats_segment); + /* We don't want to call vlib_stats_pop_heap... */ + if (oldheap) + clib_mem_set_heap (oldheap); + } + + /* main_heap */ + if (main_heap) + { + current_traced_heap = clib_mem_get_heap (); + clib_mem_trace (main_heap); + } + + return 0; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (enable_disable_memory_trace_command, static) = { .path = "memory-trace", - .short_help = "Enable/disable memory allocation trace", + .short_help = "memory-trace on|off [api-segment][stats-segment][main-heap]\n", .function = enable_disable_memory_trace, }; /* *INDENT-ON* */ @@ -803,6 +991,7 @@ static clib_error_t * test_heap_validate (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { +#if USE_DLMALLOC == 0 clib_error_t *error = 0; void *heap; mheap_t *mheap; @@ -850,6 +1039,9 @@ test_heap_validate (vlib_main_t * vm, unformat_input_t * input, } return error; +#else + return clib_error_return (0, "unimplemented..."); +#endif /* USE_DLMALLOC */ } /* *INDENT-OFF* */ @@ -864,9 +1056,23 @@ static clib_error_t * restart_cmd_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { - char *newenviron[] = { NULL }; + clib_file_main_t *fm = &file_main; + clib_file_t *f; - execve (vm->name, (char **) vm->argv, newenviron); + /* environ(7) does not indicate a header for this */ + extern char **environ; + + /* Close all known open files */ + /* *INDENT-OFF* */ + pool_foreach(f, fm->file_pool, + ({ + if (f->file_descriptor > 2) + close(f->file_descriptor); + })); + /* *INDENT-ON* */ + + /* Exec ourself */ + execve (vm->name, (char **) vm->argv, environ); return 0; } @@ -1007,19 +1213,11 @@ add_sub_command (vlib_cli_main_t * cm, uword parent_index, uword child_index) return; } - q = hash_get_mem (cm->parse_rule_index_by_name, sub_name); - if (!q) - { - clib_error ("reference to unknown rule `%%%v' in path `%v'", - sub_name, c->path); - return; - } - hash_set_mem (p->sub_rule_index_by_name, sub_name, vec_len (p->sub_rules)); vec_add2 (p->sub_rules, sr, 1); sr->name = sub_name; - sr->rule_index = q[0]; + sr->rule_index = sr - p->sub_rules; sr->command_index = child_index; return; } @@ -1203,6 +1401,8 @@ vlib_cli_register (vlib_main_t * vm, vlib_cli_command_t * c) return 0; } +#if 0 +/* $$$ turn back on again someday, maybe */ clib_error_t * vlib_cli_register_parse_rule (vlib_main_t * vm, vlib_cli_parse_rule_t * r_reg) { @@ -1235,8 +1435,6 @@ vlib_cli_register_parse_rule (vlib_main_t * vm, vlib_cli_parse_rule_t * r_reg) return error; } -#if 0 -/* $$$ turn back on again someday, maybe */ static clib_error_t *vlib_cli_register_parse_rules (vlib_main_t * vm, vlib_cli_parse_rule_t * lo, @@ -1265,52 +1463,328 @@ done: } #endif -static int -cli_path_compare (void *a1, void *a2) +static clib_error_t * +elog_trace_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) { - u8 **s1 = a1; - u8 **s2 = a2; + unformat_input_t _line_input, *line_input = &_line_input; + int enable = 1; + int api = 0, cli = 0, barrier = 0, dispatch = 0, circuit = 0; + u32 circuit_node_index; + + if (!unformat_user (input, unformat_line_input, line_input)) + goto print_status; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "api")) + api = 1; + else if (unformat (line_input, "dispatch")) + dispatch = 1; + else if (unformat (line_input, "circuit-node %U", + unformat_vlib_node, vm, &circuit_node_index)) + circuit = 1; + else if (unformat (line_input, "cli")) + cli = 1; + else if (unformat (line_input, "barrier")) + barrier = 1; + else if (unformat (line_input, "disable")) + enable = 0; + else if (unformat (line_input, "enable")) + enable = 1; + else + break; + } + unformat_free (line_input); + + vl_api_set_elog_trace_api_messages + (api ? enable : vl_api_get_elog_trace_api_messages ()); + vm->elog_trace_cli_commands = cli ? enable : vm->elog_trace_cli_commands; + vm->elog_trace_graph_dispatch = dispatch ? + enable : vm->elog_trace_graph_dispatch; + vm->elog_trace_graph_circuit = circuit ? + enable : vm->elog_trace_graph_circuit; + vlib_worker_threads->barrier_elog_enabled = + barrier ? enable : vlib_worker_threads->barrier_elog_enabled; + vm->elog_trace_graph_circuit_node_index = circuit_node_index; + + /* + * Set up start-of-buffer logic-analyzer trigger + * for main loop event logs, which are fairly heavyweight. + * See src/vlib/main/vlib_elog_main_loop_event(...), which + * will fully disable the scheme when the elog buffer fills. + */ + if (dispatch || circuit) + { + elog_main_t *em = &vm->elog_main; - if ((vec_len (*s1) < vec_len (*s2)) && - memcmp ((char *) *s1, (char *) *s2, vec_len (*s1)) == 0) - return -1; + em->n_total_events_disable_limit = + em->n_total_events + vec_len (em->event_ring); + } - if ((vec_len (*s1) > vec_len (*s2)) && - memcmp ((char *) *s1, (char *) *s2, vec_len (*s2)) == 0) - return 1; +print_status: + vlib_cli_output (vm, "Current status:"); + + vlib_cli_output + (vm, " Event log API message trace: %s\n CLI command trace: %s", + vl_api_get_elog_trace_api_messages ()? "on" : "off", + vm->elog_trace_cli_commands ? "on" : "off"); + vlib_cli_output + (vm, " Barrier sync trace: %s", + vlib_worker_threads->barrier_elog_enabled ? "on" : "off"); + vlib_cli_output + (vm, " Graph Dispatch: %s", + vm->elog_trace_graph_dispatch ? "on" : "off"); + vlib_cli_output + (vm, " Graph Circuit: %s", + vm->elog_trace_graph_circuit ? "on" : "off"); + if (vm->elog_trace_graph_circuit) + vlib_cli_output + (vm, " node %U", + format_vlib_node_name, vm, vm->elog_trace_graph_circuit_node_index); - return vec_cmp (*s1, *s2); + return 0; } +/*? + * Control event logging of api, cli, and thread barrier events + * With no arguments, displays the current trace status. + * Name the event groups you wish to trace or stop tracing. + * + * @cliexpar + * @clistart + * elog trace api cli barrier + * elog trace api cli barrier disable + * elog trace dispatch + * elog trace circuit-node ethernet-input + * elog trace + * @cliend + * @cliexcmd{elog trace [api][cli][barrier][disable]} +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (elog_trace_command, static) = +{ + .path = "elog trace", + .short_help = "elog trace [api][cli][barrier][dispatch]\n" + "[circuit-node e.g. ethernet-input][disable]", + .function = elog_trace_command_fn, +}; +/* *INDENT-ON* */ + static clib_error_t * -show_cli_cmd_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) +suspend_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vlib_process_suspend (vm, 30e-3); + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (suspend_command, static) = { + .path = "suspend", + .short_help = "suspend debug CLI for 30ms", + .function = suspend_command_fn, + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ + + +static int +sort_cmds_by_path (void *a1, void *a2) +{ + u32 *index1 = a1; + u32 *index2 = a2; + vlib_main_t *vm = vlib_get_main (); vlib_cli_main_t *cm = &vm->cli_main; - vlib_cli_command_t *cli; - u8 **paths = 0, **s; + vlib_cli_command_t *c1, *c2; + int i, lmin; - /* *INDENT-OFF* */ - vec_foreach (cli, cm->commands) - if (vec_len (cli->path) > 0) - vec_add1 (paths, (u8 *) cli->path); + c1 = vec_elt_at_index (cm->commands, *index1); + c2 = vec_elt_at_index (cm->commands, *index2); - vec_sort_with_function (paths, cli_path_compare); + lmin = vec_len (c1->path); + lmin = (vec_len (c2->path) >= lmin) ? lmin : vec_len (c2->path); - vec_foreach (s, paths) - vlib_cli_output (vm, "%v", *s); - /* *INDENT-ON* */ + for (i = 0; i < lmin; i++) + { + if (c1->path[i] < c2->path[i]) + return -1; + else if (c1->path[i] > c2->path[i]) + return 1; + } - vec_free (paths); return 0; } +typedef struct +{ + vlib_cli_main_t *cm; + u32 parent_command_index; + int show_mp_safe; + int show_not_mp_safe; + int show_hit; + int clear_hit; +} vlib_cli_walk_args_t; + +static void +cli_recursive_walk (vlib_cli_walk_args_t * aa) +{ + vlib_cli_command_t *parent; + vlib_cli_sub_command_t *sub; + vlib_cli_walk_args_t _a, *a = &_a; + vlib_cli_main_t *cm; + int i; + + /* Copy args into this stack frame */ + *a = *aa; + cm = a->cm; + + parent = vec_elt_at_index (cm->commands, a->parent_command_index); + + if (parent->function) + { + if (((a->show_mp_safe && parent->is_mp_safe) + || (a->show_not_mp_safe && !parent->is_mp_safe)) + && (a->show_hit == 0 || parent->hit_counter)) + { + vec_add1 (cm->sort_vector, a->parent_command_index); + } + + if (a->clear_hit) + parent->hit_counter = 0; + } + + for (i = 0; i < vec_len (parent->sub_commands); i++) + { + sub = vec_elt_at_index (parent->sub_commands, i); + a->parent_command_index = sub->index; + cli_recursive_walk (a); + } +} + +static u8 * +format_mp_safe (u8 * s, va_list * args) +{ + vlib_cli_main_t *cm = va_arg (*args, vlib_cli_main_t *); + int show_mp_safe = va_arg (*args, int); + int show_not_mp_safe = va_arg (*args, int); + int show_hit = va_arg (*args, int); + int clear_hit = va_arg (*args, int); + vlib_cli_command_t *c; + vlib_cli_walk_args_t _a, *a = &_a; + int i; + char *format_string = "\n%v"; + + if (show_hit) + format_string = "\n%v: %u"; + + vec_reset_length (cm->sort_vector); + + a->cm = cm; + a->parent_command_index = 0; + a->show_mp_safe = show_mp_safe; + a->show_not_mp_safe = show_not_mp_safe; + a->show_hit = show_hit; + a->clear_hit = clear_hit; + + cli_recursive_walk (a); + + vec_sort_with_function (cm->sort_vector, sort_cmds_by_path); + + for (i = 0; i < vec_len (cm->sort_vector); i++) + { + c = vec_elt_at_index (cm->commands, cm->sort_vector[i]); + s = format (s, format_string, c->path, c->hit_counter); + } + + return s; +} + + +static clib_error_t * +show_cli_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + int show_mp_safe = 0; + int show_not_mp_safe = 0; + int show_hit = 0; + int clear_hit = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "mp-safe")) + show_mp_safe = 1; + if (unformat (input, "not-mp-safe")) + show_not_mp_safe = 1; + else if (unformat (input, "hit")) + show_hit = 1; + else if (unformat (input, "clear-hit")) + clear_hit = 1; + else + break; + } + + /* default set: all cli commands */ + if (clear_hit == 0 && (show_mp_safe + show_not_mp_safe) == 0) + show_mp_safe = show_not_mp_safe = 1; + + vlib_cli_output (vm, "%U", format_mp_safe, &vm->cli_main, + show_mp_safe, show_not_mp_safe, show_hit, clear_hit); + if (clear_hit) + vlib_cli_output (vm, "hit counters cleared..."); + + return 0; +} + +/*? + * Displays debug cli command information + * + * @cliexpar + * @cliexstart{show cli [mp-safe][not-mp-safe][hit][clear-hit]} + * + * "show cli" displays the entire debug cli: + * + * abf attach + * abf policy + * adjacency counters + * api trace + * app ns + * bfd key del + * ... and so on ... + * + * "show cli mp-safe" displays mp-safe debug CLI commands: + * + * abf policy + * binary-api + * create vhost-user + * exec + * ip container + * ip mroute + * ip probe-neighbor + * ip route + * ip scan-neighbor + * ip table + * ip6 table + * + * "show cli not-mp-safe" displays debug CLI commands + * which cause worker thread barrier synchronization + * + * "show cli hit" displays commands which have been executed. Qualify + * as desired with "mp-safe" or "not-mp-safe". + * + * "show cli clear-hit" clears the per-command hit counters. + * @cliexend +?*/ + /* *INDENT-OFF* */ -VLIB_CLI_COMMAND (show_cli_command, static) = { +VLIB_CLI_COMMAND (show_cli_command, static) = +{ .path = "show cli", - .short_help = "Show cli commands", - .function = show_cli_cmd_fn, + .short_help = "show cli [mp-safe][not-mp-safe][hit][clear-hit]", + .function = show_cli_command_fn, + .is_mp_safe = 1, }; /* *INDENT-ON* */