linux/arch/metag/kernel/perf_callchain.c
<<
>>
Prefs
   1/*
   2 * Perf callchain handling code.
   3 *
   4 *   Based on the ARM perf implementation.
   5 */
   6
   7#include <linux/kernel.h>
   8#include <linux/sched.h>
   9#include <linux/perf_event.h>
  10#include <linux/uaccess.h>
  11#include <asm/ptrace.h>
  12#include <asm/stacktrace.h>
  13
  14static bool is_valid_call(unsigned long calladdr)
  15{
  16        unsigned int callinsn;
  17
  18        /* Check the possible return address is aligned. */
  19        if (!(calladdr & 0x3)) {
  20                if (!get_user(callinsn, (unsigned int *)calladdr)) {
  21                        /* Check for CALLR or SWAP PC,D1RtP. */
  22                        if ((callinsn & 0xff000000) == 0xab000000 ||
  23                            callinsn == 0xa3200aa0)
  24                                return true;
  25                }
  26        }
  27        return false;
  28}
  29
  30static struct metag_frame __user *
  31user_backtrace(struct metag_frame __user *user_frame,
  32               struct perf_callchain_entry *entry)
  33{
  34        struct metag_frame frame;
  35        unsigned long calladdr;
  36
  37        /* We cannot rely on having frame pointers in user code. */
  38        while (1) {
  39                /* Also check accessibility of one struct frame beyond */
  40                if (!access_ok(VERIFY_READ, user_frame, sizeof(frame)))
  41                        return 0;
  42                if (__copy_from_user_inatomic(&frame, user_frame,
  43                                              sizeof(frame)))
  44                        return 0;
  45
  46                --user_frame;
  47
  48                calladdr = frame.lr - 4;
  49                if (is_valid_call(calladdr)) {
  50                        perf_callchain_store(entry, calladdr);
  51                        return user_frame;
  52                }
  53        }
  54
  55        return 0;
  56}
  57
  58void
  59perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
  60{
  61        unsigned long sp = regs->ctx.AX[0].U0;
  62        struct metag_frame __user *frame;
  63
  64        frame = (struct metag_frame __user *)sp;
  65
  66        --frame;
  67
  68        while ((entry->nr < PERF_MAX_STACK_DEPTH) && frame)
  69                frame = user_backtrace(frame, entry);
  70}
  71
  72/*
  73 * Gets called by walk_stackframe() for every stackframe. This will be called
  74 * whist unwinding the stackframe and is like a subroutine return so we use
  75 * the PC.
  76 */
  77static int
  78callchain_trace(struct stackframe *fr,
  79                void *data)
  80{
  81        struct perf_callchain_entry *entry = data;
  82        perf_callchain_store(entry, fr->pc);
  83        return 0;
  84}
  85
  86void
  87perf_callchain_kernel(struct perf_callchain_entry *entry, struct pt_regs *regs)
  88{
  89        struct stackframe fr;
  90
  91        fr.fp = regs->ctx.AX[1].U0;
  92        fr.sp = regs->ctx.AX[0].U0;
  93        fr.lr = regs->ctx.DX[4].U1;
  94        fr.pc = regs->ctx.CurrPC;
  95        walk_stackframe(&fr, callchain_trace, entry);
  96}
  97