Allow user to delete multiple kprobe events by using
wild cards. This makes removing events on a specific
function easy.

e.g.)
 # echo p vfs_symlink >> kprobe_events
 # echo p vfs_symlink+5 >> kprobe_events
 # echo p vfs_read >> kprobe_events
 # cat kprobe_events
p:kprobes/p_vfs_symlink_0 vfs_symlink
p:kprobes/p_vfs_symlink_5 vfs_symlink+5
p:kprobes/p_vfs_read_0 vfs_read
 # echo -:kprobes/\*vfs_symlink_\* >> kprobe_events
 # cat kprobe_events
p:kprobes/p_vfs_read_0 vfs_read

Signed-off-by: Masami Hiramatsu <masami.hiramatsu...@hitachi.com>
Cc: Steven Rostedt <rost...@goodmis.org>
Cc: Frederic Weisbecker <fweis...@gmail.com>
Cc: Ingo Molnar <mi...@redhat.com>
---
 Documentation/trace/kprobetrace.txt |   19 ++++---
 kernel/trace/trace_kprobe.c         |   97 +++++++++++++++++++++++++----------
 2 files changed, 82 insertions(+), 34 deletions(-)

diff --git a/Documentation/trace/kprobetrace.txt 
b/Documentation/trace/kprobetrace.txt
index d68ea5f..04f22be 100644
--- a/Documentation/trace/kprobetrace.txt
+++ b/Documentation/trace/kprobetrace.txt
@@ -26,9 +26,9 @@ Synopsis of kprobe_events
   r[:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS]            : Set a return probe
   -:[GRP/]EVENT                                                : Clear a probe
 
- GRP           : Group name. If omitted, use "kprobes" for it.
+ GRP           : Group name. If omitted, use "kprobes" for it.(*)
  EVENT         : Event name. If omitted, the event name is generated
-                 based on SYM+offs or MEMADDR.
+                 based on SYM+offs or MEMADDR.(*)
  MOD           : Module name which has given SYM.
  SYM[+offs]    : Symbol+offset where the probe is inserted.
  MEMADDR       : Address where the probe is inserted.
@@ -39,15 +39,16 @@ Synopsis of kprobe_events
   @SYM[+|-offs]        : Fetch memory at SYM +|- offs (SYM should be a data 
symbol)
   $stackN      : Fetch Nth entry of stack (N >= 0)
   $stack       : Fetch stack address.
-  $retval      : Fetch return value.(*)
-  +|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(**)
+  $retval      : Fetch return value.(**)
+  +|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(***)
   NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
   FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
                  (u8/u16/u32/u64/s8/s16/s32/s64), "string" and bitfield
                  are supported.
 
-  (*) only for return probe.
-  (**) this is useful for fetching a field of data structures.
+  (*) both GRP and EVENT accept glob-style wild cards when clearing probes.
+  (**) only for return probe.
+  (***) this is useful for fetching a field of data structures.
 
 Types
 -----
@@ -143,7 +144,11 @@ REC->dfd, REC->filename, REC->flags, REC->mode
 
   echo -:myprobe >> kprobe_events
 
- This clears probe points selectively.
+ This removes a probe points selectively. Since the event name and group
+ name accept wild cards only when removing, you can clear all event as
+ below too.
+
+  echo '-:*/*' >> kprobe_events
 
  Right after definition, each event is disabled by default. For tracing these
 events, you need to enable it.
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 9f46e98..b619853 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -171,16 +171,32 @@ static void free_trace_probe(struct trace_probe *tp)
        kfree(tp);
 }
 
-static struct trace_probe *find_trace_probe(const char *event,
-                                           const char *group)
+/**
+ * find_trace_probes - find matched trace_probe and return the number of it
+ * @event: event name (glob pattern)
+ * @group: group(subsystem) name (glob pattern)
+ * @buf:   the address of an array of trace_probe *
+ * @size:  the size of @buf array
+ *
+ * Search trace_probe matched with given name (pattern) and returns
+ * the number of it. If @buf is not NULL, it records the address of
+ * the matched trace_probe on it.
+ */
+static int
+find_trace_probes(const char *event, const char *group,
+                 struct trace_probe **buf, int size)
 {
        struct trace_probe *tp;
+       int count = 0;
 
        list_for_each_entry(tp, &probe_list, list)
-               if (strcmp(tp->call.name, event) == 0 &&
-                   strcmp(tp->call.class->system, group) == 0)
-                       return tp;
-       return NULL;
+               if (strglobmatch(event, tp->call.name) &&
+                   strglobmatch(group, tp->call.class->system)) {
+                       if (buf && count < size)
+                               buf[count] = tp;
+                       count++;
+               }
+       return count;
 }
 
 static int trace_probe_nr_files(struct trace_probe *tp)
@@ -414,8 +430,9 @@ static int register_trace_probe(struct trace_probe *tp)
        mutex_lock(&probe_lock);
 
        /* Delete old (same name) event if exist */
-       old_tp = find_trace_probe(tp->call.name, tp->call.class->system);
-       if (old_tp) {
+       ret = find_trace_probes(tp->call.name, tp->call.class->system,
+                               &old_tp, 1);
+       if (ret) {
                ret = unregister_trace_probe(old_tp);
                if (ret < 0)
                        goto end;
@@ -475,6 +492,49 @@ static struct notifier_block trace_probe_module_nb = {
        .priority = 1   /* Invoked after kprobe module callback */
 };
 
+static int delete_trace_probe(const char *event, const char *group)
+{
+       struct trace_probe **tps;
+       int nr_tps, i, ret = 0;
+
+       if (!event) {
+               pr_info("Delete command needs an event name.\n");
+               return -EINVAL;
+       }
+
+       mutex_lock(&probe_lock);
+
+       nr_tps = find_trace_probes(event, group, NULL, 0);
+       if (nr_tps == 0) {
+               pr_info("Event %s/%s doesn't matched.\n", group, event);
+               ret = -ENOENT;
+               goto out_unlock;
+       }
+
+       tps = kzalloc(nr_tps * sizeof(*tps), GFP_KERNEL);
+       if (!tps) {
+               ret = -ENOMEM;
+               goto out_unlock;
+       }
+
+       find_trace_probes(event, group, tps, nr_tps);
+
+       for (i = 0; i < nr_tps; i++) {
+               ret = unregister_trace_probe(tps[i]);
+               if (ret < 0) {
+                       pr_info("Failed to delete event: %d\n", ret);
+                       continue;       /* Greedy cleanup */
+               }
+               free_trace_probe(tps[i]);
+       }
+       ret = 0;
+       kfree(tps);
+
+ out_unlock:
+       mutex_unlock(&probe_lock);
+       return ret;
+}
+
 static int create_trace_probe(int argc, char **argv)
 {
        /*
@@ -536,25 +596,8 @@ static int create_trace_probe(int argc, char **argv)
        if (!group)
                group = KPROBE_EVENT_SYSTEM;
 
-       if (is_delete) {
-               if (!event) {
-                       pr_info("Delete command needs an event name.\n");
-                       return -EINVAL;
-               }
-               mutex_lock(&probe_lock);
-               tp = find_trace_probe(event, group);
-               if (!tp) {
-                       mutex_unlock(&probe_lock);
-                       pr_info("Event %s/%s doesn't exist.\n", group, event);
-                       return -ENOENT;
-               }
-               /* delete an event */
-               ret = unregister_trace_probe(tp);
-               if (ret == 0)
-                       free_trace_probe(tp);
-               mutex_unlock(&probe_lock);
-               return ret;
-       }
+       if (is_delete)
+               return delete_trace_probe(event, group);
 
        if (argc < 2) {
                pr_info("Probe point is not specified.\n");

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to