Commit 4d7bf939 authored by Qing Zhang's avatar Qing Zhang Committed by Huacai Chen
Browse files

LoongArch: Add USER_STACKTRACE support



To get the best stacktrace output, you can compile your userspace
programs with frame pointers (at least glibc + the app you are tracing).

1, export "CC = gcc -fno-omit-frame-pointer";
2, compile your programs with "CC";
3, use uprobe to get stacktrace output.

...
     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: default avatarQing Zhang <zhangqing@loongson.cn>
Signed-off-by: default avatarHuacai Chen <chenhuacai@loongson.cn>
parent 93a4fa62
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -119,6 +119,7 @@ config LOONGARCH
	select SWIOTLB
	select TRACE_IRQFLAGS_SUPPORT
	select USE_PERCPU_NUMA_NODE_ID
	select USER_STACKTRACE_SUPPORT
	select ZONE_DMA32

config 32BIT
+5 −0
Original line number Diff line number Diff line
@@ -21,6 +21,11 @@ struct stack_info {
	unsigned long begin, end, next_sp;
};

struct stack_frame {
	unsigned long	fp;
	unsigned long	ra;
};

bool in_irq_stack(unsigned long stack, struct stack_info *info);
bool in_task_stack(unsigned long stack, struct task_struct *task, struct stack_info *info);
int get_stack_info(unsigned long stack, struct task_struct *task, struct stack_info *info);
+41 −0
Original line number Diff line number Diff line
@@ -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,43 @@ 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 = 1;
	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;

	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;
	}
}