Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 // Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd.
0003 
0004 #include <linux/perf_event.h>
0005 #include <linux/uaccess.h>
0006 
0007 /* Kernel callchain */
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  * Get the return address for a single stackframe and return a pointer to the
0045  * next frame tail.
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     /* Check accessibility of one struct frame_tail beyond */
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  * This will be called when the target is in user mode
0074  * This function will only be called when we use
0075  * "PERF_SAMPLE_CALLCHAIN" in
0076  * kernel/events/core.c:perf_prepare_sample()
0077  *
0078  * How to trigger perf_callchain_[user/kernel] :
0079  * $ perf record -e cpu-clock --call-graph fp ./program
0080  * $ perf report --call-graph
0081  *
0082  * On C-SKY platform, the program being sampled and the C library
0083  * need to be compiled with * -mbacktrace, otherwise the user
0084  * stack will not contain function frame.
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      * While backtrace from leaf function, lr is normally
0096      * not saved inside frame on C-SKY, so get lr from pt_regs
0097      * at the sample point. However, lr value can be incorrect if
0098      * lr is used as temp register
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 }