This patch series to support basic and dynamic ftrace.
1) -pg
Use `-pg` makes stub like a child function `void _mcount(void *ra)`.
Thus, it can be seen store RA and open stack before `call _mcount`.
Find `open stack` at first, and then find `store RA`.
2) -fpatchable-function-entry=2
The compiler has inserted 2 NOPs before the regular function prologue.
T series registers are available and safe because of LoongArch psABI.
At runtime, replace nop with bl to enable ftrace call and replace bl with
nop to disable ftrace call. The bl requires us to save the original RA value,
so here it saves RA at t0.
details are:
| Compiled | Disabled | Enabled |
+------------+------------------------+------------------------+
| nop | move t0, ra | move t0, ra |
| nop | nop | bl ftrace_caller |
| func_body | func_body | func_body |
The RA value will be recovered by ftrace_regs_entry, and restored into RA
before returning to the regular function prologue. When a function is not
being traced, the move t0, ra is not harmful.
performs a series of startup tests on ftrace and The test cases in selftests
has passed on LoongArch.
Changes in v2:
- Remove patch "LoongArch: ftrace: Add CALLER_ADDRx macros" there are other
better ways
Suggested by Steve:
- Add HAVE_DYNAMIC_FTRACE_WITH_ARGS support (6/10)
Suggested by Jinyang:
- Change addu16id to lu12iw and Adjust module_finalize return value (7/10)
- Use the "jr" pseudo-instruction where applicable (1/10)
- Use the "la.pcrel" instead of "la" (3/10)
Changes in v3:
Reported by Jeff:
- Fix unwind state when option func_stack_trace (10/10)
Changes in v4:
- No comments. Just resend the series.
- Rebased onto v6.0.0-rc4.
Changes in v5:
- Modify the indentation of Kconfig and small changes
Changes in v6:
Suggested by Huacai:
- Adjusting the patch Sequence
- renamed mcount-dyn.S for consistency
Changes in v7:
- delete redefinition
Qing Zhang (9):
LoongArch/ftrace: Add basic support
LoongArch/ftrace: Add recordmcount support
LoongArch/ftrace: Add dynamic function tracer support
LoongArch/ftrace: Add dynamic function graph tracer support
LoongArch/ftrace: Add HAVE_DYNAMIC_FTRACE_WITH_REGS support
LoongArch/ftrace: Add HAVE_DYNAMIC_FTRACE_WITH_ARGS support
LoongArch/ftrace: Add HAVE_FUNCTION_GRAPH_RET_ADDR_PTR support
LoongArch: modules/ftrace: Initialize PLT at load time
LoongArch: Enable CONFIG_KALLSYMS_ALL and CONFIG_DEBUG_FS
arch/loongarch/Kconfig | 7 +
arch/loongarch/Makefile | 5 +
arch/loongarch/configs/loongson3_defconfig | 2 +
arch/loongarch/include/asm/ftrace.h | 61 +++++
arch/loongarch/include/asm/inst.h | 15 ++
arch/loongarch/include/asm/module.h | 5 +-
arch/loongarch/include/asm/module.lds.h | 1 +
arch/loongarch/include/asm/unwind.h | 3 +-
arch/loongarch/kernel/Makefile | 13 +
arch/loongarch/kernel/ftrace.c | 74 ++++++
arch/loongarch/kernel/ftrace_dyn.c | 264 +++++++++++++++++++++
arch/loongarch/kernel/inst.c | 127 ++++++++++
arch/loongarch/kernel/mcount.S | 94 ++++++++
arch/loongarch/kernel/mcount_dyn.S | 154 ++++++++++++
arch/loongarch/kernel/module-sections.c | 11 +
arch/loongarch/kernel/module.c | 22 ++
arch/loongarch/kernel/unwind_guess.c | 4 +-
arch/loongarch/kernel/unwind_prologue.c | 46 +++-
scripts/recordmcount.c | 23 ++
19 files changed, 921 insertions(+), 10 deletions(-)
create mode 100644 arch/loongarch/include/asm/ftrace.h
create mode 100644 arch/loongarch/kernel/ftrace.c
create mode 100644 arch/loongarch/kernel/ftrace_dyn.c
create mode 100644 arch/loongarch/kernel/mcount.S
create mode 100644 arch/loongarch/kernel/mcount_dyn.S
--
2.36.0
Ftrace_graph_ret_addr can be called by stack unwinding code to convert
a found stack return address ('ret') to its original value, in case the
function graph tracer has modified it to be 'return_to_handler',If the
hasn't been modified, the unchanged value of 'ret' is returned.
Signed-off-by: Qing Zhang <[email protected]>
---
arch/loongarch/include/asm/ftrace.h | 3 +++
arch/loongarch/include/asm/unwind.h | 1 +
arch/loongarch/kernel/ftrace_dyn.c | 2 +-
arch/loongarch/kernel/unwind_guess.c | 4 +++-
arch/loongarch/kernel/unwind_prologue.c | 11 +++++++++--
5 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h
index 5cc13ae48164..1583ec2171fc 100644
--- a/arch/loongarch/include/asm/ftrace.h
+++ b/arch/loongarch/include/asm/ftrace.h
@@ -6,6 +6,8 @@
#ifndef _ASM_LOONGARCH_FTRACE_H
#define _ASM_LOONGARCH_FTRACE_H
+#define GRAPH_FAKE_OFFSET (sizeof(struct pt_regs) - offsetof(struct pt_regs, regs[1]))
+
#ifdef CONFIG_FUNCTION_TRACER
#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */
@@ -33,6 +35,7 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
#endif
#ifdef CONFIG_DYNAMIC_FTRACE
+#define HAVE_FUNCTION_GRAPH_RET_ADDR_PTR
static inline unsigned long ftrace_call_adjust(unsigned long addr)
{
return addr;
diff --git a/arch/loongarch/include/asm/unwind.h b/arch/loongarch/include/asm/unwind.h
index a51eec00efb8..f2b52b9ea93d 100644
--- a/arch/loongarch/include/asm/unwind.h
+++ b/arch/loongarch/include/asm/unwind.h
@@ -21,6 +21,7 @@ struct unwind_state {
struct stack_info stack_info;
struct task_struct *task;
bool first, error, is_ftrace;
+ int graph_idx;
unsigned long sp, pc, ra;
};
diff --git a/arch/loongarch/kernel/ftrace_dyn.c b/arch/loongarch/kernel/ftrace_dyn.c
index f538829312d7..cd64887e26b5 100644
--- a/arch/loongarch/kernel/ftrace_dyn.c
+++ b/arch/loongarch/kernel/ftrace_dyn.c
@@ -140,7 +140,7 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent)
old = *parent;
- if (!function_graph_enter(old, self_addr, 0, NULL))
+ if (!function_graph_enter(old, self_addr, 0, parent))
*parent = return_hooker;
}
diff --git a/arch/loongarch/kernel/unwind_guess.c b/arch/loongarch/kernel/unwind_guess.c
index 5afa6064d73e..229ba014cea0 100644
--- a/arch/loongarch/kernel/unwind_guess.c
+++ b/arch/loongarch/kernel/unwind_guess.c
@@ -2,6 +2,7 @@
/*
* Copyright (C) 2022 Loongson Technology Corporation Limited
*/
+#include <linux/ftrace.h>
#include <linux/kernel.h>
#include <asm/unwind.h>
@@ -53,7 +54,8 @@ bool unwind_next_frame(struct unwind_state *state)
state->sp < info->end;
state->sp += sizeof(unsigned long)) {
addr = *(unsigned long *)(state->sp);
-
+ state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
+ addr, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
if (__kernel_text_address(addr))
return true;
}
diff --git a/arch/loongarch/kernel/unwind_prologue.c b/arch/loongarch/kernel/unwind_prologue.c
index c5df4ae73e0d..48f7a120ec27 100644
--- a/arch/loongarch/kernel/unwind_prologue.c
+++ b/arch/loongarch/kernel/unwind_prologue.c
@@ -2,6 +2,7 @@
/*
* Copyright (C) 2022 Loongson Technology Corporation Limited
*/
+#include <linux/ftrace.h>
#include <linux/kallsyms.h>
#include <asm/inst.h>
@@ -30,6 +31,8 @@ static bool unwind_by_guess(struct unwind_state *state)
state->sp < info->end;
state->sp += sizeof(unsigned long)) {
addr = *(unsigned long *)(state->sp);
+ state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
+ addr, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
if (__kernel_text_address(addr))
return true;
}
@@ -171,8 +174,11 @@ bool unwind_next_frame(struct unwind_state *state)
break;
case UNWINDER_PROLOGUE:
- if (unwind_by_prologue(state))
+ if (unwind_by_prologue(state)) {
+ state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
+ state->pc, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
return true;
+ }
if (info->type == STACK_TYPE_IRQ &&
info->end == state->sp) {
@@ -182,8 +188,9 @@ bool unwind_next_frame(struct unwind_state *state)
if (user_mode(regs) || !__kernel_text_address(pc))
return false;
- state->pc = pc;
state->sp = regs->regs[3];
+ state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
+ pc, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
state->ra = regs->regs[1];
state->first = true;
get_stack_info(state->sp, state->task, info);
--
2.36.0
This patch contains basic ftrace support for LoongArch.
Specifically, function tracer (HAVE_FUNCTION_TRACER), function graph
tracer (HAVE_FUNCTION_GRAPH_TRACER) are implemented following the
instructions in Documentation/trace/ftrace-design.txt.
Use `-pg` makes stub like a child function `void _mcount(void *ra)`.
Thus, it can be seen store RA and open stack before `call _mcount`.
Find `open stack` at first, and then find `store RA`
Note that the functions in both inst.c and time.c should not be
hooked with the compiler's -pg option: to prevent infinite self-
referencing for the former, and to ignore early setup stuff for the
latter.
Co-developed-by: Jinyang He <[email protected]>
Signed-off-by: Jinyang He <[email protected]>
Signed-off-by: Qing Zhang <[email protected]>
---
arch/loongarch/Kconfig | 2 +
arch/loongarch/Makefile | 5 ++
arch/loongarch/include/asm/ftrace.h | 18 ++++++
arch/loongarch/kernel/Makefile | 8 +++
arch/loongarch/kernel/ftrace.c | 74 +++++++++++++++++++++++
arch/loongarch/kernel/mcount.S | 94 +++++++++++++++++++++++++++++
6 files changed, 201 insertions(+)
create mode 100644 arch/loongarch/include/asm/ftrace.h
create mode 100644 arch/loongarch/kernel/ftrace.c
create mode 100644 arch/loongarch/kernel/mcount.S
diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index 1943f840e494..92c4ec8c8527 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -91,6 +91,8 @@ config LOONGARCH
select HAVE_EBPF_JIT
select HAVE_EXIT_THREAD
select HAVE_FAST_GUP
+ select HAVE_FUNCTION_GRAPH_TRACER
+ select HAVE_FUNCTION_TRACER
select HAVE_GENERIC_VDSO
select HAVE_IOREMAP_PROT
select HAVE_IRQ_EXIT_ON_IRQ_STACK
diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile
index a0fc1f9980e3..6832a8f891fd 100644
--- a/arch/loongarch/Makefile
+++ b/arch/loongarch/Makefile
@@ -36,6 +36,11 @@ ifneq ($(SUBARCH),$(ARCH))
endif
endif
+ifdef CONFIG_DYNAMIC_FTRACE
+KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY
+CC_FLAGS_FTRACE := -fpatchable-function-entry=2
+endif
+
ifdef CONFIG_64BIT
ld-emul = $(64bit-emul)
cflags-y += -mabi=lp64s
diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h
new file mode 100644
index 000000000000..6a3e76234618
--- /dev/null
+++ b/arch/loongarch/include/asm/ftrace.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Loongson Technology Corporation Limited
+ */
+
+#ifndef _ASM_LOONGARCH_FTRACE_H
+#define _ASM_LOONGARCH_FTRACE_H
+
+#ifdef CONFIG_FUNCTION_TRACER
+#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */
+
+#ifndef __ASSEMBLY__
+extern void _mcount(void);
+#define mcount _mcount
+
+#endif /* __ASSEMBLY__ */
+#endif /* CONFIG_FUNCTION_TRACER */
+#endif /* _ASM_LOONGARCH_FTRACE_H */
diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile
index 86744531b100..3f71bce1c7ce 100644
--- a/arch/loongarch/kernel/Makefile
+++ b/arch/loongarch/kernel/Makefile
@@ -15,6 +15,14 @@ obj-$(CONFIG_EFI) += efi.o
obj-$(CONFIG_CPU_HAS_FPU) += fpu.o
+ifdef CONFIG_FUNCTION_TRACER
+obj-y += mcount.o ftrace.o
+CFLAGS_REMOVE_ftrace.o = $(CC_FLAGS_FTRACE)
+CFLAGS_REMOVE_inst.o = $(CC_FLAGS_FTRACE)
+CFLAGS_REMOVE_time.o = $(CC_FLAGS_FTRACE)
+CFLAGS_REMOVE_perf_event.o = $(CC_FLAGS_FTRACE)
+endif
+
obj-$(CONFIG_MODULES) += module.o module-sections.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
diff --git a/arch/loongarch/kernel/ftrace.c b/arch/loongarch/kernel/ftrace.c
new file mode 100644
index 000000000000..c8ddc5f11f32
--- /dev/null
+++ b/arch/loongarch/kernel/ftrace.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Loongson Technology Corporation Limited
+ */
+
+#include <linux/uaccess.h>
+#include <linux/init.h>
+#include <linux/ftrace.h>
+#include <linux/syscalls.h>
+
+#include <asm/asm.h>
+#include <asm/asm-offsets.h>
+#include <asm/cacheflush.h>
+#include <asm/inst.h>
+#include <asm/loongarch.h>
+#include <asm/syscall.h>
+#include <asm/unistd.h>
+
+#include <asm-generic/sections.h>
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+
+/*
+ * As `call _mcount` follows LoongArch psABI, ra-saved operation and
+ * stack operation can be found before this insn.
+ */
+
+static int ftrace_get_parent_ra_addr(unsigned long insn_addr, int *ra_off)
+{
+ union loongarch_instruction *insn;
+ int limit = 32;
+
+ insn = (union loongarch_instruction *)insn_addr;
+
+ do {
+ insn--;
+ limit--;
+
+ if (is_ra_save_ins(insn))
+ *ra_off = -((1 << 12) - insn->reg2i12_format.immediate);
+
+ } while (!is_stack_alloc_ins(insn) && limit);
+
+ if (!limit)
+ return -EINVAL;
+
+ return 0;
+}
+
+void prepare_ftrace_return(unsigned long self_addr,
+ unsigned long callsite_sp, unsigned long old)
+{
+ int ra_off;
+ unsigned long return_hooker = (unsigned long)&return_to_handler;
+
+ if (unlikely(ftrace_graph_is_dead()))
+ return;
+
+ if (unlikely(atomic_read(¤t->tracing_graph_pause)))
+ return;
+
+ if (ftrace_get_parent_ra_addr(self_addr, &ra_off))
+ goto out;
+
+ if (!function_graph_enter(old, self_addr, 0, NULL))
+ *(unsigned long *)(callsite_sp + ra_off) = return_hooker;
+
+ return;
+
+out:
+ ftrace_graph_stop();
+ WARN_ON(1);
+}
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
diff --git a/arch/loongarch/kernel/mcount.S b/arch/loongarch/kernel/mcount.S
new file mode 100644
index 000000000000..3de7c2d7fd12
--- /dev/null
+++ b/arch/loongarch/kernel/mcount.S
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * LoongArch specific _mcount support
+ *
+ * Copyright (C) 2022 Loongson Technology Corporation Limited
+ */
+
+#include <asm/export.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+#include <asm/ftrace.h>
+
+ .text
+
+#define MCOUNT_STACK_SIZE (2 * SZREG)
+#define MCOUNT_S0_OFFSET (0)
+#define MCOUNT_RA_OFFSET (SZREG)
+
+ .macro MCOUNT_SAVE_REGS
+ PTR_ADDI sp, sp, -MCOUNT_STACK_SIZE
+ PTR_S s0, sp, MCOUNT_S0_OFFSET
+ PTR_S ra, sp, MCOUNT_RA_OFFSET
+ move s0, a0
+ .endm
+
+ .macro MCOUNT_RESTORE_REGS
+ move a0, s0
+ PTR_L ra, sp, MCOUNT_RA_OFFSET
+ PTR_L s0, sp, MCOUNT_S0_OFFSET
+ PTR_ADDI sp, sp, MCOUNT_STACK_SIZE
+ .endm
+
+
+SYM_FUNC_START(_mcount)
+ la.pcrel t1, ftrace_stub
+ la.pcrel t2, ftrace_trace_function /* Prepare t2 for (1) */
+ PTR_L t2, t2, 0
+ beq t1, t2, fgraph_trace
+
+ MCOUNT_SAVE_REGS
+
+ move a0, ra /* arg0: self return address */
+ move a1, s0 /* arg1: parent's return address */
+ jirl ra, t2, 0 /* (1) call *ftrace_trace_function */
+
+ MCOUNT_RESTORE_REGS
+
+fgraph_trace:
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ la.pcrel t1, ftrace_stub
+ la.pcrel t3, ftrace_graph_return
+ PTR_L t3, t3, 0
+ bne t1, t3, ftrace_graph_caller
+ la.pcrel t1, ftrace_graph_entry_stub
+ la.pcrel t3, ftrace_graph_entry
+ PTR_L t3, t3, 0
+ bne t1, t3, ftrace_graph_caller
+#endif
+
+ .globl ftrace_stub
+ftrace_stub:
+ jr ra
+SYM_FUNC_END(_mcount)
+EXPORT_SYMBOL(_mcount)
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+SYM_FUNC_START(ftrace_graph_caller)
+ MCOUNT_SAVE_REGS
+
+ PTR_ADDI a0, ra, -4 /* arg0: Callsite self return addr */
+ PTR_ADDI a1, sp, MCOUNT_STACK_SIZE /* arg1: Callsite sp */
+ move a2, s0 /* arg2: Callsite parent ra */
+ bl prepare_ftrace_return
+
+ MCOUNT_RESTORE_REGS
+ jr ra
+SYM_FUNC_END(ftrace_graph_caller)
+
+SYM_FUNC_START(return_to_handler)
+ PTR_ADDI sp, sp, -2 * SZREG
+ PTR_S a0, sp, 0
+ PTR_S a1, sp, SZREG
+
+ bl ftrace_return_to_handler
+
+ /* restore the real parent address: a0 -> ra */
+ move ra, a0
+
+ PTR_L a0, sp, 0
+ PTR_L a1, sp, SZREG
+ PTR_ADDI sp, sp, 2 * SZREG
+ jr ra
+SYM_FUNC_END(return_to_handler)
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
--
2.36.0
Once the function_graph tracer is enabled, a filtered function has the
following call sequence:
1) ftracer_caller ==> on/off by ftrace_make_call/ftrace_make_nop
2) ftrace_graph_caller
3) ftrace_graph_call ==> on/off by ftrace_en/disable_ftrace_graph_caller
4) prepare_ftrace_return
Considering the following DYNAMIC_FTRACE_WITH_REGS feature, it would be
more extendable to have a ftrace_graph_caller function, instead of
calling prepare_ftrace_return directly in ftrace_caller.
Co-developed-by: Jinyang He <[email protected]>
Signed-off-by: Jinyang He <[email protected]>
Signed-off-by: Qing Zhang <[email protected]>
---
arch/loongarch/kernel/ftrace_dyn.c | 45 ++++++++++++++++++++++++++++++
arch/loongarch/kernel/inst.c | 24 ++++++++++++++++
arch/loongarch/kernel/mcount_dyn.S | 33 ++++++++++++++++++++++
3 files changed, 102 insertions(+)
diff --git a/arch/loongarch/kernel/ftrace_dyn.c b/arch/loongarch/kernel/ftrace_dyn.c
index 1f8955be8b64..3fe791b6783e 100644
--- a/arch/loongarch/kernel/ftrace_dyn.c
+++ b/arch/loongarch/kernel/ftrace_dyn.c
@@ -109,3 +109,48 @@ int __init ftrace_dyn_arch_init(void)
{
return 0;
}
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+extern void ftrace_graph_call(void);
+
+void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent)
+{
+ unsigned long return_hooker = (unsigned long)&return_to_handler;
+ unsigned long old;
+
+ if (unlikely(atomic_read(¤t->tracing_graph_pause)))
+ return;
+
+ old = *parent;
+
+ if (!function_graph_enter(old, self_addr, 0, NULL))
+ *parent = return_hooker;
+}
+
+static int ftrace_modify_graph_caller(bool enable)
+{
+ unsigned long pc, func;
+ u32 branch, nop;
+
+ pc = (unsigned long)&ftrace_graph_call;
+ func = (unsigned long)&ftrace_graph_caller;
+
+ branch = larch_insn_gen_b(pc, func);
+ nop = larch_insn_gen_nop();
+
+ if (enable)
+ return ftrace_modify_code(pc, nop, branch, true);
+ else
+ return ftrace_modify_code(pc, branch, nop, true);
+}
+
+int ftrace_enable_ftrace_graph_caller(void)
+{
+ return ftrace_modify_graph_caller(true);
+}
+
+int ftrace_disable_ftrace_graph_caller(void)
+{
+ return ftrace_modify_graph_caller(false);
+}
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c
index d62cdf4a9ffb..2d2e942eb06a 100644
--- a/arch/loongarch/kernel/inst.c
+++ b/arch/loongarch/kernel/inst.c
@@ -55,6 +55,30 @@ u32 larch_insn_gen_nop(void)
return INSN_NOP;
}
+u32 larch_insn_gen_b(unsigned long pc, unsigned long dest)
+{
+ unsigned int immediate_l, immediate_h;
+ union loongarch_instruction insn;
+ long offset = dest - pc;
+
+ if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) {
+ pr_warn("The generated b instruction is out of range.\n");
+ return INSN_BREAK;
+ }
+
+ offset >>= 2;
+
+ immediate_l = offset & 0xffff;
+ offset >>= 16;
+ immediate_h = offset & 0x3ff;
+
+ insn.reg0i26_format.opcode = b_op;
+ insn.reg0i26_format.immediate_l = immediate_l;
+ insn.reg0i26_format.immediate_h = immediate_h;
+
+ return insn.word;
+}
+
u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest)
{
unsigned int immediate_l, immediate_h;
diff --git a/arch/loongarch/kernel/mcount_dyn.S b/arch/loongarch/kernel/mcount_dyn.S
index 205925bc3822..0c12cc108e6f 100644
--- a/arch/loongarch/kernel/mcount_dyn.S
+++ b/arch/loongarch/kernel/mcount_dyn.S
@@ -62,6 +62,11 @@ SYM_CODE_START(ftrace_common)
.globl ftrace_call
ftrace_call:
bl ftrace_stub
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ .globl ftrace_graph_call
+ftrace_graph_call:
+ nop /* b ftrace_graph_caller */
+#endif
/*
* As we didn't use S series regs in this assmembly code and all calls
* are C function which will save S series regs by themselves, there is
@@ -84,6 +89,34 @@ ftrace_common_return:
jr t0
SYM_CODE_END(ftrace_common)
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+SYM_CODE_START(ftrace_graph_caller)
+ PTR_L a0, sp, PT_ERA
+ PTR_ADDI a0, a0, -8 /* arg0: self_addr */
+ PTR_ADDI a1, sp, PT_R1 /* arg1: parent */
+ bl prepare_ftrace_return
+ b ftrace_common_return
+SYM_CODE_END(ftrace_graph_caller)
+
+SYM_CODE_START(return_to_handler)
+ /* save return value regs */
+ PTR_ADDI sp, sp, -2 * SZREG
+ PTR_S a0, sp, 0
+ PTR_S a1, sp, SZREG
+
+ move a0, zero /* Has no check FP now. */
+ bl ftrace_return_to_handler
+ move ra, a0 /* parent ra */
+
+ /* restore return value regs */
+ PTR_L a0, sp, 0
+ PTR_L a1, sp, SZREG
+ PTR_ADDI sp, sp, 2 * SZREG
+
+ jr ra
+SYM_CODE_END(return_to_handler)
+#endif
+
SYM_FUNC_START(ftrace_stub)
jr ra
SYM_FUNC_END(ftrace_stub)
--
2.36.0
Hi, Qing,
On Wed, Nov 16, 2022 at 10:53 AM Qing Zhang <[email protected]> wrote:
>
> This patch contains basic ftrace support for LoongArch.
> Specifically, function tracer (HAVE_FUNCTION_TRACER), function graph
> tracer (HAVE_FUNCTION_GRAPH_TRACER) are implemented following the
> instructions in Documentation/trace/ftrace-design.txt.
>
> Use `-pg` makes stub like a child function `void _mcount(void *ra)`.
> Thus, it can be seen store RA and open stack before `call _mcount`.
> Find `open stack` at first, and then find `store RA`
>
> Note that the functions in both inst.c and time.c should not be
> hooked with the compiler's -pg option: to prevent infinite self-
> referencing for the former, and to ignore early setup stuff for the
> latter.
>
> Co-developed-by: Jinyang He <[email protected]>
> Signed-off-by: Jinyang He <[email protected]>
> Signed-off-by: Qing Zhang <[email protected]>
> ---
> arch/loongarch/Kconfig | 2 +
> arch/loongarch/Makefile | 5 ++
> arch/loongarch/include/asm/ftrace.h | 18 ++++++
> arch/loongarch/kernel/Makefile | 8 +++
> arch/loongarch/kernel/ftrace.c | 74 +++++++++++++++++++++++
> arch/loongarch/kernel/mcount.S | 94 +++++++++++++++++++++++++++++
> 6 files changed, 201 insertions(+)
> create mode 100644 arch/loongarch/include/asm/ftrace.h
> create mode 100644 arch/loongarch/kernel/ftrace.c
> create mode 100644 arch/loongarch/kernel/mcount.S
>
> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index 1943f840e494..92c4ec8c8527 100644
> --- a/arch/loongarch/Kconfig
> +++ b/arch/loongarch/Kconfig
> @@ -91,6 +91,8 @@ config LOONGARCH
> select HAVE_EBPF_JIT
> select HAVE_EXIT_THREAD
> select HAVE_FAST_GUP
> + select HAVE_FUNCTION_GRAPH_TRACER
> + select HAVE_FUNCTION_TRACER
> select HAVE_GENERIC_VDSO
> select HAVE_IOREMAP_PROT
> select HAVE_IRQ_EXIT_ON_IRQ_STACK
> diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile
> index a0fc1f9980e3..6832a8f891fd 100644
> --- a/arch/loongarch/Makefile
> +++ b/arch/loongarch/Makefile
> @@ -36,6 +36,11 @@ ifneq ($(SUBARCH),$(ARCH))
> endif
> endif
>
> +ifdef CONFIG_DYNAMIC_FTRACE
> +KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY
> +CC_FLAGS_FTRACE := -fpatchable-function-entry=2
> +endif
I think these lines should be moved to Patch-3.
Huacai
> +
> ifdef CONFIG_64BIT
> ld-emul = $(64bit-emul)
> cflags-y += -mabi=lp64s
> diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h
> new file mode 100644
> index 000000000000..6a3e76234618
> --- /dev/null
> +++ b/arch/loongarch/include/asm/ftrace.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2022 Loongson Technology Corporation Limited
> + */
> +
> +#ifndef _ASM_LOONGARCH_FTRACE_H
> +#define _ASM_LOONGARCH_FTRACE_H
> +
> +#ifdef CONFIG_FUNCTION_TRACER
> +#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */
> +
> +#ifndef __ASSEMBLY__
> +extern void _mcount(void);
> +#define mcount _mcount
> +
> +#endif /* __ASSEMBLY__ */
> +#endif /* CONFIG_FUNCTION_TRACER */
> +#endif /* _ASM_LOONGARCH_FTRACE_H */
> diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile
> index 86744531b100..3f71bce1c7ce 100644
> --- a/arch/loongarch/kernel/Makefile
> +++ b/arch/loongarch/kernel/Makefile
> @@ -15,6 +15,14 @@ obj-$(CONFIG_EFI) += efi.o
>
> obj-$(CONFIG_CPU_HAS_FPU) += fpu.o
>
> +ifdef CONFIG_FUNCTION_TRACER
> +obj-y += mcount.o ftrace.o
> +CFLAGS_REMOVE_ftrace.o = $(CC_FLAGS_FTRACE)
> +CFLAGS_REMOVE_inst.o = $(CC_FLAGS_FTRACE)
> +CFLAGS_REMOVE_time.o = $(CC_FLAGS_FTRACE)
> +CFLAGS_REMOVE_perf_event.o = $(CC_FLAGS_FTRACE)
> +endif
> +
> obj-$(CONFIG_MODULES) += module.o module-sections.o
> obj-$(CONFIG_STACKTRACE) += stacktrace.o
>
> diff --git a/arch/loongarch/kernel/ftrace.c b/arch/loongarch/kernel/ftrace.c
> new file mode 100644
> index 000000000000..c8ddc5f11f32
> --- /dev/null
> +++ b/arch/loongarch/kernel/ftrace.c
> @@ -0,0 +1,74 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2022 Loongson Technology Corporation Limited
> + */
> +
> +#include <linux/uaccess.h>
> +#include <linux/init.h>
> +#include <linux/ftrace.h>
> +#include <linux/syscalls.h>
> +
> +#include <asm/asm.h>
> +#include <asm/asm-offsets.h>
> +#include <asm/cacheflush.h>
> +#include <asm/inst.h>
> +#include <asm/loongarch.h>
> +#include <asm/syscall.h>
> +#include <asm/unistd.h>
> +
> +#include <asm-generic/sections.h>
> +
> +#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> +
> +/*
> + * As `call _mcount` follows LoongArch psABI, ra-saved operation and
> + * stack operation can be found before this insn.
> + */
> +
> +static int ftrace_get_parent_ra_addr(unsigned long insn_addr, int *ra_off)
> +{
> + union loongarch_instruction *insn;
> + int limit = 32;
> +
> + insn = (union loongarch_instruction *)insn_addr;
> +
> + do {
> + insn--;
> + limit--;
> +
> + if (is_ra_save_ins(insn))
> + *ra_off = -((1 << 12) - insn->reg2i12_format.immediate);
> +
> + } while (!is_stack_alloc_ins(insn) && limit);
> +
> + if (!limit)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +void prepare_ftrace_return(unsigned long self_addr,
> + unsigned long callsite_sp, unsigned long old)
> +{
> + int ra_off;
> + unsigned long return_hooker = (unsigned long)&return_to_handler;
> +
> + if (unlikely(ftrace_graph_is_dead()))
> + return;
> +
> + if (unlikely(atomic_read(¤t->tracing_graph_pause)))
> + return;
> +
> + if (ftrace_get_parent_ra_addr(self_addr, &ra_off))
> + goto out;
> +
> + if (!function_graph_enter(old, self_addr, 0, NULL))
> + *(unsigned long *)(callsite_sp + ra_off) = return_hooker;
> +
> + return;
> +
> +out:
> + ftrace_graph_stop();
> + WARN_ON(1);
> +}
> +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
> diff --git a/arch/loongarch/kernel/mcount.S b/arch/loongarch/kernel/mcount.S
> new file mode 100644
> index 000000000000..3de7c2d7fd12
> --- /dev/null
> +++ b/arch/loongarch/kernel/mcount.S
> @@ -0,0 +1,94 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * LoongArch specific _mcount support
> + *
> + * Copyright (C) 2022 Loongson Technology Corporation Limited
> + */
> +
> +#include <asm/export.h>
> +#include <asm/regdef.h>
> +#include <asm/stackframe.h>
> +#include <asm/ftrace.h>
> +
> + .text
> +
> +#define MCOUNT_STACK_SIZE (2 * SZREG)
> +#define MCOUNT_S0_OFFSET (0)
> +#define MCOUNT_RA_OFFSET (SZREG)
> +
> + .macro MCOUNT_SAVE_REGS
> + PTR_ADDI sp, sp, -MCOUNT_STACK_SIZE
> + PTR_S s0, sp, MCOUNT_S0_OFFSET
> + PTR_S ra, sp, MCOUNT_RA_OFFSET
> + move s0, a0
> + .endm
> +
> + .macro MCOUNT_RESTORE_REGS
> + move a0, s0
> + PTR_L ra, sp, MCOUNT_RA_OFFSET
> + PTR_L s0, sp, MCOUNT_S0_OFFSET
> + PTR_ADDI sp, sp, MCOUNT_STACK_SIZE
> + .endm
> +
> +
> +SYM_FUNC_START(_mcount)
> + la.pcrel t1, ftrace_stub
> + la.pcrel t2, ftrace_trace_function /* Prepare t2 for (1) */
> + PTR_L t2, t2, 0
> + beq t1, t2, fgraph_trace
> +
> + MCOUNT_SAVE_REGS
> +
> + move a0, ra /* arg0: self return address */
> + move a1, s0 /* arg1: parent's return address */
> + jirl ra, t2, 0 /* (1) call *ftrace_trace_function */
> +
> + MCOUNT_RESTORE_REGS
> +
> +fgraph_trace:
> +#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> + la.pcrel t1, ftrace_stub
> + la.pcrel t3, ftrace_graph_return
> + PTR_L t3, t3, 0
> + bne t1, t3, ftrace_graph_caller
> + la.pcrel t1, ftrace_graph_entry_stub
> + la.pcrel t3, ftrace_graph_entry
> + PTR_L t3, t3, 0
> + bne t1, t3, ftrace_graph_caller
> +#endif
> +
> + .globl ftrace_stub
> +ftrace_stub:
> + jr ra
> +SYM_FUNC_END(_mcount)
> +EXPORT_SYMBOL(_mcount)
> +
> +#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> +SYM_FUNC_START(ftrace_graph_caller)
> + MCOUNT_SAVE_REGS
> +
> + PTR_ADDI a0, ra, -4 /* arg0: Callsite self return addr */
> + PTR_ADDI a1, sp, MCOUNT_STACK_SIZE /* arg1: Callsite sp */
> + move a2, s0 /* arg2: Callsite parent ra */
> + bl prepare_ftrace_return
> +
> + MCOUNT_RESTORE_REGS
> + jr ra
> +SYM_FUNC_END(ftrace_graph_caller)
> +
> +SYM_FUNC_START(return_to_handler)
> + PTR_ADDI sp, sp, -2 * SZREG
> + PTR_S a0, sp, 0
> + PTR_S a1, sp, SZREG
> +
> + bl ftrace_return_to_handler
> +
> + /* restore the real parent address: a0 -> ra */
> + move ra, a0
> +
> + PTR_L a0, sp, 0
> + PTR_L a1, sp, SZREG
> + PTR_ADDI sp, sp, 2 * SZREG
> + jr ra
> +SYM_FUNC_END(return_to_handler)
> +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
> --
> 2.36.0
>
>