Add functions used to generate kprobe event commands, built on top of the dynevent_cmd interface. gen_kprobe_cmd() is used to create a kprobe event command using a variable arg list. add_probe_fields() can be used to add single fields one by one or as a group. Once all desired fields are added, create_dynevent() is used to actually execute the command and create the event. gen_kprobe_cmd() can be used to create kretprobe commands as well. Signed-off-by: Tom Zanussi <zanussi@xxxxxxxxxx> --- include/linux/trace_events.h | 18 +++++ kernel/trace/trace_kprobe.c | 161 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 177 insertions(+), 2 deletions(-) diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index c25b18db84eb..7ae9ad182b0e 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -464,6 +464,24 @@ extern int add_synth_val(const char *field_name, u64 val, struct synth_gen_state *gen_state); extern int trace_synth_event_end(struct synth_gen_state *gen_state); +extern void kprobe_dynevent_cmd_init(struct dynevent_cmd *cmd, + char *buf, int maxlen); + +#define gen_kprobe_cmd(cmd, name, loc, ...) \ + __gen_kprobe_cmd(cmd, name, loc, false, ## __VA_ARGS__, NULL) + +#define gen_kretprobe_cmd(cmd, name, loc, ...) \ + __gen_kprobe_cmd(cmd, name, loc, true, ## __VA_ARGS__, NULL) + +extern int __gen_kprobe_cmd(struct dynevent_cmd *cmd, const char *name, + const char *loc, bool kretprobe, ...); + +#define add_probe_fields(cmd, ...) \ + __add_probe_fields(cmd, ## __VA_ARGS__, NULL) + +extern int __add_probe_fields(struct dynevent_cmd *cmd, ...); +extern int delete_kprobe_event(const char *name); + /* * Event file flags: * ENABLED - The event is enabled diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 25dac3745afb..f911b3d74b46 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -902,11 +902,168 @@ static int create_or_delete_trace_kprobe(int argc, char **argv) return ret == -ECANCELED ? -EINVAL : ret; } -int trace_kprobe_run_command(const char *command) +static int trace_kprobe_run_command(struct dynevent_cmd *cmd) { - return trace_run_command(command, create_or_delete_trace_kprobe); + return trace_run_command(cmd->buf, create_or_delete_trace_kprobe); } +/** + * kprobe_dynevent_cmd_init - Initialize a kprobe event command object + * @cmd: A pointer to the dynevent_cmd struct representing the new event + * @buf: A pointer to the buffer used to build the command + * @maxlen: The length of the buffer passed in @buf + * + * Initialize a synthetic event command object. Use this before + * calling any of the other dyenvent_cmd functions. + */ +void kprobe_dynevent_cmd_init(struct dynevent_cmd *cmd, char *buf, int maxlen) +{ + dynevent_cmd_init(cmd, buf, maxlen, DYNEVENT_TYPE_KPROBE, + trace_kprobe_run_command); +} +EXPORT_SYMBOL_GPL(kprobe_dynevent_cmd_init); + +/** + * __gen_kprobe_cmd - Generate a synthetic event command from arg list + * @cmd: A pointer to the dynevent_cmd struct representing the new event + * @name: The name of the kprobe event + * @loc: The location of the kprobe event + * @kretprobe: Is this a return probe? + * @args: Variable number of arg (pairs), one pair for each field + * + * NOTE: Users normally won't want to call this function directly, but + * rather use the gen_kprobe_cmd() wrapper, which automatically adds a + * NULL to the end of the arg list. If this function is used + * directly, make suer he last arg in the variable arg list is NULL. + * + * Generate a kprobe event command to be executed by + * create_dynevent(). This function can be used to generate the + * complete command or only the first part of it; in the latter case, + * add_probe_fields() can be used to add more fields following this. + * + * Return: 0 if successful, error otherwise. + */ +int __gen_kprobe_cmd(struct dynevent_cmd *cmd, const char *name, + const char *loc, bool kretprobe, ...) +{ + char buf[MAX_EVENT_NAME_LEN]; + struct dynevent_arg arg; + va_list args; + int ret; + + if (cmd->type != DYNEVENT_TYPE_KPROBE) + return -EINVAL; + + if (kretprobe) + snprintf(buf, MAX_EVENT_NAME_LEN, "r:%s", name); + else + snprintf(buf, MAX_EVENT_NAME_LEN, "p:%s", name); + + dynevent_arg_init(&arg, NULL, 0); + arg.str = buf; + ret = add_dynevent_arg(cmd, &arg); + if (ret) + return ret; + + dynevent_arg_init(&arg, NULL, 0); + arg.str = loc; + ret = add_dynevent_arg(cmd, &arg); + if (ret) + return ret; + + va_start(args, kretprobe); + for (;;) { + const char *field; + + field = va_arg(args, const char *); + if (!field) + break; + + if (++cmd->n_fields > MAX_TRACE_ARGS) { + ret = -EINVAL; + break; + } + + dynevent_arg_init(&arg, NULL, 0); + arg.str = field; + ret = add_dynevent_arg(cmd, &arg); + if (ret) + break; + } + va_end(args); + + return ret; +} +EXPORT_SYMBOL_GPL(__gen_kprobe_cmd); + +/** + * __add_probe_fields - Add probe fields to a kprobe command from arg list + * @cmd: A pointer to the dynevent_cmd struct representing the new event + * @args: Variable number of arg (pairs), one pair for each field + * + * NOTE: Users normally won't want to call this function directly, but + * rather use the add_probe_fields() wrapper, which automatically adds a + * NULL to the end of the arg list. If this function is used + * directly, make suer he last arg in the variable arg list is NULL. + * + * Add probe fields to an existing kprobe command using a variable + * list of args. Fields are added in the same order they're listed. + * + * Return: 0 if successful, error otherwise. + */ +int __add_probe_fields(struct dynevent_cmd *cmd, ...) +{ + struct dynevent_arg arg; + va_list args; + int ret; + + if (cmd->type != DYNEVENT_TYPE_KPROBE) + return -EINVAL; + + va_start(args, cmd); + for (;;) { + const char *field; + + field = va_arg(args, const char *); + if (!field) + break; + + if (++cmd->n_fields > MAX_TRACE_ARGS) { + ret = -EINVAL; + break; + } + + dynevent_arg_init(&arg, NULL, 0); + arg.str = field; + ret = add_dynevent_arg(cmd, &arg); + if (ret) + break; + } + va_end(args); + + return ret; +} +EXPORT_SYMBOL_GPL(__add_probe_fields); + +/** + * delete_kprobe_event - Delete a kprobe event + * @name: The name of the kprobe event to delete + * + * Delete a kprobe event with the give @name from kernel code rather + * than directly from the command line. + * + * Return: 0 if successful, error otherwise. + */ +int delete_kprobe_event(const char *name) +{ + char buf[MAX_EVENT_NAME_LEN]; + + snprintf(buf, MAX_EVENT_NAME_LEN, "-:%s", name); + + return trace_run_command(buf, create_or_delete_trace_kprobe); +} +EXPORT_SYMBOL_GPL(delete_kprobe_event); + static int trace_kprobe_release(struct dyn_event *ev) { struct trace_kprobe *tk = to_trace_kprobe(ev); -- 2.14.1