LoongArch/ftrace: Add dynamic function graph tracer support
authorQing Zhang <zhangqing@loongson.cn>
Sat, 10 Dec 2022 14:40:15 +0000 (22:40 +0800)
committerHuacai Chen <chenhuacai@loongson.cn>
Wed, 14 Dec 2022 00:41:53 +0000 (08:41 +0800)
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 <hejinyang@loongson.cn>
Signed-off-by: Jinyang He <hejinyang@loongson.cn>
Signed-off-by: Qing Zhang <zhangqing@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
arch/loongarch/kernel/ftrace_dyn.c
arch/loongarch/kernel/inst.c
arch/loongarch/kernel/mcount_dyn.S

index 3b82bfa..5a801c3 100644 (file)
@@ -108,3 +108,47 @@ int __init ftrace_dyn_arch_init(void)
 {
        return 0;
 }
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent)
+{
+       unsigned long old;
+       unsigned long return_hooker = (unsigned long)&return_to_handler;
+
+       if (unlikely(atomic_read(&current->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)
+{
+       u32 branch, nop;
+       unsigned long pc, func;
+       extern void ftrace_graph_call(void);
+
+       pc = (unsigned long)&ftrace_graph_call;
+       func = (unsigned long)&ftrace_graph_caller;
+
+       nop = larch_insn_gen_nop();
+       branch = larch_insn_gen_b(pc, func);
+
+       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 */
index 4fd22b4..39671e8 100644 (file)
@@ -55,6 +55,30 @@ u32 larch_insn_gen_nop(void)
        return INSN_NOP;
 }
 
+u32 larch_insn_gen_b(unsigned long pc, unsigned long dest)
+{
+       long offset = dest - pc;
+       unsigned int immediate_l, immediate_h;
+       union loongarch_instruction insn;
+
+       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)
 {
        long offset = dest - pc;
index 45ba88d..cce3daa 100644 (file)
@@ -57,6 +57,11 @@ SYM_CODE_START(ftrace_common)
 
 SYM_INNER_LABEL(ftrace_call, SYM_L_GLOBAL)
        bl              ftrace_stub
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+SYM_INNER_LABEL(ftrace_graph_call, SYM_L_GLOBAL)
+       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
@@ -83,3 +88,31 @@ SYM_CODE_START(ftrace_caller)
        ftrace_regs_entry
        b               ftrace_common
 SYM_CODE_END(ftrace_caller)
+
+#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
+       bl              ftrace_return_to_handler
+       move            ra, a0
+
+       /* 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