linux/arch/csky/kernel/perf_callchain.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd.
   3
   4#include <linux/perf_event.h>
   5#include <linux/uaccess.h>
   6
   7/* Kernel callchain */
   8struct stackframe {
   9        unsigned long fp;
  10        unsigned long lr;
  11};
  12
  13static int unwind_frame_kernel(struct stackframe *frame)
  14{
  15        if (kstack_end((void *)frame->fp))
  16                return -EPERM;
  17        if (frame->fp & 0x3 || frame->fp < TASK_SIZE)
  18                return -EPERM;
  19
  20        *frame = *(struct stackframe *)frame->fp;
  21        if (__kernel_text_address(frame->lr)) {
  22                int graph = 0;
  23
  24                frame->lr = ftrace_graph_ret_addr(NULL, &graph, frame->lr,
  25                                NULL);
  26        }
  27        return 0;
  28}
  29
  30static void notrace walk_stackframe(struct stackframe *fr,
  31                        struct perf_callchain_entry_ctx *entry)
  32{
  33        do {
  34                perf_callchain_store(entry, fr->lr);
  35        } while (unwind_frame_kernel(fr) >= 0);
  36}
  37
  38/*
  39 * Get the return address for a single stackframe and return a pointer to the
  40 * next frame tail.
  41 */
  42static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry,
  43                        unsigned long fp, unsigned long reg_lr)
  44{
  45        struct stackframe buftail;
  46        unsigned long lr = 0;
  47        unsigned long *user_frame_tail = (unsigned long *)fp;
  48
  49        /* Check accessibility of one struct frame_tail beyond */
  50        if (!access_ok(user_frame_tail, sizeof(buftail)))
  51                return 0;
  52        if (__copy_from_user_inatomic(&buftail, user_frame_tail,
  53                                      sizeof(buftail)))
  54                return 0;
  55
  56        if (reg_lr != 0)
  57                lr = reg_lr;
  58        else
  59                lr = buftail.lr;
  60
  61        fp = buftail.fp;
  62        perf_callchain_store(entry, lr);
  63
  64        return fp;
  65}
  66
  67/*
  68 * This will be called when the target is in user mode
  69 * This function will only be called when we use
  70 * "PERF_SAMPLE_CALLCHAIN" in
  71 * kernel/events/core.c:perf_prepare_sample()
  72 *
  73 * How to trigger perf_callchain_[user/kernel] :
  74 * $ perf record -e cpu-clock --call-graph fp ./program
  75 * $ perf report --call-graph
  76 *
  77 * On C-SKY platform, the program being sampled and the C library
  78 * need to be compiled with * -mbacktrace, otherwise the user
  79 * stack will not contain function frame.
  80 */
  81void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
  82                         struct pt_regs *regs)
  83{
  84        unsigned long fp = 0;
  85
  86        /* C-SKY does not support virtualization. */
  87        if (perf_guest_cbs && perf_guest_cbs->is_in_guest())
  88                return;
  89
  90        fp = regs->regs[4];
  91        perf_callchain_store(entry, regs->pc);
  92
  93        /*
  94         * While backtrace from leaf function, lr is normally
  95         * not saved inside frame on C-SKY, so get lr from pt_regs
  96         * at the sample point. However, lr value can be incorrect if
  97         * lr is used as temp register
  98         */
  99        fp = user_backtrace(entry, fp, regs->lr);
 100
 101        while (fp && !(fp & 0x3) && entry->nr < entry->max_stack)
 102                fp = user_backtrace(entry, fp, 0);
 103}
 104
 105void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
 106                           struct pt_regs *regs)
 107{
 108        struct stackframe fr;
 109
 110        /* C-SKY does not support virtualization. */
 111        if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
 112                pr_warn("C-SKY does not support perf in guest mode!");
 113                return;
 114        }
 115
 116        fr.fp = regs->regs[4];
 117        fr.lr = regs->lr;
 118        walk_stackframe(&fr, entry);
 119}
 120