From: Wu Zhangjin <[email protected]>
ftrace is a mcount based kernel tracing tool/framework, which is originally
from RT_PREEMPT(http://rt.wiki.kernel.org).
ftrace is short for function tracer, this is its original name, but now, it
becomes a kernel tracing framework, lots of kernel tracers are built on it,
such as irqoff tracer, wakeup tracer and so forth. these tracers are
arch-independent(?), but some of them are arch-dependent, such as the original
ftrace: function tracer, and dynamic function tracer, function graph tracer,
and also, system call tracer.
here is the mips porting of these four arch-dependent tracers, it will enable
the following new kernel config options in linux-mips system.
kernel hacking --->
Tracers -->
[*] Kernel Function Tracer
[*] Kernel Function Graph Tracer
...
[*] Trace syscalls
...
[*] enable/disable ftrace tracepoints dynamically
in reality, because the timestamp getting function are arch-dependent, lots of
the tracers are arch-dependent. the arch-dependent part is that: sched_clock().
the original sched_clock() in mips is jiffes based, only give 10ms precision in
1000HZ, which is not enough for ftrace. to get high-precise timestamp, we must
implement a new native_sched_clock() via reading the MIPS clock counter, but
since it is only 32bit long, so, overflow should be handled carefully.
this -v3 patch series is based on the -v2 patch series and incorporates the
feedback from Steven Rostedt and Thomas Gleixner.
!!NOTE!!
this -v3 not support modules yet, really hope this can be fixed in -v4 patch.
read the following document, and play with it:
Documentation/trace/ftrace.txt
Wu Zhangjin (7):
mips static function tracer support
add an endian argument to scripts/recordmcount.pl
mips dynamic function tracer support
filter local function prefixed by $L
mips function graph tracer support
mips specific clock function to get precise timestamp
mips specific system call tracer
arch/mips/Kconfig | 7 +
arch/mips/Makefile | 2 +
arch/mips/include/asm/ftrace.h | 35 ++++-
arch/mips/include/asm/ptrace.h | 2 +
arch/mips/include/asm/reg.h | 5 +
arch/mips/include/asm/syscall.h | 84 +++++++++
arch/mips/include/asm/thread_info.h | 5 +-
arch/mips/kernel/Makefile | 12 ++
arch/mips/kernel/csrc-r4k.c | 2 +-
arch/mips/kernel/entry.S | 2 +-
arch/mips/kernel/ftrace.c | 344 +++++++++++++++++++++++++++++++++++
arch/mips/kernel/ftrace_clock.c | 71 +++++++
arch/mips/kernel/mcount.S | 184 +++++++++++++++++++
arch/mips/kernel/mips_ksyms.c | 5 +
arch/mips/kernel/ptrace.c | 14 ++-
arch/mips/kernel/scall64-o32.S | 2 +-
arch/mips/kernel/vmlinux.lds.S | 1 +
kernel/trace/trace_clock.c | 2 +-
scripts/Makefile.build | 1 +
scripts/recordmcount.pl | 30 +++-
20 files changed, 799 insertions(+), 11 deletions(-)
create mode 100644 arch/mips/include/asm/syscall.h
create mode 100644 arch/mips/kernel/ftrace.c
create mode 100644 arch/mips/kernel/ftrace_clock.c
create mode 100644 arch/mips/kernel/mcount.S
From: Wu Zhangjin <[email protected]>
if -pg of gcc is enabled. a calling to _mcount will be inserted to each
kernel function. so, there is a possibility to trace the functions in
_mcount.
here is the implementation of mips specific _mcount for static function
tracer.
-ffunction-sections option not works with -pg, so disable it if enables
FUNCTION_TRACER.
Reviewed-by: Steven Rostedt <[email protected]>
Signed-off-by: Wu Zhangjin <[email protected]>
---
arch/mips/Kconfig | 2 +
arch/mips/Makefile | 2 +
arch/mips/include/asm/ftrace.h | 25 ++++++++++-
arch/mips/kernel/Makefile | 7 +++
arch/mips/kernel/mcount.S | 98 ++++++++++++++++++++++++++++++++++++++++
arch/mips/kernel/mips_ksyms.c | 5 ++
6 files changed, 138 insertions(+), 1 deletions(-)
create mode 100644 arch/mips/kernel/mcount.S
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 25f3b0a..2e2fdbe 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -4,6 +4,8 @@ config MIPS
select HAVE_IDE
select HAVE_OPROFILE
select HAVE_ARCH_KGDB
+ select HAVE_FUNCTION_TRACER
+ select HAVE_FUNCTION_TRACE_MCOUNT_TEST
# Horrible source of confusion. Die, die, die ...
select EMBEDDED
select RTC_LIB
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index c4cae9e..f86fb15 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -48,7 +48,9 @@ ifneq ($(SUBARCH),$(ARCH))
endif
endif
+ifndef CONFIG_FUNCTION_TRACER
cflags-y := -ffunction-sections
+endif
cflags-y += $(call cc-option, -mno-check-zero-division)
ifdef CONFIG_32BIT
diff --git a/arch/mips/include/asm/ftrace.h b/arch/mips/include/asm/ftrace.h
index 40a8c17..5f8ebcf 100644
--- a/arch/mips/include/asm/ftrace.h
+++ b/arch/mips/include/asm/ftrace.h
@@ -1 +1,24 @@
-/* empty */
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive for
+ * more details.
+ *
+ * Copyright (C) 2009 DSLab, Lanzhou University, China
+ * Author: Wu Zhangjin <[email protected]>
+ */
+
+#ifndef _ASM_MIPS_FTRACE_H
+#define _ASM_MIPS_FTRACE_H
+
+#ifdef CONFIG_FUNCTION_TRACER
+
+#define MCOUNT_ADDR ((unsigned long)(_mcount))
+#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_MIPS_FTRACE_H */
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index e961221..559a820 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -8,6 +8,11 @@ obj-y += cpu-probe.o branch.o entry.o genex.o irq.o process.o \
ptrace.o reset.o setup.o signal.o syscall.o \
time.o topology.o traps.o unaligned.o watch.o
+ifdef CONFIG_FUNCTION_TRACER
+# Do not profile debug and lowlevel utilities
+CFLAGS_REMOVE_early_printk.o = -pg
+endif
+
obj-$(CONFIG_CEVT_BCM1480) += cevt-bcm1480.o
obj-$(CONFIG_CEVT_R4K_LIB) += cevt-r4k.o
obj-$(CONFIG_MIPS_MT_SMTC) += cevt-smtc.o
@@ -24,6 +29,8 @@ obj-$(CONFIG_SYNC_R4K) += sync-r4k.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_MODULES) += mips_ksyms.o module.o
+obj-$(CONFIG_FUNCTION_TRACER) += mcount.o
+
obj-$(CONFIG_CPU_LOONGSON2) += r4k_fpu.o r4k_switch.o
obj-$(CONFIG_CPU_MIPS32) += r4k_fpu.o r4k_switch.o
obj-$(CONFIG_CPU_MIPS64) += r4k_fpu.o r4k_switch.o
diff --git a/arch/mips/kernel/mcount.S b/arch/mips/kernel/mcount.S
new file mode 100644
index 0000000..268724e
--- /dev/null
+++ b/arch/mips/kernel/mcount.S
@@ -0,0 +1,98 @@
+/*
+ * the mips-specific _mcount implementation
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive for
+ * more details.
+ *
+ * Copyright (C) 2009 DSLab, Lanzhou University, China
+ * Author: Wu Zhangjin <[email protected]>
+ */
+
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+#include <asm/ftrace.h>
+
+ .text
+ .set noreorder
+ .set noat
+
+ /* since there is a "addiu sp,sp,-8" before "jal _mcount" in 32bit */
+ .macro RESTORE_SP_FOR_32BIT
+#ifdef CONFIG_32BIT
+ PTR_ADDIU sp, 8
+#endif
+ .endm
+
+ .macro MCOUNT_SAVE_REGS
+ PTR_SUBU sp, PT_SIZE
+ PTR_S ra, PT_R31(sp)
+ PTR_S $1, PT_R1(sp)
+ PTR_S a0, PT_R4(sp)
+ PTR_S a1, PT_R5(sp)
+ PTR_S a2, PT_R6(sp)
+ PTR_S a3, PT_R7(sp)
+#ifdef CONFIG_64BIT
+ PTR_S a4, PT_R8(sp)
+ PTR_S a5, PT_R9(sp)
+ PTR_S a6, PT_R10(sp)
+ PTR_S a7, PT_R11(sp)
+#endif
+ .endm
+
+ .macro MCOUNT_RESTORE_REGS
+ PTR_L ra, PT_R31(sp)
+ PTR_L $1, PT_R1(sp)
+ PTR_L a0, PT_R4(sp)
+ PTR_L a1, PT_R5(sp)
+ PTR_L a2, PT_R6(sp)
+ PTR_L a3, PT_R7(sp)
+#ifdef CONFIG_64BIT
+ PTR_L a4, PT_R8(sp)
+ PTR_L a5, PT_R9(sp)
+ PTR_L a6, PT_R10(sp)
+ PTR_L a7, PT_R11(sp)
+#endif
+ PTR_ADDIU sp, PT_SIZE
+.endm
+
+ .macro MCOUNT_SET_ARGS
+ move a0, ra /* arg1: next ip, selfaddr */
+ move a1, $1 /* arg2: the caller's next ip, parent */
+ PTR_SUBU a0, MCOUNT_INSN_SIZE
+ .endm
+
+ .macro RETURN_BACK
+ jr ra
+ move ra, $1
+ .endm
+
+NESTED(_mcount, PT_SIZE, ra)
+ RESTORE_SP_FOR_32BIT
+ PTR_L t0, function_trace_stop
+ bnez t0, ftrace_stub
+ nop
+
+ PTR_LA t0, ftrace_stub
+ PTR_L t1, ftrace_trace_function /* please don't use t1 later, safe? */
+ bne t0, t1, static_trace
+ nop
+
+ j ftrace_stub
+ nop
+
+static_trace:
+ MCOUNT_SAVE_REGS
+
+ MCOUNT_SET_ARGS /* call *ftrace_trace_function */
+ jalr t1
+ nop
+
+ MCOUNT_RESTORE_REGS
+ .globl ftrace_stub
+ftrace_stub:
+ RETURN_BACK
+ END(_mcount)
+
+ .set at
+ .set reorder
diff --git a/arch/mips/kernel/mips_ksyms.c b/arch/mips/kernel/mips_ksyms.c
index 225755d..1d04807 100644
--- a/arch/mips/kernel/mips_ksyms.c
+++ b/arch/mips/kernel/mips_ksyms.c
@@ -13,6 +13,7 @@
#include <asm/checksum.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
+#include <asm/ftrace.h>
extern void *__bzero(void *__s, size_t __count);
extern long __strncpy_from_user_nocheck_asm(char *__to,
@@ -51,3 +52,7 @@ EXPORT_SYMBOL(csum_partial_copy_nocheck);
EXPORT_SYMBOL(__csum_partial_copy_user);
EXPORT_SYMBOL(invalid_pte_table);
+#ifdef CONFIG_FUNCTION_TRACER
+/* _mcount is defined in arch/mips/kernel/mcount.S */
+EXPORT_SYMBOL(_mcount);
+#endif
--
1.6.0.4
From: Wu Zhangjin <[email protected]>
mips architecture need this argument to handle big/little endian
differently.
Reviewed-by: Steven Rostedt <[email protected]>
Signed-off-by: Wu Zhangjin <[email protected]>
---
scripts/Makefile.build | 1 +
scripts/recordmcount.pl | 6 +++---
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 5c4b7a4..548d575 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -207,6 +207,7 @@ endif
ifdef CONFIG_FTRACE_MCOUNT_RECORD
cmd_record_mcount = perl $(srctree)/scripts/recordmcount.pl "$(ARCH)" \
+ "$(if $(CONFIG_CPU_BIG_ENDIAN),big,little)" \
"$(if $(CONFIG_64BIT),64,32)" \
"$(OBJDUMP)" "$(OBJCOPY)" "$(CC)" "$(LD)" "$(NM)" "$(RM)" "$(MV)" \
"$(if $(part-of-module),1,0)" "$(@)";
diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
index 0fae7da..f1e3e9c 100755
--- a/scripts/recordmcount.pl
+++ b/scripts/recordmcount.pl
@@ -100,13 +100,13 @@ $P =~ s@.*/@@g;
my $V = '0.1';
-if ($#ARGV < 7) {
- print "usage: $P arch bits objdump objcopy cc ld nm rm mv is_module inputfile\n";
+if ($#ARGV < 8) {
+ print "usage: $P arch endian bits objdump objcopy cc ld nm rm mv is_module inputfile\n";
print "version: $V\n";
exit(1);
}
-my ($arch, $bits, $objdump, $objcopy, $cc,
+my ($arch, $endian, $bits, $objdump, $objcopy, $cc,
$ld, $nm, $rm, $mv, $is_module, $inputfile) = @ARGV;
# This file refers to mcount and shouldn't be ftraced, so lets' ignore it
--
1.6.0.4
From: Wu Zhangjin <[email protected]>
dynamic function tracer need to replace "nop" to "jumps & links" and
something reversely.
Reviewed-by: Steven Rostedt <[email protected]>
Signed-off-by: Wu Zhangjin <[email protected]>
---
arch/mips/Kconfig | 3 +
arch/mips/include/asm/ftrace.h | 10 ++
arch/mips/kernel/Makefile | 2 +
arch/mips/kernel/ftrace.c | 207 ++++++++++++++++++++++++++++++++++++++++
arch/mips/kernel/mcount.S | 31 ++++++
scripts/recordmcount.pl | 20 ++++
6 files changed, 273 insertions(+), 0 deletions(-)
create mode 100644 arch/mips/kernel/ftrace.c
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 2e2fdbe..0857239 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -6,6 +6,9 @@ config MIPS
select HAVE_ARCH_KGDB
select HAVE_FUNCTION_TRACER
select HAVE_FUNCTION_TRACE_MCOUNT_TEST
+ select HAVE_DYNAMIC_FTRACE
+ select HAVE_FTRACE_MCOUNT_RECORD
+ select HAVE_FTRACE_NMI_ENTER if DYNAMIC_FTRACE
# Horrible source of confusion. Die, die, die ...
select EMBEDDED
select RTC_LIB
diff --git a/arch/mips/include/asm/ftrace.h b/arch/mips/include/asm/ftrace.h
index 5f8ebcf..b4970c9 100644
--- a/arch/mips/include/asm/ftrace.h
+++ b/arch/mips/include/asm/ftrace.h
@@ -19,6 +19,16 @@
extern void _mcount(void);
#define mcount _mcount
+#ifdef CONFIG_DYNAMIC_FTRACE
+/* reloction of mcount call site is the same as the address */
+static inline unsigned long ftrace_call_adjust(unsigned long addr)
+{
+ return addr;
+}
+
+struct dyn_arch_ftrace {
+};
+#endif /* CONFIG_DYNAMIC_FTRACE */
#endif /* __ASSEMBLY__ */
#endif /* CONFIG_FUNCTION_TRACER */
#endif /* _ASM_MIPS_FTRACE_H */
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index 559a820..8dabcc6 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -10,6 +10,7 @@ obj-y += cpu-probe.o branch.o entry.o genex.o irq.o process.o \
ifdef CONFIG_FUNCTION_TRACER
# Do not profile debug and lowlevel utilities
+CFLAGS_REMOVE_ftrace.o = -pg
CFLAGS_REMOVE_early_printk.o = -pg
endif
@@ -30,6 +31,7 @@ obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_MODULES) += mips_ksyms.o module.o
obj-$(CONFIG_FUNCTION_TRACER) += mcount.o
+obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o
obj-$(CONFIG_CPU_LOONGSON2) += r4k_fpu.o r4k_switch.o
obj-$(CONFIG_CPU_MIPS32) += r4k_fpu.o r4k_switch.o
diff --git a/arch/mips/kernel/ftrace.c b/arch/mips/kernel/ftrace.c
new file mode 100644
index 0000000..ad490cc
--- /dev/null
+++ b/arch/mips/kernel/ftrace.c
@@ -0,0 +1,207 @@
+/*
+ * Code for replacing ftrace calls with jumps.
+ *
+ * Copyright (C) 2007-2008 Steven Rostedt <[email protected]>
+ * Copyright (C) 2009 DSLab, Lanzhou University, China
+ * Author: Wu Zhangjin <[email protected]>
+ *
+ * Thanks goes to Steven Rostedt for writing the original x86 version.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/hardirq.h>
+#include <linux/uaccess.h>
+#include <linux/percpu.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/ftrace.h>
+
+#include <asm/cacheflush.h>
+#include <asm/ftrace.h>
+#include <asm/asm.h>
+#include <asm/unistd.h>
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+
+#define JAL 0x0c000000 /* jump & link: ip --> ra, jump to target */
+#define ADDR_MASK 0x03ffffff /* op_code|addr : 31...26|25 ....0 */
+
+static unsigned int ftrace_nop = 0x00000000;
+
+static unsigned char *ftrace_call_replace(unsigned long op_code,
+ unsigned long addr)
+{
+ static unsigned int op;
+
+ op = op_code | ((addr >> 2) & ADDR_MASK);
+
+ return (unsigned char *) &op;
+}
+
+static atomic_t nmi_running = ATOMIC_INIT(0);
+static int mod_code_status; /* holds return value of text write */
+static int mod_code_write; /* set when NMI should do the write */
+static void *mod_code_ip; /* holds the IP to write to */
+static void *mod_code_newcode; /* holds the text to write to the IP */
+
+static unsigned nmi_wait_count;
+static atomic_t nmi_update_count = ATOMIC_INIT(0);
+
+int ftrace_arch_read_dyn_info(char *buf, int size)
+{
+ int r;
+
+ r = snprintf(buf, size, "%u %u",
+ nmi_wait_count, atomic_read(&nmi_update_count));
+ return r;
+}
+
+static void ftrace_mod_code(void)
+{
+ /*
+ * Yes, more than one CPU process can be writing to mod_code_status.
+ * (and the code itself)
+ * But if one were to fail, then they all should, and if one were
+ * to succeed, then they all should.
+ */
+ mod_code_status = probe_kernel_write(mod_code_ip, mod_code_newcode,
+ MCOUNT_INSN_SIZE);
+
+ /* if we fail, then kill any new writers */
+ if (mod_code_status)
+ mod_code_write = 0;
+}
+
+void ftrace_nmi_enter(void)
+{
+ atomic_inc(&nmi_running);
+ /* Must have nmi_running seen before reading write flag */
+ smp_mb();
+ if (mod_code_write) {
+ ftrace_mod_code();
+ atomic_inc(&nmi_update_count);
+ }
+}
+
+void ftrace_nmi_exit(void)
+{
+ /* Finish all executions before clearing nmi_running */
+ smp_wmb();
+ atomic_dec(&nmi_running);
+}
+
+static void wait_for_nmi(void)
+{
+ int waited = 0;
+
+ while (atomic_read(&nmi_running)) {
+ waited = 1;
+ cpu_relax();
+ }
+
+ if (waited)
+ nmi_wait_count++;
+}
+
+static int do_ftrace_mod_code(unsigned long ip, void *new_code)
+{
+ mod_code_ip = (void *) ip;
+ mod_code_newcode = new_code;
+
+ /* The buffers need to be visible before we let NMIs write them */
+ smp_wmb();
+
+ mod_code_write = 1;
+
+ /* Make sure write bit is visible before we wait on NMIs */
+ smp_mb();
+
+ wait_for_nmi();
+
+ /* Make sure all running NMIs have finished before we write the code */
+ smp_mb();
+
+ ftrace_mod_code();
+
+ /* Make sure the write happens before clearing the bit */
+ smp_wmb();
+
+ mod_code_write = 0;
+
+ /* make sure NMIs see the cleared bit */
+ smp_mb();
+
+ wait_for_nmi();
+
+ return mod_code_status;
+}
+
+static unsigned char *ftrace_nop_replace(void)
+{
+ return (unsigned char *) &ftrace_nop;
+}
+
+static int
+ftrace_modify_code(unsigned long ip, unsigned char *old_code,
+ unsigned char *new_code)
+{
+ unsigned char replaced[MCOUNT_INSN_SIZE];
+
+ /* read the text we want to modify */
+ if (probe_kernel_read(replaced, (void *) ip, MCOUNT_INSN_SIZE))
+ return -EFAULT;
+
+ /* Make sure it is what we expect it to be */
+ if (memcmp(replaced, old_code, MCOUNT_INSN_SIZE) != 0)
+ return -EINVAL;
+
+ /* replace the text with the new text */
+ if (do_ftrace_mod_code(ip, new_code))
+ return -EPERM;
+
+ return 0;
+}
+
+int ftrace_make_nop(struct module *mod,
+ struct dyn_ftrace *rec, unsigned long addr)
+{
+ unsigned char *new, *old;
+
+ old = ftrace_call_replace(JAL, addr);
+ new = ftrace_nop_replace();
+
+ return ftrace_modify_code(rec->ip, old, new);
+}
+
+int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
+{
+ unsigned char *new, *old;
+
+ old = ftrace_nop_replace();
+ new = ftrace_call_replace(JAL, addr);
+
+ return ftrace_modify_code(rec->ip, old, new);
+}
+
+int ftrace_update_ftrace_func(ftrace_func_t func)
+{
+ unsigned long ip = (unsigned long) (&ftrace_call);
+ unsigned char old[MCOUNT_INSN_SIZE], *new;
+ int ret;
+
+ memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE);
+ new = ftrace_call_replace(JAL, (unsigned long) func);
+ ret = ftrace_modify_code(ip, old, new);
+
+ return ret;
+}
+
+int __init ftrace_dyn_arch_init(void *data)
+{
+ /* The return code is retured via data */
+ *(unsigned long *) data = 0;
+
+ return 0;
+}
+#endif /* CONFIG_DYNAMIC_FTRACE */
diff --git a/arch/mips/kernel/mcount.S b/arch/mips/kernel/mcount.S
index 268724e..723ace2 100644
--- a/arch/mips/kernel/mcount.S
+++ b/arch/mips/kernel/mcount.S
@@ -67,6 +67,35 @@
move ra, $1
.endm
+#ifdef CONFIG_DYNAMIC_FTRACE
+
+LEAF(_mcount)
+ RESTORE_SP_FOR_32BIT
+ RETURN_BACK
+ END(_mcount)
+
+NESTED(ftrace_caller, PT_SIZE, ra)
+ RESTORE_SP_FOR_32BIT
+ lw t0, function_trace_stop
+ bnez t0, ftrace_stub
+ nop
+
+ MCOUNT_SAVE_REGS
+
+ MCOUNT_SET_ARGS
+ .globl ftrace_call
+ftrace_call:
+ jal ftrace_stub
+ nop
+
+ MCOUNT_RESTORE_REGS
+ .globl ftrace_stub
+ftrace_stub:
+ RETURN_BACK
+ END(ftrace_caller)
+
+#else /* ! CONFIG_DYNAMIC_FTRACE */
+
NESTED(_mcount, PT_SIZE, ra)
RESTORE_SP_FOR_32BIT
PTR_L t0, function_trace_stop
@@ -94,5 +123,7 @@ ftrace_stub:
RETURN_BACK
END(_mcount)
+#endif /* ! CONFIG_DYNAMIC_FTRACE */
+
.set at
.set reorder
diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
index f1e3e9c..533d3bf 100755
--- a/scripts/recordmcount.pl
+++ b/scripts/recordmcount.pl
@@ -213,6 +213,26 @@ if ($arch eq "x86_64") {
if ($is_module eq "0") {
$cc .= " -mconstant-gp";
}
+
+} elsif ($arch eq "mips") {
+ $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\s_mcount\$";
+ $objdump .= " -Melf-trad".$endian."mips ";
+
+ if ($endian eq "big") {
+ $endian = " -EB ";
+ $ld .= " -melf".$bits."btsmip";
+ } else {
+ $endian = " -EL ";
+ $ld .= " -melf".$bits."ltsmip";
+ }
+
+ $cc .= " -mno-abicalls -fno-pic -mabi=" . $bits . $endian;
+ $ld .= $endian;
+
+ if ($bits == 64) {
+ $type = ".dword";
+ }
+
} else {
die "Arch $arch is not supported with CONFIG_FTRACE_MCOUNT_RECORD";
}
--
1.6.0.4
From: Wu Zhangjin <[email protected]>
this patch fixed the warning as following:
mipsel-linux-gnu-objcopy: 'fs/proc/.tmp_gl_devices.o': No such file
mipsel-linux-gnu-ld: fs/proc/.tmp_gl_devices.o: No such file: No such
file or directory
rm: cannot remove `fs/proc/.tmp_gl_devices.o': No such file or directory
rm: cannot remove `fs/proc/.tmp_mx_devices.o': No such file or directory
the real reason of above warning is that the $Lxx local functions will
be treated as global symbols, so, should be filtered.
Signed-off-by: Wu Zhangjin <[email protected]>
---
scripts/recordmcount.pl | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
index 533d3bf..542cb04 100755
--- a/scripts/recordmcount.pl
+++ b/scripts/recordmcount.pl
@@ -343,6 +343,10 @@ sub update_funcs
if (!$use_locals) {
return;
}
+ # filter $LXXX tags
+ if ("$ref_func" =~ m/\$L/) {
+ return;
+ }
$convert{$ref_func} = 1;
}
--
1.6.0.4
From: Wu Zhangjin <[email protected]>
this works something like -finstrument-functions does, instead of using
void __cyg_profile_func_enter (void *this_fn,
void *call_site);
void __cyg_profile_func_exit (void *this_fn,
void *call_site);
-pg use _mcount, so some tricks are adoptive by the author of orignal function
graph tracer:
the _mcount function will call prepare_function_return to save the
parent_ip, ip and calltime in a tracing array, if success, the
address of a hooker function &return_to_handler will be substitued
to the parent_ip, so, after return from _mcount it will call the
&return_to_handler, not back to the parent_ip, but calling
ftrace_return_to_handler to remember the rettime, and return the
parent_ip to let &return_to_handler go back to the real parent.
Reviewed-by: Steven Rostedt <[email protected]>
Signed-off-by: Wu Zhangjin <[email protected]>
---
arch/mips/Kconfig | 1 +
arch/mips/kernel/ftrace.c | 66 ++++++++++++++++++++++++++++++++++++++++
arch/mips/kernel/mcount.S | 61 +++++++++++++++++++++++++++++++++++--
arch/mips/kernel/vmlinux.lds.S | 1 +
4 files changed, 126 insertions(+), 3 deletions(-)
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 0857239..5ac9f45 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -9,6 +9,7 @@ config MIPS
select HAVE_DYNAMIC_FTRACE
select HAVE_FTRACE_MCOUNT_RECORD
select HAVE_FTRACE_NMI_ENTER if DYNAMIC_FTRACE
+ select HAVE_FUNCTION_GRAPH_TRACER
# Horrible source of confusion. Die, die, die ...
select EMBEDDED
select RTC_LIB
diff --git a/arch/mips/kernel/ftrace.c b/arch/mips/kernel/ftrace.c
index ad490cc..65d4d56 100644
--- a/arch/mips/kernel/ftrace.c
+++ b/arch/mips/kernel/ftrace.c
@@ -205,3 +205,69 @@ int __init ftrace_dyn_arch_init(void *data)
return 0;
}
#endif /* CONFIG_DYNAMIC_FTRACE */
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+
+#define JMP 0x08000000 /* jump to target directly */
+extern void ftrace_graph_call(void);
+
+int ftrace_enable_ftrace_graph_caller(void)
+{
+ unsigned long ip = (unsigned long) (&ftrace_graph_call);
+ unsigned char old[MCOUNT_INSN_SIZE], *new;
+ int ret;
+
+ /* j ftrace_stub */
+ memcpy(old, (unsigned long *) ip, MCOUNT_INSN_SIZE);
+ new = ftrace_call_replace(JMP, (unsigned long) ftrace_graph_caller);
+
+ ret = ftrace_modify_code(ip, old, new);
+
+ return ret;
+}
+
+int ftrace_disable_ftrace_graph_caller(void)
+{
+ unsigned long ip = (unsigned long) (&ftrace_graph_call);
+ unsigned char old[MCOUNT_INSN_SIZE], *new;
+ int ret;
+
+ /* j ftrace_graph_caller */
+ memcpy(old, (unsigned long *) ip, MCOUNT_INSN_SIZE);
+ new = ftrace_call_replace(JMP, (unsigned long) ftrace_stub);
+
+ ret = ftrace_modify_code(ip, old, new);
+
+ return ret;
+}
+
+#endif /* !CONFIG_DYNAMIC_FTRACE */
+
+/*
+ * Hook the return address and push it in the stack of return addrs
+ * in current thread info.
+ */
+
+unsigned long prepare_ftrace_return(unsigned long ip,
+ unsigned long parent_ip)
+{
+ struct ftrace_graph_ent trace;
+
+ /* Nmi's are currently unsupported */
+ if (unlikely(in_nmi()) ||
+ unlikely(atomic_read(¤t->tracing_graph_pause)) ||
+ (ftrace_push_return_trace(parent_ip, ip, &trace.depth) == -EBUSY))
+ return parent_ip;
+
+ trace.func = ip;
+
+ /* Only trace if the calling function expects to */
+ if (ftrace_graph_entry(&trace))
+ return (unsigned long) &return_to_handler;
+
+ current->curr_ret_stack--;
+ return parent_ip;
+}
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
diff --git a/arch/mips/kernel/mcount.S b/arch/mips/kernel/mcount.S
index 723ace2..559f9bd 100644
--- a/arch/mips/kernel/mcount.S
+++ b/arch/mips/kernel/mcount.S
@@ -27,7 +27,6 @@
.macro MCOUNT_SAVE_REGS
PTR_SUBU sp, PT_SIZE
PTR_S ra, PT_R31(sp)
- PTR_S $1, PT_R1(sp)
PTR_S a0, PT_R4(sp)
PTR_S a1, PT_R5(sp)
PTR_S a2, PT_R6(sp)
@@ -42,7 +41,6 @@
.macro MCOUNT_RESTORE_REGS
PTR_L ra, PT_R31(sp)
- PTR_L $1, PT_R1(sp)
PTR_L a0, PT_R4(sp)
PTR_L a1, PT_R5(sp)
PTR_L a2, PT_R6(sp)
@@ -81,6 +79,7 @@ NESTED(ftrace_caller, PT_SIZE, ra)
nop
MCOUNT_SAVE_REGS
+ PTR_S $1, PT_R1(sp)
MCOUNT_SET_ARGS
.globl ftrace_call
@@ -88,7 +87,16 @@ ftrace_call:
jal ftrace_stub
nop
+ PTR_L $1, PT_R1(sp)
MCOUNT_RESTORE_REGS
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ .globl ftrace_graph_call
+ftrace_graph_call:
+ j ftrace_stub
+ nop
+#endif
+
.globl ftrace_stub
ftrace_stub:
RETURN_BACK
@@ -106,17 +114,27 @@ NESTED(_mcount, PT_SIZE, ra)
PTR_L t1, ftrace_trace_function /* please don't use t1 later, safe? */
bne t0, t1, static_trace
nop
-
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ PTR_L t2, ftrace_graph_return
+ bne t0, t2, ftrace_graph_caller
+ nop
+ PTR_LA t0, ftrace_graph_entry_stub
+ PTR_L t2, ftrace_graph_entry
+ bne t0, t2, ftrace_graph_caller
+ nop
+#endif
j ftrace_stub
nop
static_trace:
MCOUNT_SAVE_REGS
+ PTR_S $1, PT_R1(sp)
MCOUNT_SET_ARGS /* call *ftrace_trace_function */
jalr t1
nop
+ PTR_L $1, PT_R1(sp)
MCOUNT_RESTORE_REGS
.globl ftrace_stub
ftrace_stub:
@@ -125,5 +143,42 @@ ftrace_stub:
#endif /* ! CONFIG_DYNAMIC_FTRACE */
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+
+NESTED(ftrace_graph_caller, PT_SIZE, ra)
+ MCOUNT_SAVE_REGS
+ PTR_S v0, PT_R2(sp)
+
+ MCOUNT_SET_ARGS
+ jal prepare_ftrace_return
+ nop
+
+ /* overwrite the parent as &return_to_handler: v0 -> $1(at) */
+ move $1, v0
+
+ PTR_L v0, PT_R2(sp)
+ MCOUNT_RESTORE_REGS
+ RETURN_BACK
+ END(ftrace_graph_caller)
+
+ .align 2
+ .globl return_to_handler
+return_to_handler:
+ PTR_SUBU sp, PT_SIZE
+ PTR_S v0, PT_R2(sp)
+
+ jal ftrace_return_to_handler
+ nop
+
+ /* restore the real parent address: v0 -> ra */
+ move ra, v0
+
+ PTR_L v0, PT_R2(sp)
+ PTR_ADDIU sp, PT_SIZE
+
+ jr ra
+
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
+
.set at
.set reorder
diff --git a/arch/mips/kernel/vmlinux.lds.S b/arch/mips/kernel/vmlinux.lds.S
index 58738c8..67435e5 100644
--- a/arch/mips/kernel/vmlinux.lds.S
+++ b/arch/mips/kernel/vmlinux.lds.S
@@ -36,6 +36,7 @@ SECTIONS
SCHED_TEXT
LOCK_TEXT
KPROBES_TEXT
+ IRQENTRY_TEXT
*(.text.*)
*(.fixup)
*(.gnu.warning)
--
1.6.0.4
From: Wu Zhangjin <[email protected]>
by default, trace_clock_local calling sched_clock(jiffies-based) to get
timestamp, in x86, there is a tsc(64bit) based sched_clock, but in mips,
the 'tsc'(clock counter) is only 32bit long, which will easily rollover,
and there is no existing high precise sched_clock in mips, we need to
get one ourselves.
to avoid invading the whole linux-mips, i do not want to implement a
tsc-based native_sched_clock instead of sched_clock like x86 does.
because, there is a need to handling rollover of the only 32-bit long
'tsc' of mips, which will need extra overhead. in reality, i have tried
to do it, but made the kernel hangs when booting, I'm not sure why it
not work.
so, I just implement a native_sched_clock in arch/mips/kernel/ftrace.c,
but not override the original sched_clock(). to get high precise
timestamp, we implement a native_trace_clock_local, which will not call
original sched_clock again, but native_sched_clock().
and what about the trace_clock and trace_clock_global function, should
we also implement a mips-secific one? I'm not sure.
Reviewed-by: Steven Rostedt <[email protected]>
Signed-off-by: Wu Zhangjin <[email protected]>
---
arch/mips/kernel/Makefile | 2 +
arch/mips/kernel/csrc-r4k.c | 2 +-
arch/mips/kernel/ftrace_clock.c | 71 +++++++++++++++++++++++++++++++++++++++
kernel/trace/trace_clock.c | 2 +-
4 files changed, 75 insertions(+), 2 deletions(-)
create mode 100644 arch/mips/kernel/ftrace_clock.c
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index 8dabcc6..44ec7e0 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -11,6 +11,7 @@ obj-y += cpu-probe.o branch.o entry.o genex.o irq.o process.o \
ifdef CONFIG_FUNCTION_TRACER
# Do not profile debug and lowlevel utilities
CFLAGS_REMOVE_ftrace.o = -pg
+CFLAGS_REMOVE_ftrace_clock.o = -pg
CFLAGS_REMOVE_early_printk.o = -pg
endif
@@ -32,6 +33,7 @@ obj-$(CONFIG_MODULES) += mips_ksyms.o module.o
obj-$(CONFIG_FUNCTION_TRACER) += mcount.o
obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o
+obj-$(CONFIG_NOP_TRACER) += ftrace_clock.o
obj-$(CONFIG_CPU_LOONGSON2) += r4k_fpu.o r4k_switch.o
obj-$(CONFIG_CPU_MIPS32) += r4k_fpu.o r4k_switch.o
diff --git a/arch/mips/kernel/csrc-r4k.c b/arch/mips/kernel/csrc-r4k.c
index e95a3cd..3da1c7a 100644
--- a/arch/mips/kernel/csrc-r4k.c
+++ b/arch/mips/kernel/csrc-r4k.c
@@ -10,7 +10,7 @@
#include <asm/time.h>
-static cycle_t c0_hpt_read(struct clocksource *cs)
+static cycle_t notrace c0_hpt_read(struct clocksource *cs)
{
return read_c0_count();
}
diff --git a/arch/mips/kernel/ftrace_clock.c b/arch/mips/kernel/ftrace_clock.c
new file mode 100644
index 0000000..8ad896e
--- /dev/null
+++ b/arch/mips/kernel/ftrace_clock.c
@@ -0,0 +1,71 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive for
+ * more details.
+ *
+ * Copyright (C) 2009 DSLab, Lanzhou University, China
+ * Author: Wu Zhangjin <[email protected]>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
+#include <linux/clocksource.h>
+
+/*
+ * mips-specific high precise sched_clock() implementation,
+ *
+ * currently, this is only needed in ftrace, so not override the original
+ * sched_clock().
+ */
+
+unsigned long long native_sched_clock(void)
+{
+ u64 current_cycles;
+ static unsigned long old_jiffies;
+ static u64 time, old_cycles;
+
+ preempt_disable_notrace();
+ /* update timestamp to avoid missing the timer interrupt */
+ if (time_before(jiffies, old_jiffies)) {
+ old_jiffies = jiffies;
+ time = sched_clock();
+ old_cycles = clock->cycle_last;
+ }
+ current_cycles = clock->read(clock);
+
+ time = (time + cyc2ns(clock, (current_cycles - old_cycles)
+ & clock->mask));
+
+ old_cycles = current_cycles;
+ preempt_enable_no_resched_notrace();
+
+ return time;
+}
+
+/*
+ * native_trace_clock_local(): the simplest and least coherent tracing clock.
+ *
+ * Useful for tracing that does not cross to other CPUs nor
+ * does it go through idle events.
+ */
+u64 native_trace_clock_local(void)
+{
+ unsigned long flags;
+ u64 clock;
+
+ /*
+ * herein, we use the above native_sched_clock() to get high precise
+ * timestamp, because the original sched_clock in mips is jiffies based,
+ * which not have enough precision.
+ */
+ raw_local_irq_save(flags);
+ clock = native_sched_clock();
+ raw_local_irq_restore(flags);
+
+ return clock;
+}
+
+u64 trace_clock_local(void)
+ __attribute__((alias("native_trace_clock_local")));
+
diff --git a/kernel/trace/trace_clock.c b/kernel/trace/trace_clock.c
index b588fd8..78c98c8 100644
--- a/kernel/trace/trace_clock.c
+++ b/kernel/trace/trace_clock.c
@@ -26,7 +26,7 @@
* Useful for tracing that does not cross to other CPUs nor
* does it go through idle events.
*/
-u64 notrace trace_clock_local(void)
+u64 __attribute__((weak)) notrace trace_clock_local(void)
{
unsigned long flags;
u64 clock;
--
1.6.0.4
From: Wu Zhangjin <[email protected]>
FIXME: there are several different sys_call_entry in mips64, but
currently, i only use the the one in arch/mips/kernel/scall64-o32.S
so,if people not use o32 standard, it will not compiled normally.
the system call tracing demo in a debian system on
qemu-system-mipsel/malta:
debian-mips-malta:~# mount -t debugfs nodev /debug
debian-mips-malta:~# echo 20000 > /debug/tracing/buffer_size_kb
debian-mips-malta:~# cat /debug/tracing/available_tracers
syscall nop
debian-mips-malta:~# echo syscall > /debug/tracing/current_tracer
debian-mips-malta:~# echo 1 > /debug/tracing/tracing_enabled
debian-mips-malta:~# sleep 1
debian-mips-malta:~# echo 0 > /debug/tracing/tracing_enabled
debian-mips-malta:~# cat /debug/tracing/trace | head -20
<...>-533 [000] 60.458291: sys_write(fd: 1, buf: 4fc408, count: 8)
<...>-533 [000] 64.325614: sys_getrlimit(resource: 3, rlim: 530020)
<...>-533 [000] 64.327089: sys_read(fd: 2, buf: 4fc008, count: 6)
<...>-533 [000] 64.969663: sys_exit(error_code: 2)
<...>-533 [000] 65.608794: sys_exit(error_code: 2)
<...>-533 [000] 66.231796: sys_read(fd: 2, buf: 4fc008, count: 6)
<...>-533 [000] 66.913687: sys_open(filename: 1, flags: 0, mode: a)
<...>-533 [000] 66.914617: sys_exit(error_code: 1)
<...>-533 [000] 70.797507: sys_exit(error_code: 503be8)
<...>-536 [000] 70.833108: sys_exit(error_code: 2aac6cfc)
<...>-536 [000] 70.833897: sys_exit(error_code: 2aac6540)
<...>-536 [000] 70.835711: sys_exit(error_code: 2aac6cfc)
<...>-536 [000] 70.840609: sys_lchown(filename: 3, user: 7fb08b38, group: 20)
<...>-533 [000] 71.877785: sys_open(filename: ffffffff, flags: 7fcf08c8, mode: b)
<...>-533 [000] 75.531122: sys_open(filename: 1, flags: 0, mode: a)
Signed-off-by: Wu Zhangjin <[email protected]>
---
arch/mips/Kconfig | 1 +
arch/mips/include/asm/ptrace.h | 2 +
arch/mips/include/asm/reg.h | 5 ++
arch/mips/include/asm/syscall.h | 84 +++++++++++++++++++++++++++++++++++
arch/mips/include/asm/thread_info.h | 5 ++-
arch/mips/kernel/Makefile | 1 +
arch/mips/kernel/entry.S | 2 +-
arch/mips/kernel/ftrace.c | 71 +++++++++++++++++++++++++++++
arch/mips/kernel/ptrace.c | 14 +++++-
arch/mips/kernel/scall64-o32.S | 2 +-
10 files changed, 182 insertions(+), 5 deletions(-)
create mode 100644 arch/mips/include/asm/syscall.h
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 5ac9f45..a4a5af5 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -10,6 +10,7 @@ config MIPS
select HAVE_FTRACE_MCOUNT_RECORD
select HAVE_FTRACE_NMI_ENTER if DYNAMIC_FTRACE
select HAVE_FUNCTION_GRAPH_TRACER
+ select HAVE_FTRACE_SYSCALLS
# Horrible source of confusion. Die, die, die ...
select EMBEDDED
select RTC_LIB
diff --git a/arch/mips/include/asm/ptrace.h b/arch/mips/include/asm/ptrace.h
index ce47118..32e5b62 100644
--- a/arch/mips/include/asm/ptrace.h
+++ b/arch/mips/include/asm/ptrace.h
@@ -45,6 +45,8 @@ struct pt_regs {
unsigned long cp0_badvaddr;
unsigned long cp0_cause;
unsigned long cp0_epc;
+ /* Used for restarting system calls */
+ unsigned long orig_v0;
#ifdef CONFIG_MIPS_MT_SMTC
unsigned long cp0_tcstatus;
#endif /* CONFIG_MIPS_MT_SMTC */
diff --git a/arch/mips/include/asm/reg.h b/arch/mips/include/asm/reg.h
index 634b55d..93d66bc 100644
--- a/arch/mips/include/asm/reg.h
+++ b/arch/mips/include/asm/reg.h
@@ -65,6 +65,8 @@
#define EF_CP0_CAUSE 43
#define EF_UNUSED0 44
+#define EF_ORIG_V0 45
+
#define EF_SIZE 180
#endif
@@ -121,6 +123,9 @@
#define EF_CP0_STATUS 36
#define EF_CP0_CAUSE 37
+
+#define EF_ORIG_V0 38
+
#define EF_SIZE 304 /* size in bytes */
#endif /* CONFIG_64BIT */
diff --git a/arch/mips/include/asm/syscall.h b/arch/mips/include/asm/syscall.h
new file mode 100644
index 0000000..b785098
--- /dev/null
+++ b/arch/mips/include/asm/syscall.h
@@ -0,0 +1,84 @@
+/*
+ * Access to user system call parameters and results
+ *
+ * Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009 DSLab, Lanzhou University, China
+ * Author: Wu Zhangjin <[email protected]>
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * See asm-generic/syscall.h for descriptions of what we must do here.
+ */
+
+#ifndef _ASM_SYSCALL_H
+#define _ASM_SYSCALL_H 1
+
+#include <linux/sched.h>
+
+static inline long syscall_get_nr(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ /* syscall Exc-Code: 0 1000 00 v0 */
+ return ((regs->cp0_cause&0xff) == 0x20) ? regs->regs[2] : -1L;
+}
+
+static inline void syscall_rollback(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ regs->regs[2] = regs->orig_v0;
+}
+
+static inline long syscall_get_error(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ return regs->regs[2] ? -regs->regs[2] : 0;
+}
+
+static inline long syscall_get_return_value(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ return regs->regs[2];
+}
+
+static inline void syscall_set_return_value(struct task_struct *task,
+ struct pt_regs *regs,
+ int error, long val)
+{
+ if (error)
+ regs->regs[2] = -error;
+ else
+ regs->regs[2] = val;
+}
+
+static inline void syscall_get_arguments(struct task_struct *task,
+ struct pt_regs *regs,
+ unsigned int i, unsigned int n,
+ unsigned long *args)
+{
+#ifdef CONFIG_32BIT
+ /* fixme: only 4 argument register available in mip32, so, how to handle
+ * others?
+ */
+ BUG_ON(i + n > 4);
+#else
+ BUG_ON(i + n > 6);
+#endif
+ memcpy(args, ®s->regs[4 + i], n * sizeof(args[0]));
+}
+
+static inline void syscall_set_arguments(struct task_struct *task,
+ struct pt_regs *regs,
+ unsigned int i, unsigned int n,
+ const unsigned long *args)
+{
+#ifdef CONFIG_32BIT
+ BUG_ON(i + n > 4);
+#else
+ BUG_ON(i + n > 6);
+#endif
+ memcpy(®s->regs[4 + i], args, n * sizeof(args[0]));
+}
+
+#endif /* _ASM_SYSCALL_H */
diff --git a/arch/mips/include/asm/thread_info.h b/arch/mips/include/asm/thread_info.h
index 143a481..1d55dc0 100644
--- a/arch/mips/include/asm/thread_info.h
+++ b/arch/mips/include/asm/thread_info.h
@@ -128,6 +128,7 @@ register struct thread_info *__current_thread_info __asm__("$28");
#define TIF_32BIT_ADDR 23 /* 32-bit address space (o32/n32) */
#define TIF_FPUBOUND 24 /* thread bound to FPU-full CPU set */
#define TIF_LOAD_WATCH 25 /* If set, load watch registers */
+#define TIF_SYSCALL_FTRACE 27 /* for ftrace syscall instrumentation */
#define TIF_SYSCALL_TRACE 31 /* syscall trace active */
#ifdef CONFIG_MIPS32_O32
@@ -151,11 +152,13 @@ register struct thread_info *__current_thread_info __asm__("$28");
#define _TIF_32BIT_ADDR (1<<TIF_32BIT_ADDR)
#define _TIF_FPUBOUND (1<<TIF_FPUBOUND)
#define _TIF_LOAD_WATCH (1<<TIF_LOAD_WATCH)
+#define _TIF_SYSCALL_FTRACE (1<<TIF_SYSCALL_FTRACE)
/* work to do on interrupt/exception return */
#define _TIF_WORK_MASK (0x0000ffef & ~_TIF_SECCOMP)
/* work to do on any return to u-space */
-#define _TIF_ALLWORK_MASK (0x8000ffff & ~_TIF_SECCOMP)
+#define _TIF_ALLWORK_MASK \
+ ((0x8000ffff & ~_TIF_SECCOMP) | _TIF_SYSCALL_FTRACE)
#endif /* __KERNEL__ */
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index 44ec7e0..1114c85 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_MODULES) += mips_ksyms.o module.o
obj-$(CONFIG_FUNCTION_TRACER) += mcount.o
obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o
+obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o
obj-$(CONFIG_NOP_TRACER) += ftrace_clock.o
obj-$(CONFIG_CPU_LOONGSON2) += r4k_fpu.o r4k_switch.o
diff --git a/arch/mips/kernel/entry.S b/arch/mips/kernel/entry.S
index ffa3310..786e4ef 100644
--- a/arch/mips/kernel/entry.S
+++ b/arch/mips/kernel/entry.S
@@ -167,7 +167,7 @@ work_notifysig: # deal with pending signals and
FEXPORT(syscall_exit_work_partial)
SAVE_STATIC
syscall_exit_work:
- li t0, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT
+ li t0, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SYSCALL_FTRACE
and t0, a2 # a2 is preloaded with TI_FLAGS
beqz t0, work_pending # trace bit set?
local_irq_enable # could let do_syscall_trace()
diff --git a/arch/mips/kernel/ftrace.c b/arch/mips/kernel/ftrace.c
index 65d4d56..c83b586 100644
--- a/arch/mips/kernel/ftrace.c
+++ b/arch/mips/kernel/ftrace.c
@@ -22,6 +22,8 @@
#include <asm/asm.h>
#include <asm/unistd.h>
+#include <trace/syscall.h>
+
#ifdef CONFIG_DYNAMIC_FTRACE
#define JAL 0x0c000000 /* jump & link: ip --> ra, jump to target */
@@ -271,3 +273,72 @@ unsigned long prepare_ftrace_return(unsigned long ip,
return parent_ip;
}
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
+
+#ifdef CONFIG_FTRACE_SYSCALLS
+
+extern unsigned long __start_syscalls_metadata[];
+extern unsigned long __stop_syscalls_metadata[];
+
+/* fixme: in mips64, there are different entries of sys_call_table when using
+ * different standards, in loongson2f based machines: Fuloong & Yeeloong, the
+ * system use o32 standard, so here, we only use the sys_call_table in
+ * arch/mips/kernel/scall64-o32.S */
+
+extern unsigned long *sys_call_table;
+
+static struct syscall_metadata **syscalls_metadata;
+
+static struct syscall_metadata *find_syscall_meta(unsigned long *syscall)
+{
+ struct syscall_metadata *start;
+ struct syscall_metadata *stop;
+ char str[KSYM_SYMBOL_LEN];
+
+
+ start = (struct syscall_metadata *)__start_syscalls_metadata;
+ stop = (struct syscall_metadata *)__stop_syscalls_metadata;
+ kallsyms_lookup((unsigned long) syscall, NULL, NULL, NULL, str);
+
+ for ( ; start < stop; start++) {
+ if (start->name && !strcmp(start->name, str))
+ return start;
+ }
+ return NULL;
+}
+
+struct syscall_metadata *syscall_nr_to_meta(int nr)
+{
+ if (!syscalls_metadata || nr >= __NR_Linux_syscalls || nr < 0)
+ return NULL;
+
+ return syscalls_metadata[nr];
+}
+
+void arch_init_ftrace_syscalls(void)
+{
+ int i;
+ struct syscall_metadata *meta;
+ unsigned long **psys_syscall_table = &sys_call_table;
+ static atomic_t refs;
+
+ if (atomic_inc_return(&refs) != 1)
+ goto end;
+
+ syscalls_metadata = kzalloc(sizeof(*syscalls_metadata) *
+ __NR_Linux_syscalls, GFP_KERNEL);
+ if (!syscalls_metadata) {
+ WARN_ON(1);
+ return;
+ }
+
+ for (i = 0; i < __NR_Linux_syscalls; i++) {
+ meta = find_syscall_meta(psys_syscall_table[i]);
+ syscalls_metadata[i] = meta;
+ }
+ return;
+
+ /* Paranoid: avoid overflow */
+end:
+ atomic_dec(&refs);
+}
+#endif /* CONFIG_FTRACE_SYSCALLS */
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c
index 054861c..fa762dc 100644
--- a/arch/mips/kernel/ptrace.c
+++ b/arch/mips/kernel/ptrace.c
@@ -25,6 +25,7 @@
#include <linux/security.h>
#include <linux/audit.h>
#include <linux/seccomp.h>
+#include <linux/ftrace.h>
#include <asm/byteorder.h>
#include <asm/cpu.h>
@@ -39,6 +40,7 @@
#include <asm/bootinfo.h>
#include <asm/reg.h>
+#include <trace/syscall.h>
/*
* Called by kernel/ptrace.c when detaching..
*
@@ -60,7 +62,7 @@ int ptrace_getregs(struct task_struct *child, __s64 __user *data)
struct pt_regs *regs;
int i;
- if (!access_ok(VERIFY_WRITE, data, 38 * 8))
+ if (!access_ok(VERIFY_WRITE, data, 39 * 8))
return -EIO;
regs = task_pt_regs(child);
@@ -73,6 +75,7 @@ int ptrace_getregs(struct task_struct *child, __s64 __user *data)
__put_user((long)regs->cp0_badvaddr, data + EF_CP0_BADVADDR - EF_R0);
__put_user((long)regs->cp0_status, data + EF_CP0_STATUS - EF_R0);
__put_user((long)regs->cp0_cause, data + EF_CP0_CAUSE - EF_R0);
+ __put_user((long)regs->orig_v0, data + EF_ORIG_V0 - EF_R0);
return 0;
}
@@ -87,7 +90,7 @@ int ptrace_setregs(struct task_struct *child, __s64 __user *data)
struct pt_regs *regs;
int i;
- if (!access_ok(VERIFY_READ, data, 38 * 8))
+ if (!access_ok(VERIFY_READ, data, 39 * 8))
return -EIO;
regs = task_pt_regs(child);
@@ -97,6 +100,7 @@ int ptrace_setregs(struct task_struct *child, __s64 __user *data)
__get_user(regs->lo, data + EF_LO - EF_R0);
__get_user(regs->hi, data + EF_HI - EF_R0);
__get_user(regs->cp0_epc, data + EF_CP0_EPC - EF_R0);
+ __get_user(regs->orig_v0, data + EF_ORIG_V0 - EF_R0);
/* badvaddr, status, and cause may not be written. */
@@ -575,6 +579,9 @@ asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit)
if (!(current->ptrace & PT_PTRACED))
goto out;
+ if (unlikely(test_thread_flag(TIF_SYSCALL_FTRACE)))
+ ftrace_syscall_exit(regs);
+
if (!test_thread_flag(TIF_SYSCALL_TRACE))
goto out;
@@ -594,6 +601,9 @@ asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit)
}
out:
+ if (unlikely(test_thread_flag(TIF_SYSCALL_FTRACE)))
+ ftrace_syscall_enter(regs);
+
if (unlikely(current->audit_context) && !entryexit)
audit_syscall_entry(audit_arch(), regs->regs[0],
regs->regs[4], regs->regs[5],
diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S
index a5598b2..dd1f13a 100644
--- a/arch/mips/kernel/scall64-o32.S
+++ b/arch/mips/kernel/scall64-o32.S
@@ -202,7 +202,7 @@ einval: li v0, -ENOSYS
.align 3
.type sys_call_table,@object
-sys_call_table:
+EXPORT(sys_call_table)
PTR sys32_syscall /* 4000 */
PTR sys_exit
PTR sys_fork
--
1.6.0.4
Wu Zhangjin wrote:
> From: Wu Zhangjin <[email protected]>
>
> this patch fixed the warning as following:
>
> mipsel-linux-gnu-objcopy: 'fs/proc/.tmp_gl_devices.o': No such file
> mipsel-linux-gnu-ld: fs/proc/.tmp_gl_devices.o: No such file: No such
> file or directory
> rm: cannot remove `fs/proc/.tmp_gl_devices.o': No such file or directory
> rm: cannot remove `fs/proc/.tmp_mx_devices.o': No such file or directory
>
> the real reason of above warning is that the $Lxx local functions will
> be treated as global symbols, so, should be filtered.
>
> Signed-off-by: Wu Zhangjin <[email protected]>
> ---
> scripts/recordmcount.pl | 4 ++++
> 1 files changed, 4 insertions(+), 0 deletions(-)
>
> diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
> index 533d3bf..542cb04 100755
> --- a/scripts/recordmcount.pl
> +++ b/scripts/recordmcount.pl
> @@ -343,6 +343,10 @@ sub update_funcs
> if (!$use_locals) {
> return;
> }
> + # filter $LXXX tags
> + if ("$ref_func" =~ m/\$L/) {
> + return;
> + }
Certainly this is true for mips. I doubt it is for all architectures
targed by Linux.
David Daney
On Mon, 15 Jun 2009, David Daney wrote:
> Wu Zhangjin wrote:
> > From: Wu Zhangjin <[email protected]>
> >
> > this patch fixed the warning as following:
> >
> > mipsel-linux-gnu-objcopy: 'fs/proc/.tmp_gl_devices.o': No such file
> > mipsel-linux-gnu-ld: fs/proc/.tmp_gl_devices.o: No such file: No such
> > file or directory
> > rm: cannot remove `fs/proc/.tmp_gl_devices.o': No such file or directory
> > rm: cannot remove `fs/proc/.tmp_mx_devices.o': No such file or directory
> >
> > the real reason of above warning is that the $Lxx local functions will
> > be treated as global symbols, so, should be filtered.
> >
> > Signed-off-by: Wu Zhangjin <[email protected]>
> > ---
> > scripts/recordmcount.pl | 4 ++++
> > 1 files changed, 4 insertions(+), 0 deletions(-)
> >
> > diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
> > index 533d3bf..542cb04 100755
> > --- a/scripts/recordmcount.pl
> > +++ b/scripts/recordmcount.pl
> > @@ -343,6 +343,10 @@ sub update_funcs
> > if (!$use_locals) {
> > return;
> > }
> > + # filter $LXXX tags
> > + if ("$ref_func" =~ m/\$L/) {
> > + return;
> > + }
>
> Certainly this is true for mips. I doubt it is for all architectures targed
> by Linux.
Yes, that should probably go into a mips only change. Unless you can
reproduce it on all other archs, or at least x86.
You could also do this in the function_regex variable.
"^([0-9a-fA-F]+)\\s+<(.|[^\$]L.*?|\$[^L].*?|[^\$][^L].*?)>:"
There may even be a better way, but I'm not in the mood to look it up ;-)
-- Steve
On Mon, 2009-06-15 at 12:30 -0400, Steven Rostedt wrote:
> On Mon, 15 Jun 2009, David Daney wrote:
>
> > Wu Zhangjin wrote:
> > > From: Wu Zhangjin <[email protected]>
> > >
> > > this patch fixed the warning as following:
> > >
> > > mipsel-linux-gnu-objcopy: 'fs/proc/.tmp_gl_devices.o': No such file
> > > mipsel-linux-gnu-ld: fs/proc/.tmp_gl_devices.o: No such file: No such
> > > file or directory
> > > rm: cannot remove `fs/proc/.tmp_gl_devices.o': No such file or directory
> > > rm: cannot remove `fs/proc/.tmp_mx_devices.o': No such file or directory
> > >
> > > the real reason of above warning is that the $Lxx local functions will
> > > be treated as global symbols, so, should be filtered.
> > >
> > > Signed-off-by: Wu Zhangjin <[email protected]>
> > > ---
> > > scripts/recordmcount.pl | 4 ++++
> > > 1 files changed, 4 insertions(+), 0 deletions(-)
> > >
> > > diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
> > > index 533d3bf..542cb04 100755
> > > --- a/scripts/recordmcount.pl
> > > +++ b/scripts/recordmcount.pl
> > > @@ -343,6 +343,10 @@ sub update_funcs
> > > if (!$use_locals) {
> > > return;
> > > }
> > > + # filter $LXXX tags
> > > + if ("$ref_func" =~ m/\$L/) {
> > > + return;
> > > + }
> >
> > Certainly this is true for mips. I doubt it is for all architectures targed
> > by Linux.
>
have tried to use function_regex instead, but for I'm poor in playing
with regular expression of perl. at last, i use something easier like
above. and I _guess_ this problem maybe exist in some other platforms,
so just put it there.
> Yes, that should probably go into a mips only change. Unless you can
> reproduce it on all other archs, or at least x86.
>
> You could also do this in the function_regex variable.
>
> "^([0-9a-fA-F]+)\\s+<(.|[^\$]L.*?|\$[^L].*?|[^\$][^L].*?)>:"
>
works well, and this seems mips64-specific, so, I moved this specific
function_regex to "if ($bits == 64) { ... }".
> There may even be a better way, but I'm not in the mood to look it up ;-)
seems not that easy to understand :-)
-- Wu Zhangjin