Merge tag 'trace-v5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt...
[linux-2.6-microblaze.git] / kernel / trace / trace_kprobe.c
index aa515d5..362cca5 100644 (file)
@@ -22,7 +22,6 @@
 
 #define KPROBE_EVENT_SYSTEM "kprobes"
 #define KRETPROBE_MAXACTIVE_MAX 4096
-#define MAX_KPROBE_CMDLINE_SIZE 1024
 
 /* Kprobe early definition from command line */
 static char kprobe_boot_events_buf[COMMAND_LINE_SIZE] __initdata;
@@ -902,6 +901,167 @@ static int create_or_delete_trace_kprobe(int argc, char **argv)
        return ret == -ECANCELED ? -EINVAL : ret;
 }
 
+static int trace_kprobe_run_command(struct dynevent_cmd *cmd)
+{
+       return trace_run_command(cmd->seq.buffer, create_or_delete_trace_kprobe);
+}
+
+/**
+ * kprobe_event_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 kprobe_event functions.
+ */
+void kprobe_event_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_event_cmd_init);
+
+/**
+ * __kprobe_event_gen_cmd_start - Generate a kprobe 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 kprobe_event_gen_cmd_start() wrapper, which automatically
+ * adds a NULL to the end of the arg list.  If this function is used
+ * directly, make sure the last arg in the variable arg list is NULL.
+ *
+ * Generate a kprobe event command to be executed by
+ * kprobe_event_gen_cmd_end().  This function can be used to generate the
+ * complete command or only the first part of it; in the latter case,
+ * kprobe_event_add_fields() can be used to add more fields following this.
+ *
+ * Return: 0 if successful, error otherwise.
+ */
+int __kprobe_event_gen_cmd_start(struct dynevent_cmd *cmd, bool kretprobe,
+                                const char *name, const char *loc, ...)
+{
+       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:kprobes/%s", name);
+       else
+               snprintf(buf, MAX_EVENT_NAME_LEN, "p:kprobes/%s", name);
+
+       ret = dynevent_str_add(cmd, buf);
+       if (ret)
+               return ret;
+
+       dynevent_arg_init(&arg, 0);
+       arg.str = loc;
+       ret = dynevent_arg_add(cmd, &arg, NULL);
+       if (ret)
+               return ret;
+
+       va_start(args, loc);
+       for (;;) {
+               const char *field;
+
+               field = va_arg(args, const char *);
+               if (!field)
+                       break;
+
+               if (++cmd->n_fields > MAX_TRACE_ARGS) {
+                       ret = -EINVAL;
+                       break;
+               }
+
+               arg.str = field;
+               ret = dynevent_arg_add(cmd, &arg, NULL);
+               if (ret)
+                       break;
+       }
+       va_end(args);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(__kprobe_event_gen_cmd_start);
+
+/**
+ * __kprobe_event_add_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 kprobe_event_add_fields() wrapper, which
+ * automatically adds a NULL to the end of the arg list.  If this
+ * function is used directly, make sure the 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 __kprobe_event_add_fields(struct dynevent_cmd *cmd, ...)
+{
+       struct dynevent_arg arg;
+       va_list args;
+       int ret = 0;
+
+       if (cmd->type != DYNEVENT_TYPE_KPROBE)
+               return -EINVAL;
+
+       dynevent_arg_init(&arg, 0);
+
+       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;
+               }
+
+               arg.str = field;
+               ret = dynevent_arg_add(cmd, &arg, NULL);
+               if (ret)
+                       break;
+       }
+       va_end(args);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(__kprobe_event_add_fields);
+
+/**
+ * kprobe_event_delete - 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 kprobe_event_delete(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(kprobe_event_delete);
+
 static int trace_kprobe_release(struct dyn_event *ev)
 {
        struct trace_kprobe *tk = to_trace_kprobe(ev);
@@ -1175,35 +1335,35 @@ __kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs,
                    struct trace_event_file *trace_file)
 {
        struct kprobe_trace_entry_head *entry;
-       struct ring_buffer_event *event;
-       struct ring_buffer *buffer;
-       int size, dsize, pc;
-       unsigned long irq_flags;
        struct trace_event_call *call = trace_probe_event_call(&tk->tp);
+       struct trace_event_buffer fbuffer;
+       int dsize;
 
        WARN_ON(call != trace_file->event_call);
 
        if (trace_trigger_soft_disabled(trace_file))
                return;
 
-       local_save_flags(irq_flags);
-       pc = preempt_count();
+       local_save_flags(fbuffer.flags);
+       fbuffer.pc = preempt_count();
+       fbuffer.trace_file = trace_file;
 
        dsize = __get_data_size(&tk->tp, regs);
-       size = sizeof(*entry) + tk->tp.size + dsize;
 
-       event = trace_event_buffer_lock_reserve(&buffer, trace_file,
-                                               call->event.type,
-                                               size, irq_flags, pc);
-       if (!event)
+       fbuffer.event =
+               trace_event_buffer_lock_reserve(&fbuffer.buffer, trace_file,
+                                       call->event.type,
+                                       sizeof(*entry) + tk->tp.size + dsize,
+                                       fbuffer.flags, fbuffer.pc);
+       if (!fbuffer.event)
                return;
 
-       entry = ring_buffer_event_data(event);
+       fbuffer.regs = regs;
+       entry = fbuffer.entry = ring_buffer_event_data(fbuffer.event);
        entry->ip = (unsigned long)tk->rp.kp.addr;
        store_trace_args(&entry[1], &tk->tp, regs, sizeof(*entry), dsize);
 
-       event_trigger_unlock_commit_regs(trace_file, buffer, event,
-                                        entry, irq_flags, pc, regs);
+       trace_event_buffer_commit(&fbuffer);
 }
 
 static void
@@ -1223,36 +1383,35 @@ __kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
                       struct trace_event_file *trace_file)
 {
        struct kretprobe_trace_entry_head *entry;
-       struct ring_buffer_event *event;
-       struct ring_buffer *buffer;
-       int size, pc, dsize;
-       unsigned long irq_flags;
+       struct trace_event_buffer fbuffer;
        struct trace_event_call *call = trace_probe_event_call(&tk->tp);
+       int dsize;
 
        WARN_ON(call != trace_file->event_call);
 
        if (trace_trigger_soft_disabled(trace_file))
                return;
 
-       local_save_flags(irq_flags);
-       pc = preempt_count();
+       local_save_flags(fbuffer.flags);
+       fbuffer.pc = preempt_count();
+       fbuffer.trace_file = trace_file;
 
        dsize = __get_data_size(&tk->tp, regs);
-       size = sizeof(*entry) + tk->tp.size + dsize;
-
-       event = trace_event_buffer_lock_reserve(&buffer, trace_file,
-                                               call->event.type,
-                                               size, irq_flags, pc);
-       if (!event)
+       fbuffer.event =
+               trace_event_buffer_lock_reserve(&fbuffer.buffer, trace_file,
+                                       call->event.type,
+                                       sizeof(*entry) + tk->tp.size + dsize,
+                                       fbuffer.flags, fbuffer.pc);
+       if (!fbuffer.event)
                return;
 
-       entry = ring_buffer_event_data(event);
+       fbuffer.regs = regs;
+       entry = fbuffer.entry = ring_buffer_event_data(fbuffer.event);
        entry->func = (unsigned long)tk->rp.kp.addr;
        entry->ret_ip = (unsigned long)ri->ret_addr;
        store_trace_args(&entry[1], &tk->tp, regs, sizeof(*entry), dsize);
 
-       event_trigger_unlock_commit_regs(trace_file, buffer, event,
-                                        entry, irq_flags, pc, regs);
+       trace_event_buffer_commit(&fbuffer);
 }
 
 static void
@@ -1698,11 +1857,12 @@ static __init void setup_boot_kprobe_events(void)
        enable_boot_kprobe_events();
 }
 
-/* Make a tracefs interface for controlling probe points */
-static __init int init_kprobe_trace(void)
+/*
+ * Register dynevent at subsys_initcall. This allows kernel to setup kprobe
+ * events in fs_initcall without tracefs.
+ */
+static __init int init_kprobe_trace_early(void)
 {
-       struct dentry *d_tracer;
-       struct dentry *entry;
        int ret;
 
        ret = dyn_event_register(&trace_kprobe_ops);
@@ -1712,6 +1872,16 @@ static __init int init_kprobe_trace(void)
        if (register_module_notifier(&trace_kprobe_module_nb))
                return -EINVAL;
 
+       return 0;
+}
+subsys_initcall(init_kprobe_trace_early);
+
+/* Make a tracefs interface for controlling probe points */
+static __init int init_kprobe_trace(void)
+{
+       struct dentry *d_tracer;
+       struct dentry *entry;
+
        d_tracer = tracing_init_dentry();
        if (IS_ERR(d_tracer))
                return 0;