162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/perf_event.h> 562306a36Sopenharmony_ci#include <linux/uaccess.h> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <asm/stacktrace.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci * Get the return address for a single stackframe and return a pointer to the 1162306a36Sopenharmony_ci * next frame tail. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_cistatic unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry, 1462306a36Sopenharmony_ci unsigned long fp, unsigned long reg_ra) 1562306a36Sopenharmony_ci{ 1662306a36Sopenharmony_ci struct stackframe buftail; 1762306a36Sopenharmony_ci unsigned long ra = 0; 1862306a36Sopenharmony_ci unsigned long __user *user_frame_tail = 1962306a36Sopenharmony_ci (unsigned long __user *)(fp - sizeof(struct stackframe)); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci /* Check accessibility of one struct frame_tail beyond */ 2262306a36Sopenharmony_ci if (!access_ok(user_frame_tail, sizeof(buftail))) 2362306a36Sopenharmony_ci return 0; 2462306a36Sopenharmony_ci if (__copy_from_user_inatomic(&buftail, user_frame_tail, 2562306a36Sopenharmony_ci sizeof(buftail))) 2662306a36Sopenharmony_ci return 0; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci if (reg_ra != 0) 2962306a36Sopenharmony_ci ra = reg_ra; 3062306a36Sopenharmony_ci else 3162306a36Sopenharmony_ci ra = buftail.ra; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci fp = buftail.fp; 3462306a36Sopenharmony_ci if (ra != 0) 3562306a36Sopenharmony_ci perf_callchain_store(entry, ra); 3662306a36Sopenharmony_ci else 3762306a36Sopenharmony_ci return 0; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci return fp; 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * This will be called when the target is in user mode 4462306a36Sopenharmony_ci * This function will only be called when we use 4562306a36Sopenharmony_ci * "PERF_SAMPLE_CALLCHAIN" in 4662306a36Sopenharmony_ci * kernel/events/core.c:perf_prepare_sample() 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * How to trigger perf_callchain_[user/kernel] : 4962306a36Sopenharmony_ci * $ perf record -e cpu-clock --call-graph fp ./program 5062306a36Sopenharmony_ci * $ perf report --call-graph 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * On RISC-V platform, the program being sampled and the C library 5362306a36Sopenharmony_ci * need to be compiled with -fno-omit-frame-pointer, otherwise 5462306a36Sopenharmony_ci * the user stack will not contain function frame. 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_civoid perf_callchain_user(struct perf_callchain_entry_ctx *entry, 5762306a36Sopenharmony_ci struct pt_regs *regs) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci unsigned long fp = 0; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci fp = regs->s0; 6262306a36Sopenharmony_ci perf_callchain_store(entry, regs->epc); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci fp = user_backtrace(entry, fp, regs->ra); 6562306a36Sopenharmony_ci while (fp && !(fp & 0x3) && entry->nr < entry->max_stack) 6662306a36Sopenharmony_ci fp = user_backtrace(entry, fp, 0); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic bool fill_callchain(void *entry, unsigned long pc) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci return perf_callchain_store(entry, pc) == 0; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_civoid perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, 7562306a36Sopenharmony_ci struct pt_regs *regs) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci walk_stackframe(NULL, regs, fill_callchain, entry); 7862306a36Sopenharmony_ci} 79