Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 121173
b: refs/heads/master
c: f201ae2
h: refs/heads/master
i:
  121171: 5f401bb
v: v3
  • Loading branch information
Frederic Weisbecker authored and Ingo Molnar committed Nov 23, 2008
1 parent 5ffa153 commit 44cdaf1
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 114 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 69bb54ec05f57da7f6fac2cec0820cbc970df20f
refs/heads/master: f201ae2356c74bcae130b2177b3dca903ea98071
1 change: 0 additions & 1 deletion trunk/arch/x86/include/asm/ftrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ struct dyn_arch_ftrace {
#endif /* CONFIG_FUNCTION_TRACER */

#ifdef CONFIG_FUNCTION_RET_TRACER
#define FTRACE_RET_STACK_SIZE 20

#ifndef __ASSEMBLY__

Expand Down
29 changes: 0 additions & 29 deletions trunk/arch/x86/include/asm/thread_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,36 +40,8 @@ struct thread_info {
*/
__u8 supervisor_stack[0];
#endif

#ifdef CONFIG_FUNCTION_RET_TRACER
/* 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];
/*
* Number of functions that haven't been traced
* because of depth overrun.
*/
atomic_t trace_overrun;
#endif
};

#ifdef CONFIG_FUNCTION_RET_TRACER
#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,\
.trace_overrun = ATOMIC_INIT(0) \
}
#else
#define INIT_THREAD_INFO(tsk) \
{ \
.task = &tsk, \
Expand All @@ -82,7 +54,6 @@ 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)
Expand Down
29 changes: 15 additions & 14 deletions trunk/arch/x86/kernel/ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -350,19 +350,21 @@ static int push_return_trace(unsigned long ret, unsigned long long time,
unsigned long func)
{
int index;
struct thread_info *ti = current_thread_info();

if (!current->ret_stack)
return -EBUSY;

/* The return trace stack is full */
if (ti->curr_ret_stack == FTRACE_RET_STACK_SIZE - 1) {
atomic_inc(&ti->trace_overrun);
if (current->curr_ret_stack == FTRACE_RETFUNC_DEPTH - 1) {
atomic_inc(&current->trace_overrun);
return -EBUSY;
}

index = ++ti->curr_ret_stack;
index = ++current->curr_ret_stack;
barrier();
ti->ret_stack[index].ret = ret;
ti->ret_stack[index].func = func;
ti->ret_stack[index].calltime = time;
current->ret_stack[index].ret = ret;
current->ret_stack[index].func = func;
current->ret_stack[index].calltime = time;

return 0;
}
Expand All @@ -373,13 +375,12 @@ static void pop_return_trace(unsigned long *ret, unsigned long long *time,
{
int index;

struct thread_info *ti = current_thread_info();
index = ti->curr_ret_stack;
*ret = ti->ret_stack[index].ret;
*func = ti->ret_stack[index].func;
*time = ti->ret_stack[index].calltime;
*overrun = atomic_read(&ti->trace_overrun);
ti->curr_ret_stack--;
index = current->curr_ret_stack;
*ret = current->ret_stack[index].ret;
*func = current->ret_stack[index].func;
*time = current->ret_stack[index].calltime;
*overrun = atomic_read(&current->trace_overrun);
current->curr_ret_stack--;
}

/*
Expand Down
7 changes: 5 additions & 2 deletions trunk/include/linux/ftrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,6 @@ extern int ftrace_dump_on_oops;

extern void tracing_start(void);
extern void tracing_stop(void);
extern void ftrace_off_permanent(void);

extern void
ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3);
Expand Down Expand Up @@ -291,7 +290,6 @@ ftrace_printk(const char *fmt, ...) __attribute__ ((format (printf, 1, 0)));

static inline void tracing_start(void) { }
static inline void tracing_stop(void) { }
static inline void ftrace_off_permanent(void) { }
static inline int
ftrace_printk(const char *fmt, ...)
{
Expand Down Expand Up @@ -325,13 +323,18 @@ struct ftrace_retfunc {
};

#ifdef CONFIG_FUNCTION_RET_TRACER
#define FTRACE_RETFUNC_DEPTH 50
#define FTRACE_RETSTACK_ALLOC_SIZE 32
/* Type of a callback handler of tracing return function */
typedef void (*trace_function_return_t)(struct ftrace_retfunc *);

extern int 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);

extern void ftrace_retfunc_init_task(struct task_struct *t);
extern void ftrace_retfunc_exit_task(struct task_struct *t);
#endif

#endif /* _LINUX_FTRACE_H */
23 changes: 11 additions & 12 deletions trunk/include/linux/sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -1352,6 +1352,17 @@ struct task_struct {
unsigned long default_timer_slack_ns;

struct list_head *scm_work_list;
#ifdef CONFIG_FUNCTION_RET_TRACER
/* 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;
/*
* Number of functions that haven't been traced
* because of depth overrun.
*/
atomic_t trace_overrun;
#endif
};

/*
Expand Down Expand Up @@ -2006,18 +2017,6 @@ 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_FUNCTION_RET_TRACER
/*
* 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;
atomic_set(&task_thread_info(p)->trace_overrun, 0);
#endif
}

static inline unsigned long *end_of_stack(struct task_struct *p)
Expand Down
5 changes: 4 additions & 1 deletion trunk/kernel/exit.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include <linux/task_io_accounting_ops.h>
#include <linux/tracehook.h>
#include <trace/sched.h>
#include <linux/ftrace.h>

#include <asm/uaccess.h>
#include <asm/unistd.h>
Expand Down Expand Up @@ -1127,7 +1128,9 @@ NORET_TYPE void do_exit(long code)
preempt_disable();
/* causes final put_task_struct in finish_task_switch(). */
tsk->state = TASK_DEAD;

#ifdef CONFIG_FUNCTION_RET_TRACER
ftrace_retfunc_exit_task(tsk);
#endif
schedule();
BUG();
/* Avoid "noreturn function does return". */
Expand Down
4 changes: 4 additions & 0 deletions trunk/kernel/fork.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include <linux/mount.h>
#include <linux/audit.h>
#include <linux/memcontrol.h>
#include <linux/ftrace.h>
#include <linux/profile.h>
#include <linux/rmap.h>
#include <linux/acct.h>
Expand Down Expand Up @@ -1269,6 +1270,9 @@ static struct task_struct *copy_process(unsigned long clone_flags,
total_forks++;
spin_unlock(&current->sighand->siglock);
write_unlock_irq(&tasklist_lock);
#ifdef CONFIG_FUNCTION_RET_TRACER
ftrace_retfunc_init_task(p);
#endif
proc_fork_connector(p);
cgroup_post_fork(p);
return p;
Expand Down
3 changes: 3 additions & 0 deletions trunk/kernel/sched.c
Original file line number Diff line number Diff line change
Expand Up @@ -5901,6 +5901,9 @@ void __cpuinit init_idle(struct task_struct *idle, int cpu)
* The idle tasks have their own, simple scheduling class:
*/
idle->sched_class = &idle_sched_class;
#ifdef CONFIG_FUNCTION_RET_TRACER
ftrace_retfunc_init_task(idle);
#endif
}

/*
Expand Down
96 changes: 95 additions & 1 deletion trunk/kernel/trace/ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -1498,10 +1498,77 @@ ftrace_enable_sysctl(struct ctl_table *table, int write,

#ifdef CONFIG_FUNCTION_RET_TRACER

static atomic_t ftrace_retfunc_active;

/* The callback that hooks the return of a function */
trace_function_return_t ftrace_function_return =
(trace_function_return_t)ftrace_stub;


/* Try to assign a return stack array on FTRACE_RETSTACK_ALLOC_SIZE tasks. */
static int alloc_retstack_tasklist(struct ftrace_ret_stack **ret_stack_list)
{
int i;
int ret = 0;
unsigned long flags;
int start = 0, end = FTRACE_RETSTACK_ALLOC_SIZE;
struct task_struct *g, *t;

for (i = 0; i < FTRACE_RETSTACK_ALLOC_SIZE; i++) {
ret_stack_list[i] = kmalloc(FTRACE_RETFUNC_DEPTH
* sizeof(struct ftrace_ret_stack),
GFP_KERNEL);
if (!ret_stack_list[i]) {
start = 0;
end = i;
ret = -ENOMEM;
goto free;
}
}

read_lock_irqsave(&tasklist_lock, flags);
do_each_thread(g, t) {
if (start == end) {
ret = -EAGAIN;
goto unlock;
}

if (t->ret_stack == NULL) {
t->ret_stack = ret_stack_list[start++];
t->curr_ret_stack = -1;
atomic_set(&t->trace_overrun, 0);
}
} while_each_thread(g, t);

unlock:
read_unlock_irqrestore(&tasklist_lock, flags);
free:
for (i = start; i < end; i++)
kfree(ret_stack_list[i]);
return ret;
}

/* Allocate a return stack for each task */
static int start_return_tracing(void)
{
struct ftrace_ret_stack **ret_stack_list;
int ret;

ret_stack_list = kmalloc(FTRACE_RETSTACK_ALLOC_SIZE *
sizeof(struct ftrace_ret_stack *),
GFP_KERNEL);

if (!ret_stack_list)
return -ENOMEM;

do {
ret = alloc_retstack_tasklist(ret_stack_list);
} while (ret == -EAGAIN);

kfree(ret_stack_list);
return ret;
}

int register_ftrace_return(trace_function_return_t func)
{
int ret = 0;
Expand All @@ -1516,7 +1583,12 @@ int register_ftrace_return(trace_function_return_t func)
ret = -EBUSY;
goto out;
}

atomic_inc(&ftrace_retfunc_active);
ret = start_return_tracing();
if (ret) {
atomic_dec(&ftrace_retfunc_active);
goto out;
}
ftrace_tracing_type = FTRACE_TYPE_RETURN;
ftrace_function_return = func;
ftrace_startup();
Expand All @@ -1530,13 +1602,35 @@ void unregister_ftrace_return(void)
{
mutex_lock(&ftrace_sysctl_lock);

atomic_dec(&ftrace_retfunc_active);
ftrace_function_return = (trace_function_return_t)ftrace_stub;
ftrace_shutdown();
/* Restore normal tracing type */
ftrace_tracing_type = FTRACE_TYPE_ENTER;

mutex_unlock(&ftrace_sysctl_lock);
}

/* Allocate a return stack for newly created task */
void ftrace_retfunc_init_task(struct task_struct *t)
{
if (atomic_read(&ftrace_retfunc_active)) {
t->ret_stack = kmalloc(FTRACE_RETFUNC_DEPTH
* sizeof(struct ftrace_ret_stack),
GFP_KERNEL);
if (!t->ret_stack)
return;
t->curr_ret_stack = -1;
atomic_set(&t->trace_overrun, 0);
} else
t->ret_stack = NULL;
}

void ftrace_retfunc_exit_task(struct task_struct *t)
{
kfree(t->ret_stack);
t->ret_stack = NULL;
}
#endif


Expand Down
15 changes: 0 additions & 15 deletions trunk/kernel/trace/trace.c
Original file line number Diff line number Diff line change
Expand Up @@ -660,21 +660,6 @@ static void trace_init_cmdlines(void)
static int trace_stop_count;
static DEFINE_SPINLOCK(tracing_start_lock);

/**
* ftrace_off_permanent - disable all ftrace code permanently
*
* This should only be called when a serious anomally has
* been detected. This will turn off the function tracing,
* ring buffers, and other tracing utilites. It takes no
* locks and can be called from any context.
*/
void ftrace_off_permanent(void)
{
tracing_disabled = 1;
ftrace_stop();
tracing_off_permanent();
}

/**
* tracing_start - quick start of the tracer
*
Expand Down
Loading

0 comments on commit 44cdaf1

Please sign in to comment.