diff options
Diffstat (limited to 'kernel/trace/trace_functions_graph.c')
| -rw-r--r-- | kernel/trace/trace_functions_graph.c | 635 | 
1 files changed, 427 insertions, 208 deletions
| diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index 930c08e5b38e..d28687e7b3a7 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c @@ -1,7 +1,7 @@  /*   *   * Function graph tracer. - * Copyright (c) 2008 Frederic Weisbecker <[email protected]> + * Copyright (c) 2008-2009 Frederic Weisbecker <[email protected]>   * Mostly borrowed from function tracer which   * is Copyright (c) Steven Rostedt <[email protected]>   * @@ -12,6 +12,12 @@  #include <linux/fs.h>  #include "trace.h" +#include "trace_output.h" + +struct fgraph_data { +	pid_t		last_pid; +	int		depth; +};  #define TRACE_GRAPH_INDENT	2 @@ -20,9 +26,11 @@  #define TRACE_GRAPH_PRINT_CPU		0x2  #define TRACE_GRAPH_PRINT_OVERHEAD	0x4  #define TRACE_GRAPH_PRINT_PROC		0x8 +#define TRACE_GRAPH_PRINT_DURATION	0x10 +#define TRACE_GRAPH_PRINT_ABS_TIME	0X20  static struct tracer_opt trace_opts[] = { -	/* Display overruns ? */ +	/* Display overruns? (for self-debug purpose) */  	{ TRACER_OPT(funcgraph-overrun, TRACE_GRAPH_PRINT_OVERRUN) },  	/* Display CPU ? */  	{ TRACER_OPT(funcgraph-cpu, TRACE_GRAPH_PRINT_CPU) }, @@ -30,26 +38,103 @@ static struct tracer_opt trace_opts[] = {  	{ TRACER_OPT(funcgraph-overhead, TRACE_GRAPH_PRINT_OVERHEAD) },  	/* Display proc name/pid */  	{ TRACER_OPT(funcgraph-proc, TRACE_GRAPH_PRINT_PROC) }, +	/* Display duration of execution */ +	{ TRACER_OPT(funcgraph-duration, TRACE_GRAPH_PRINT_DURATION) }, +	/* Display absolute time of an entry */ +	{ TRACER_OPT(funcgraph-abstime, TRACE_GRAPH_PRINT_ABS_TIME) },  	{ } /* Empty entry */  };  static struct tracer_flags tracer_flags = {  	/* Don't display overruns and proc by default */ -	.val = TRACE_GRAPH_PRINT_CPU | TRACE_GRAPH_PRINT_OVERHEAD, +	.val = TRACE_GRAPH_PRINT_CPU | TRACE_GRAPH_PRINT_OVERHEAD | +	       TRACE_GRAPH_PRINT_DURATION,  	.opts = trace_opts  };  /* pid on the last trace processed */ -static pid_t last_pid[NR_CPUS] = { [0 ... NR_CPUS-1] = -1 }; -static int graph_trace_init(struct trace_array *tr) + +/* Add a function return address to the trace stack on thread info.*/ +int +ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth)  { -	int cpu, ret; +	unsigned long long calltime; +	int index; + +	if (!current->ret_stack) +		return -EBUSY; + +	/* The return trace stack is full */ +	if (current->curr_ret_stack == FTRACE_RETFUNC_DEPTH - 1) { +		atomic_inc(¤t->trace_overrun); +		return -EBUSY; +	} + +	calltime = trace_clock_local(); + +	index = ++current->curr_ret_stack; +	barrier(); +	current->ret_stack[index].ret = ret; +	current->ret_stack[index].func = func; +	current->ret_stack[index].calltime = calltime; +	*depth = index; + +	return 0; +} + +/* Retrieve a function return address to the trace stack on thread info.*/ +void +ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret) +{ +	int index; + +	index = current->curr_ret_stack; + +	if (unlikely(index < 0)) { +		ftrace_graph_stop(); +		WARN_ON(1); +		/* Might as well panic, otherwise we have no where to go */ +		*ret = (unsigned long)panic; +		return; +	} -	for_each_online_cpu(cpu) -		tracing_reset(tr, cpu); +	*ret = current->ret_stack[index].ret; +	trace->func = current->ret_stack[index].func; +	trace->calltime = current->ret_stack[index].calltime; +	trace->overrun = atomic_read(¤t->trace_overrun); +	trace->depth = index; +	barrier(); +	current->curr_ret_stack--; -	ret = register_ftrace_graph(&trace_graph_return, +} + +/* + * Send the trace to the ring-buffer. + * @return the original return address. + */ +unsigned long ftrace_return_to_handler(void) +{ +	struct ftrace_graph_ret trace; +	unsigned long ret; + +	ftrace_pop_return_trace(&trace, &ret); +	trace.rettime = trace_clock_local(); +	ftrace_graph_return(&trace); + +	if (unlikely(!ret)) { +		ftrace_graph_stop(); +		WARN_ON(1); +		/* Might as well panic. What else to do? */ +		ret = (unsigned long)panic; +	} + +	return ret; +} + +static int graph_trace_init(struct trace_array *tr) +{ +	int ret = register_ftrace_graph(&trace_graph_return,  					&trace_graph_entry);  	if (ret)  		return ret; @@ -112,15 +197,15 @@ print_graph_cpu(struct trace_seq *s, int cpu)  static enum print_line_t  print_graph_proc(struct trace_seq *s, pid_t pid)  { -	int i; -	int ret; -	int len; -	char comm[8]; -	int spaces = 0; +	char comm[TASK_COMM_LEN];  	/* sign + log10(MAX_INT) + '\0' */  	char pid_str[11]; +	int spaces = 0; +	int ret; +	int len; +	int i; -	strncpy(comm, trace_find_cmdline(pid), 7); +	trace_find_cmdline(pid, comm);  	comm[7] = '\0';  	sprintf(pid_str, "%d", pid); @@ -153,17 +238,25 @@ print_graph_proc(struct trace_seq *s, pid_t pid)  /* If the pid changed since the last trace, output this event */  static enum print_line_t -verif_pid(struct trace_seq *s, pid_t pid, int cpu) +verif_pid(struct trace_seq *s, pid_t pid, int cpu, struct fgraph_data *data)  {  	pid_t prev_pid; +	pid_t *last_pid;  	int ret; -	if (last_pid[cpu] != -1 && last_pid[cpu] == pid) +	if (!data)  		return TRACE_TYPE_HANDLED; -	prev_pid = last_pid[cpu]; -	last_pid[cpu] = pid; +	last_pid = &(per_cpu_ptr(data, cpu)->last_pid); +	if (*last_pid == pid) +		return TRACE_TYPE_HANDLED; + +	prev_pid = *last_pid; +	*last_pid = pid; + +	if (prev_pid == -1) +		return TRACE_TYPE_HANDLED;  /*   * Context-switch trace line: @@ -175,34 +268,34 @@ verif_pid(struct trace_seq *s, pid_t pid, int cpu)  	ret = trace_seq_printf(s,  		" ------------------------------------------\n");  	if (!ret) -		TRACE_TYPE_PARTIAL_LINE; +		return TRACE_TYPE_PARTIAL_LINE;  	ret = print_graph_cpu(s, cpu);  	if (ret == TRACE_TYPE_PARTIAL_LINE) -		TRACE_TYPE_PARTIAL_LINE; +		return TRACE_TYPE_PARTIAL_LINE;  	ret = print_graph_proc(s, prev_pid);  	if (ret == TRACE_TYPE_PARTIAL_LINE) -		TRACE_TYPE_PARTIAL_LINE; +		return TRACE_TYPE_PARTIAL_LINE;  	ret = trace_seq_printf(s, " => ");  	if (!ret) -		TRACE_TYPE_PARTIAL_LINE; +		return TRACE_TYPE_PARTIAL_LINE;  	ret = print_graph_proc(s, pid);  	if (ret == TRACE_TYPE_PARTIAL_LINE) -		TRACE_TYPE_PARTIAL_LINE; +		return TRACE_TYPE_PARTIAL_LINE;  	ret = trace_seq_printf(s,  		"\n ------------------------------------------\n\n");  	if (!ret) -		TRACE_TYPE_PARTIAL_LINE; +		return TRACE_TYPE_PARTIAL_LINE; -	return ret; +	return TRACE_TYPE_HANDLED;  } -static bool -trace_branch_is_leaf(struct trace_iterator *iter, +static struct ftrace_graph_ret_entry * +get_return_for_leaf(struct trace_iterator *iter,  		struct ftrace_graph_ent_entry *curr)  {  	struct ring_buffer_iter *ring_iter; @@ -211,65 +304,123 @@ trace_branch_is_leaf(struct trace_iterator *iter,  	ring_iter = iter->buffer_iter[iter->cpu]; -	if (!ring_iter) -		return false; - -	event = ring_buffer_iter_peek(ring_iter, NULL); +	/* First peek to compare current entry and the next one */ +	if (ring_iter) +		event = ring_buffer_iter_peek(ring_iter, NULL); +	else { +	/* We need to consume the current entry to see the next one */ +		ring_buffer_consume(iter->tr->buffer, iter->cpu, NULL); +		event = ring_buffer_peek(iter->tr->buffer, iter->cpu, +					NULL); +	}  	if (!event) -		return false; +		return NULL;  	next = ring_buffer_event_data(event);  	if (next->ent.type != TRACE_GRAPH_RET) -		return false; +		return NULL;  	if (curr->ent.pid != next->ent.pid ||  			curr->graph_ent.func != next->ret.func) -		return false; +		return NULL; + +	/* this is a leaf, now advance the iterator */ +	if (ring_iter) +		ring_buffer_read(ring_iter, NULL); + +	return next; +} + +/* Signal a overhead of time execution to the output */ +static int +print_graph_overhead(unsigned long long duration, struct trace_seq *s) +{ +	/* If duration disappear, we don't need anything */ +	if (!(tracer_flags.val & TRACE_GRAPH_PRINT_DURATION)) +		return 1; + +	/* Non nested entry or return */ +	if (duration == -1) +		return trace_seq_printf(s, "  "); + +	if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { +		/* Duration exceeded 100 msecs */ +		if (duration > 100000ULL) +			return trace_seq_printf(s, "! "); -	return true; +		/* Duration exceeded 10 msecs */ +		if (duration > 10000ULL) +			return trace_seq_printf(s, "+ "); +	} + +	return trace_seq_printf(s, "  "); +} + +static int print_graph_abs_time(u64 t, struct trace_seq *s) +{ +	unsigned long usecs_rem; + +	usecs_rem = do_div(t, NSEC_PER_SEC); +	usecs_rem /= 1000; + +	return trace_seq_printf(s, "%5lu.%06lu |  ", +			(unsigned long)t, usecs_rem);  }  static enum print_line_t -print_graph_irq(struct trace_seq *s, unsigned long addr, -				enum trace_type type, int cpu, pid_t pid) +print_graph_irq(struct trace_iterator *iter, unsigned long addr, +		enum trace_type type, int cpu, pid_t pid)  {  	int ret; +	struct trace_seq *s = &iter->seq;  	if (addr < (unsigned long)__irqentry_text_start ||  		addr >= (unsigned long)__irqentry_text_end)  		return TRACE_TYPE_UNHANDLED; -	if (type == TRACE_GRAPH_ENT) { -		ret = trace_seq_printf(s, "==========> |  "); -	} else { -		/* Cpu */ -		if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) { -			ret = print_graph_cpu(s, cpu); -			if (ret == TRACE_TYPE_PARTIAL_LINE) -				return TRACE_TYPE_PARTIAL_LINE; -		} -		/* Proc */ -		if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) { -			ret = print_graph_proc(s, pid); -			if (ret == TRACE_TYPE_PARTIAL_LINE) -				return TRACE_TYPE_PARTIAL_LINE; +	/* Absolute time */ +	if (tracer_flags.val & TRACE_GRAPH_PRINT_ABS_TIME) { +		ret = print_graph_abs_time(iter->ts, s); +		if (!ret) +			return TRACE_TYPE_PARTIAL_LINE; +	} -			ret = trace_seq_printf(s, " | "); -			if (!ret) -				return TRACE_TYPE_PARTIAL_LINE; -		} +	/* Cpu */ +	if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) { +		ret = print_graph_cpu(s, cpu); +		if (ret == TRACE_TYPE_PARTIAL_LINE) +			return TRACE_TYPE_PARTIAL_LINE; +	} +	/* Proc */ +	if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) { +		ret = print_graph_proc(s, pid); +		if (ret == TRACE_TYPE_PARTIAL_LINE) +			return TRACE_TYPE_PARTIAL_LINE; +		ret = trace_seq_printf(s, " | "); +		if (!ret) +			return TRACE_TYPE_PARTIAL_LINE; +	} -		/* No overhead */ -		if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { -			ret = trace_seq_printf(s, "  "); -			if (!ret) -				return TRACE_TYPE_PARTIAL_LINE; -		} +	/* No overhead */ +	ret = print_graph_overhead(-1, s); +	if (!ret) +		return TRACE_TYPE_PARTIAL_LINE; + +	if (type == TRACE_GRAPH_ENT) +		ret = trace_seq_printf(s, "==========>"); +	else +		ret = trace_seq_printf(s, "<=========="); + +	if (!ret) +		return TRACE_TYPE_PARTIAL_LINE; + +	/* Don't close the duration column if haven't one */ +	if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) +		trace_seq_printf(s, " |"); +	ret = trace_seq_printf(s, "\n"); -		ret = trace_seq_printf(s, "<========== |\n"); -	}  	if (!ret)  		return TRACE_TYPE_PARTIAL_LINE;  	return TRACE_TYPE_HANDLED; @@ -288,7 +439,7 @@ print_graph_duration(unsigned long long duration, struct trace_seq *s)  	sprintf(msecs_str, "%lu", (unsigned long) duration);  	/* Print msecs */ -	ret = trace_seq_printf(s, msecs_str); +	ret = trace_seq_printf(s, "%s", msecs_str);  	if (!ret)  		return TRACE_TYPE_PARTIAL_LINE; @@ -321,52 +472,47 @@ print_graph_duration(unsigned long long duration, struct trace_seq *s)  } -/* Signal a overhead of time execution to the output */ -static int -print_graph_overhead(unsigned long long duration, struct trace_seq *s) -{ -	/* Duration exceeded 100 msecs */ -	if (duration > 100000ULL) -		return trace_seq_printf(s, "! "); - -	/* Duration exceeded 10 msecs */ -	if (duration > 10000ULL) -		return trace_seq_printf(s, "+ "); - -	return trace_seq_printf(s, "  "); -} -  /* Case of a leaf function on its call entry */  static enum print_line_t  print_graph_entry_leaf(struct trace_iterator *iter, -		struct ftrace_graph_ent_entry *entry, struct trace_seq *s) +		struct ftrace_graph_ent_entry *entry, +		struct ftrace_graph_ret_entry *ret_entry, struct trace_seq *s)  { -	struct ftrace_graph_ret_entry *ret_entry; +	struct fgraph_data *data = iter->private;  	struct ftrace_graph_ret *graph_ret; -	struct ring_buffer_event *event;  	struct ftrace_graph_ent *call;  	unsigned long long duration;  	int ret;  	int i; -	event = ring_buffer_read(iter->buffer_iter[iter->cpu], NULL); -	ret_entry = ring_buffer_event_data(event);  	graph_ret = &ret_entry->ret;  	call = &entry->graph_ent;  	duration = graph_ret->rettime - graph_ret->calltime; -	/* Overhead */ -	if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { -		ret = print_graph_overhead(duration, s); -		if (!ret) -			return TRACE_TYPE_PARTIAL_LINE; +	if (data) { +		int cpu = iter->cpu; +		int *depth = &(per_cpu_ptr(data, cpu)->depth); + +		/* +		 * Comments display at + 1 to depth. Since +		 * this is a leaf function, keep the comments +		 * equal to this depth. +		 */ +		*depth = call->depth - 1;  	} -	/* Duration */ -	ret = print_graph_duration(duration, s); -	if (ret == TRACE_TYPE_PARTIAL_LINE) +	/* Overhead */ +	ret = print_graph_overhead(duration, s); +	if (!ret)  		return TRACE_TYPE_PARTIAL_LINE; +	/* Duration */ +	if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) { +		ret = print_graph_duration(duration, s); +		if (ret == TRACE_TYPE_PARTIAL_LINE) +			return TRACE_TYPE_PARTIAL_LINE; +	} +  	/* Function */  	for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) {  		ret = trace_seq_printf(s, " "); @@ -386,33 +532,34 @@ print_graph_entry_leaf(struct trace_iterator *iter,  }  static enum print_line_t -print_graph_entry_nested(struct ftrace_graph_ent_entry *entry, -			struct trace_seq *s, pid_t pid, int cpu) +print_graph_entry_nested(struct trace_iterator *iter, +			 struct ftrace_graph_ent_entry *entry, +			 struct trace_seq *s, int cpu)  { -	int i; -	int ret;  	struct ftrace_graph_ent *call = &entry->graph_ent; +	struct fgraph_data *data = iter->private; +	int ret; +	int i; -	/* No overhead */ -	if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { -		ret = trace_seq_printf(s, "  "); -		if (!ret) -			return TRACE_TYPE_PARTIAL_LINE; +	if (data) { +		int cpu = iter->cpu; +		int *depth = &(per_cpu_ptr(data, cpu)->depth); + +		*depth = call->depth;  	} -	/* Interrupt */ -	ret = print_graph_irq(s, call->func, TRACE_GRAPH_ENT, cpu, pid); -	if (ret == TRACE_TYPE_UNHANDLED) { -		/* No time */ +	/* No overhead */ +	ret = print_graph_overhead(-1, s); +	if (!ret) +		return TRACE_TYPE_PARTIAL_LINE; + +	/* No time */ +	if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) {  		ret = trace_seq_printf(s, "            |  ");  		if (!ret)  			return TRACE_TYPE_PARTIAL_LINE; -	} else { -		if (ret == TRACE_TYPE_PARTIAL_LINE) -			return TRACE_TYPE_PARTIAL_LINE;  	} -  	/* Function */  	for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) {  		ret = trace_seq_printf(s, " "); @@ -428,20 +575,40 @@ print_graph_entry_nested(struct ftrace_graph_ent_entry *entry,  	if (!ret)  		return TRACE_TYPE_PARTIAL_LINE; -	return TRACE_TYPE_HANDLED; +	/* +	 * we already consumed the current entry to check the next one +	 * and see if this is a leaf. +	 */ +	return TRACE_TYPE_NO_CONSUME;  }  static enum print_line_t -print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s, -			struct trace_iterator *iter, int cpu) +print_graph_prologue(struct trace_iterator *iter, struct trace_seq *s, +		     int type, unsigned long addr)  { -	int ret; +	struct fgraph_data *data = iter->private;  	struct trace_entry *ent = iter->ent; +	int cpu = iter->cpu; +	int ret;  	/* Pid */ -	if (verif_pid(s, ent->pid, cpu) == TRACE_TYPE_PARTIAL_LINE) +	if (verif_pid(s, ent->pid, cpu, data) == TRACE_TYPE_PARTIAL_LINE)  		return TRACE_TYPE_PARTIAL_LINE; +	if (type) { +		/* Interrupt */ +		ret = print_graph_irq(iter, addr, type, cpu, ent->pid); +		if (ret == TRACE_TYPE_PARTIAL_LINE) +			return TRACE_TYPE_PARTIAL_LINE; +	} + +	/* Absolute time */ +	if (tracer_flags.val & TRACE_GRAPH_PRINT_ABS_TIME) { +		ret = print_graph_abs_time(iter->ts, s); +		if (!ret) +			return TRACE_TYPE_PARTIAL_LINE; +	} +  	/* Cpu */  	if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) {  		ret = print_graph_cpu(s, cpu); @@ -460,54 +627,65 @@ print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s,  			return TRACE_TYPE_PARTIAL_LINE;  	} -	if (trace_branch_is_leaf(iter, field)) -		return print_graph_entry_leaf(iter, field, s); +	return 0; +} + +static enum print_line_t +print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s, +			struct trace_iterator *iter) +{ +	int cpu = iter->cpu; +	struct ftrace_graph_ent *call = &field->graph_ent; +	struct ftrace_graph_ret_entry *leaf_ret; + +	if (print_graph_prologue(iter, s, TRACE_GRAPH_ENT, call->func)) +		return TRACE_TYPE_PARTIAL_LINE; + +	leaf_ret = get_return_for_leaf(iter, field); +	if (leaf_ret) +		return print_graph_entry_leaf(iter, field, leaf_ret, s);  	else -		return print_graph_entry_nested(field, s, iter->ent->pid, cpu); +		return print_graph_entry_nested(iter, field, s, cpu);  }  static enum print_line_t  print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, -		   struct trace_entry *ent, int cpu) +		   struct trace_entry *ent, struct trace_iterator *iter)  { -	int i; -	int ret;  	unsigned long long duration = trace->rettime - trace->calltime; +	struct fgraph_data *data = iter->private; +	pid_t pid = ent->pid; +	int cpu = iter->cpu; +	int ret; +	int i; -	/* Pid */ -	if (verif_pid(s, ent->pid, cpu) == TRACE_TYPE_PARTIAL_LINE) -		return TRACE_TYPE_PARTIAL_LINE; +	if (data) { +		int cpu = iter->cpu; +		int *depth = &(per_cpu_ptr(data, cpu)->depth); -	/* Cpu */ -	if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) { -		ret = print_graph_cpu(s, cpu); -		if (ret == TRACE_TYPE_PARTIAL_LINE) -			return TRACE_TYPE_PARTIAL_LINE; +		/* +		 * Comments display at + 1 to depth. This is the +		 * return from a function, we now want the comments +		 * to display at the same level of the bracket. +		 */ +		*depth = trace->depth - 1;  	} -	/* Proc */ -	if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) { -		ret = print_graph_proc(s, ent->pid); -		if (ret == TRACE_TYPE_PARTIAL_LINE) -			return TRACE_TYPE_PARTIAL_LINE; - -		ret = trace_seq_printf(s, " | "); -		if (!ret) -			return TRACE_TYPE_PARTIAL_LINE; -	} +	if (print_graph_prologue(iter, s, 0, 0)) +		return TRACE_TYPE_PARTIAL_LINE;  	/* Overhead */ -	if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { -		ret = print_graph_overhead(duration, s); -		if (!ret) -			return TRACE_TYPE_PARTIAL_LINE; -	} +	ret = print_graph_overhead(duration, s); +	if (!ret) +		return TRACE_TYPE_PARTIAL_LINE;  	/* Duration */ -	ret = print_graph_duration(duration, s); -	if (ret == TRACE_TYPE_PARTIAL_LINE) -		return TRACE_TYPE_PARTIAL_LINE; +	if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) { +		ret = print_graph_duration(duration, s); +		if (ret == TRACE_TYPE_PARTIAL_LINE) +			return TRACE_TYPE_PARTIAL_LINE; +	}  	/* Closing brace */  	for (i = 0; i < trace->depth * TRACE_GRAPH_INDENT; i++) { @@ -528,7 +706,7 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,  			return TRACE_TYPE_PARTIAL_LINE;  	} -	ret = print_graph_irq(s, trace->func, TRACE_GRAPH_RET, cpu, ent->pid); +	ret = print_graph_irq(iter, trace->func, TRACE_GRAPH_RET, cpu, pid);  	if (ret == TRACE_TYPE_PARTIAL_LINE)  		return TRACE_TYPE_PARTIAL_LINE; @@ -536,61 +714,73 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,  }  static enum print_line_t -print_graph_comment(struct print_entry *trace, struct trace_seq *s, -		   struct trace_entry *ent, struct trace_iterator *iter) +print_graph_comment(struct trace_seq *s,  struct trace_entry *ent, +		    struct trace_iterator *iter)  { -	int i; +	unsigned long sym_flags = (trace_flags & TRACE_ITER_SYM_MASK); +	struct fgraph_data *data = iter->private; +	struct trace_event *event; +	int depth = 0;  	int ret; +	int i; -	/* Pid */ -	if (verif_pid(s, ent->pid, iter->cpu) == TRACE_TYPE_PARTIAL_LINE) -		return TRACE_TYPE_PARTIAL_LINE; - -	/* Cpu */ -	if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) { -		ret = print_graph_cpu(s, iter->cpu); -		if (ret == TRACE_TYPE_PARTIAL_LINE) -			return TRACE_TYPE_PARTIAL_LINE; -	} - -	/* Proc */ -	if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) { -		ret = print_graph_proc(s, ent->pid); -		if (ret == TRACE_TYPE_PARTIAL_LINE) -			return TRACE_TYPE_PARTIAL_LINE; +	if (data) +		depth = per_cpu_ptr(data, iter->cpu)->depth; -		ret = trace_seq_printf(s, " | "); -		if (!ret) -			return TRACE_TYPE_PARTIAL_LINE; -	} +	if (print_graph_prologue(iter, s, 0, 0)) +		return TRACE_TYPE_PARTIAL_LINE;  	/* No overhead */ -	if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { -		ret = trace_seq_printf(s, "  "); +	ret = print_graph_overhead(-1, s); +	if (!ret) +		return TRACE_TYPE_PARTIAL_LINE; + +	/* No time */ +	if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) { +		ret = trace_seq_printf(s, "            |  ");  		if (!ret)  			return TRACE_TYPE_PARTIAL_LINE;  	} -	/* No time */ -	ret = trace_seq_printf(s, "            |  "); -	if (!ret) -		return TRACE_TYPE_PARTIAL_LINE; -  	/* Indentation */ -	if (trace->depth > 0) -		for (i = 0; i < (trace->depth + 1) * TRACE_GRAPH_INDENT; i++) { +	if (depth > 0) +		for (i = 0; i < (depth + 1) * TRACE_GRAPH_INDENT; i++) {  			ret = trace_seq_printf(s, " ");  			if (!ret)  				return TRACE_TYPE_PARTIAL_LINE;  		}  	/* The comment */ -	ret = trace_seq_printf(s, "/* %s", trace->buf); +	ret = trace_seq_printf(s, "/* ");  	if (!ret)  		return TRACE_TYPE_PARTIAL_LINE; -	if (ent->flags & TRACE_FLAG_CONT) -		trace_seq_print_cont(s, iter); +	switch (iter->ent->type) { +	case TRACE_BPRINT: +		ret = trace_print_bprintk_msg_only(iter); +		if (ret != TRACE_TYPE_HANDLED) +			return ret; +		break; +	case TRACE_PRINT: +		ret = trace_print_printk_msg_only(iter); +		if (ret != TRACE_TYPE_HANDLED) +			return ret; +		break; +	default: +		event = ftrace_find_event(ent->type); +		if (!event) +			return TRACE_TYPE_UNHANDLED; + +		ret = event->trace(iter, sym_flags); +		if (ret != TRACE_TYPE_HANDLED) +			return ret; +	} + +	/* Strip ending newline */ +	if (s->buffer[s->len - 1] == '\n') { +		s->buffer[s->len - 1] = '\0'; +		s->len--; +	}  	ret = trace_seq_printf(s, " */\n");  	if (!ret) @@ -603,62 +793,91 @@ print_graph_comment(struct print_entry *trace, struct trace_seq *s,  enum print_line_t  print_graph_function(struct trace_iterator *iter)  { -	struct trace_seq *s = &iter->seq;  	struct trace_entry *entry = iter->ent; +	struct trace_seq *s = &iter->seq;  	switch (entry->type) {  	case TRACE_GRAPH_ENT: {  		struct ftrace_graph_ent_entry *field;  		trace_assign_type(field, entry); -		return print_graph_entry(field, s, iter, -					 iter->cpu); +		return print_graph_entry(field, s, iter);  	}  	case TRACE_GRAPH_RET: {  		struct ftrace_graph_ret_entry *field;  		trace_assign_type(field, entry); -		return print_graph_return(&field->ret, s, entry, iter->cpu); -	} -	case TRACE_PRINT: { -		struct print_entry *field; -		trace_assign_type(field, entry); -		return print_graph_comment(field, s, entry, iter); +		return print_graph_return(&field->ret, s, entry, iter);  	}  	default: -		return TRACE_TYPE_UNHANDLED; +		return print_graph_comment(s, entry, iter);  	} + +	return TRACE_TYPE_HANDLED;  }  static void print_graph_headers(struct seq_file *s)  {  	/* 1st line */  	seq_printf(s, "# "); +	if (tracer_flags.val & TRACE_GRAPH_PRINT_ABS_TIME) +		seq_printf(s, "     TIME       ");  	if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) -		seq_printf(s, "CPU "); +		seq_printf(s, "CPU");  	if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) -		seq_printf(s, "TASK/PID     "); -	if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) -		seq_printf(s, "OVERHEAD/"); -	seq_printf(s, "DURATION            FUNCTION CALLS\n"); +		seq_printf(s, "  TASK/PID      "); +	if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) +		seq_printf(s, "  DURATION   "); +	seq_printf(s, "               FUNCTION CALLS\n");  	/* 2nd line */  	seq_printf(s, "# "); +	if (tracer_flags.val & TRACE_GRAPH_PRINT_ABS_TIME) +		seq_printf(s, "      |         ");  	if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) -		seq_printf(s, "|   "); +		seq_printf(s, "|  ");  	if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) -		seq_printf(s, "|      |     "); -	if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { -		seq_printf(s, "|        "); -		seq_printf(s, "|                   |   |   |   |\n"); -	} else -		seq_printf(s, "    |               |   |   |   |\n"); +		seq_printf(s, "  |    |        "); +	if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) +		seq_printf(s, "   |   |      "); +	seq_printf(s, "               |   |   |   |\n"); +} + +static void graph_trace_open(struct trace_iterator *iter) +{ +	/* pid and depth on the last trace processed */ +	struct fgraph_data *data = alloc_percpu(struct fgraph_data); +	int cpu; + +	if (!data) +		pr_warning("function graph tracer: not enough memory\n"); +	else +		for_each_possible_cpu(cpu) { +			pid_t *pid = &(per_cpu_ptr(data, cpu)->last_pid); +			int *depth = &(per_cpu_ptr(data, cpu)->depth); +			*pid = -1; +			*depth = 0; +		} + +	iter->private = data;  } + +static void graph_trace_close(struct trace_iterator *iter) +{ +	free_percpu(iter->private); +} +  static struct tracer graph_trace __read_mostly = { -	.name	     	= "function_graph", -	.init	     	= graph_trace_init, -	.reset	     	= graph_trace_reset, +	.name		= "function_graph", +	.open		= graph_trace_open, +	.close		= graph_trace_close, +	.wait_pipe	= poll_wait_pipe, +	.init		= graph_trace_init, +	.reset		= graph_trace_reset,  	.print_line	= print_graph_function,  	.print_header	= print_graph_headers,  	.flags		= &tracer_flags, +#ifdef CONFIG_FTRACE_SELFTEST +	.selftest	= trace_selftest_startup_function_graph, +#endif  };  static __init int init_graph_trace(void) |