diff options
| author | Steven Rostedt <srostedt@redhat.com> | 2009-01-15 19:12:40 -0500 | 
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2009-01-16 12:15:32 +0100 | 
| commit | 5361499101306cfb776c3cfa0f69d0479bc63868 (patch) | |
| tree | 1acf51a942abe6582e08ed86b4bbb98f9c095c89 /kernel/trace/trace_functions.c | |
| parent | 6c1a99afbda99cd8d8c69d756387041567a13d87 (diff) | |
| download | olio-linux-3.10-5361499101306cfb776c3cfa0f69d0479bc63868.tar.xz olio-linux-3.10-5361499101306cfb776c3cfa0f69d0479bc63868.zip  | |
ftrace: add stack trace to function tracer
Impact: new feature to stack trace any function
Chris Mason asked about being able to pick and choose a function
and get a stack trace from it. This feature enables his request.
 # echo io_schedule > /debug/tracing/set_ftrace_filter
 # echo function > /debug/tracing/current_tracer
 # echo func_stack_trace > /debug/tracing/trace_options
Produces the following in /debug/tracing/trace:
       kjournald-702   [001]   135.673060: io_schedule <-sync_buffer
       kjournald-702   [002]   135.673671:
 <= sync_buffer
 <= __wait_on_bit
 <= out_of_line_wait_on_bit
 <= __wait_on_buffer
 <= sync_dirty_buffer
 <= journal_commit_transaction
 <= kjournald
Note, be careful about turning this on without filtering the functions.
You may find that you have a 10 second lag between typing and seeing
what you typed. This is why the stack trace for the function tracer
does not use the same stack_trace flag as the other tracers use.
Signed-off-by: Steven Rostedt <srostedt@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/trace/trace_functions.c')
| -rw-r--r-- | kernel/trace/trace_functions.c | 84 | 
1 files changed, 84 insertions, 0 deletions
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c index 9236d7e25a1..3a5fa08cedb 100644 --- a/kernel/trace/trace_functions.c +++ b/kernel/trace/trace_functions.c @@ -16,6 +16,8 @@  #include "trace.h" +static struct trace_array	*func_trace; +  static void start_function_trace(struct trace_array *tr)  {  	tr->cpu = get_cpu(); @@ -34,6 +36,7 @@ static void stop_function_trace(struct trace_array *tr)  static int function_trace_init(struct trace_array *tr)  { +	func_trace = tr;  	start_function_trace(tr);  	return 0;  } @@ -48,12 +51,93 @@ static void function_trace_start(struct trace_array *tr)  	tracing_reset_online_cpus(tr);  } +static void +function_stack_trace_call(unsigned long ip, unsigned long parent_ip) +{ +	struct trace_array *tr = func_trace; +	struct trace_array_cpu *data; +	unsigned long flags; +	long disabled; +	int cpu; +	int pc; + +	if (unlikely(!ftrace_function_enabled)) +		return; + +	/* +	 * Need to use raw, since this must be called before the +	 * recursive protection is performed. +	 */ +	local_irq_save(flags); +	cpu = raw_smp_processor_id(); +	data = tr->data[cpu]; +	disabled = atomic_inc_return(&data->disabled); + +	if (likely(disabled == 1)) { +		pc = preempt_count(); +		/* +		 * skip over 5 funcs: +		 *    __ftrace_trace_stack, +		 *    __trace_stack, +		 *    function_stack_trace_call +		 *    ftrace_list_func +		 *    ftrace_call +		 */ +		__trace_stack(tr, data, flags, 5, pc); +	} + +	atomic_dec(&data->disabled); +	local_irq_restore(flags); +} + +static struct ftrace_ops trace_stack_ops __read_mostly = +{ +	.func = function_stack_trace_call, +}; + +/* Our two options */ +enum { +	TRACE_FUNC_OPT_STACK = 0x1, +}; + +static struct tracer_opt func_opts[] = { +#ifdef CONFIG_STACKTRACE +	{ TRACER_OPT(func_stack_trace, TRACE_FUNC_OPT_STACK) }, +#endif +	{ } /* Always set a last empty entry */ +}; + +static struct tracer_flags func_flags = { +	.val = 0, /* By default: all flags disabled */ +	.opts = func_opts +}; + +static int func_set_flag(u32 old_flags, u32 bit, int set) +{ +	if (bit == TRACE_FUNC_OPT_STACK) { +		/* do nothing if already set */ +		if (!!set == !!(func_flags.val & TRACE_FUNC_OPT_STACK)) +			return 0; + +		if (set) +			register_ftrace_function(&trace_stack_ops); +		else +			unregister_ftrace_function(&trace_stack_ops); + +		return 0; +	} + +	return -EINVAL; +} +  static struct tracer function_trace __read_mostly =  {  	.name	     = "function",  	.init	     = function_trace_init,  	.reset	     = function_trace_reset,  	.start	     = function_trace_start, +	.flags		= &func_flags, +	.set_flag	= func_set_flag,  #ifdef CONFIG_FTRACE_SELFTEST  	.selftest    = trace_selftest_startup_function,  #endif  |