diff options
-rw-r--r-- | tools/perf/util/Build | 1 | ||||
-rw-r--r-- | tools/perf/util/help-unknown-cmd.c | 103 | ||||
-rw-r--r-- | tools/perf/util/help-unknown-cmd.h | 0 | ||||
-rw-r--r-- | tools/perf/util/help.c | 107 | ||||
-rw-r--r-- | tools/perf/util/help.h | 3 |
5 files changed, 110 insertions, 104 deletions
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 65fef5951c7d..99b3dae57806 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build | |||
@@ -87,6 +87,7 @@ libperf-$(CONFIG_AUXTRACE) += intel-bts.o | |||
87 | libperf-y += parse-branch-options.o | 87 | libperf-y += parse-branch-options.o |
88 | libperf-y += parse-regs-options.o | 88 | libperf-y += parse-regs-options.o |
89 | libperf-y += term.o | 89 | libperf-y += term.o |
90 | libperf-y += help-unknown-cmd.o | ||
90 | 91 | ||
91 | libperf-$(CONFIG_LIBBPF) += bpf-loader.o | 92 | libperf-$(CONFIG_LIBBPF) += bpf-loader.o |
92 | libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o | 93 | libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o |
diff --git a/tools/perf/util/help-unknown-cmd.c b/tools/perf/util/help-unknown-cmd.c new file mode 100644 index 000000000000..a0820f16f511 --- /dev/null +++ b/tools/perf/util/help-unknown-cmd.c | |||
@@ -0,0 +1,103 @@ | |||
1 | #include "cache.h" | ||
2 | #include "help.h" | ||
3 | #include "../builtin.h" | ||
4 | #include "levenshtein.h" | ||
5 | |||
6 | static int autocorrect; | ||
7 | static struct cmdnames aliases; | ||
8 | |||
9 | static int perf_unknown_cmd_config(const char *var, const char *value, void *cb) | ||
10 | { | ||
11 | if (!strcmp(var, "help.autocorrect")) | ||
12 | autocorrect = perf_config_int(var,value); | ||
13 | /* Also use aliases for command lookup */ | ||
14 | if (!prefixcmp(var, "alias.")) | ||
15 | add_cmdname(&aliases, var + 6, strlen(var + 6)); | ||
16 | |||
17 | return perf_default_config(var, value, cb); | ||
18 | } | ||
19 | |||
20 | static int levenshtein_compare(const void *p1, const void *p2) | ||
21 | { | ||
22 | const struct cmdname *const *c1 = p1, *const *c2 = p2; | ||
23 | const char *s1 = (*c1)->name, *s2 = (*c2)->name; | ||
24 | int l1 = (*c1)->len; | ||
25 | int l2 = (*c2)->len; | ||
26 | return l1 != l2 ? l1 - l2 : strcmp(s1, s2); | ||
27 | } | ||
28 | |||
29 | static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old) | ||
30 | { | ||
31 | unsigned int i; | ||
32 | |||
33 | ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc); | ||
34 | |||
35 | for (i = 0; i < old->cnt; i++) | ||
36 | cmds->names[cmds->cnt++] = old->names[i]; | ||
37 | zfree(&old->names); | ||
38 | old->cnt = 0; | ||
39 | } | ||
40 | |||
41 | const char *help_unknown_cmd(const char *cmd) | ||
42 | { | ||
43 | unsigned int i, n = 0, best_similarity = 0; | ||
44 | struct cmdnames main_cmds, other_cmds; | ||
45 | |||
46 | memset(&main_cmds, 0, sizeof(main_cmds)); | ||
47 | memset(&other_cmds, 0, sizeof(main_cmds)); | ||
48 | memset(&aliases, 0, sizeof(aliases)); | ||
49 | |||
50 | perf_config(perf_unknown_cmd_config, NULL); | ||
51 | |||
52 | load_command_list("perf-", &main_cmds, &other_cmds); | ||
53 | |||
54 | add_cmd_list(&main_cmds, &aliases); | ||
55 | add_cmd_list(&main_cmds, &other_cmds); | ||
56 | qsort(main_cmds.names, main_cmds.cnt, | ||
57 | sizeof(main_cmds.names), cmdname_compare); | ||
58 | uniq(&main_cmds); | ||
59 | |||
60 | if (main_cmds.cnt) { | ||
61 | /* This reuses cmdname->len for similarity index */ | ||
62 | for (i = 0; i < main_cmds.cnt; ++i) | ||
63 | main_cmds.names[i]->len = | ||
64 | levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4); | ||
65 | |||
66 | qsort(main_cmds.names, main_cmds.cnt, | ||
67 | sizeof(*main_cmds.names), levenshtein_compare); | ||
68 | |||
69 | best_similarity = main_cmds.names[0]->len; | ||
70 | n = 1; | ||
71 | while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len) | ||
72 | ++n; | ||
73 | } | ||
74 | |||
75 | if (autocorrect && n == 1) { | ||
76 | const char *assumed = main_cmds.names[0]->name; | ||
77 | |||
78 | main_cmds.names[0] = NULL; | ||
79 | clean_cmdnames(&main_cmds); | ||
80 | fprintf(stderr, "WARNING: You called a perf program named '%s', " | ||
81 | "which does not exist.\n" | ||
82 | "Continuing under the assumption that you meant '%s'\n", | ||
83 | cmd, assumed); | ||
84 | if (autocorrect > 0) { | ||
85 | fprintf(stderr, "in %0.1f seconds automatically...\n", | ||
86 | (float)autocorrect/10.0); | ||
87 | poll(NULL, 0, autocorrect * 100); | ||
88 | } | ||
89 | return assumed; | ||
90 | } | ||
91 | |||
92 | fprintf(stderr, "perf: '%s' is not a perf-command. See 'perf --help'.\n", cmd); | ||
93 | |||
94 | if (main_cmds.cnt && best_similarity < 6) { | ||
95 | fprintf(stderr, "\nDid you mean %s?\n", | ||
96 | n < 2 ? "this": "one of these"); | ||
97 | |||
98 | for (i = 0; i < n; i++) | ||
99 | fprintf(stderr, "\t%s\n", main_cmds.names[i]->name); | ||
100 | } | ||
101 | |||
102 | exit(1); | ||
103 | } | ||
diff --git a/tools/perf/util/help-unknown-cmd.h b/tools/perf/util/help-unknown-cmd.h new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/perf/util/help-unknown-cmd.h | |||
diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c index 929c93f2c333..8d74f7d05674 100644 --- a/tools/perf/util/help.c +++ b/tools/perf/util/help.c | |||
@@ -1,9 +1,7 @@ | |||
1 | #include "cache.h" | 1 | #include "cache.h" |
2 | #include "../builtin.h" | 2 | #include "../builtin.h" |
3 | #include "exec_cmd.h" | 3 | #include "exec_cmd.h" |
4 | #include "levenshtein.h" | ||
5 | #include "help.h" | 4 | #include "help.h" |
6 | #include <termios.h> | ||
7 | 5 | ||
8 | void add_cmdname(struct cmdnames *cmds, const char *name, size_t len) | 6 | void add_cmdname(struct cmdnames *cmds, const char *name, size_t len) |
9 | { | 7 | { |
@@ -17,7 +15,7 @@ void add_cmdname(struct cmdnames *cmds, const char *name, size_t len) | |||
17 | cmds->names[cmds->cnt++] = ent; | 15 | cmds->names[cmds->cnt++] = ent; |
18 | } | 16 | } |
19 | 17 | ||
20 | static void clean_cmdnames(struct cmdnames *cmds) | 18 | void clean_cmdnames(struct cmdnames *cmds) |
21 | { | 19 | { |
22 | unsigned int i; | 20 | unsigned int i; |
23 | 21 | ||
@@ -28,14 +26,14 @@ static void clean_cmdnames(struct cmdnames *cmds) | |||
28 | cmds->alloc = 0; | 26 | cmds->alloc = 0; |
29 | } | 27 | } |
30 | 28 | ||
31 | static int cmdname_compare(const void *a_, const void *b_) | 29 | int cmdname_compare(const void *a_, const void *b_) |
32 | { | 30 | { |
33 | struct cmdname *a = *(struct cmdname **)a_; | 31 | struct cmdname *a = *(struct cmdname **)a_; |
34 | struct cmdname *b = *(struct cmdname **)b_; | 32 | struct cmdname *b = *(struct cmdname **)b_; |
35 | return strcmp(a->name, b->name); | 33 | return strcmp(a->name, b->name); |
36 | } | 34 | } |
37 | 35 | ||
38 | static void uniq(struct cmdnames *cmds) | 36 | void uniq(struct cmdnames *cmds) |
39 | { | 37 | { |
40 | unsigned int i, j; | 38 | unsigned int i, j; |
41 | 39 | ||
@@ -233,102 +231,3 @@ int is_in_cmdlist(struct cmdnames *c, const char *s) | |||
233 | return 1; | 231 | return 1; |
234 | return 0; | 232 | return 0; |
235 | } | 233 | } |
236 | |||
237 | static int autocorrect; | ||
238 | static struct cmdnames aliases; | ||
239 | |||
240 | static int perf_unknown_cmd_config(const char *var, const char *value, void *cb) | ||
241 | { | ||
242 | if (!strcmp(var, "help.autocorrect")) | ||
243 | autocorrect = perf_config_int(var,value); | ||
244 | /* Also use aliases for command lookup */ | ||
245 | if (!prefixcmp(var, "alias.")) | ||
246 | add_cmdname(&aliases, var + 6, strlen(var + 6)); | ||
247 | |||
248 | return perf_default_config(var, value, cb); | ||
249 | } | ||
250 | |||
251 | static int levenshtein_compare(const void *p1, const void *p2) | ||
252 | { | ||
253 | const struct cmdname *const *c1 = p1, *const *c2 = p2; | ||
254 | const char *s1 = (*c1)->name, *s2 = (*c2)->name; | ||
255 | int l1 = (*c1)->len; | ||
256 | int l2 = (*c2)->len; | ||
257 | return l1 != l2 ? l1 - l2 : strcmp(s1, s2); | ||
258 | } | ||
259 | |||
260 | static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old) | ||
261 | { | ||
262 | unsigned int i; | ||
263 | |||
264 | ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc); | ||
265 | |||
266 | for (i = 0; i < old->cnt; i++) | ||
267 | cmds->names[cmds->cnt++] = old->names[i]; | ||
268 | zfree(&old->names); | ||
269 | old->cnt = 0; | ||
270 | } | ||
271 | |||
272 | const char *help_unknown_cmd(const char *cmd) | ||
273 | { | ||
274 | unsigned int i, n = 0, best_similarity = 0; | ||
275 | struct cmdnames main_cmds, other_cmds; | ||
276 | |||
277 | memset(&main_cmds, 0, sizeof(main_cmds)); | ||
278 | memset(&other_cmds, 0, sizeof(main_cmds)); | ||
279 | memset(&aliases, 0, sizeof(aliases)); | ||
280 | |||
281 | perf_config(perf_unknown_cmd_config, NULL); | ||
282 | |||
283 | load_command_list("perf-", &main_cmds, &other_cmds); | ||
284 | |||
285 | add_cmd_list(&main_cmds, &aliases); | ||
286 | add_cmd_list(&main_cmds, &other_cmds); | ||
287 | qsort(main_cmds.names, main_cmds.cnt, | ||
288 | sizeof(main_cmds.names), cmdname_compare); | ||
289 | uniq(&main_cmds); | ||
290 | |||
291 | if (main_cmds.cnt) { | ||
292 | /* This reuses cmdname->len for similarity index */ | ||
293 | for (i = 0; i < main_cmds.cnt; ++i) | ||
294 | main_cmds.names[i]->len = | ||
295 | levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4); | ||
296 | |||
297 | qsort(main_cmds.names, main_cmds.cnt, | ||
298 | sizeof(*main_cmds.names), levenshtein_compare); | ||
299 | |||
300 | best_similarity = main_cmds.names[0]->len; | ||
301 | n = 1; | ||
302 | while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len) | ||
303 | ++n; | ||
304 | } | ||
305 | |||
306 | if (autocorrect && n == 1) { | ||
307 | const char *assumed = main_cmds.names[0]->name; | ||
308 | |||
309 | main_cmds.names[0] = NULL; | ||
310 | clean_cmdnames(&main_cmds); | ||
311 | fprintf(stderr, "WARNING: You called a perf program named '%s', " | ||
312 | "which does not exist.\n" | ||
313 | "Continuing under the assumption that you meant '%s'\n", | ||
314 | cmd, assumed); | ||
315 | if (autocorrect > 0) { | ||
316 | fprintf(stderr, "in %0.1f seconds automatically...\n", | ||
317 | (float)autocorrect/10.0); | ||
318 | poll(NULL, 0, autocorrect * 100); | ||
319 | } | ||
320 | return assumed; | ||
321 | } | ||
322 | |||
323 | fprintf(stderr, "perf: '%s' is not a perf-command. See 'perf --help'.\n", cmd); | ||
324 | |||
325 | if (main_cmds.cnt && best_similarity < 6) { | ||
326 | fprintf(stderr, "\nDid you mean %s?\n", | ||
327 | n < 2 ? "this": "one of these"); | ||
328 | |||
329 | for (i = 0; i < n; i++) | ||
330 | fprintf(stderr, "\t%s\n", main_cmds.names[i]->name); | ||
331 | } | ||
332 | |||
333 | exit(1); | ||
334 | } | ||
diff --git a/tools/perf/util/help.h b/tools/perf/util/help.h index 7f5c6dedd714..14851b0e44f5 100644 --- a/tools/perf/util/help.h +++ b/tools/perf/util/help.h | |||
@@ -20,6 +20,9 @@ void load_command_list(const char *prefix, | |||
20 | struct cmdnames *main_cmds, | 20 | struct cmdnames *main_cmds, |
21 | struct cmdnames *other_cmds); | 21 | struct cmdnames *other_cmds); |
22 | void add_cmdname(struct cmdnames *cmds, const char *name, size_t len); | 22 | void add_cmdname(struct cmdnames *cmds, const char *name, size_t len); |
23 | void clean_cmdnames(struct cmdnames *cmds); | ||
24 | int cmdname_compare(const void *a, const void *b); | ||
25 | void uniq(struct cmdnames *cmds); | ||
23 | /* Here we require that excludes is a sorted list. */ | 26 | /* Here we require that excludes is a sorted list. */ |
24 | void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes); | 27 | void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes); |
25 | int is_in_cmdlist(struct cmdnames *c, const char *s); | 28 | int is_in_cmdlist(struct cmdnames *c, const char *s); |