2018-08-15 09:37:00

by Zong Li

[permalink] [raw]
Subject: [PATCH 0/6] Support ftrace on NDS32 architecture

This patch set implements the ftrace function and function graph tracer
on NDS32 architecture. We try to use C code to do everything, so we don't
need to separate the implementation to assembly code and C code, we just
need one ftrace.c file.

In mcount function, we use the prologue/epilogue which generated by compiler
to help us to save/restore the status. On the other hand, we use naked
attribute of gcc to avoid to generate the prologue/epilogue when we don't
need it(such as ftrace_stub and return_to_handler).

We pass the following tests:

1. FTRACE_STARTUP_TEST: PASS

2. tools/testing/selftests/ftrace/:
# of passed: 28
# of failed: 1
# of unresolved: 0
# of untested: 0
# of unsupported: 38
# of xfailed: 0
# of undefined(test bug): 0

The failed case is func-filter-glob.tc.
When setting the '*aw*lock' to the set_ftrace_filter in this test case,
we got the error because we have no these functions such as _raw_spin_lock,
_raw_spin_trylock and so on in our kernel.

Zong Li (6):
nds32/ftrace: Support static function tracer
nds32/ftrace: Support static function graph tracer
nds32/ftrace: Add RECORD_MCOUNT support
nds32/ftrace: Support dynamic function tracer
nds32/ftrace: Support dynamic function graph tracer
nds32/stack: Get real return address by using ftrace_graph_ret_addr

arch/nds32/Kconfig | 4 +
arch/nds32/Makefile | 4 +
arch/nds32/include/asm/ftrace.h | 46 +++++++
arch/nds32/kernel/Makefile | 6 +
arch/nds32/kernel/ftrace.c | 297 ++++++++++++++++++++++++++++++++++++++++
arch/nds32/kernel/stacktrace.c | 4 +
arch/nds32/kernel/traps.c | 30 +---
scripts/recordmcount.pl | 3 +
8 files changed, 370 insertions(+), 24 deletions(-)
create mode 100644 arch/nds32/include/asm/ftrace.h
create mode 100644 arch/nds32/kernel/ftrace.c

--
2.7.4



2018-08-15 09:37:09

by Zong Li

[permalink] [raw]
Subject: [PATCH 1/6] nds32/ftrace: Support static function tracer

This patch support the static function tracer. On nds32 ABI, we need to
always push return address to stack for __builtin_return_address can
work correctly, otherwise, it will get the wrong value of $lp at leaf
function.

Signed-off-by: Zong Li <[email protected]>
---
arch/nds32/Kconfig | 1 +
arch/nds32/Makefile | 4 ++++
arch/nds32/include/asm/ftrace.h | 20 ++++++++++++++++++++
arch/nds32/kernel/Makefile | 6 ++++++
arch/nds32/kernel/ftrace.c | 28 ++++++++++++++++++++++++++++
5 files changed, 59 insertions(+)
create mode 100644 arch/nds32/include/asm/ftrace.h
create mode 100644 arch/nds32/kernel/ftrace.c

diff --git a/arch/nds32/Kconfig b/arch/nds32/Kconfig
index 34f7222..af30e1a 100644
--- a/arch/nds32/Kconfig
+++ b/arch/nds32/Kconfig
@@ -40,6 +40,7 @@ config NDS32
select NO_IOPORT_MAP
select RTC_LIB
select THREAD_INFO_IN_TASK
+ select HAVE_FUNCTION_TRACER
help
Andes(nds32) Linux support.

diff --git a/arch/nds32/Makefile b/arch/nds32/Makefile
index 031c676..3ca8172 100644
--- a/arch/nds32/Makefile
+++ b/arch/nds32/Makefile
@@ -5,6 +5,10 @@ KBUILD_DEFCONFIG := defconfig

comma = ,

+ifdef CONFIG_FUNCTION_TRACER
+arch-y += -malways-save-lp -mno-relax
+endif
+
KBUILD_CFLAGS += $(call cc-option, -mno-sched-prolog-epilog)
KBUILD_CFLAGS += -mcmodel=large

diff --git a/arch/nds32/include/asm/ftrace.h b/arch/nds32/include/asm/ftrace.h
new file mode 100644
index 0000000..bac7657
--- /dev/null
+++ b/arch/nds32/include/asm/ftrace.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __ASM_NDS32_FTRACE_H
+#define __ASM_NDS32_FTRACE_H
+
+#ifdef CONFIG_FUNCTION_TRACER
+
+#define HAVE_FUNCTION_GRAPH_FP_TEST
+
+#define MCOUNT_ADDR ((unsigned long)(_mcount))
+/* mcount call is composed of three instructions:
+ * sethi + ori + jral
+ */
+#define MCOUNT_INSN_SIZE 12
+
+extern void _mcount(unsigned long parent_ip);
+
+#endif /* CONFIG_FUNCTION_TRACER */
+
+#endif /* __ASM_NDS32_FTRACE_H */
diff --git a/arch/nds32/kernel/Makefile b/arch/nds32/kernel/Makefile
index 4279274..27cded3 100644
--- a/arch/nds32/kernel/Makefile
+++ b/arch/nds32/kernel/Makefile
@@ -21,3 +21,9 @@ extra-y := head.o vmlinux.lds


obj-y += vdso/
+
+obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o
+
+ifdef CONFIG_FUNCTION_TRACER
+CFLAGS_REMOVE_ftrace.o = $(CC_FLAGS_FTRACE)
+endif
diff --git a/arch/nds32/kernel/ftrace.c b/arch/nds32/kernel/ftrace.c
new file mode 100644
index 0000000..563f64c
--- /dev/null
+++ b/arch/nds32/kernel/ftrace.c
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/ftrace.h>
+#include <linux/uaccess.h>
+#include <asm/cacheflush.h>
+
+extern void (*ftrace_trace_function)(unsigned long, unsigned long,
+ struct ftrace_ops*, struct pt_regs*);
+
+noinline void __naked ftrace_stub(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *op, struct pt_regs *regs)
+{
+ __asm__ (""); /* avoid to optimize as pure function */
+}
+
+noinline void _mcount(unsigned long parent_ip)
+{
+ /* save all state by the compiler prologue */
+
+ unsigned long ip = (unsigned long)__builtin_return_address(0);
+
+ if (ftrace_trace_function != ftrace_stub)
+ ftrace_trace_function(ip - MCOUNT_INSN_SIZE, parent_ip,
+ NULL, NULL);
+
+ /* restore all state by the compiler epilogue */
+}
+EXPORT_SYMBOL(_mcount);
--
2.7.4


2018-08-15 09:37:16

by Zong Li

[permalink] [raw]
Subject: [PATCH 2/6] nds32/ftrace: Support static function graph tracer

This patch contains implementation of static function graph tracer.

Signed-off-by: Zong Li <[email protected]>
---
arch/nds32/Kconfig | 1 +
arch/nds32/kernel/ftrace.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 70 insertions(+)

diff --git a/arch/nds32/Kconfig b/arch/nds32/Kconfig
index af30e1a..ae1a94ca 100644
--- a/arch/nds32/Kconfig
+++ b/arch/nds32/Kconfig
@@ -41,6 +41,7 @@ config NDS32
select RTC_LIB
select THREAD_INFO_IN_TASK
select HAVE_FUNCTION_TRACER
+ select HAVE_FUNCTION_GRAPH_TRACER
help
Andes(nds32) Linux support.

diff --git a/arch/nds32/kernel/ftrace.c b/arch/nds32/kernel/ftrace.c
index 563f64c..707fce7 100644
--- a/arch/nds32/kernel/ftrace.c
+++ b/arch/nds32/kernel/ftrace.c
@@ -6,6 +6,8 @@

extern void (*ftrace_trace_function)(unsigned long, unsigned long,
struct ftrace_ops*, struct pt_regs*);
+extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace);
+extern void ftrace_graph_caller(void);

noinline void __naked ftrace_stub(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *op, struct pt_regs *regs)
@@ -23,6 +25,73 @@ noinline void _mcount(unsigned long parent_ip)
ftrace_trace_function(ip - MCOUNT_INSN_SIZE, parent_ip,
NULL, NULL);

+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ if (ftrace_graph_return != (trace_func_graph_ret_t)ftrace_stub
+ || ftrace_graph_entry != ftrace_graph_entry_stub)
+ ftrace_graph_caller();
+#endif
+
/* restore all state by the compiler epilogue */
}
EXPORT_SYMBOL(_mcount);
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
+ unsigned long frame_pointer)
+{
+ unsigned long return_hooker = (unsigned long)&return_to_handler;
+ struct ftrace_graph_ent trace;
+ unsigned long old;
+ int err;
+
+ if (unlikely(atomic_read(&current->tracing_graph_pause)))
+ return;
+
+ old = *parent;
+
+ trace.func = self_addr;
+ trace.depth = current->curr_ret_stack + 1;
+
+ /* Only trace if the calling function expects to */
+ if (!ftrace_graph_entry(&trace))
+ return;
+
+ err = ftrace_push_return_trace(old, self_addr, &trace.depth,
+ frame_pointer, NULL);
+
+ if (err == -EBUSY)
+ return;
+
+ *parent = return_hooker;
+}
+
+noinline void ftrace_graph_caller(void)
+{
+ unsigned long *parent_ip =
+ (unsigned long *)(__builtin_frame_address(2) - 4);
+
+ unsigned long selfpc =
+ (unsigned long)(__builtin_return_address(1) - MCOUNT_INSN_SIZE);
+
+ unsigned long frame_pointer =
+ (unsigned long)__builtin_frame_address(3);
+
+ prepare_ftrace_return(parent_ip, selfpc, frame_pointer);
+}
+
+extern unsigned long ftrace_return_to_handler(unsigned long frame_pointer);
+void __naked return_to_handler(void)
+{
+ __asm__ __volatile__ (
+ /* save state needed by the ABI */
+ "smw.adm $r0,[$sp],$r1,#0x0 \n\t"
+
+ /* get original return address */
+ "move $r0, $fp \n\t"
+ "bal ftrace_return_to_handler\n\t"
+ "move $lp, $r0 \n\t"
+
+ /* restore state nedded by the ABI */
+ "lmw.bim $r0,[$sp],$r1,#0x0 \n\t");
+}
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
--
2.7.4


2018-08-15 09:37:24

by Zong Li

[permalink] [raw]
Subject: [PATCH 3/6] nds32/ftrace: Add RECORD_MCOUNT support

Recognize NDS32 object files in recordmcount.pl.

Signed-off-by: Zong Li <[email protected]>
---
arch/nds32/Kconfig | 1 +
scripts/recordmcount.pl | 3 +++
2 files changed, 4 insertions(+)

diff --git a/arch/nds32/Kconfig b/arch/nds32/Kconfig
index ae1a94ca..66d507d 100644
--- a/arch/nds32/Kconfig
+++ b/arch/nds32/Kconfig
@@ -42,6 +42,7 @@ config NDS32
select THREAD_INFO_IN_TASK
select HAVE_FUNCTION_TRACER
select HAVE_FUNCTION_GRAPH_TRACER
+ select HAVE_FTRACE_MCOUNT_RECORD
help
Andes(nds32) Linux support.

diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
index fe06e77..f599031 100755
--- a/scripts/recordmcount.pl
+++ b/scripts/recordmcount.pl
@@ -389,6 +389,9 @@ if ($arch eq "x86_64") {
$mcount_regex = "^\\s*([0-9a-fA-F]+):\\sR_RISCV_CALL\\s_mcount\$";
$type = ".quad";
$alignment = 2;
+} elsif ($arch eq "nds32") {
+ $mcount_regex = "^\\s*([0-9a-fA-F]+):\\s*R_NDS32_HI20_RELA\\s+_mcount\$";
+ $alignment = 2;
} else {
die "Arch $arch is not supported with CONFIG_FTRACE_MCOUNT_RECORD";
}
--
2.7.4


2018-08-15 09:37:29

by Zong Li

[permalink] [raw]
Subject: [PATCH 4/6] nds32/ftrace: Support dynamic function tracer

This patch contains the implementation of dynamic function tracer.
The mcount call is composed of three instructions, so there are three
nop for enough placeholder.

Signed-off-by: Zong Li <[email protected]>
---
arch/nds32/Kconfig | 1 +
arch/nds32/include/asm/ftrace.h | 26 +++++++
arch/nds32/kernel/ftrace.c | 164 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 191 insertions(+)

diff --git a/arch/nds32/Kconfig b/arch/nds32/Kconfig
index 66d507d..9db36c2 100644
--- a/arch/nds32/Kconfig
+++ b/arch/nds32/Kconfig
@@ -43,6 +43,7 @@ config NDS32
select HAVE_FUNCTION_TRACER
select HAVE_FUNCTION_GRAPH_TRACER
select HAVE_FTRACE_MCOUNT_RECORD
+ select HAVE_DYNAMIC_FTRACE
help
Andes(nds32) Linux support.

diff --git a/arch/nds32/include/asm/ftrace.h b/arch/nds32/include/asm/ftrace.h
index bac7657..2f96cc9 100644
--- a/arch/nds32/include/asm/ftrace.h
+++ b/arch/nds32/include/asm/ftrace.h
@@ -15,6 +15,32 @@

extern void _mcount(unsigned long parent_ip);

+#ifdef CONFIG_DYNAMIC_FTRACE
+
+#define FTRACE_ADDR ((unsigned long)_ftrace_caller)
+
+#ifdef __NDS32_EL__
+#define INSN_NOP 0x09000040
+#define INSN_SIZE(insn) (((insn & 0x00000080) == 0) ? 4 : 2)
+#define IS_SETHI(insn) ((insn & 0x000000fe) == 0x00000046)
+#define ENDIAN_CONVERT(insn) be32_to_cpu(insn)
+#else /* __NDS32_EB__ */
+#define INSN_NOP 0x40000009
+#define INSN_SIZE(insn) (((insn & 0x80000000) == 0) ? 4 : 2)
+#define IS_SETHI(insn) ((insn & 0xfe000000) == 0x46000000)
+#define ENDIAN_CONVERT(insn) (insn)
+#endif
+
+extern void _ftrace_caller(unsigned long parent_ip);
+static inline unsigned long ftrace_call_adjust(unsigned long addr)
+{
+ return addr;
+}
+struct dyn_arch_ftrace {
+};
+
+#endif /* CONFIG_DYNAMIC_FTRACE */
+
#endif /* CONFIG_FUNCTION_TRACER */

#endif /* __ASM_NDS32_FTRACE_H */
diff --git a/arch/nds32/kernel/ftrace.c b/arch/nds32/kernel/ftrace.c
index 707fce7..3ca676b 100644
--- a/arch/nds32/kernel/ftrace.c
+++ b/arch/nds32/kernel/ftrace.c
@@ -4,6 +4,7 @@
#include <linux/uaccess.h>
#include <asm/cacheflush.h>

+#ifndef CONFIG_DYNAMIC_FTRACE
extern void (*ftrace_trace_function)(unsigned long, unsigned long,
struct ftrace_ops*, struct pt_regs*);
extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace);
@@ -35,6 +36,168 @@ noinline void _mcount(unsigned long parent_ip)
}
EXPORT_SYMBOL(_mcount);

+#else /* CONFIG_DYNAMIC_FTRACE */
+
+noinline void __naked ftrace_stub(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *op, struct pt_regs *regs)
+{
+ __asm__ (""); /* avoid to optimize as pure function */
+}
+
+noinline void __naked _mcount(unsigned long parent_ip)
+{
+ __asm__ (""); /* avoid to optimize as pure function */
+}
+EXPORT_SYMBOL(_mcount);
+
+#define XSTR(s) STR(s)
+#define STR(s) #s
+void _ftrace_caller(unsigned long parent_ip)
+{
+ /* save all state needed by the compiler prologue */
+
+ /*
+ * prepare arguments for real tracing function
+ * first arg : __builtin_return_address(0) - MCOUNT_INSN_SIZE
+ * second arg : parent_ip
+ */
+ __asm__ __volatile__ (
+ "move $r1, %0 \n\t"
+ "addi $r0, %1, #-" XSTR(MCOUNT_INSN_SIZE) "\n\t"
+ :
+ : "r" (parent_ip), "r" (__builtin_return_address(0)));
+
+ /* a placeholder for the call to a real tracing function */
+ __asm__ __volatile__ (
+ "ftrace_call: \n\t"
+ "nop \n\t"
+ "nop \n\t"
+ "nop \n\t");
+
+ /* restore all state needed by the compiler epilogue */
+}
+
+int __init ftrace_dyn_arch_init(void)
+{
+ return 0;
+}
+
+int ftrace_arch_code_modify_prepare(void)
+{
+ set_all_modules_text_rw();
+ return 0;
+}
+
+int ftrace_arch_code_modify_post_process(void)
+{
+ set_all_modules_text_ro();
+ return 0;
+}
+
+static unsigned long gen_sethi_insn(unsigned long addr)
+{
+ unsigned long opcode = 0x46000000;
+ unsigned long imm = addr >> 12;
+ unsigned long rt_num = 0xf << 20;
+
+ return ENDIAN_CONVERT(opcode | rt_num | imm);
+}
+
+static unsigned long gen_ori_insn(unsigned long addr)
+{
+ unsigned long opcode = 0x58000000;
+ unsigned long imm = addr & 0x0000fff;
+ unsigned long rt_num = 0xf << 20;
+ unsigned long ra_num = 0xf << 15;
+
+ return ENDIAN_CONVERT(opcode | rt_num | ra_num | imm);
+}
+
+static unsigned long gen_jral_insn(unsigned long addr)
+{
+ unsigned long opcode = 0x4a000001;
+ unsigned long rt_num = 0x1e << 20;
+ unsigned long rb_num = 0xf << 10;
+
+ return ENDIAN_CONVERT(opcode | rt_num | rb_num);
+}
+
+static void ftrace_gen_call_insn(unsigned long *call_insns,
+ unsigned long addr)
+{
+ call_insns[0] = gen_sethi_insn(addr); /* sethi $r15, imm20u */
+ call_insns[1] = gen_ori_insn(addr); /* ori $r15, $r15, imm15u */
+ call_insns[2] = gen_jral_insn(addr); /* jral $lp, $r15 */
+}
+
+static int __ftrace_modify_code(unsigned long pc, unsigned long *old_insn,
+ unsigned long *new_insn, bool validate)
+{
+ unsigned long orig_insn[3];
+
+ if (validate) {
+ if (probe_kernel_read(orig_insn, (void *)pc, MCOUNT_INSN_SIZE))
+ return -EFAULT;
+ if (memcmp(orig_insn, old_insn, MCOUNT_INSN_SIZE))
+ return -EINVAL;
+ }
+
+ if (probe_kernel_write((void *)pc, new_insn, MCOUNT_INSN_SIZE))
+ return -EPERM;
+
+ return 0;
+}
+
+static int ftrace_modify_code(unsigned long pc, unsigned long *old_insn,
+ unsigned long *new_insn, bool validate)
+{
+ int ret;
+
+ ret = __ftrace_modify_code(pc, old_insn, new_insn, validate);
+ if (ret)
+ return ret;
+
+ flush_icache_range(pc, pc + MCOUNT_INSN_SIZE);
+
+ return ret;
+}
+
+int ftrace_update_ftrace_func(ftrace_func_t func)
+{
+ unsigned long pc = (unsigned long)&ftrace_call;
+ unsigned long old_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
+ unsigned long new_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
+
+ if (func != ftrace_stub)
+ ftrace_gen_call_insn(new_insn, (unsigned long)func);
+
+ return ftrace_modify_code(pc, old_insn, new_insn, false);
+}
+
+int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
+{
+ unsigned long pc = rec->ip;
+ unsigned long nop_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
+ unsigned long call_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
+
+ ftrace_gen_call_insn(call_insn, addr);
+
+ return ftrace_modify_code(pc, nop_insn, call_insn, true);
+}
+
+int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
+ unsigned long addr)
+{
+ unsigned long pc = rec->ip;
+ unsigned long nop_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
+ unsigned long call_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
+
+ ftrace_gen_call_insn(call_insn, addr);
+
+ return ftrace_modify_code(pc, call_insn, nop_insn, true);
+}
+#endif /* CONFIG_DYNAMIC_FTRACE */
+
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
unsigned long frame_pointer)
@@ -94,4 +257,5 @@ void __naked return_to_handler(void)
/* restore state nedded by the ABI */
"lmw.bim $r0,[$sp],$r1,#0x0 \n\t");
}
+
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
--
2.7.4


2018-08-15 09:38:05

by Zong Li

[permalink] [raw]
Subject: [PATCH 5/6] nds32/ftrace: Support dynamic function graph tracer

This patch contains the implementation of dynamic function graph tracer.

Signed-off-by: Zong Li <[email protected]>
---
arch/nds32/kernel/ftrace.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)

diff --git a/arch/nds32/kernel/ftrace.c b/arch/nds32/kernel/ftrace.c
index 3ca676b..a646a83 100644
--- a/arch/nds32/kernel/ftrace.c
+++ b/arch/nds32/kernel/ftrace.c
@@ -74,6 +74,14 @@ void _ftrace_caller(unsigned long parent_ip)
"nop \n\t"
"nop \n\t");

+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ /* a placeholder for the call to ftrace_graph_caller */
+ __asm__ __volatile__ (
+ "ftrace_graph_call: \n\t"
+ "nop \n\t"
+ "nop \n\t"
+ "nop \n\t");
+#endif
/* restore all state needed by the compiler epilogue */
}

@@ -258,4 +266,32 @@ void __naked return_to_handler(void)
"lmw.bim $r0,[$sp],$r1,#0x0 \n\t");
}

+#ifdef CONFIG_DYNAMIC_FTRACE
+extern unsigned long ftrace_graph_call;
+
+static int ftrace_modify_graph_caller(bool enable)
+{
+ unsigned long pc = (unsigned long)&ftrace_graph_call;
+ unsigned long nop_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
+ unsigned long call_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
+
+ ftrace_gen_call_insn(call_insn, (unsigned long)ftrace_graph_caller);
+
+ if (enable)
+ return ftrace_modify_code(pc, nop_insn, call_insn, true);
+ else
+ return ftrace_modify_code(pc, call_insn, nop_insn, 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_DYNAMIC_FTRACE */
+
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
--
2.7.4


2018-08-15 09:38:18

by Zong Li

[permalink] [raw]
Subject: [PATCH 6/6] nds32/stack: Get real return address by using ftrace_graph_ret_addr

Function graph tracer has modified the return address to
'return_to_handler' on stack, and provide the 'ftrace_graph_ret_addr' to
get the real return address.

Signed-off-by: Zong Li <[email protected]>
---
arch/nds32/kernel/stacktrace.c | 4 ++++
arch/nds32/kernel/traps.c | 30 ++++++------------------------
2 files changed, 10 insertions(+), 24 deletions(-)

diff --git a/arch/nds32/kernel/stacktrace.c b/arch/nds32/kernel/stacktrace.c
index 8b231e9..36bc870 100644
--- a/arch/nds32/kernel/stacktrace.c
+++ b/arch/nds32/kernel/stacktrace.c
@@ -4,6 +4,7 @@
#include <linux/sched/debug.h>
#include <linux/sched/task_stack.h>
#include <linux/stacktrace.h>
+#include <linux/ftrace.h>

void save_stack_trace(struct stack_trace *trace)
{
@@ -16,6 +17,7 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
unsigned long *fpn;
int skip = trace->skip;
int savesched;
+ int graph_idx = 0;

if (tsk == current) {
__asm__ __volatile__("\tori\t%0, $fp, #0\n":"=r"(fpn));
@@ -33,6 +35,8 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
fpp = fpn[FP_OFFSET];
if (!__kernel_text_address(lpp))
break;
+ else
+ lpp = ftrace_graph_ret_addr(tsk, &graph_idx, lpp, NULL);

if (savesched || !in_sched_functions(lpp)) {
if (skip) {
diff --git a/arch/nds32/kernel/traps.c b/arch/nds32/kernel/traps.c
index f0e9743..7684c8f 100644
--- a/arch/nds32/kernel/traps.c
+++ b/arch/nds32/kernel/traps.c
@@ -8,6 +8,7 @@
#include <linux/kdebug.h>
#include <linux/sched/task_stack.h>
#include <linux/uaccess.h>
+#include <linux/ftrace.h>

#include <asm/proc-fns.h>
#include <asm/unistd.h>
@@ -94,28 +95,6 @@ static void dump_instr(struct pt_regs *regs)
set_fs(fs);
}

-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
-#include <linux/ftrace.h>
-static void
-get_real_ret_addr(unsigned long *addr, struct task_struct *tsk, int *graph)
-{
- if (*addr == (unsigned long)return_to_handler) {
- int index = tsk->curr_ret_stack;
-
- if (tsk->ret_stack && index >= *graph) {
- index -= *graph;
- *addr = tsk->ret_stack[index].ret;
- (*graph)++;
- }
- }
-}
-#else
-static inline void
-get_real_ret_addr(unsigned long *addr, struct task_struct *tsk, int *graph)
-{
-}
-#endif
-
#define LOOP_TIMES (100)
static void __dump(struct task_struct *tsk, unsigned long *base_reg)
{
@@ -126,7 +105,8 @@ static void __dump(struct task_struct *tsk, unsigned long *base_reg)
while (!kstack_end(base_reg)) {
ret_addr = *base_reg++;
if (__kernel_text_address(ret_addr)) {
- get_real_ret_addr(&ret_addr, tsk, &graph);
+ ret_addr = ftrace_graph_ret_addr(
+ tsk, &graph, ret_addr, NULL);
print_ip_sym(ret_addr);
}
if (--cnt < 0)
@@ -145,7 +125,9 @@ static void __dump(struct task_struct *tsk, unsigned long *base_reg)
next_fp = base_reg[FP_OFFSET];
#endif
if (__kernel_text_address(ret_addr)) {
- get_real_ret_addr(&ret_addr, tsk, &graph);
+
+ ret_addr = ftrace_graph_ret_addr(
+ tsk, &graph, ret_addr, NULL);
print_ip_sym(ret_addr);
}
if (--cnt < 0)
--
2.7.4


2018-08-15 20:12:28

by Steven Rostedt

[permalink] [raw]
Subject: Re: [PATCH 1/6] nds32/ftrace: Support static function tracer

On Wed, 15 Aug 2018 17:35:18 +0800
Zong Li <[email protected]> wrote:

> This patch support the static function tracer. On nds32 ABI, we need to
> always push return address to stack for __builtin_return_address can
> work correctly, otherwise, it will get the wrong value of $lp at leaf
> function.
>
> Signed-off-by: Zong Li <[email protected]>
> ---
> arch/nds32/Kconfig | 1 +
> arch/nds32/Makefile | 4 ++++
> arch/nds32/include/asm/ftrace.h | 20 ++++++++++++++++++++
> arch/nds32/kernel/Makefile | 6 ++++++
> arch/nds32/kernel/ftrace.c | 28 ++++++++++++++++++++++++++++
> 5 files changed, 59 insertions(+)
> create mode 100644 arch/nds32/include/asm/ftrace.h
> create mode 100644 arch/nds32/kernel/ftrace.c
>
> diff --git a/arch/nds32/Kconfig b/arch/nds32/Kconfig
> index 34f7222..af30e1a 100644
> --- a/arch/nds32/Kconfig
> +++ b/arch/nds32/Kconfig
> @@ -40,6 +40,7 @@ config NDS32
> select NO_IOPORT_MAP
> select RTC_LIB
> select THREAD_INFO_IN_TASK
> + select HAVE_FUNCTION_TRACER
> help
> Andes(nds32) Linux support.
>
> diff --git a/arch/nds32/Makefile b/arch/nds32/Makefile
> index 031c676..3ca8172 100644
> --- a/arch/nds32/Makefile
> +++ b/arch/nds32/Makefile
> @@ -5,6 +5,10 @@ KBUILD_DEFCONFIG := defconfig
>
> comma = ,
>
> +ifdef CONFIG_FUNCTION_TRACER
> +arch-y += -malways-save-lp -mno-relax
> +endif
> +
> KBUILD_CFLAGS += $(call cc-option, -mno-sched-prolog-epilog)
> KBUILD_CFLAGS += -mcmodel=large
>
> diff --git a/arch/nds32/include/asm/ftrace.h b/arch/nds32/include/asm/ftrace.h
> new file mode 100644
> index 0000000..bac7657
> --- /dev/null
> +++ b/arch/nds32/include/asm/ftrace.h
> @@ -0,0 +1,20 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef __ASM_NDS32_FTRACE_H
> +#define __ASM_NDS32_FTRACE_H
> +
> +#ifdef CONFIG_FUNCTION_TRACER
> +
> +#define HAVE_FUNCTION_GRAPH_FP_TEST
> +
> +#define MCOUNT_ADDR ((unsigned long)(_mcount))
> +/* mcount call is composed of three instructions:
> + * sethi + ori + jral
> + */
> +#define MCOUNT_INSN_SIZE 12
> +
> +extern void _mcount(unsigned long parent_ip);
> +
> +#endif /* CONFIG_FUNCTION_TRACER */
> +
> +#endif /* __ASM_NDS32_FTRACE_H */
> diff --git a/arch/nds32/kernel/Makefile b/arch/nds32/kernel/Makefile
> index 4279274..27cded3 100644
> --- a/arch/nds32/kernel/Makefile
> +++ b/arch/nds32/kernel/Makefile
> @@ -21,3 +21,9 @@ extra-y := head.o vmlinux.lds
>
>
> obj-y += vdso/
> +
> +obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o
> +
> +ifdef CONFIG_FUNCTION_TRACER
> +CFLAGS_REMOVE_ftrace.o = $(CC_FLAGS_FTRACE)
> +endif
> diff --git a/arch/nds32/kernel/ftrace.c b/arch/nds32/kernel/ftrace.c
> new file mode 100644
> index 0000000..563f64c
> --- /dev/null
> +++ b/arch/nds32/kernel/ftrace.c
> @@ -0,0 +1,28 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/ftrace.h>
> +#include <linux/uaccess.h>
> +#include <asm/cacheflush.h>
> +
> +extern void (*ftrace_trace_function)(unsigned long, unsigned long,
> + struct ftrace_ops*, struct pt_regs*);
> +
> +noinline void __naked ftrace_stub(unsigned long ip, unsigned long parent_ip,
> + struct ftrace_ops *op, struct pt_regs *regs)
> +{
> + __asm__ (""); /* avoid to optimize as pure function */
> +}
> +
> +noinline void _mcount(unsigned long parent_ip)
> +{

Wow! The NDS32 arch allows for _mcount to be a C function! That's
nice :-)

-- Steve

> + /* save all state by the compiler prologue */
> +
> + unsigned long ip = (unsigned long)__builtin_return_address(0);
> +
> + if (ftrace_trace_function != ftrace_stub)
> + ftrace_trace_function(ip - MCOUNT_INSN_SIZE, parent_ip,
> + NULL, NULL);
> +
> + /* restore all state by the compiler epilogue */
> +}
> +EXPORT_SYMBOL(_mcount);


2018-08-21 07:05:46

by Greentime Hu

[permalink] [raw]
Subject: Re: [PATCH 0/6] Support ftrace on NDS32 architecture

Zong Li <[email protected]> 於 2018年8月15日 週三 下午5:35寫道:
>
> This patch set implements the ftrace function and function graph tracer
> on NDS32 architecture. We try to use C code to do everything, so we don't
> need to separate the implementation to assembly code and C code, we just
> need one ftrace.c file.
>
> In mcount function, we use the prologue/epilogue which generated by compiler
> to help us to save/restore the status. On the other hand, we use naked
> attribute of gcc to avoid to generate the prologue/epilogue when we don't
> need it(such as ftrace_stub and return_to_handler).
>
> We pass the following tests:
>
> 1. FTRACE_STARTUP_TEST: PASS
>
> 2. tools/testing/selftests/ftrace/:
> # of passed: 28
> # of failed: 1
> # of unresolved: 0
> # of untested: 0
> # of unsupported: 38
> # of xfailed: 0
> # of undefined(test bug): 0
>
> The failed case is func-filter-glob.tc.
> When setting the '*aw*lock' to the set_ftrace_filter in this test case,
> we got the error because we have no these functions such as _raw_spin_lock,
> _raw_spin_trylock and so on in our kernel.
>
> Zong Li (6):
> nds32/ftrace: Support static function tracer
> nds32/ftrace: Support static function graph tracer
> nds32/ftrace: Add RECORD_MCOUNT support
> nds32/ftrace: Support dynamic function tracer
> nds32/ftrace: Support dynamic function graph tracer
> nds32/stack: Get real return address by using ftrace_graph_ret_addr
>
> arch/nds32/Kconfig | 4 +
> arch/nds32/Makefile | 4 +
> arch/nds32/include/asm/ftrace.h | 46 +++++++
> arch/nds32/kernel/Makefile | 6 +
> arch/nds32/kernel/ftrace.c | 297 ++++++++++++++++++++++++++++++++++++++++
> arch/nds32/kernel/stacktrace.c | 4 +
> arch/nds32/kernel/traps.c | 30 +---
> scripts/recordmcount.pl | 3 +
> 8 files changed, 370 insertions(+), 24 deletions(-)
> create mode 100644 arch/nds32/include/asm/ftrace.h
> create mode 100644 arch/nds32/kernel/ftrace.c

Acked-by: Greentime Hu <[email protected]>