Skip to content

Commit

Permalink
riscv: Add perf callchain support
Browse files Browse the repository at this point in the history
This patch add support for perf callchain sampling on riscv platforms.
The return address of leaf function is retrieved from pt_regs as
it is not saved in the outmost frame.

Signed-off-by: Mao Han <han_mao@c-sky.com>
Cc: Paul Walmsley <paul.walmsley@sifive.com>
Cc: Greentime Hu <green.hu@gmail.com>
Cc: Palmer Dabbelt <palmer@sifive.com>
Cc: linux-riscv <linux-riscv@lists.infradead.org>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Guo Ren <guoren@kernel.org>
Tested-by: Greentime Hu <greentime.hu@sifive.com>
[paul.walmsley@sifive.com: fixed some 'checkpatch.pl --strict' issues;
 fixed patch description spelling]
Signed-off-by: Paul Walmsley <paul.walmsley@sifive.com>
  • Loading branch information
Mao Han authored and Paul Walmsley committed Sep 4, 2019
1 parent 909548d commit dbeb90b
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 3 deletions.
3 changes: 3 additions & 0 deletions arch/riscv/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ endif
ifeq ($(CONFIG_MODULE_SECTIONS),y)
KBUILD_LDFLAGS_MODULE += -T $(srctree)/arch/riscv/kernel/module.lds
endif
ifeq ($(CONFIG_PERF_EVENTS),y)
KBUILD_CFLAGS += -fno-omit-frame-pointer
endif

KBUILD_CFLAGS_MODULE += $(call cc-option,-mno-relax)

Expand Down
3 changes: 2 additions & 1 deletion arch/riscv/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ obj-$(CONFIG_MODULE_SECTIONS) += module-sections.o
obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace.o
obj-$(CONFIG_DYNAMIC_FTRACE) += mcount-dyn.o

obj-$(CONFIG_PERF_EVENTS) += perf_event.o
obj-$(CONFIG_PERF_EVENTS) += perf_event.o
obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o

clean:
94 changes: 94 additions & 0 deletions arch/riscv/kernel/perf_callchain.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. */

#include <linux/perf_event.h>
#include <linux/uaccess.h>

/* Kernel callchain */
struct stackframe {
unsigned long fp;
unsigned long ra;
};

/*
* Get the return address for a single stackframe and return a pointer to the
* next frame tail.
*/
static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry,
unsigned long fp, unsigned long reg_ra)
{
struct stackframe buftail;
unsigned long ra = 0;
unsigned long *user_frame_tail =
(unsigned long *)(fp - sizeof(struct stackframe));

/* Check accessibility of one struct frame_tail beyond */
if (!access_ok(user_frame_tail, sizeof(buftail)))
return 0;
if (__copy_from_user_inatomic(&buftail, user_frame_tail,
sizeof(buftail)))
return 0;

if (reg_ra != 0)
ra = reg_ra;
else
ra = buftail.ra;

fp = buftail.fp;
if (ra != 0)
perf_callchain_store(entry, ra);
else
return 0;

return fp;
}

/*
* This will be called when the target is in user mode
* This function will only be called when we use
* "PERF_SAMPLE_CALLCHAIN" in
* kernel/events/core.c:perf_prepare_sample()
*
* How to trigger perf_callchain_[user/kernel] :
* $ perf record -e cpu-clock --call-graph fp ./program
* $ perf report --call-graph
*
* On RISC-V platform, the program being sampled and the C library
* need to be compiled with -fno-omit-frame-pointer, otherwise
* the user stack will not contain function frame.
*/
void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
struct pt_regs *regs)
{
unsigned long fp = 0;

/* RISC-V does not support perf in guest mode. */
if (perf_guest_cbs && perf_guest_cbs->is_in_guest())
return;

fp = regs->s0;
perf_callchain_store(entry, regs->sepc);

fp = user_backtrace(entry, fp, regs->ra);
while (fp && !(fp & 0x3) && entry->nr < entry->max_stack)
fp = user_backtrace(entry, fp, 0);
}

bool fill_callchain(unsigned long pc, void *entry)
{
return perf_callchain_store(entry, pc);
}

void notrace walk_stackframe(struct task_struct *task,
struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg);
void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
struct pt_regs *regs)
{
/* RISC-V does not support perf in guest mode. */
if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
pr_warn("RISC-V does not support perf in guest mode!");
return;
}

walk_stackframe(NULL, regs, fill_callchain, entry);
}
4 changes: 2 additions & 2 deletions arch/riscv/kernel/stacktrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ struct stackframe {
unsigned long ra;
};

static void notrace walk_stackframe(struct task_struct *task,
struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg)
void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
bool (*fn)(unsigned long, void *), void *arg)
{
unsigned long fp, sp, pc;

Expand Down

0 comments on commit dbeb90b

Please sign in to comment.