^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /* Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) #include <linux/perf_event.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) #include <linux/uaccess.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) /* Kernel callchain */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) struct stackframe {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) unsigned long fp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) unsigned long ra;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * Get the return address for a single stackframe and return a pointer to the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) * next frame tail.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) unsigned long fp, unsigned long reg_ra)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) struct stackframe buftail;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) unsigned long ra = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) unsigned long __user *user_frame_tail =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) (unsigned long __user *)(fp - sizeof(struct stackframe));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) /* Check accessibility of one struct frame_tail beyond */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) if (!access_ok(user_frame_tail, sizeof(buftail)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) if (__copy_from_user_inatomic(&buftail, user_frame_tail,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) sizeof(buftail)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) if (reg_ra != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) ra = reg_ra;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) ra = buftail.ra;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) fp = buftail.fp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) if (ra != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) perf_callchain_store(entry, ra);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) return fp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) * This will be called when the target is in user mode
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) * This function will only be called when we use
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) * "PERF_SAMPLE_CALLCHAIN" in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) * kernel/events/core.c:perf_prepare_sample()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) * How to trigger perf_callchain_[user/kernel] :
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) * $ perf record -e cpu-clock --call-graph fp ./program
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) * $ perf report --call-graph
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) * On RISC-V platform, the program being sampled and the C library
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) * need to be compiled with -fno-omit-frame-pointer, otherwise
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) * the user stack will not contain function frame.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) struct pt_regs *regs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) struct perf_guest_info_callbacks *guest_cbs = perf_get_guest_cbs();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) unsigned long fp = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) /* RISC-V does not support perf in guest mode. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) if (guest_cbs && guest_cbs->is_in_guest())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) fp = regs->s0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) perf_callchain_store(entry, regs->epc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) fp = user_backtrace(entry, fp, regs->ra);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) while (fp && !(fp & 0x3) && entry->nr < entry->max_stack)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) fp = user_backtrace(entry, fp, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) bool fill_callchain(unsigned long pc, void *entry)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) return perf_callchain_store(entry, pc) == 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) void notrace walk_stackframe(struct task_struct *task,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) struct pt_regs *regs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) struct perf_guest_info_callbacks *guest_cbs = perf_get_guest_cbs();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) /* RISC-V does not support perf in guest mode. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) if (guest_cbs && guest_cbs->is_in_guest()) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) pr_warn("RISC-V does not support perf in guest mode!");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) walk_stackframe(NULL, regs, fill_callchain, entry);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) }