xref: /openbmc/linux/arch/arm/kernel/perf_callchain.c (revision d39976f0fd144d1cef4830d696e2a1e6d8058dc6)
1*d39976f0SMark Rutland /*
2*d39976f0SMark Rutland  * ARM callchain support
3*d39976f0SMark Rutland  *
4*d39976f0SMark Rutland  * Copyright (C) 2009 picoChip Designs, Ltd., Jamie Iles
5*d39976f0SMark Rutland  * Copyright (C) 2010 ARM Ltd., Will Deacon <will.deacon@arm.com>
6*d39976f0SMark Rutland  *
7*d39976f0SMark Rutland  * This code is based on the ARM OProfile backtrace code.
8*d39976f0SMark Rutland  */
9*d39976f0SMark Rutland #include <linux/perf_event.h>
10*d39976f0SMark Rutland #include <linux/uaccess.h>
11*d39976f0SMark Rutland 
12*d39976f0SMark Rutland #include <asm/stacktrace.h>
13*d39976f0SMark Rutland 
14*d39976f0SMark Rutland /*
15*d39976f0SMark Rutland  * The registers we're interested in are at the end of the variable
16*d39976f0SMark Rutland  * length saved register structure. The fp points at the end of this
17*d39976f0SMark Rutland  * structure so the address of this struct is:
18*d39976f0SMark Rutland  * (struct frame_tail *)(xxx->fp)-1
19*d39976f0SMark Rutland  *
20*d39976f0SMark Rutland  * This code has been adapted from the ARM OProfile support.
21*d39976f0SMark Rutland  */
22*d39976f0SMark Rutland struct frame_tail {
23*d39976f0SMark Rutland 	struct frame_tail __user *fp;
24*d39976f0SMark Rutland 	unsigned long sp;
25*d39976f0SMark Rutland 	unsigned long lr;
26*d39976f0SMark Rutland } __attribute__((packed));
27*d39976f0SMark Rutland 
28*d39976f0SMark Rutland /*
29*d39976f0SMark Rutland  * Get the return address for a single stackframe and return a pointer to the
30*d39976f0SMark Rutland  * next frame tail.
31*d39976f0SMark Rutland  */
32*d39976f0SMark Rutland static struct frame_tail __user *
33*d39976f0SMark Rutland user_backtrace(struct frame_tail __user *tail,
34*d39976f0SMark Rutland 	       struct perf_callchain_entry *entry)
35*d39976f0SMark Rutland {
36*d39976f0SMark Rutland 	struct frame_tail buftail;
37*d39976f0SMark Rutland 	unsigned long err;
38*d39976f0SMark Rutland 
39*d39976f0SMark Rutland 	if (!access_ok(VERIFY_READ, tail, sizeof(buftail)))
40*d39976f0SMark Rutland 		return NULL;
41*d39976f0SMark Rutland 
42*d39976f0SMark Rutland 	pagefault_disable();
43*d39976f0SMark Rutland 	err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
44*d39976f0SMark Rutland 	pagefault_enable();
45*d39976f0SMark Rutland 
46*d39976f0SMark Rutland 	if (err)
47*d39976f0SMark Rutland 		return NULL;
48*d39976f0SMark Rutland 
49*d39976f0SMark Rutland 	perf_callchain_store(entry, buftail.lr);
50*d39976f0SMark Rutland 
51*d39976f0SMark Rutland 	/*
52*d39976f0SMark Rutland 	 * Frame pointers should strictly progress back up the stack
53*d39976f0SMark Rutland 	 * (towards higher addresses).
54*d39976f0SMark Rutland 	 */
55*d39976f0SMark Rutland 	if (tail + 1 >= buftail.fp)
56*d39976f0SMark Rutland 		return NULL;
57*d39976f0SMark Rutland 
58*d39976f0SMark Rutland 	return buftail.fp - 1;
59*d39976f0SMark Rutland }
60*d39976f0SMark Rutland 
61*d39976f0SMark Rutland void
62*d39976f0SMark Rutland perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
63*d39976f0SMark Rutland {
64*d39976f0SMark Rutland 	struct frame_tail __user *tail;
65*d39976f0SMark Rutland 
66*d39976f0SMark Rutland 	if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
67*d39976f0SMark Rutland 		/* We don't support guest os callchain now */
68*d39976f0SMark Rutland 		return;
69*d39976f0SMark Rutland 	}
70*d39976f0SMark Rutland 
71*d39976f0SMark Rutland 	perf_callchain_store(entry, regs->ARM_pc);
72*d39976f0SMark Rutland 
73*d39976f0SMark Rutland 	if (!current->mm)
74*d39976f0SMark Rutland 		return;
75*d39976f0SMark Rutland 
76*d39976f0SMark Rutland 	tail = (struct frame_tail __user *)regs->ARM_fp - 1;
77*d39976f0SMark Rutland 
78*d39976f0SMark Rutland 	while ((entry->nr < PERF_MAX_STACK_DEPTH) &&
79*d39976f0SMark Rutland 	       tail && !((unsigned long)tail & 0x3))
80*d39976f0SMark Rutland 		tail = user_backtrace(tail, entry);
81*d39976f0SMark Rutland }
82*d39976f0SMark Rutland 
83*d39976f0SMark Rutland /*
84*d39976f0SMark Rutland  * Gets called by walk_stackframe() for every stackframe. This will be called
85*d39976f0SMark Rutland  * whist unwinding the stackframe and is like a subroutine return so we use
86*d39976f0SMark Rutland  * the PC.
87*d39976f0SMark Rutland  */
88*d39976f0SMark Rutland static int
89*d39976f0SMark Rutland callchain_trace(struct stackframe *fr,
90*d39976f0SMark Rutland 		void *data)
91*d39976f0SMark Rutland {
92*d39976f0SMark Rutland 	struct perf_callchain_entry *entry = data;
93*d39976f0SMark Rutland 	perf_callchain_store(entry, fr->pc);
94*d39976f0SMark Rutland 	return 0;
95*d39976f0SMark Rutland }
96*d39976f0SMark Rutland 
97*d39976f0SMark Rutland void
98*d39976f0SMark Rutland perf_callchain_kernel(struct perf_callchain_entry *entry, struct pt_regs *regs)
99*d39976f0SMark Rutland {
100*d39976f0SMark Rutland 	struct stackframe fr;
101*d39976f0SMark Rutland 
102*d39976f0SMark Rutland 	if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
103*d39976f0SMark Rutland 		/* We don't support guest os callchain now */
104*d39976f0SMark Rutland 		return;
105*d39976f0SMark Rutland 	}
106*d39976f0SMark Rutland 
107*d39976f0SMark Rutland 	arm_get_current_stackframe(regs, &fr);
108*d39976f0SMark Rutland 	walk_stackframe(&fr, callchain_trace, entry);
109*d39976f0SMark Rutland }
110*d39976f0SMark Rutland 
111*d39976f0SMark Rutland unsigned long perf_instruction_pointer(struct pt_regs *regs)
112*d39976f0SMark Rutland {
113*d39976f0SMark Rutland 	if (perf_guest_cbs && perf_guest_cbs->is_in_guest())
114*d39976f0SMark Rutland 		return perf_guest_cbs->get_guest_ip();
115*d39976f0SMark Rutland 
116*d39976f0SMark Rutland 	return instruction_pointer(regs);
117*d39976f0SMark Rutland }
118*d39976f0SMark Rutland 
119*d39976f0SMark Rutland unsigned long perf_misc_flags(struct pt_regs *regs)
120*d39976f0SMark Rutland {
121*d39976f0SMark Rutland 	int misc = 0;
122*d39976f0SMark Rutland 
123*d39976f0SMark Rutland 	if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
124*d39976f0SMark Rutland 		if (perf_guest_cbs->is_user_mode())
125*d39976f0SMark Rutland 			misc |= PERF_RECORD_MISC_GUEST_USER;
126*d39976f0SMark Rutland 		else
127*d39976f0SMark Rutland 			misc |= PERF_RECORD_MISC_GUEST_KERNEL;
128*d39976f0SMark Rutland 	} else {
129*d39976f0SMark Rutland 		if (user_mode(regs))
130*d39976f0SMark Rutland 			misc |= PERF_RECORD_MISC_USER;
131*d39976f0SMark Rutland 		else
132*d39976f0SMark Rutland 			misc |= PERF_RECORD_MISC_KERNEL;
133*d39976f0SMark Rutland 	}
134*d39976f0SMark Rutland 
135*d39976f0SMark Rutland 	return misc;
136*d39976f0SMark Rutland }
137