diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-18 08:35:04 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-18 08:35:04 -0700 | 
| commit | 752f114fb83c5839de37a250b4f8257ed5438341 (patch) | |
| tree | c565c3b6670d21ad7f5cd6cdda5864a76d3f2e0c /kernel/trace/trace_irqsoff.c | |
| parent | b8ae30ee26d379db436b0b8c8c3ff1b52f69e5d1 (diff) | |
| parent | ad56b0797e67df5e04b2f1a1e02900145c5c16f2 (diff) | |
| download | olio-linux-3.10-752f114fb83c5839de37a250b4f8257ed5438341.tar.xz olio-linux-3.10-752f114fb83c5839de37a250b4f8257ed5438341.zip  | |
Merge branch 'tracing-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'tracing-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  tracing: Fix "integer as NULL pointer" warning.
  tracing: Fix tracepoint.h DECLARE_TRACE() to allow more than one header
  tracing: Make the documentation clear on trace_event boot option
  ring-buffer: Wrap open-coded WARN_ONCE
  tracing: Convert nop macros to static inlines
  tracing: Fix sleep time function profiling
  tracing: Show sample std dev in function profiling
  tracing: Add documentation for trace commands mod, traceon/traceoff
  ring-buffer: Make benchmark handle missed events
  ring-buffer: Make non-consuming read less expensive with lots of cpus.
  tracing: Add graph output support for irqsoff tracer
  tracing: Have graph flags passed in to ouput functions
  tracing: Add ftrace events for graph tracer
  tracing: Dump either the oops's cpu source or all cpus buffers
  tracing: Fix uninitialized variable of tracing/trace output
Diffstat (limited to 'kernel/trace/trace_irqsoff.c')
| -rw-r--r-- | kernel/trace/trace_irqsoff.c | 271 | 
1 files changed, 260 insertions, 11 deletions
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c index 2974bc7538c..6fd486e0cef 100644 --- a/kernel/trace/trace_irqsoff.c +++ b/kernel/trace/trace_irqsoff.c @@ -34,6 +34,9 @@ static int trace_type __read_mostly;  static int save_lat_flag; +static void stop_irqsoff_tracer(struct trace_array *tr, int graph); +static int start_irqsoff_tracer(struct trace_array *tr, int graph); +  #ifdef CONFIG_PREEMPT_TRACER  static inline int  preempt_trace(void) @@ -55,6 +58,23 @@ irq_trace(void)  # define irq_trace() (0)  #endif +#define TRACE_DISPLAY_GRAPH	1 + +static struct tracer_opt trace_opts[] = { +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +	/* display latency trace as call graph */ +	{ TRACER_OPT(display-graph, TRACE_DISPLAY_GRAPH) }, +#endif +	{ } /* Empty entry */ +}; + +static struct tracer_flags tracer_flags = { +	.val  = 0, +	.opts = trace_opts, +}; + +#define is_graph() (tracer_flags.val & TRACE_DISPLAY_GRAPH) +  /*   * Sequence count - we record it when starting a measurement and   * skip the latency if the sequence has changed - some other section @@ -108,6 +128,202 @@ static struct ftrace_ops trace_ops __read_mostly =  };  #endif /* CONFIG_FUNCTION_TRACER */ +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +static int irqsoff_set_flag(u32 old_flags, u32 bit, int set) +{ +	int cpu; + +	if (!(bit & TRACE_DISPLAY_GRAPH)) +		return -EINVAL; + +	if (!(is_graph() ^ set)) +		return 0; + +	stop_irqsoff_tracer(irqsoff_trace, !set); + +	for_each_possible_cpu(cpu) +		per_cpu(tracing_cpu, cpu) = 0; + +	tracing_max_latency = 0; +	tracing_reset_online_cpus(irqsoff_trace); + +	return start_irqsoff_tracer(irqsoff_trace, set); +} + +static int irqsoff_graph_entry(struct ftrace_graph_ent *trace) +{ +	struct trace_array *tr = irqsoff_trace; +	struct trace_array_cpu *data; +	unsigned long flags; +	long disabled; +	int ret; +	int cpu; +	int pc; + +	cpu = raw_smp_processor_id(); +	if (likely(!per_cpu(tracing_cpu, cpu))) +		return 0; + +	local_save_flags(flags); +	/* slight chance to get a false positive on tracing_cpu */ +	if (!irqs_disabled_flags(flags)) +		return 0; + +	data = tr->data[cpu]; +	disabled = atomic_inc_return(&data->disabled); + +	if (likely(disabled == 1)) { +		pc = preempt_count(); +		ret = __trace_graph_entry(tr, trace, flags, pc); +	} else +		ret = 0; + +	atomic_dec(&data->disabled); +	return ret; +} + +static void irqsoff_graph_return(struct ftrace_graph_ret *trace) +{ +	struct trace_array *tr = irqsoff_trace; +	struct trace_array_cpu *data; +	unsigned long flags; +	long disabled; +	int cpu; +	int pc; + +	cpu = raw_smp_processor_id(); +	if (likely(!per_cpu(tracing_cpu, cpu))) +		return; + +	local_save_flags(flags); +	/* slight chance to get a false positive on tracing_cpu */ +	if (!irqs_disabled_flags(flags)) +		return; + +	data = tr->data[cpu]; +	disabled = atomic_inc_return(&data->disabled); + +	if (likely(disabled == 1)) { +		pc = preempt_count(); +		__trace_graph_return(tr, trace, flags, pc); +	} + +	atomic_dec(&data->disabled); +} + +static void irqsoff_trace_open(struct trace_iterator *iter) +{ +	if (is_graph()) +		graph_trace_open(iter); + +} + +static void irqsoff_trace_close(struct trace_iterator *iter) +{ +	if (iter->private) +		graph_trace_close(iter); +} + +#define GRAPH_TRACER_FLAGS (TRACE_GRAPH_PRINT_CPU | \ +			    TRACE_GRAPH_PRINT_PROC) + +static enum print_line_t irqsoff_print_line(struct trace_iterator *iter) +{ +	u32 flags = GRAPH_TRACER_FLAGS; + +	if (trace_flags & TRACE_ITER_LATENCY_FMT) +		flags |= TRACE_GRAPH_PRINT_DURATION; +	else +		flags |= TRACE_GRAPH_PRINT_ABS_TIME; + +	/* +	 * In graph mode call the graph tracer output function, +	 * otherwise go with the TRACE_FN event handler +	 */ +	if (is_graph()) +		return print_graph_function_flags(iter, flags); + +	return TRACE_TYPE_UNHANDLED; +} + +static void irqsoff_print_header(struct seq_file *s) +{ +	if (is_graph()) { +		struct trace_iterator *iter = s->private; +		u32 flags = GRAPH_TRACER_FLAGS; + +		if (trace_flags & TRACE_ITER_LATENCY_FMT) { +			/* print nothing if the buffers are empty */ +			if (trace_empty(iter)) +				return; + +			print_trace_header(s, iter); +			flags |= TRACE_GRAPH_PRINT_DURATION; +		} else +			flags |= TRACE_GRAPH_PRINT_ABS_TIME; + +		print_graph_headers_flags(s, flags); +	} else +		trace_default_header(s); +} + +static void +trace_graph_function(struct trace_array *tr, +		 unsigned long ip, unsigned long flags, int pc) +{ +	u64 time = trace_clock_local(); +	struct ftrace_graph_ent ent = { +		.func  = ip, +		.depth = 0, +	}; +	struct ftrace_graph_ret ret = { +		.func     = ip, +		.depth    = 0, +		.calltime = time, +		.rettime  = time, +	}; + +	__trace_graph_entry(tr, &ent, flags, pc); +	__trace_graph_return(tr, &ret, flags, pc); +} + +static void +__trace_function(struct trace_array *tr, +		 unsigned long ip, unsigned long parent_ip, +		 unsigned long flags, int pc) +{ +	if (!is_graph()) +		trace_function(tr, ip, parent_ip, flags, pc); +	else { +		trace_graph_function(tr, parent_ip, flags, pc); +		trace_graph_function(tr, ip, flags, pc); +	} +} + +#else +#define __trace_function trace_function + +static int irqsoff_set_flag(u32 old_flags, u32 bit, int set) +{ +	return -EINVAL; +} + +static int irqsoff_graph_entry(struct ftrace_graph_ent *trace) +{ +	return -1; +} + +static enum print_line_t irqsoff_print_line(struct trace_iterator *iter) +{ +	return TRACE_TYPE_UNHANDLED; +} + +static void irqsoff_graph_return(struct ftrace_graph_ret *trace) { } +static void irqsoff_print_header(struct seq_file *s) { } +static void irqsoff_trace_open(struct trace_iterator *iter) { } +static void irqsoff_trace_close(struct trace_iterator *iter) { } +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ +  /*   * Should this new latency be reported/recorded?   */ @@ -150,7 +366,7 @@ check_critical_timing(struct trace_array *tr,  	if (!report_latency(delta))  		goto out_unlock; -	trace_function(tr, CALLER_ADDR0, parent_ip, flags, pc); +	__trace_function(tr, CALLER_ADDR0, parent_ip, flags, pc);  	/* Skip 5 functions to get to the irq/preempt enable function */  	__trace_stack(tr, flags, 5, pc); @@ -172,7 +388,7 @@ out_unlock:  out:  	data->critical_sequence = max_sequence;  	data->preempt_timestamp = ftrace_now(cpu); -	trace_function(tr, CALLER_ADDR0, parent_ip, flags, pc); +	__trace_function(tr, CALLER_ADDR0, parent_ip, flags, pc);  }  static inline void @@ -204,7 +420,7 @@ start_critical_timing(unsigned long ip, unsigned long parent_ip)  	local_save_flags(flags); -	trace_function(tr, ip, parent_ip, flags, preempt_count()); +	__trace_function(tr, ip, parent_ip, flags, preempt_count());  	per_cpu(tracing_cpu, cpu) = 1; @@ -238,7 +454,7 @@ stop_critical_timing(unsigned long ip, unsigned long parent_ip)  	atomic_inc(&data->disabled);  	local_save_flags(flags); -	trace_function(tr, ip, parent_ip, flags, preempt_count()); +	__trace_function(tr, ip, parent_ip, flags, preempt_count());  	check_critical_timing(tr, data, parent_ip ? : ip, cpu);  	data->critical_start = 0;  	atomic_dec(&data->disabled); @@ -347,19 +563,32 @@ void trace_preempt_off(unsigned long a0, unsigned long a1)  }  #endif /* CONFIG_PREEMPT_TRACER */ -static void start_irqsoff_tracer(struct trace_array *tr) +static int start_irqsoff_tracer(struct trace_array *tr, int graph)  { -	register_ftrace_function(&trace_ops); -	if (tracing_is_enabled()) +	int ret = 0; + +	if (!graph) +		ret = register_ftrace_function(&trace_ops); +	else +		ret = register_ftrace_graph(&irqsoff_graph_return, +					    &irqsoff_graph_entry); + +	if (!ret && tracing_is_enabled())  		tracer_enabled = 1;  	else  		tracer_enabled = 0; + +	return ret;  } -static void stop_irqsoff_tracer(struct trace_array *tr) +static void stop_irqsoff_tracer(struct trace_array *tr, int graph)  {  	tracer_enabled = 0; -	unregister_ftrace_function(&trace_ops); + +	if (!graph) +		unregister_ftrace_function(&trace_ops); +	else +		unregister_ftrace_graph();  }  static void __irqsoff_tracer_init(struct trace_array *tr) @@ -372,12 +601,14 @@ static void __irqsoff_tracer_init(struct trace_array *tr)  	/* make sure that the tracer is visible */  	smp_wmb();  	tracing_reset_online_cpus(tr); -	start_irqsoff_tracer(tr); + +	if (start_irqsoff_tracer(tr, is_graph())) +		printk(KERN_ERR "failed to start irqsoff tracer\n");  }  static void irqsoff_tracer_reset(struct trace_array *tr)  { -	stop_irqsoff_tracer(tr); +	stop_irqsoff_tracer(tr, is_graph());  	if (!save_lat_flag)  		trace_flags &= ~TRACE_ITER_LATENCY_FMT; @@ -409,9 +640,15 @@ static struct tracer irqsoff_tracer __read_mostly =  	.start		= irqsoff_tracer_start,  	.stop		= irqsoff_tracer_stop,  	.print_max	= 1, +	.print_header   = irqsoff_print_header, +	.print_line     = irqsoff_print_line, +	.flags		= &tracer_flags, +	.set_flag	= irqsoff_set_flag,  #ifdef CONFIG_FTRACE_SELFTEST  	.selftest    = trace_selftest_startup_irqsoff,  #endif +	.open           = irqsoff_trace_open, +	.close          = irqsoff_trace_close,  };  # define register_irqsoff(trace) register_tracer(&trace)  #else @@ -435,9 +672,15 @@ static struct tracer preemptoff_tracer __read_mostly =  	.start		= irqsoff_tracer_start,  	.stop		= irqsoff_tracer_stop,  	.print_max	= 1, +	.print_header   = irqsoff_print_header, +	.print_line     = irqsoff_print_line, +	.flags		= &tracer_flags, +	.set_flag	= irqsoff_set_flag,  #ifdef CONFIG_FTRACE_SELFTEST  	.selftest    = trace_selftest_startup_preemptoff,  #endif +	.open		= irqsoff_trace_open, +	.close		= irqsoff_trace_close,  };  # define register_preemptoff(trace) register_tracer(&trace)  #else @@ -463,9 +706,15 @@ static struct tracer preemptirqsoff_tracer __read_mostly =  	.start		= irqsoff_tracer_start,  	.stop		= irqsoff_tracer_stop,  	.print_max	= 1, +	.print_header   = irqsoff_print_header, +	.print_line     = irqsoff_print_line, +	.flags		= &tracer_flags, +	.set_flag	= irqsoff_set_flag,  #ifdef CONFIG_FTRACE_SELFTEST  	.selftest    = trace_selftest_startup_preemptirqsoff,  #endif +	.open		= irqsoff_trace_open, +	.close		= irqsoff_trace_close,  };  # define register_preemptirqsoff(trace) register_tracer(&trace)  |