Add low level support for ftrace return tracing
RFC PATCH v3 1/2 Add low level support for ftrace return tracing
Add low level support for ftrace return tracing.
This plug-in stores return addresses on the thread_info structure of
the current task.
The index of the current return address is initialized when the task is the first one (init)
and when a process forks (the child). It is not needed when a task does a sys_execve because after this syscall, it still needs to return on the kernel functions it called. Note that the code of return_to_handler has been suggested by Steven Rostedt as almost
all of the ideas of improvements in this V3.
For purpose of security, arch/x86/kernel/process_32.c is not traced because __switch_to() changes the
current task during its execution. That could cause inconsistency in the stored return address of this
function even if I didn't have any crash after testing with tracing on this function enabled.
---
arch/x86/Kconfig +1 -0
@@ -11,6 +11,7 @@ config 64BIT config X86_32 def_bool !64BIT + select HAVE_FTRACE_RETURN config X86_64 def_bool 64BIT
arch/x86/include/asm/ftrace.h +26 -0
@@ -20,4 +20,30 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr)#endif /* __ASSEMBLY__ */ #endif /* CONFIG_FUNCTION_TRACER */ +#ifdef CONFIG_FTRACE_RETURN +#define FTRACE_RET_STACK_SIZE 20 + +#ifndef __ASSEMBLY__ + +/* +* Stack of return addresses for functions +* of a thread. +* Used in struct thread_info +*/ +struct ftrace_ret_stack { +unsigned long ret; +unsigned long func; +unsigned long long calltime; +}; + +/* +* Primary handler of a function return. +* It relays on ftrace_return_to_handler. +* Defined in entry32.S +*/ +extern void return_to_handler (void ); + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_FTRACE_RETURN */ +#endif /* _ASM_X86_FTRACE_H */
arch/x86/include/asm/thread_info.h +24 -0
@@ -20,6 +20,7 @@struct task_struct ;struct exec_domain ;#include <asm/processor.h> +#include <asm/ftrace.h> struct thread_info {struct task_struct *task;/* main task structure */
@@ -38,8 +39,30 @@ struct thread_info {*/ __u8 supervisor_stack[0 ];#endif + +#ifdef CONFIG_FTRACE_RETURN +/* Index of current stored adress in ret_stack */ +int curr_ret_stack; +/* Stack of return addresses for return function tracing */ +struct ftrace_ret_stack ret_stack[FTRACE_RET_STACK_SIZE ]; +#endif }; +#ifdef CONFIG_FTRACE_RETURN +#define INIT_THREAD_INFO (tsk) \ +{ \ + .task = &tsk, \ + .exec_domain = &default_exec_domain, \ + .flags = 0, \ + .cpu = 0, \ + .preempt_count = 1, \ + .addr_limit = KERNEL_DS, \ + .restart_block = { \ + .fn = do_no_restart_syscall, \ + }, \ + .curr_ret_stack = -1,\ +} +#else #define INIT_THREAD_INFO (tsk) \ { \ .task = &tsk, \
@@ -52,6 +75,7 @@ struct thread_info { .fn = do_no_restart_syscall, \ }, \ } +#endif #define init_thread_info (init_thread_union.thread_info)#define init_stack (init_thread_union.stack)
arch/x86/kernel/Makefile +6 -0
@@ -15,6 +15,11 @@ CFLAGS_REMOVE_ftrace.o = -pg CFLAGS_REMOVE_early_printk.o = -pg endif +ifdef CONFIG_FTRACE_RETURN +# Don't trace __switch_to() but let it for function tracer +CFLAGS_REMOVE_process_32.o = -pg +endif + # # vsyscalls (which work on the user stack) should have # no stack-protector checks:
@@ -67,6 +72,7 @@ obj-$(CONFIG_X86_LOCAL_APIC) += apic.o nmi.o obj-$(CONFIG_X86_IO_APIC) += io_apic.o obj-$(CONFIG_X86_REBOOTFIXUPS) += reboot_fixups_32.o obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o +obj-$(CONFIG_FTRACE_RETURN) += ftrace.o obj-$(CONFIG_KEXEC) += machine_kexec_$(BITS).o obj-$(CONFIG_KEXEC) += relocate_kernel_$(BITS).o crash.o obj-$(CONFIG_CRASH_DUMP) += crash_dump_$(BITS).o
arch/x86/kernel/entry_32.S +33 -0
@@ -1188,6 +1188,10 @@ ENTRY(mcount) cmpl $ftrace_stub, ftrace_trace_function jnz trace +#ifdef CONFIG_FTRACE_RETURN + cmpl $ftrace_stub, ftrace_function_return + jnz trace_return +#endif .globl ftrace_stub ftrace_stub: ret
@@ -1206,8 +1210,37 @@ trace: popl %edx popl %ecx popl %eax + jmp ftrace_stub +#ifdef CONFIG_FTRACE_RETURN +trace_return: + pushl %eax + pushl %ecx + pushl %edx + movl 0xc(%esp), %eax + pushl %eax + lea 0x4(%ebp), %eax + pushl %eax + call prepare_ftrace_return + addl $8, %esp + popl %edx + popl %ecx + popl %eax jmp ftrace_stub + +.globl return_to_handler +return_to_handler: + pushl $0 + pushl %eax + pushl %ecx + pushl %edx + call ftrace_return_to_handler + movl %eax, 0xc(%esp) + popl %edx + popl %ecx + popl %eax + ret +#endif /* CONFIG_FTRACE_RETURN */ END(mcount) #endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* CONFIG_FUNCTION_TRACER */
arch/x86/kernel/ftrace.c +174 -7
210 lines not on this page · raw message
include/linux/ftrace.h +20 -0
@@ -268,6 +268,26 @@ ftrace_init_module(unsigned long *start, unsigned long *end) { }/* +* Structure that defines a return function trace. +*/ +struct ftrace_retfunc { +unsigned long ret;/* Return address */ +unsigned long func;/* Current function */ +unsigned long long calltime; +unsigned long long rettime; +}; + +#ifdef CONFIG_FTRACE_RETURN +/* Type of a callback handler of tracing return function */ +typedef void (*trace_function_return_t )(struct ftrace_retfunc *); + +extern void register_ftrace_return (trace_function_return_t func); +/* The current handler in use */ +extern trace_function_return_t ftrace_function_return; +extern void unregister_ftrace_return (void ); +#endif + +/* * Structure which defines the trace of an initcall. * You don't have to fill the func field since it is * only used internally by the tracer.
include/linux/ftrace_irq.h +1 -1
@@ -2,7 +2,7 @@#define _LINUX_FTRACE_IRQ_H -#ifdef CONFIG_DYNAMIC_FTRACE +#if defined(CONFIG_DYNAMIC_FTRACE ) || defined(CONFIG_FTRACE_RETURN )extern void ftrace_nmi_enter (void );extern void ftrace_nmi_exit (void );#else
include/linux/sched.h +11 -0
@@ -2004,6 +2004,17 @@ static inline void setup_thread_stack(struct task_struct *p, struct task_struct { *task_thread_info (p) = *task_thread_info (org);task_thread_info (p)->task = p; + +#ifdef CONFIG_FTRACE_RETURN +/* +* When fork() creates a child process, this function is called. +* But the child task may not inherit the return adresses traced +* by the return function tracer because it will directly execute +* in userspace and will not return to kernel functions its parent +* used. +*/ +task_thread_info (p)->curr_ret_stack =-1 ; +#endif }static inline unsigned long *end_of_stack (struct task_struct *p)
kernel/Makefile +4 -0
@@ -22,6 +22,10 @@ CFLAGS_REMOVE_rtmutex-debug.o = -pg CFLAGS_REMOVE_cgroup-debug.o = -pg CFLAGS_REMOVE_sched_clock.o = -pg endif +ifdef CONFIG_FTRACE_RETURN +CFLAGS_REMOVE_extable.o = -pg # For __kernel_text_address() +CFLAGS_REMOVE_module.o = -pg # For __module_text_address() +endif obj-$(CONFIG_FREEZER) += freezer.o obj-$(CONFIG_PROFILING) += profile.o