linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] uprobes: list all active uprobes in the system
@ 2023-01-11 10:48 luolongjun
  2023-01-11 21:35 ` Ian Rogers
  2023-01-13  4:03 ` Ravi Bangoria
  0 siblings, 2 replies; 7+ messages in thread
From: luolongjun @ 2023-01-11 10:48 UTC (permalink / raw)
  To: peterz, mingo, acme, mark.rutland, alexander.shishkin, jolsa, namhyung
  Cc: linux-perf-users, linux-kernel, sangyan, luanjianhai, zhuling8,
	lizongwu, luolongjuna, Longjun Luo

From: Longjun Luo <luolongjun@huawei.com>

Since uprobes will replace instructions in the process
memory, it is necessary to provide one way to list
all active uprobes. One can access this file through
/sys/kernel/debug/uprobes/list.

Output looks like this
=====================
inode+offset/ref_ctr_offset    4160760670+0x30a10/0x0
    ref                        1
    register_rwsem             (unlocked)
    consumer_rwsem             (unlocked)
    consumers-1
        handler                0xffffffffc03ee02e(handler) uprobe
        ret_handler            0x0000000000000000( )
        filter                 0xffffffffc03ee010(filter) uprobe
    consumers-2
        handler                0xffffffffc03e902e(handler_1) uprobe_1
        ret_handler            0x0000000000000000( )
        filter                 0xffffffffc03e9010(filter_1) uprobe_1

Signed-off-by: Longjun Luo <luolongjun@huawei.com>
---
 kernel/events/uprobes.c | 120 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 109 insertions(+), 11 deletions(-)

diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index d9e357b7e17c..2fa9b910abc7 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -27,6 +27,9 @@
 #include <linux/task_work.h>
 #include <linux/shmem_fs.h>
 #include <linux/khugepaged.h>
+#include <linux/rbtree.h>
+#include <linux/debugfs.h>
+#include <linux/kallsyms.h>
 
 #include <linux/uprobes.h>
 
@@ -40,7 +43,7 @@ static struct rb_root uprobes_tree = RB_ROOT;
  */
 #define no_uprobe_events()	RB_EMPTY_ROOT(&uprobes_tree)
 
-static DEFINE_SPINLOCK(uprobes_treelock);	/* serialize rbtree access */
+static DECLARE_RWSEM(uprobes_treelock);	/* serialize rbtree access */
 
 #define UPROBES_HASH_SZ	13
 /* serialize uprobe->pending_list */
@@ -672,9 +675,9 @@ static struct uprobe *find_uprobe(struct inode *inode, loff_t offset)
 {
 	struct uprobe *uprobe;
 
-	spin_lock(&uprobes_treelock);
+	down_read(&uprobes_treelock);
 	uprobe = __find_uprobe(inode, offset);
-	spin_unlock(&uprobes_treelock);
+	up_read(&uprobes_treelock);
 
 	return uprobe;
 }
@@ -704,9 +707,9 @@ static struct uprobe *insert_uprobe(struct uprobe *uprobe)
 {
 	struct uprobe *u;
 
-	spin_lock(&uprobes_treelock);
+	down_write(&uprobes_treelock);
 	u = __insert_uprobe(uprobe);
-	spin_unlock(&uprobes_treelock);
+	up_write(&uprobes_treelock);
 
 	return u;
 }
@@ -938,9 +941,9 @@ static void delete_uprobe(struct uprobe *uprobe)
 	if (WARN_ON(!uprobe_is_active(uprobe)))
 		return;
 
-	spin_lock(&uprobes_treelock);
+	down_write(&uprobes_treelock);
 	rb_erase(&uprobe->rb_node, &uprobes_tree);
-	spin_unlock(&uprobes_treelock);
+	up_write(&uprobes_treelock);
 	RB_CLEAR_NODE(&uprobe->rb_node); /* for uprobe_is_active() */
 	put_uprobe(uprobe);
 }
@@ -1301,7 +1304,7 @@ static void build_probe_list(struct inode *inode,
 	min = vaddr_to_offset(vma, start);
 	max = min + (end - start) - 1;
 
-	spin_lock(&uprobes_treelock);
+	down_read(&uprobes_treelock);
 	n = find_node_in_range(inode, min, max);
 	if (n) {
 		for (t = n; t; t = rb_prev(t)) {
@@ -1319,7 +1322,7 @@ static void build_probe_list(struct inode *inode,
 			get_uprobe(u);
 		}
 	}
-	spin_unlock(&uprobes_treelock);
+	up_read(&uprobes_treelock);
 }
 
 /* @vma contains reference counter, not the probed instruction. */
@@ -1410,9 +1413,9 @@ vma_has_uprobes(struct vm_area_struct *vma, unsigned long start, unsigned long e
 	min = vaddr_to_offset(vma, start);
 	max = min + (end - start) - 1;
 
-	spin_lock(&uprobes_treelock);
+	down_read(&uprobes_treelock);
 	n = find_node_in_range(inode, min, max);
-	spin_unlock(&uprobes_treelock);
+	up_read(&uprobes_treelock);
 
 	return !!n;
 }
@@ -2357,3 +2360,98 @@ void __init uprobes_init(void)
 
 	BUG_ON(register_die_notifier(&uprobe_exception_nb));
 }
+
+#ifdef CONFIG_DEBUG_FS
+
+static void *uprobe_seq_start(struct seq_file *m, loff_t *pos)
+{
+	loff_t num = *pos;
+	struct rb_node *rbp;
+
+	down_read(&uprobes_treelock);
+	for (rbp = rb_first(&uprobes_tree); rbp; rbp = rb_next(rbp)) {
+		if (num-- == 0)
+			return rbp;
+	}
+
+	return NULL;
+}
+
+static void *uprobe_seq_next(struct seq_file *f, void *v, loff_t *pos)
+{
+	++*pos;
+	return (void *)rb_next(v);
+}
+
+static void uprobe_seq_stop(struct seq_file *f, void *v)
+{
+	up_read(&uprobes_treelock);
+}
+
+static int show_uprobe(struct seq_file *m, void *v)
+{
+	struct uprobe *uprobe = rb_entry_safe(v, struct uprobe, rb_node);
+	struct uprobe_consumer *con;
+	char *modname, namebuf[KSYM_NAME_LEN];
+	const char *sym;
+	unsigned int cnt = 1;
+
+	seq_printf(m, "inode+offset/ref_ctr_offset    %lu+0x%llx/0x%llx\n",
+		uprobe->inode->i_ino, uprobe->offset, uprobe->ref_ctr_offset);
+	seq_printf(m, "    ref                        %u\n",
+		refcount_read(&uprobe->ref));
+	seq_printf(m, "    register_rwsem             %s\n",
+		rwsem_is_locked(&uprobe->register_rwsem)?"(locked)":"(unlocked)");
+	seq_printf(m, "    consumer_rwsem             %s\n",
+		rwsem_is_locked(&uprobe->consumer_rwsem)?"(locked)":"(unlocked)");
+
+	/* lock consumer_rwsem when hold uprobes_treelock */
+	down_read(&uprobe->consumer_rwsem);
+	for (con = uprobe->consumers; con; con = con->next, cnt++) {
+		seq_printf(m, "    consumers-%u\n", cnt);
+
+		modname = NULL;
+		sym = kallsyms_lookup((unsigned long)con->handler, NULL, NULL,
+			&modname, namebuf);
+		seq_printf(m, "        handler                0x%px(%s) %s\n",
+			con->handler, (sym ? sym : " "), (modname ? modname : " "));
+
+		modname = NULL;
+		sym = kallsyms_lookup((unsigned long)con->ret_handler, NULL, NULL,
+			&modname, namebuf);
+		seq_printf(m, "        ret_handler            0x%px(%s) %s\n",
+			con->ret_handler, (sym ? sym : " "), (modname ? modname : " "));
+
+		modname = NULL;
+		sym = kallsyms_lookup((unsigned long)con->filter, NULL, NULL,
+			&modname, namebuf);
+		seq_printf(m, "        filter                 0x%px(%s) %s\n",
+			con->filter, (sym ? sym : " "), (modname ? modname : " "));
+	}
+	up_read(&uprobe->consumer_rwsem);
+
+	return 0;
+}
+
+static const struct seq_operations uprobes_sops = {
+	.start = uprobe_seq_start,
+	.next  = uprobe_seq_next,
+	.stop  = uprobe_seq_stop,
+	.show  = show_uprobe,
+};
+
+DEFINE_SEQ_ATTRIBUTE(uprobes);
+
+static int __init debugfs_uprobe_init(void)
+{
+	struct dentry *dir;
+
+	dir = debugfs_create_dir("uprobes", NULL);
+
+	debugfs_create_file("list", 0400, dir, NULL, &uprobes_fops);
+
+	return 0;
+}
+late_initcall(debugfs_uprobe_init);
+
+#endif /* CONFIG_DEBUG_FS */
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH] uprobes: list all active uprobes in the system
  2023-01-11 10:48 [PATCH] uprobes: list all active uprobes in the system luolongjun
@ 2023-01-11 21:35 ` Ian Rogers
  2023-01-12  4:15   ` Longjun Luo
  2023-01-13  4:03 ` Ravi Bangoria
  1 sibling, 1 reply; 7+ messages in thread
From: Ian Rogers @ 2023-01-11 21:35 UTC (permalink / raw)
  To: luolongjun
  Cc: peterz, mingo, acme, mark.rutland, alexander.shishkin, jolsa,
	namhyung, linux-perf-users, linux-kernel, sangyan, luanjianhai,
	zhuling8, lizongwu, luolongjuna

On Wed, Jan 11, 2023 at 2:49 AM <luolongjun@huawei.com> wrote:
>
> From: Longjun Luo <luolongjun@huawei.com>
>
> Since uprobes will replace instructions in the process
> memory, it is necessary to provide one way to list
> all active uprobes. One can access this file through
> /sys/kernel/debug/uprobes/list.
>
> Output looks like this
> =====================
> inode+offset/ref_ctr_offset    4160760670+0x30a10/0x0
>     ref                        1
>     register_rwsem             (unlocked)
>     consumer_rwsem             (unlocked)
>     consumers-1
>         handler                0xffffffffc03ee02e(handler) uprobe
>         ret_handler            0x0000000000000000( )
>         filter                 0xffffffffc03ee010(filter) uprobe
>     consumers-2
>         handler                0xffffffffc03e902e(handler_1) uprobe_1
>         ret_handler            0x0000000000000000( )
>         filter                 0xffffffffc03e9010(filter_1) uprobe_1
>
> Signed-off-by: Longjun Luo <luolongjun@huawei.com>
> ---
>  kernel/events/uprobes.c | 120 ++++++++++++++++++++++++++++++++++++----
>  1 file changed, 109 insertions(+), 11 deletions(-)
>
> diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
> index d9e357b7e17c..2fa9b910abc7 100644
> --- a/kernel/events/uprobes.c
> +++ b/kernel/events/uprobes.c
> @@ -27,6 +27,9 @@
>  #include <linux/task_work.h>
>  #include <linux/shmem_fs.h>
>  #include <linux/khugepaged.h>
> +#include <linux/rbtree.h>
> +#include <linux/debugfs.h>
> +#include <linux/kallsyms.h>
>
>  #include <linux/uprobes.h>
>
> @@ -40,7 +43,7 @@ static struct rb_root uprobes_tree = RB_ROOT;
>   */
>  #define no_uprobe_events()     RB_EMPTY_ROOT(&uprobes_tree)
>
> -static DEFINE_SPINLOCK(uprobes_treelock);      /* serialize rbtree access */
> +static DECLARE_RWSEM(uprobes_treelock);        /* serialize rbtree access */
>
>  #define UPROBES_HASH_SZ        13
>  /* serialize uprobe->pending_list */
> @@ -672,9 +675,9 @@ static struct uprobe *find_uprobe(struct inode *inode, loff_t offset)
>  {
>         struct uprobe *uprobe;
>
> -       spin_lock(&uprobes_treelock);
> +       down_read(&uprobes_treelock);
>         uprobe = __find_uprobe(inode, offset);
> -       spin_unlock(&uprobes_treelock);
> +       up_read(&uprobes_treelock);
>
>         return uprobe;
>  }
> @@ -704,9 +707,9 @@ static struct uprobe *insert_uprobe(struct uprobe *uprobe)
>  {
>         struct uprobe *u;
>
> -       spin_lock(&uprobes_treelock);
> +       down_write(&uprobes_treelock);
>         u = __insert_uprobe(uprobe);
> -       spin_unlock(&uprobes_treelock);
> +       up_write(&uprobes_treelock);
>
>         return u;
>  }
> @@ -938,9 +941,9 @@ static void delete_uprobe(struct uprobe *uprobe)
>         if (WARN_ON(!uprobe_is_active(uprobe)))
>                 return;
>
> -       spin_lock(&uprobes_treelock);
> +       down_write(&uprobes_treelock);
>         rb_erase(&uprobe->rb_node, &uprobes_tree);
> -       spin_unlock(&uprobes_treelock);
> +       up_write(&uprobes_treelock);
>         RB_CLEAR_NODE(&uprobe->rb_node); /* for uprobe_is_active() */
>         put_uprobe(uprobe);
>  }
> @@ -1301,7 +1304,7 @@ static void build_probe_list(struct inode *inode,
>         min = vaddr_to_offset(vma, start);
>         max = min + (end - start) - 1;
>
> -       spin_lock(&uprobes_treelock);
> +       down_read(&uprobes_treelock);
>         n = find_node_in_range(inode, min, max);
>         if (n) {
>                 for (t = n; t; t = rb_prev(t)) {
> @@ -1319,7 +1322,7 @@ static void build_probe_list(struct inode *inode,
>                         get_uprobe(u);
>                 }
>         }
> -       spin_unlock(&uprobes_treelock);
> +       up_read(&uprobes_treelock);
>  }
>
>  /* @vma contains reference counter, not the probed instruction. */
> @@ -1410,9 +1413,9 @@ vma_has_uprobes(struct vm_area_struct *vma, unsigned long start, unsigned long e
>         min = vaddr_to_offset(vma, start);
>         max = min + (end - start) - 1;
>
> -       spin_lock(&uprobes_treelock);
> +       down_read(&uprobes_treelock);
>         n = find_node_in_range(inode, min, max);
> -       spin_unlock(&uprobes_treelock);
> +       up_read(&uprobes_treelock);
>
>         return !!n;
>  }
> @@ -2357,3 +2360,98 @@ void __init uprobes_init(void)
>
>         BUG_ON(register_die_notifier(&uprobe_exception_nb));
>  }
> +
> +#ifdef CONFIG_DEBUG_FS
> +
> +static void *uprobe_seq_start(struct seq_file *m, loff_t *pos)
> +{
> +       loff_t num = *pos;
> +       struct rb_node *rbp;
> +
> +       down_read(&uprobes_treelock);

Is there a missing corresponding up_read?

Thanks,
Ian

> +       for (rbp = rb_first(&uprobes_tree); rbp; rbp = rb_next(rbp)) {
> +               if (num-- == 0)
> +                       return rbp;
> +       }
> +
> +       return NULL;
> +}
> +
> +static void *uprobe_seq_next(struct seq_file *f, void *v, loff_t *pos)
> +{
> +       ++*pos;
> +       return (void *)rb_next(v);
> +}
> +
> +static void uprobe_seq_stop(struct seq_file *f, void *v)
> +{
> +       up_read(&uprobes_treelock);
> +}
> +
> +static int show_uprobe(struct seq_file *m, void *v)
> +{
> +       struct uprobe *uprobe = rb_entry_safe(v, struct uprobe, rb_node);
> +       struct uprobe_consumer *con;
> +       char *modname, namebuf[KSYM_NAME_LEN];
> +       const char *sym;
> +       unsigned int cnt = 1;
> +
> +       seq_printf(m, "inode+offset/ref_ctr_offset    %lu+0x%llx/0x%llx\n",
> +               uprobe->inode->i_ino, uprobe->offset, uprobe->ref_ctr_offset);
> +       seq_printf(m, "    ref                        %u\n",
> +               refcount_read(&uprobe->ref));
> +       seq_printf(m, "    register_rwsem             %s\n",
> +               rwsem_is_locked(&uprobe->register_rwsem)?"(locked)":"(unlocked)");
> +       seq_printf(m, "    consumer_rwsem             %s\n",
> +               rwsem_is_locked(&uprobe->consumer_rwsem)?"(locked)":"(unlocked)");
> +
> +       /* lock consumer_rwsem when hold uprobes_treelock */
> +       down_read(&uprobe->consumer_rwsem);
> +       for (con = uprobe->consumers; con; con = con->next, cnt++) {
> +               seq_printf(m, "    consumers-%u\n", cnt);
> +
> +               modname = NULL;
> +               sym = kallsyms_lookup((unsigned long)con->handler, NULL, NULL,
> +                       &modname, namebuf);
> +               seq_printf(m, "        handler                0x%px(%s) %s\n",
> +                       con->handler, (sym ? sym : " "), (modname ? modname : " "));
> +
> +               modname = NULL;
> +               sym = kallsyms_lookup((unsigned long)con->ret_handler, NULL, NULL,
> +                       &modname, namebuf);
> +               seq_printf(m, "        ret_handler            0x%px(%s) %s\n",
> +                       con->ret_handler, (sym ? sym : " "), (modname ? modname : " "));
> +
> +               modname = NULL;
> +               sym = kallsyms_lookup((unsigned long)con->filter, NULL, NULL,
> +                       &modname, namebuf);
> +               seq_printf(m, "        filter                 0x%px(%s) %s\n",
> +                       con->filter, (sym ? sym : " "), (modname ? modname : " "));
> +       }
> +       up_read(&uprobe->consumer_rwsem);
> +
> +       return 0;
> +}
> +
> +static const struct seq_operations uprobes_sops = {
> +       .start = uprobe_seq_start,
> +       .next  = uprobe_seq_next,
> +       .stop  = uprobe_seq_stop,
> +       .show  = show_uprobe,
> +};
> +
> +DEFINE_SEQ_ATTRIBUTE(uprobes);
> +
> +static int __init debugfs_uprobe_init(void)
> +{
> +       struct dentry *dir;
> +
> +       dir = debugfs_create_dir("uprobes", NULL);
> +
> +       debugfs_create_file("list", 0400, dir, NULL, &uprobes_fops);
> +
> +       return 0;
> +}
> +late_initcall(debugfs_uprobe_init);
> +
> +#endif /* CONFIG_DEBUG_FS */
> --
> 2.38.1
>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] uprobes: list all active uprobes in the system
  2023-01-11 21:35 ` Ian Rogers
@ 2023-01-12  4:15   ` Longjun Luo
  0 siblings, 0 replies; 7+ messages in thread
From: Longjun Luo @ 2023-01-12  4:15 UTC (permalink / raw)
  To: Ian Rogers
  Cc: peterz, mingo, acme, mark.rutland, alexander.shishkin, jolsa,
	namhyung, linux-perf-users, linux-kernel, sangyan, luanjianhai,
	zhuling8, lizongwu, luolongjuna


在 1/12/2023 5:35 AM, Ian Rogers 写道:
> On Wed, Jan 11, 2023 at 2:49 AM <luolongjun@huawei.com> wrote:
>> From: Longjun Luo <luolongjun@huawei.com>
>>
>> Since uprobes will replace instructions in the process
>> memory, it is necessary to provide one way to list
>> all active uprobes. One can access this file through
>> /sys/kernel/debug/uprobes/list.
>>
>> Output looks like this
>> =====================
>> inode+offset/ref_ctr_offset    4160760670+0x30a10/0x0
>>      ref                        1
>>      register_rwsem             (unlocked)
>>      consumer_rwsem             (unlocked)
>>      consumers-1
>>          handler                0xffffffffc03ee02e(handler) uprobe
>>          ret_handler            0x0000000000000000( )
>>          filter                 0xffffffffc03ee010(filter) uprobe
>>      consumers-2
>>          handler                0xffffffffc03e902e(handler_1) uprobe_1
>>          ret_handler            0x0000000000000000( )
>>          filter                 0xffffffffc03e9010(filter_1) uprobe_1
>>
>> Signed-off-by: Longjun Luo <luolongjun@huawei.com>
>> ---
>>   kernel/events/uprobes.c | 120 ++++++++++++++++++++++++++++++++++++----
>>   1 file changed, 109 insertions(+), 11 deletions(-)
>>
>> diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
>> index d9e357b7e17c..2fa9b910abc7 100644
>> --- a/kernel/events/uprobes.c
>> +++ b/kernel/events/uprobes.c
>> @@ -27,6 +27,9 @@
>>   #include <linux/task_work.h>
>>   #include <linux/shmem_fs.h>
>>   #include <linux/khugepaged.h>
>> +#include <linux/rbtree.h>
>> +#include <linux/debugfs.h>
>> +#include <linux/kallsyms.h>
>>
>>   #include <linux/uprobes.h>
>>
>> @@ -40,7 +43,7 @@ static struct rb_root uprobes_tree = RB_ROOT;
>>    */
>>   #define no_uprobe_events()     RB_EMPTY_ROOT(&uprobes_tree)
>>
>> -static DEFINE_SPINLOCK(uprobes_treelock);      /* serialize rbtree access */
>> +static DECLARE_RWSEM(uprobes_treelock);        /* serialize rbtree access */
>>
>>   #define UPROBES_HASH_SZ        13
>>   /* serialize uprobe->pending_list */
>> @@ -672,9 +675,9 @@ static struct uprobe *find_uprobe(struct inode *inode, loff_t offset)
>>   {
>>          struct uprobe *uprobe;
>>
>> -       spin_lock(&uprobes_treelock);
>> +       down_read(&uprobes_treelock);
>>          uprobe = __find_uprobe(inode, offset);
>> -       spin_unlock(&uprobes_treelock);
>> +       up_read(&uprobes_treelock);
>>
>>          return uprobe;
>>   }
>> @@ -704,9 +707,9 @@ static struct uprobe *insert_uprobe(struct uprobe *uprobe)
>>   {
>>          struct uprobe *u;
>>
>> -       spin_lock(&uprobes_treelock);
>> +       down_write(&uprobes_treelock);
>>          u = __insert_uprobe(uprobe);
>> -       spin_unlock(&uprobes_treelock);
>> +       up_write(&uprobes_treelock);
>>
>>          return u;
>>   }
>> @@ -938,9 +941,9 @@ static void delete_uprobe(struct uprobe *uprobe)
>>          if (WARN_ON(!uprobe_is_active(uprobe)))
>>                  return;
>>
>> -       spin_lock(&uprobes_treelock);
>> +       down_write(&uprobes_treelock);
>>          rb_erase(&uprobe->rb_node, &uprobes_tree);
>> -       spin_unlock(&uprobes_treelock);
>> +       up_write(&uprobes_treelock);
>>          RB_CLEAR_NODE(&uprobe->rb_node); /* for uprobe_is_active() */
>>          put_uprobe(uprobe);
>>   }
>> @@ -1301,7 +1304,7 @@ static void build_probe_list(struct inode *inode,
>>          min = vaddr_to_offset(vma, start);
>>          max = min + (end - start) - 1;
>>
>> -       spin_lock(&uprobes_treelock);
>> +       down_read(&uprobes_treelock);
>>          n = find_node_in_range(inode, min, max);
>>          if (n) {
>>                  for (t = n; t; t = rb_prev(t)) {
>> @@ -1319,7 +1322,7 @@ static void build_probe_list(struct inode *inode,
>>                          get_uprobe(u);
>>                  }
>>          }
>> -       spin_unlock(&uprobes_treelock);
>> +       up_read(&uprobes_treelock);
>>   }
>>
>>   /* @vma contains reference counter, not the probed instruction. */
>> @@ -1410,9 +1413,9 @@ vma_has_uprobes(struct vm_area_struct *vma, unsigned long start, unsigned long e
>>          min = vaddr_to_offset(vma, start);
>>          max = min + (end - start) - 1;
>>
>> -       spin_lock(&uprobes_treelock);
>> +       down_read(&uprobes_treelock);
>>          n = find_node_in_range(inode, min, max);
>> -       spin_unlock(&uprobes_treelock);
>> +       up_read(&uprobes_treelock);
>>
>>          return !!n;
>>   }
>> @@ -2357,3 +2360,98 @@ void __init uprobes_init(void)
>>
>>          BUG_ON(register_die_notifier(&uprobe_exception_nb));
>>   }
>> +
>> +#ifdef CONFIG_DEBUG_FS
>> +
>> +static void *uprobe_seq_start(struct seq_file *m, loff_t *pos)
>> +{
>> +       loff_t num = *pos;
>> +       struct rb_node *rbp;
>> +
>> +       down_read(&uprobes_treelock);
> Is there a missing corresponding up_read?
>
> Thanks,
> Ian
This missing corresponding up_read is in the uprobe_seq_stop.

In my understanding, uprobe_seq_start and uprobe_seq_stop is one pair of 
API.

uprobe_seq_start holds the lock and the uprobe_seq_stop releases it.

I am not sure if it is right.

By the way, may I ask if the community still plan to evolve the uprobe 
since the uprobe hasn't changed for a long time.
>> +       for (rbp = rb_first(&uprobes_tree); rbp; rbp = rb_next(rbp)) {
>> +               if (num-- == 0)
>> +                       return rbp;
>> +       }
>> +
>> +       return NULL;
>> +}
>> +
>> +static void *uprobe_seq_next(struct seq_file *f, void *v, loff_t *pos)
>> +{
>> +       ++*pos;
>> +       return (void *)rb_next(v);
>> +}
>> +
>> +static void uprobe_seq_stop(struct seq_file *f, void *v)
>> +{
>> +       up_read(&uprobes_treelock);
>> +}
>> +
>> +static int show_uprobe(struct seq_file *m, void *v)
>> +{
>> +       struct uprobe *uprobe = rb_entry_safe(v, struct uprobe, rb_node);
>> +       struct uprobe_consumer *con;
>> +       char *modname, namebuf[KSYM_NAME_LEN];
>> +       const char *sym;
>> +       unsigned int cnt = 1;
>> +
>> +       seq_printf(m, "inode+offset/ref_ctr_offset    %lu+0x%llx/0x%llx\n",
>> +               uprobe->inode->i_ino, uprobe->offset, uprobe->ref_ctr_offset);
>> +       seq_printf(m, "    ref                        %u\n",
>> +               refcount_read(&uprobe->ref));
>> +       seq_printf(m, "    register_rwsem             %s\n",
>> +               rwsem_is_locked(&uprobe->register_rwsem)?"(locked)":"(unlocked)");
>> +       seq_printf(m, "    consumer_rwsem             %s\n",
>> +               rwsem_is_locked(&uprobe->consumer_rwsem)?"(locked)":"(unlocked)");
>> +
>> +       /* lock consumer_rwsem when hold uprobes_treelock */
>> +       down_read(&uprobe->consumer_rwsem);
>> +       for (con = uprobe->consumers; con; con = con->next, cnt++) {
>> +               seq_printf(m, "    consumers-%u\n", cnt);
>> +
>> +               modname = NULL;
>> +               sym = kallsyms_lookup((unsigned long)con->handler, NULL, NULL,
>> +                       &modname, namebuf);
>> +               seq_printf(m, "        handler                0x%px(%s) %s\n",
>> +                       con->handler, (sym ? sym : " "), (modname ? modname : " "));
>> +
>> +               modname = NULL;
>> +               sym = kallsyms_lookup((unsigned long)con->ret_handler, NULL, NULL,
>> +                       &modname, namebuf);
>> +               seq_printf(m, "        ret_handler            0x%px(%s) %s\n",
>> +                       con->ret_handler, (sym ? sym : " "), (modname ? modname : " "));
>> +
>> +               modname = NULL;
>> +               sym = kallsyms_lookup((unsigned long)con->filter, NULL, NULL,
>> +                       &modname, namebuf);
>> +               seq_printf(m, "        filter                 0x%px(%s) %s\n",
>> +                       con->filter, (sym ? sym : " "), (modname ? modname : " "));
>> +       }
>> +       up_read(&uprobe->consumer_rwsem);
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct seq_operations uprobes_sops = {
>> +       .start = uprobe_seq_start,
>> +       .next  = uprobe_seq_next,
>> +       .stop  = uprobe_seq_stop,
>> +       .show  = show_uprobe,
>> +};
>> +
>> +DEFINE_SEQ_ATTRIBUTE(uprobes);
>> +
>> +static int __init debugfs_uprobe_init(void)
>> +{
>> +       struct dentry *dir;
>> +
>> +       dir = debugfs_create_dir("uprobes", NULL);
>> +
>> +       debugfs_create_file("list", 0400, dir, NULL, &uprobes_fops);
>> +
>> +       return 0;
>> +}
>> +late_initcall(debugfs_uprobe_init);
>> +
>> +#endif /* CONFIG_DEBUG_FS */
>> --
>> 2.38.1
>>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] uprobes: list all active uprobes in the system
  2023-01-11 10:48 [PATCH] uprobes: list all active uprobes in the system luolongjun
  2023-01-11 21:35 ` Ian Rogers
@ 2023-01-13  4:03 ` Ravi Bangoria
  2023-01-13  7:49   ` Longjun Luo
  2023-01-13 15:29   ` Masami Hiramatsu
  1 sibling, 2 replies; 7+ messages in thread
From: Ravi Bangoria @ 2023-01-13  4:03 UTC (permalink / raw)
  To: luolongjun, peterz, mingo, acme, mark.rutland,
	alexander.shishkin, jolsa, namhyung
  Cc: linux-perf-users, linux-kernel, sangyan, luanjianhai, zhuling8,
	lizongwu, luolongjuna, Ravi Bangoria, rostedt, mhiramat, oleg

+cc: Steven, Oleg, Masami

On 11-Jan-23 4:18 PM, luolongjun@huawei.com wrote:
> From: Longjun Luo <luolongjun@huawei.com>
> 
> Since uprobes will replace instructions in the process
> memory, it is necessary to provide one way to list
> all active uprobes. One can access this file through
> /sys/kernel/debug/uprobes/list.
> 
> Output looks like this
> =====================
> inode+offset/ref_ctr_offset    4160760670+0x30a10/0x0
>     ref                        1
>     register_rwsem             (unlocked)
>     consumer_rwsem             (unlocked)
>     consumers-1
>         handler                0xffffffffc03ee02e(handler) uprobe
>         ret_handler            0x0000000000000000( )
>         filter                 0xffffffffc03ee010(filter) uprobe
>     consumers-2
>         handler                0xffffffffc03e902e(handler_1) uprobe_1
>         ret_handler            0x0000000000000000( )
>         filter                 0xffffffffc03e9010(filter_1) uprobe_1

You can list uprobes via /sys/kernel/tracing/uprobe_events. Any specific reason
to introduce another way?

Thanks,
Ravi

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] uprobes: list all active uprobes in the system
  2023-01-13  4:03 ` Ravi Bangoria
@ 2023-01-13  7:49   ` Longjun Luo
  2023-01-13 15:29   ` Masami Hiramatsu
  1 sibling, 0 replies; 7+ messages in thread
From: Longjun Luo @ 2023-01-13  7:49 UTC (permalink / raw)
  To: Ravi Bangoria, peterz, mingo, acme, mark.rutland,
	alexander.shishkin, jolsa, namhyung
  Cc: linux-perf-users, linux-kernel, sangyan, luanjianhai, zhuling8,
	lizongwu, luolongjuna, rostedt, mhiramat, oleg


在 1/13/2023 12:03 PM, Ravi Bangoria 写道:
> +cc: Steven, Oleg, Masami
>
> On 11-Jan-23 4:18 PM, luolongjun@huawei.com wrote:
>> From: Longjun Luo <luolongjun@huawei.com>
>>
>> Since uprobes will replace instructions in the process
>> memory, it is necessary to provide one way to list
>> all active uprobes. One can access this file through
>> /sys/kernel/debug/uprobes/list.
>>
>> Output looks like this
>> =====================
>> inode+offset/ref_ctr_offset    4160760670+0x30a10/0x0
>>      ref                        1
>>      register_rwsem             (unlocked)
>>      consumer_rwsem             (unlocked)
>>      consumers-1
>>          handler                0xffffffffc03ee02e(handler) uprobe
>>          ret_handler            0x0000000000000000( )
>>          filter                 0xffffffffc03ee010(filter) uprobe
>>      consumers-2
>>          handler                0xffffffffc03e902e(handler_1) uprobe_1
>>          ret_handler            0x0000000000000000( )
>>          filter                 0xffffffffc03e9010(filter_1) uprobe_1
> You can list uprobes via /sys/kernel/tracing/uprobe_events. Any specific reason
> to introduce another way?
>
> Thanks,
> Ravi

/sys/kernel/tracing/uprobe_events only lists tracing events, not all uprobe events.
For example, if I write a kernel module to call the uprobe_register to add a new uprobe event, it will not be listed by /sys/kernel/tracing/uprobe_events.

Compare it with the kprobe is like:
/sys/kernel/tracing/uprobe_events -> /sys/kernel/tracing/kprobe_events
/sys/kernel/debug/uprobes/list -> /sys/kernel/debug/kprobes/list

Nowadays, only the trace system uses the uprobe, so /sys/kernel/tracing/uprobe_events is enough.

But we try to extend the usage of the uprobe, and we add many uprobe events by calling the uprobe_register.

For this situation, we need a approach to see all the uprobe events in the kernel.


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] uprobes: list all active uprobes in the system
  2023-01-13  4:03 ` Ravi Bangoria
  2023-01-13  7:49   ` Longjun Luo
@ 2023-01-13 15:29   ` Masami Hiramatsu
  1 sibling, 0 replies; 7+ messages in thread
From: Masami Hiramatsu @ 2023-01-13 15:29 UTC (permalink / raw)
  To: Ravi Bangoria
  Cc: luolongjun, peterz, mingo, acme, mark.rutland,
	alexander.shishkin, jolsa, namhyung, linux-perf-users,
	linux-kernel, sangyan, luanjianhai, zhuling8, lizongwu,
	luolongjuna, rostedt, mhiramat, oleg

On Fri, 13 Jan 2023 09:33:41 +0530
Ravi Bangoria <ravi.bangoria@amd.com> wrote:

> +cc: Steven, Oleg, Masami
> 
> On 11-Jan-23 4:18 PM, luolongjun@huawei.com wrote:
> > From: Longjun Luo <luolongjun@huawei.com>
> > 
> > Since uprobes will replace instructions in the process
> > memory, it is necessary to provide one way to list
> > all active uprobes. One can access this file through
> > /sys/kernel/debug/uprobes/list.
> > 
> > Output looks like this
> > =====================
> > inode+offset/ref_ctr_offset    4160760670+0x30a10/0x0
> >     ref                        1
> >     register_rwsem             (unlocked)
> >     consumer_rwsem             (unlocked)
> >     consumers-1
> >         handler                0xffffffffc03ee02e(handler) uprobe
> >         ret_handler            0x0000000000000000( )
> >         filter                 0xffffffffc03ee010(filter) uprobe
> >     consumers-2
> >         handler                0xffffffffc03e902e(handler_1) uprobe_1
> >         ret_handler            0x0000000000000000( )
> >         filter                 0xffffffffc03e9010(filter_1) uprobe_1
> 
> You can list uprobes via /sys/kernel/tracing/uprobe_events. Any specific reason
> to introduce another way?

Note that /sys/kernel/tracing/uprobe_events is only for the uprobes defined
by ftrace, it doesn't include the uprobes registered by out-of-tree modules.

Thank you,

> 
> Thanks,
> Ravi


-- 
Masami Hiramatsu (Google) <mhiramat@kernel.org>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH] uprobes: list all active uprobes in the system
@ 2023-01-11 10:42 luolongjun
  0 siblings, 0 replies; 7+ messages in thread
From: luolongjun @ 2023-01-11 10:42 UTC (permalink / raw)
  To: eterz, mingo, acme, mark.rutland, alexander.shishkin, jolsa, namhyung
  Cc: linux-perf-users, linux-kernel, sangyan, luanjianhai, zhuling8,
	lizongwu, luolongjuna, Longjun Luo

From: Longjun Luo <luolongjun@huawei.com>

Since uprobes will replace instructions in the process
memory, it is necessary to provide one way to list
all active uprobes. One can access this file through
/sys/kernel/debug/uprobes/list.

Output looks like this
=====================
inode+offset/ref_ctr_offset    4160760670+0x30a10/0x0
    ref                        1
    register_rwsem             (unlocked)
    consumer_rwsem             (unlocked)
    consumers-1
        handler                0xffffffffc03ee02e(handler) uprobe
        ret_handler            0x0000000000000000( )
        filter                 0xffffffffc03ee010(filter) uprobe
    consumers-2
        handler                0xffffffffc03e902e(handler_1) uprobe_1
        ret_handler            0x0000000000000000( )
        filter                 0xffffffffc03e9010(filter_1) uprobe_1

Signed-off-by: Longjun Luo <luolongjun@huawei.com>
---
 kernel/events/uprobes.c | 120 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 109 insertions(+), 11 deletions(-)

diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index d9e357b7e17c..2fa9b910abc7 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -27,6 +27,9 @@
 #include <linux/task_work.h>
 #include <linux/shmem_fs.h>
 #include <linux/khugepaged.h>
+#include <linux/rbtree.h>
+#include <linux/debugfs.h>
+#include <linux/kallsyms.h>
 
 #include <linux/uprobes.h>
 
@@ -40,7 +43,7 @@ static struct rb_root uprobes_tree = RB_ROOT;
  */
 #define no_uprobe_events()	RB_EMPTY_ROOT(&uprobes_tree)
 
-static DEFINE_SPINLOCK(uprobes_treelock);	/* serialize rbtree access */
+static DECLARE_RWSEM(uprobes_treelock);	/* serialize rbtree access */
 
 #define UPROBES_HASH_SZ	13
 /* serialize uprobe->pending_list */
@@ -672,9 +675,9 @@ static struct uprobe *find_uprobe(struct inode *inode, loff_t offset)
 {
 	struct uprobe *uprobe;
 
-	spin_lock(&uprobes_treelock);
+	down_read(&uprobes_treelock);
 	uprobe = __find_uprobe(inode, offset);
-	spin_unlock(&uprobes_treelock);
+	up_read(&uprobes_treelock);
 
 	return uprobe;
 }
@@ -704,9 +707,9 @@ static struct uprobe *insert_uprobe(struct uprobe *uprobe)
 {
 	struct uprobe *u;
 
-	spin_lock(&uprobes_treelock);
+	down_write(&uprobes_treelock);
 	u = __insert_uprobe(uprobe);
-	spin_unlock(&uprobes_treelock);
+	up_write(&uprobes_treelock);
 
 	return u;
 }
@@ -938,9 +941,9 @@ static void delete_uprobe(struct uprobe *uprobe)
 	if (WARN_ON(!uprobe_is_active(uprobe)))
 		return;
 
-	spin_lock(&uprobes_treelock);
+	down_write(&uprobes_treelock);
 	rb_erase(&uprobe->rb_node, &uprobes_tree);
-	spin_unlock(&uprobes_treelock);
+	up_write(&uprobes_treelock);
 	RB_CLEAR_NODE(&uprobe->rb_node); /* for uprobe_is_active() */
 	put_uprobe(uprobe);
 }
@@ -1301,7 +1304,7 @@ static void build_probe_list(struct inode *inode,
 	min = vaddr_to_offset(vma, start);
 	max = min + (end - start) - 1;
 
-	spin_lock(&uprobes_treelock);
+	down_read(&uprobes_treelock);
 	n = find_node_in_range(inode, min, max);
 	if (n) {
 		for (t = n; t; t = rb_prev(t)) {
@@ -1319,7 +1322,7 @@ static void build_probe_list(struct inode *inode,
 			get_uprobe(u);
 		}
 	}
-	spin_unlock(&uprobes_treelock);
+	up_read(&uprobes_treelock);
 }
 
 /* @vma contains reference counter, not the probed instruction. */
@@ -1410,9 +1413,9 @@ vma_has_uprobes(struct vm_area_struct *vma, unsigned long start, unsigned long e
 	min = vaddr_to_offset(vma, start);
 	max = min + (end - start) - 1;
 
-	spin_lock(&uprobes_treelock);
+	down_read(&uprobes_treelock);
 	n = find_node_in_range(inode, min, max);
-	spin_unlock(&uprobes_treelock);
+	up_read(&uprobes_treelock);
 
 	return !!n;
 }
@@ -2357,3 +2360,98 @@ void __init uprobes_init(void)
 
 	BUG_ON(register_die_notifier(&uprobe_exception_nb));
 }
+
+#ifdef CONFIG_DEBUG_FS
+
+static void *uprobe_seq_start(struct seq_file *m, loff_t *pos)
+{
+	loff_t num = *pos;
+	struct rb_node *rbp;
+
+	down_read(&uprobes_treelock);
+	for (rbp = rb_first(&uprobes_tree); rbp; rbp = rb_next(rbp)) {
+		if (num-- == 0)
+			return rbp;
+	}
+
+	return NULL;
+}
+
+static void *uprobe_seq_next(struct seq_file *f, void *v, loff_t *pos)
+{
+	++*pos;
+	return (void *)rb_next(v);
+}
+
+static void uprobe_seq_stop(struct seq_file *f, void *v)
+{
+	up_read(&uprobes_treelock);
+}
+
+static int show_uprobe(struct seq_file *m, void *v)
+{
+	struct uprobe *uprobe = rb_entry_safe(v, struct uprobe, rb_node);
+	struct uprobe_consumer *con;
+	char *modname, namebuf[KSYM_NAME_LEN];
+	const char *sym;
+	unsigned int cnt = 1;
+
+	seq_printf(m, "inode+offset/ref_ctr_offset    %lu+0x%llx/0x%llx\n",
+		uprobe->inode->i_ino, uprobe->offset, uprobe->ref_ctr_offset);
+	seq_printf(m, "    ref                        %u\n",
+		refcount_read(&uprobe->ref));
+	seq_printf(m, "    register_rwsem             %s\n",
+		rwsem_is_locked(&uprobe->register_rwsem)?"(locked)":"(unlocked)");
+	seq_printf(m, "    consumer_rwsem             %s\n",
+		rwsem_is_locked(&uprobe->consumer_rwsem)?"(locked)":"(unlocked)");
+
+	/* lock consumer_rwsem when hold uprobes_treelock */
+	down_read(&uprobe->consumer_rwsem);
+	for (con = uprobe->consumers; con; con = con->next, cnt++) {
+		seq_printf(m, "    consumers-%u\n", cnt);
+
+		modname = NULL;
+		sym = kallsyms_lookup((unsigned long)con->handler, NULL, NULL,
+			&modname, namebuf);
+		seq_printf(m, "        handler                0x%px(%s) %s\n",
+			con->handler, (sym ? sym : " "), (modname ? modname : " "));
+
+		modname = NULL;
+		sym = kallsyms_lookup((unsigned long)con->ret_handler, NULL, NULL,
+			&modname, namebuf);
+		seq_printf(m, "        ret_handler            0x%px(%s) %s\n",
+			con->ret_handler, (sym ? sym : " "), (modname ? modname : " "));
+
+		modname = NULL;
+		sym = kallsyms_lookup((unsigned long)con->filter, NULL, NULL,
+			&modname, namebuf);
+		seq_printf(m, "        filter                 0x%px(%s) %s\n",
+			con->filter, (sym ? sym : " "), (modname ? modname : " "));
+	}
+	up_read(&uprobe->consumer_rwsem);
+
+	return 0;
+}
+
+static const struct seq_operations uprobes_sops = {
+	.start = uprobe_seq_start,
+	.next  = uprobe_seq_next,
+	.stop  = uprobe_seq_stop,
+	.show  = show_uprobe,
+};
+
+DEFINE_SEQ_ATTRIBUTE(uprobes);
+
+static int __init debugfs_uprobe_init(void)
+{
+	struct dentry *dir;
+
+	dir = debugfs_create_dir("uprobes", NULL);
+
+	debugfs_create_file("list", 0400, dir, NULL, &uprobes_fops);
+
+	return 0;
+}
+late_initcall(debugfs_uprobe_init);
+
+#endif /* CONFIG_DEBUG_FS */
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2023-01-13 15:37 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-11 10:48 [PATCH] uprobes: list all active uprobes in the system luolongjun
2023-01-11 21:35 ` Ian Rogers
2023-01-12  4:15   ` Longjun Luo
2023-01-13  4:03 ` Ravi Bangoria
2023-01-13  7:49   ` Longjun Luo
2023-01-13 15:29   ` Masami Hiramatsu
  -- strict thread matches above, loose matches on Subject: below --
2023-01-11 10:42 luolongjun

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).