Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf tools: Suggest inbuilt commands for unknown command

The existing unknown command code looks for perf scripts like
perf-archive.sh and perf-iostat.sh, however, inbuilt commands aren't
suggested. Add the inbuilt commands so they may be suggested too.

Before:

$ perf reccord
perf: 'reccord' is not a perf-command. See 'perf --help'.
$

After:

$ perf reccord
perf: 'reccord' is not a perf-command. See 'perf --help'.

Did you mean this?
record
$

Signed-off-by: Ian Rogers <irogers@google.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://lore.kernel.org/r/20240301201306.2680986-1-irogers@google.com
[ Added some fixes from Ian to problems I noticed while testing ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ian Rogers and committed by
Arnaldo Carvalho de Melo
f664d515 5f2f051a

+41 -31
+3 -1
tools/perf/builtin.h
··· 2 2 #ifndef BUILTIN_H 3 3 #define BUILTIN_H 4 4 5 + struct cmdnames; 6 + 5 7 void list_common_cmds_help(void); 6 - const char *help_unknown_cmd(const char *cmd); 8 + const char *help_unknown_cmd(const char *cmd, struct cmdnames *main_cmds); 7 9 8 10 int cmd_annotate(int argc, const char **argv); 9 11 int cmd_bench(int argc, const char **argv);
+17 -6
tools/perf/perf.c
··· 18 18 #include <subcmd/run-command.h> 19 19 #include "util/parse-events.h" 20 20 #include <subcmd/parse-options.h> 21 + #include <subcmd/help.h> 21 22 #include "util/debug.h" 22 23 #include "util/event.h" 23 24 #include "util/util.h" // usage() ··· 459 458 460 459 int main(int argc, const char **argv) 461 460 { 462 - int err; 461 + int err, done_help = 0; 463 462 const char *cmd; 464 463 char sbuf[STRERR_BUFSIZE]; 465 464 ··· 558 557 pthread__block_sigwinch(); 559 558 560 559 while (1) { 561 - static int done_help; 562 - 563 560 run_argv(&argc, &argv); 564 561 565 562 if (errno != ENOENT) 566 563 break; 567 564 568 565 if (!done_help) { 569 - cmd = argv[0] = help_unknown_cmd(cmd); 566 + struct cmdnames main_cmds = {}; 567 + 568 + for (unsigned int i = 0; i < ARRAY_SIZE(commands); i++) { 569 + add_cmdname(&main_cmds, 570 + commands[i].cmd, 571 + strlen(commands[i].cmd)); 572 + } 573 + cmd = argv[0] = help_unknown_cmd(cmd, &main_cmds); 574 + clean_cmdnames(&main_cmds); 570 575 done_help = 1; 576 + if (!cmd) 577 + break; 571 578 } else 572 579 break; 573 580 } 574 581 575 - fprintf(stderr, "Failed to run command '%s': %s\n", 576 - cmd, str_error_r(errno, sbuf, sizeof(sbuf))); 582 + if (cmd) { 583 + fprintf(stderr, "Failed to run command '%s': %s\n", 584 + cmd, str_error_r(errno, sbuf, sizeof(sbuf))); 585 + } 577 586 out: 578 587 if (debug_fp) 579 588 fclose(debug_fp);
+21 -24
tools/perf/util/help-unknown-cmd.c
··· 52 52 return 0; 53 53 } 54 54 55 - const char *help_unknown_cmd(const char *cmd) 55 + const char *help_unknown_cmd(const char *cmd, struct cmdnames *main_cmds) 56 56 { 57 57 unsigned int i, n = 0, best_similarity = 0; 58 - struct cmdnames main_cmds, other_cmds; 58 + struct cmdnames other_cmds; 59 59 60 - memset(&main_cmds, 0, sizeof(main_cmds)); 61 - memset(&other_cmds, 0, sizeof(main_cmds)); 60 + memset(&other_cmds, 0, sizeof(other_cmds)); 62 61 63 62 perf_config(perf_unknown_cmd_config, NULL); 64 63 65 - load_command_list("perf-", &main_cmds, &other_cmds); 64 + load_command_list("perf-", main_cmds, &other_cmds); 66 65 67 - if (add_cmd_list(&main_cmds, &other_cmds) < 0) { 66 + if (add_cmd_list(main_cmds, &other_cmds) < 0) { 68 67 fprintf(stderr, "ERROR: Failed to allocate command list for unknown command.\n"); 69 68 goto end; 70 69 } 71 - qsort(main_cmds.names, main_cmds.cnt, 72 - sizeof(main_cmds.names), cmdname_compare); 73 - uniq(&main_cmds); 70 + qsort(main_cmds->names, main_cmds->cnt, 71 + sizeof(main_cmds->names), cmdname_compare); 72 + uniq(main_cmds); 74 73 75 - if (main_cmds.cnt) { 74 + if (main_cmds->cnt) { 76 75 /* This reuses cmdname->len for similarity index */ 77 - for (i = 0; i < main_cmds.cnt; ++i) 78 - main_cmds.names[i]->len = 79 - levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4); 76 + for (i = 0; i < main_cmds->cnt; ++i) 77 + main_cmds->names[i]->len = 78 + levenshtein(cmd, main_cmds->names[i]->name, 0, 2, 1, 4); 80 79 81 - qsort(main_cmds.names, main_cmds.cnt, 82 - sizeof(*main_cmds.names), levenshtein_compare); 80 + qsort(main_cmds->names, main_cmds->cnt, 81 + sizeof(*main_cmds->names), levenshtein_compare); 83 82 84 - best_similarity = main_cmds.names[0]->len; 83 + best_similarity = main_cmds->names[0]->len; 85 84 n = 1; 86 - while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len) 85 + while (n < main_cmds->cnt && best_similarity == main_cmds->names[n]->len) 87 86 ++n; 88 87 } 89 88 90 89 if (autocorrect && n == 1) { 91 - const char *assumed = main_cmds.names[0]->name; 90 + const char *assumed = main_cmds->names[0]->name; 92 91 93 - main_cmds.names[0] = NULL; 94 - clean_cmdnames(&main_cmds); 92 + main_cmds->names[0] = NULL; 95 93 clean_cmdnames(&other_cmds); 96 94 fprintf(stderr, "WARNING: You called a perf program named '%s', " 97 95 "which does not exist.\n" ··· 105 107 106 108 fprintf(stderr, "perf: '%s' is not a perf-command. See 'perf --help'.\n", cmd); 107 109 108 - if (main_cmds.cnt && best_similarity < 6) { 110 + if (main_cmds->cnt && best_similarity < 6) { 109 111 fprintf(stderr, "\nDid you mean %s?\n", 110 112 n < 2 ? "this": "one of these"); 111 113 112 114 for (i = 0; i < n; i++) 113 - fprintf(stderr, "\t%s\n", main_cmds.names[i]->name); 115 + fprintf(stderr, "\t%s\n", main_cmds->names[i]->name); 114 116 } 115 117 end: 116 - clean_cmdnames(&main_cmds); 117 118 clean_cmdnames(&other_cmds); 118 - exit(1); 119 + return NULL; 119 120 }