0001
0002
0003
0004 #include <linux/perf_event.h>
0005 #include <linux/uaccess.h>
0006
0007
0008 struct stackframe {
0009 unsigned long fp;
0010 unsigned long lr;
0011 };
0012
0013 static int unwind_frame_kernel(struct stackframe *frame)
0014 {
0015 unsigned long low = (unsigned long)task_stack_page(current);
0016 unsigned long high = low + THREAD_SIZE;
0017
0018 if (unlikely(frame->fp < low || frame->fp > high))
0019 return -EPERM;
0020
0021 if (kstack_end((void *)frame->fp) || frame->fp & 0x3)
0022 return -EPERM;
0023
0024 *frame = *(struct stackframe *)frame->fp;
0025
0026 if (__kernel_text_address(frame->lr)) {
0027 int graph = 0;
0028
0029 frame->lr = ftrace_graph_ret_addr(NULL, &graph, frame->lr,
0030 NULL);
0031 }
0032 return 0;
0033 }
0034
0035 static void notrace walk_stackframe(struct stackframe *fr,
0036 struct perf_callchain_entry_ctx *entry)
0037 {
0038 do {
0039 perf_callchain_store(entry, fr->lr);
0040 } while (unwind_frame_kernel(fr) >= 0);
0041 }
0042
0043
0044
0045
0046
0047 static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry,
0048 unsigned long fp, unsigned long reg_lr)
0049 {
0050 struct stackframe buftail;
0051 unsigned long lr = 0;
0052 unsigned long __user *user_frame_tail = (unsigned long __user *)fp;
0053
0054
0055 if (!access_ok(user_frame_tail, sizeof(buftail)))
0056 return 0;
0057 if (__copy_from_user_inatomic(&buftail, user_frame_tail,
0058 sizeof(buftail)))
0059 return 0;
0060
0061 if (reg_lr != 0)
0062 lr = reg_lr;
0063 else
0064 lr = buftail.lr;
0065
0066 fp = buftail.fp;
0067 perf_callchain_store(entry, lr);
0068
0069 return fp;
0070 }
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086 void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
0087 struct pt_regs *regs)
0088 {
0089 unsigned long fp = 0;
0090
0091 fp = regs->regs[4];
0092 perf_callchain_store(entry, regs->pc);
0093
0094
0095
0096
0097
0098
0099
0100 fp = user_backtrace(entry, fp, regs->lr);
0101
0102 while (fp && !(fp & 0x3) && entry->nr < entry->max_stack)
0103 fp = user_backtrace(entry, fp, 0);
0104 }
0105
0106 void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
0107 struct pt_regs *regs)
0108 {
0109 struct stackframe fr;
0110
0111 fr.fp = regs->regs[4];
0112 fr.lr = regs->lr;
0113 walk_stackframe(&fr, entry);
0114 }