| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (C) 2022 Google Inc, Steven Rostedt <rostedt@goodmis.org> |
| */ |
| #include "ktrace.h" |
| |
| static struct ktrace_commands create_cmds[] = |
| { |
| { |
| "kprobe", |
| "create kprobe name function/address fields", |
| "# Create a kprobe with 'name' at a function or an address given.\n" |
| "# followed by a set of fields." |
| }, |
| { |
| "eprobe", |
| "create eprobe name system/event fields", |
| "# Create an event on top of another event.\n" |
| "# Where 'name' is the new event.\n" |
| "# 'system/event' is the event to attach to.\n" |
| "# Followed by the fields to display", |
| }, |
| { |
| "synthetic", |
| "create synthetic name system/event.field1[,field2,..] system2/event2.field1[,field2,..] field1=systemX/eventX.field[ field2=systemX/eventX.field ...]", |
| "# Where the synthetic event is created when the fields of system/event\n" |
| "# match system/event2 fields. Then add the fields of the synthetic event\n" |
| "# on how they will map to the other fields.\n#\n" |
| "# A synthetic event field may also equal timestamps:\n" |
| "# start=system/event.TIMESTAMP\n" |
| "# start=system/event.TIMESTAMP_USECS\n#\n" |
| "# Or even a delta:\n" |
| "# delta=system/event.TIMESTAMP-system2/event2.TIMESTAMP" |
| }, |
| { |
| NULL |
| } |
| }; |
| |
| static struct ktrace_commands enable_cmds[] = |
| { |
| { |
| "tracing", |
| "enable tracing", |
| " Makes the ring buffer writable" |
| }, |
| { |
| "event", |
| "enable event (all|system|system/event)", |
| " Enable an event.\n" |
| " Key word 'all' will enable all events\n" |
| " Specify just a system to enable all events within that system.\n" |
| " Specify system/event, with '/' to separate the two, to enable just one event." |
| }, |
| { |
| NULL |
| } |
| }; |
| |
| static struct ktrace_commands disable_cmds[] = |
| { |
| { |
| "tracing", |
| "disable tracing", |
| " Disable writing to the ring buffer" |
| }, |
| { |
| "event", |
| "disable event (all|system|system/event)", |
| " Disable an event.\n" |
| " Key word 'all' will disable all events\n" |
| " Specify just a system to disable all events within that system.\n" |
| " Specify system/event, with '/' to separate the two, to disable just one event." |
| }, |
| { |
| NULL |
| } |
| }; |
| |
| static struct ktrace_commands ktrace_cmds[] = |
| { |
| { |
| "create", |
| "create (kprobe|eprobe|synthetic) options", |
| " kprobe - to create a kernel probe event.\n" |
| " eprobe - to create an event on top of another event.\n" |
| " synthetic - to create a synthetic event", |
| .link = create_cmds |
| }, |
| { |
| "enable", |
| "enable (tracing|event)", |
| " tracing - to enable tracing if it is stopped.\n" |
| " event - to enable an event", |
| .link = enable_cmds |
| }, |
| { |
| "disable", |
| "disable (tracing|event)", |
| " tracing - to disable tracing, just stops writing to the ring buffer.\n" |
| " event - stop an event.", |
| .link = disable_cmds |
| }, |
| { |
| NULL |
| } |
| }; |
| |
| static int help_cmd(struct ccli *ccli, const char *arg, |
| struct ktrace_commands *commands) |
| { |
| int l = 1; |
| int i; |
| |
| for (i = 0; l > 0 && commands[i].command; i++) { |
| if (strcmp(arg, commands[i].command) == 0) { |
| l = ccli_page(ccli, l, "usage: %s\n", |
| commands[i].usage); |
| l = ccli_page(ccli, l, "%s\n", |
| commands[i].help); |
| return 0; |
| } |
| } |
| ccli_printf(ccli, "Command %s not found\n", arg); |
| return 0; |
| } |
| |
| int ktrace_help(struct ccli *ccli, const char *arg1, const char *arg2) |
| { |
| int l = 1; |
| int i; |
| |
| if (!arg1) { |
| for (i = 0; l > 0 && ktrace_cmds[i].command; i++) { |
| if (i) |
| l = ccli_page(ccli, l, "\n"); |
| l = ccli_page(ccli, l, "command: %s\n", |
| ktrace_cmds[i].command); |
| l = ccli_page(ccli, l, "usage: %s\n", |
| ktrace_cmds[i].usage); |
| l = ccli_page(ccli, l, "%s\n", |
| ktrace_cmds[i].help); |
| } |
| return 0; |
| } |
| |
| if (!arg2) |
| return help_cmd(ccli, arg1, ktrace_cmds); |
| |
| for (i = 0; l > 0 && ktrace_cmds[i].command; i++) { |
| if (strcmp(arg1, ktrace_cmds[i].command) == 0) { |
| if (ktrace_cmds[i].link) |
| return help_cmd(ccli, arg2, ktrace_cmds[i].link); |
| l = ccli_page(ccli, l, "usage: %s\n", |
| ktrace_cmds[i].usage); |
| l = ccli_page(ccli, l, "%s\n", |
| ktrace_cmds[i].help); |
| return 0; |
| } |
| ccli_printf(ccli, "Command %s not found\n", arg1); |
| } |
| |
| return 0; |
| } |
| |
| int cmd_help(struct ccli *ccli, const char *command, const char *line, |
| void *data, int argc, char **argv) |
| { |
| if (argc < 2) |
| return ktrace_help(ccli, NULL, NULL); |
| |
| if (argc < 3) |
| return ktrace_help(ccli, argv[1], NULL); |
| |
| return ktrace_help(ccli, argv[1], argv[2]); |
| } |
| |
| static int help_complete(struct ccli *ccli, char ***list, |
| struct ktrace_commands *commands) |
| { |
| int cnt = 0; |
| int ret = 0; |
| int i; |
| |
| for (i = 0; ret >= 0 && commands[i].command; i++) |
| ret = ccli_list_add(ccli, list, &cnt, commands[i].command); |
| |
| return ret; |
| } |
| |
| int help_completion(struct ccli *ccli, const char *command, |
| const char *line, int word, |
| char *match, char ***list, void *data) |
| { |
| char **argv; |
| int ret = 0; |
| int argc; |
| int i; |
| |
| if (word > 2) |
| return 0; |
| |
| if (word == 1) |
| return help_complete(ccli, list, ktrace_cmds); |
| |
| argc = ccli_line_parse(line, &argv); |
| if (argc < 1) |
| return 0; |
| |
| ret = 0; |
| for (i = 0; argc > 1 && ktrace_cmds[i].command; i++) { |
| if (strcmp(argv[1], ktrace_cmds[i].command) == 0) { |
| if (!ktrace_cmds[i].link) |
| break; |
| ret = help_complete(ccli, list, ktrace_cmds[i].link); |
| break; |
| } |
| } |
| ccli_argv_free(argv); |
| return ret; |
| } |