[PATCH 4/4] LoongArch: Add USER_STACKTRACE support

Qing Zhang posted 4 patches 3 years, 8 months ago
[PATCH 4/4] LoongArch: Add USER_STACKTRACE support
Posted by Qing Zhang 3 years, 8 months ago
To get the best output you can compile your userspace programs with
frame pointers (at least glibc + the app you are tracing export "CC
=gcc -fno-omit-frame-pointer".

...
     echo 'p:malloc /usr/lib64/libc.so.6:0x0a4704 size=%r4:u64'
                                                > uprobe_events
     echo 'p:free /usr/lib64/libc.so.6:0x0a4d50 ptr=%r4:x64'
                                               >> uprobe_events
     echo 'comm == "demo"' > ./events/uprobes/malloc/filter
     echo 'comm == "demo"' > ./events/uprobes/free/filter
     echo 1 > ./options/userstacktrace
     echo 1 > ./options/sym-userobj
 ...

Signed-off-by: Qing Zhang <zhangqing@loongson.cn>
---
 arch/loongarch/Kconfig                  |  1 +
 arch/loongarch/include/asm/stacktrace.h |  5 +++
 arch/loongarch/kernel/stacktrace.c      | 42 +++++++++++++++++++++++++
 3 files changed, 48 insertions(+)

diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index 85d0fa3147cd..05906384d564 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -107,6 +107,7 @@ config LOONGARCH
 	select SWIOTLB
 	select TRACE_IRQFLAGS_SUPPORT
 	select USE_PERCPU_NUMA_NODE_ID
+	select USER_STACKTRACE_SUPPORT
 	select ZONE_DMA32
 	select MMU_GATHER_MERGE_VMAS if MMU
 
diff --git a/arch/loongarch/include/asm/stacktrace.h b/arch/loongarch/include/asm/stacktrace.h
index 49cb89213aeb..77fdb8ad662d 100644
--- a/arch/loongarch/include/asm/stacktrace.h
+++ b/arch/loongarch/include/asm/stacktrace.h
@@ -21,6 +21,11 @@ struct stack_info {
 	unsigned long begin, end, next_sp;
 };
 
+struct stack_frame {
+	unsigned long	fp;
+	unsigned long	ra;
+};
+
 bool in_task_stack(unsigned long stack, struct task_struct *task,
 			struct stack_info *info);
 bool in_irq_stack(unsigned long stack, struct stack_info *info);
diff --git a/arch/loongarch/kernel/stacktrace.c b/arch/loongarch/kernel/stacktrace.c
index f4f4b8ad3917..344224c7cb0e 100644
--- a/arch/loongarch/kernel/stacktrace.c
+++ b/arch/loongarch/kernel/stacktrace.c
@@ -6,6 +6,7 @@
  */
 #include <linux/sched.h>
 #include <linux/stacktrace.h>
+#include <linux/uaccess.h>
 
 #include <asm/stacktrace.h>
 #include <asm/unwind.h>
@@ -35,3 +36,44 @@ void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,
 			break;
 	}
 }
+
+static int
+copy_stack_frame(unsigned long fp, struct stack_frame *frame)
+{
+	int ret;
+	unsigned long err;
+	unsigned long __user *user_frame_tail;
+
+	user_frame_tail = (unsigned long *)(fp - sizeof(struct stack_frame));
+	if (!access_ok(user_frame_tail, sizeof(*frame)))
+		return 0;
+
+	ret = 1;
+	pagefault_disable();
+	err = (__copy_from_user_inatomic(frame, user_frame_tail, sizeof(*frame)));
+	if (err || (unsigned long)user_frame_tail >= frame->fp)
+		ret = 0;
+	pagefault_enable();
+
+	return ret;
+}
+
+void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie,
+			  const struct pt_regs *regs)
+{
+	unsigned long fp = regs->regs[22];
+
+	while (fp && !((unsigned long)fp & 0xf)) {
+		struct stack_frame frame;
+
+		frame.fp = 0;
+		frame.ra = 0;
+		if (!copy_stack_frame(fp, &frame))
+			break;
+		if (!frame.ra)
+			break;
+		if (!consume_entry(cookie, frame.ra))
+			break;
+		fp = frame.fp;
+	}
+}
-- 
2.20.1
Re: [PATCH 4/4] LoongArch: Add USER_STACKTRACE support
Posted by Huacai Chen 3 years, 8 months ago
Hi, Qing,

On Mon, Aug 1, 2022 at 8:17 PM Qing Zhang <zhangqing@loongson.cn> wrote:
>
> To get the best output you can compile your userspace programs with
> frame pointers (at least glibc + the app you are tracing export "CC
> =gcc -fno-omit-frame-pointer".
>
> ...
>      echo 'p:malloc /usr/lib64/libc.so.6:0x0a4704 size=%r4:u64'
>                                                 > uprobe_events
>      echo 'p:free /usr/lib64/libc.so.6:0x0a4d50 ptr=%r4:x64'
>                                                >> uprobe_events
>      echo 'comm == "demo"' > ./events/uprobes/malloc/filter
>      echo 'comm == "demo"' > ./events/uprobes/free/filter
>      echo 1 > ./options/userstacktrace
>      echo 1 > ./options/sym-userobj
>  ...
>
> Signed-off-by: Qing Zhang <zhangqing@loongson.cn>
> ---
>  arch/loongarch/Kconfig                  |  1 +
>  arch/loongarch/include/asm/stacktrace.h |  5 +++
>  arch/loongarch/kernel/stacktrace.c      | 42 +++++++++++++++++++++++++
>  3 files changed, 48 insertions(+)
>
> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index 85d0fa3147cd..05906384d564 100644
> --- a/arch/loongarch/Kconfig
> +++ b/arch/loongarch/Kconfig
> @@ -107,6 +107,7 @@ config LOONGARCH
>         select SWIOTLB
>         select TRACE_IRQFLAGS_SUPPORT
>         select USE_PERCPU_NUMA_NODE_ID
> +       select USER_STACKTRACE_SUPPORT
>         select ZONE_DMA32
>         select MMU_GATHER_MERGE_VMAS if MMU
>
> diff --git a/arch/loongarch/include/asm/stacktrace.h b/arch/loongarch/include/asm/stacktrace.h
> index 49cb89213aeb..77fdb8ad662d 100644
> --- a/arch/loongarch/include/asm/stacktrace.h
> +++ b/arch/loongarch/include/asm/stacktrace.h
> @@ -21,6 +21,11 @@ struct stack_info {
>         unsigned long begin, end, next_sp;
>  };
>
> +struct stack_frame {
> +       unsigned long   fp;
> +       unsigned long   ra;
> +};
> +
>  bool in_task_stack(unsigned long stack, struct task_struct *task,
>                         struct stack_info *info);
>  bool in_irq_stack(unsigned long stack, struct stack_info *info);
> diff --git a/arch/loongarch/kernel/stacktrace.c b/arch/loongarch/kernel/stacktrace.c
> index f4f4b8ad3917..344224c7cb0e 100644
> --- a/arch/loongarch/kernel/stacktrace.c
> +++ b/arch/loongarch/kernel/stacktrace.c
> @@ -6,6 +6,7 @@
>   */
>  #include <linux/sched.h>
>  #include <linux/stacktrace.h>
> +#include <linux/uaccess.h>
>
>  #include <asm/stacktrace.h>
>  #include <asm/unwind.h>
> @@ -35,3 +36,44 @@ void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,
>                         break;
>         }
>  }
> +
> +static int
> +copy_stack_frame(unsigned long fp, struct stack_frame *frame)
> +{
> +       int ret;
> +       unsigned long err;
> +       unsigned long __user *user_frame_tail;
> +
> +       user_frame_tail = (unsigned long *)(fp - sizeof(struct stack_frame));
> +       if (!access_ok(user_frame_tail, sizeof(*frame)))
> +               return 0;
> +
> +       ret = 1;
Maybe initializing it at its definition is better?

Huacai
> +       pagefault_disable();
> +       err = (__copy_from_user_inatomic(frame, user_frame_tail, sizeof(*frame)));
> +       if (err || (unsigned long)user_frame_tail >= frame->fp)
> +               ret = 0;
> +       pagefault_enable();
> +
> +       return ret;
> +}
> +
> +void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie,
> +                         const struct pt_regs *regs)
> +{
> +       unsigned long fp = regs->regs[22];
> +
> +       while (fp && !((unsigned long)fp & 0xf)) {
> +               struct stack_frame frame;
> +
> +               frame.fp = 0;
> +               frame.ra = 0;
> +               if (!copy_stack_frame(fp, &frame))
> +                       break;
> +               if (!frame.ra)
> +                       break;
> +               if (!consume_entry(cookie, frame.ra))
> +                       break;
> +               fp = frame.fp;
> +       }
> +}
> --
> 2.20.1
>
>