~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arm64/kernel/perf_callchain.c

Version: ~ [ linux-5.10-rc5 ] ~ [ linux-5.9.10 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.79 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.159 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.208 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.245 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.245 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.85 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * arm64 callchain support
  3  *
  4  * Copyright (C) 2015 ARM Limited
  5  *
  6  * This program is free software; you can redistribute it and/or modify
  7  * it under the terms of the GNU General Public License version 2 as
  8  * published by the Free Software Foundation.
  9  *
 10  * This program is distributed in the hope that it will be useful,
 11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13  * GNU General Public License for more details.
 14  *
 15  * You should have received a copy of the GNU General Public License
 16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 17  */
 18 #include <linux/perf_event.h>
 19 #include <linux/uaccess.h>
 20 
 21 #include <asm/stacktrace.h>
 22 
 23 struct frame_tail {
 24         struct frame_tail       __user *fp;
 25         unsigned long           lr;
 26 } __attribute__((packed));
 27 
 28 /*
 29  * Get the return address for a single stackframe and return a pointer to the
 30  * next frame tail.
 31  */
 32 static struct frame_tail __user *
 33 user_backtrace(struct frame_tail __user *tail,
 34                struct perf_callchain_entry_ctx *entry)
 35 {
 36         struct frame_tail buftail;
 37         unsigned long err;
 38 
 39         /* Also check accessibility of one struct frame_tail beyond */
 40         if (!access_ok(VERIFY_READ, tail, sizeof(buftail)))
 41                 return NULL;
 42 
 43         pagefault_disable();
 44         err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
 45         pagefault_enable();
 46 
 47         if (err)
 48                 return NULL;
 49 
 50         perf_callchain_store(entry, buftail.lr);
 51 
 52         /*
 53          * Frame pointers should strictly progress back up the stack
 54          * (towards higher addresses).
 55          */
 56         if (tail >= buftail.fp)
 57                 return NULL;
 58 
 59         return buftail.fp;
 60 }
 61 
 62 #ifdef CONFIG_COMPAT
 63 /*
 64  * The registers we're interested in are at the end of the variable
 65  * length saved register structure. The fp points at the end of this
 66  * structure so the address of this struct is:
 67  * (struct compat_frame_tail *)(xxx->fp)-1
 68  *
 69  * This code has been adapted from the ARM OProfile support.
 70  */
 71 struct compat_frame_tail {
 72         compat_uptr_t   fp; /* a (struct compat_frame_tail *) in compat mode */
 73         u32             sp;
 74         u32             lr;
 75 } __attribute__((packed));
 76 
 77 static struct compat_frame_tail __user *
 78 compat_user_backtrace(struct compat_frame_tail __user *tail,
 79                       struct perf_callchain_entry_ctx *entry)
 80 {
 81         struct compat_frame_tail buftail;
 82         unsigned long err;
 83 
 84         /* Also check accessibility of one struct frame_tail beyond */
 85         if (!access_ok(VERIFY_READ, tail, sizeof(buftail)))
 86                 return NULL;
 87 
 88         pagefault_disable();
 89         err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
 90         pagefault_enable();
 91 
 92         if (err)
 93                 return NULL;
 94 
 95         perf_callchain_store(entry, buftail.lr);
 96 
 97         /*
 98          * Frame pointers should strictly progress back up the stack
 99          * (towards higher addresses).
100          */
101         if (tail + 1 >= (struct compat_frame_tail __user *)
102                         compat_ptr(buftail.fp))
103                 return NULL;
104 
105         return (struct compat_frame_tail __user *)compat_ptr(buftail.fp) - 1;
106 }
107 #endif /* CONFIG_COMPAT */
108 
109 void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
110                          struct pt_regs *regs)
111 {
112         if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
113                 /* We don't support guest os callchain now */
114                 return;
115         }
116 
117         perf_callchain_store(entry, regs->pc);
118 
119         if (!compat_user_mode(regs)) {
120                 /* AARCH64 mode */
121                 struct frame_tail __user *tail;
122 
123                 tail = (struct frame_tail __user *)regs->regs[29];
124 
125                 while (entry->nr < entry->max_stack &&
126                        tail && !((unsigned long)tail & 0xf))
127                         tail = user_backtrace(tail, entry);
128         } else {
129 #ifdef CONFIG_COMPAT
130                 /* AARCH32 compat mode */
131                 struct compat_frame_tail __user *tail;
132 
133                 tail = (struct compat_frame_tail __user *)regs->compat_fp - 1;
134 
135                 while ((entry->nr < entry->max_stack) &&
136                         tail && !((unsigned long)tail & 0x3))
137                         tail = compat_user_backtrace(tail, entry);
138 #endif
139         }
140 }
141 
142 /*
143  * Gets called by walk_stackframe() for every stackframe. This will be called
144  * whist unwinding the stackframe and is like a subroutine return so we use
145  * the PC.
146  */
147 static int callchain_trace(struct stackframe *frame, void *data)
148 {
149         struct perf_callchain_entry_ctx *entry = data;
150         perf_callchain_store(entry, frame->pc);
151         return 0;
152 }
153 
154 void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
155                            struct pt_regs *regs)
156 {
157         struct stackframe frame;
158 
159         if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
160                 /* We don't support guest os callchain now */
161                 return;
162         }
163 
164         frame.fp = regs->regs[29];
165         frame.sp = regs->sp;
166         frame.pc = regs->pc;
167 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
168         frame.graph = current->curr_ret_stack;
169 #endif
170 
171         walk_stackframe(current, &frame, callchain_trace, entry);
172 }
173 
174 unsigned long perf_instruction_pointer(struct pt_regs *regs)
175 {
176         if (perf_guest_cbs && perf_guest_cbs->is_in_guest())
177                 return perf_guest_cbs->get_guest_ip();
178 
179         return instruction_pointer(regs);
180 }
181 
182 unsigned long perf_misc_flags(struct pt_regs *regs)
183 {
184         int misc = 0;
185 
186         if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
187                 if (perf_guest_cbs->is_user_mode())
188                         misc |= PERF_RECORD_MISC_GUEST_USER;
189                 else
190                         misc |= PERF_RECORD_MISC_GUEST_KERNEL;
191         } else {
192                 if (user_mode(regs))
193                         misc |= PERF_RECORD_MISC_USER;
194                 else
195                         misc |= PERF_RECORD_MISC_KERNEL;
196         }
197 
198         return misc;
199 }
200 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp