diff options
Diffstat (limited to 'kernel/trace/trace_stack.c')
| -rw-r--r-- | kernel/trace/trace_stack.c | 78 | 
1 files changed, 70 insertions, 8 deletions
| diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c index 42ca822fc70..b20428c5efe 100644 --- a/kernel/trace/trace_stack.c +++ b/kernel/trace/trace_stack.c @@ -20,13 +20,24 @@  #define STACK_TRACE_ENTRIES 500 +#ifdef CC_USING_FENTRY +# define fentry		1 +#else +# define fentry		0 +#endif +  static unsigned long stack_dump_trace[STACK_TRACE_ENTRIES+1] =  	 { [0 ... (STACK_TRACE_ENTRIES)] = ULONG_MAX };  static unsigned stack_dump_index[STACK_TRACE_ENTRIES]; +/* + * Reserve one entry for the passed in ip. This will allow + * us to remove most or all of the stack size overhead + * added by the stack tracer itself. + */  static struct stack_trace max_stack_trace = { -	.max_entries		= STACK_TRACE_ENTRIES, -	.entries		= stack_dump_trace, +	.max_entries		= STACK_TRACE_ENTRIES - 1, +	.entries		= &stack_dump_trace[1],  };  static unsigned long max_stack_size; @@ -39,25 +50,34 @@ static DEFINE_MUTEX(stack_sysctl_mutex);  int stack_tracer_enabled;  static int last_stack_tracer_enabled; -static inline void check_stack(void) +static inline void +check_stack(unsigned long ip, unsigned long *stack)  {  	unsigned long this_size, flags;  	unsigned long *p, *top, *start; +	static int tracer_frame; +	int frame_size = ACCESS_ONCE(tracer_frame);  	int i; -	this_size = ((unsigned long)&this_size) & (THREAD_SIZE-1); +	this_size = ((unsigned long)stack) & (THREAD_SIZE-1);  	this_size = THREAD_SIZE - this_size; +	/* Remove the frame of the tracer */ +	this_size -= frame_size;  	if (this_size <= max_stack_size)  		return;  	/* we do not handle interrupt stacks yet */ -	if (!object_is_on_stack(&this_size)) +	if (!object_is_on_stack(stack))  		return;  	local_irq_save(flags);  	arch_spin_lock(&max_stack_lock); +	/* In case another CPU set the tracer_frame on us */ +	if (unlikely(!frame_size)) +		this_size -= tracer_frame; +  	/* a race could have already updated it */  	if (this_size <= max_stack_size)  		goto out; @@ -70,10 +90,18 @@ static inline void check_stack(void)  	save_stack_trace(&max_stack_trace);  	/* +	 * Add the passed in ip from the function tracer. +	 * Searching for this on the stack will skip over +	 * most of the overhead from the stack tracer itself. +	 */ +	stack_dump_trace[0] = ip; +	max_stack_trace.nr_entries++; + +	/*  	 * Now find where in the stack these are.  	 */  	i = 0; -	start = &this_size; +	start = stack;  	top = (unsigned long *)  		(((unsigned long)start & ~(THREAD_SIZE-1)) + THREAD_SIZE); @@ -97,6 +125,18 @@ static inline void check_stack(void)  				found = 1;  				/* Start the search from here */  				start = p + 1; +				/* +				 * We do not want to show the overhead +				 * of the stack tracer stack in the +				 * max stack. If we haven't figured +				 * out what that is, then figure it out +				 * now. +				 */ +				if (unlikely(!tracer_frame) && i == 1) { +					tracer_frame = (p - stack) * +						sizeof(unsigned long); +					max_stack_size -= tracer_frame; +				}  			}  		} @@ -113,6 +153,7 @@ static void  stack_trace_call(unsigned long ip, unsigned long parent_ip,  		 struct ftrace_ops *op, struct pt_regs *pt_regs)  { +	unsigned long stack;  	int cpu;  	preempt_disable_notrace(); @@ -122,7 +163,26 @@ stack_trace_call(unsigned long ip, unsigned long parent_ip,  	if (per_cpu(trace_active, cpu)++ != 0)  		goto out; -	check_stack(); +	/* +	 * When fentry is used, the traced function does not get +	 * its stack frame set up, and we lose the parent. +	 * The ip is pretty useless because the function tracer +	 * was called before that function set up its stack frame. +	 * In this case, we use the parent ip. +	 * +	 * By adding the return address of either the parent ip +	 * or the current ip we can disregard most of the stack usage +	 * caused by the stack tracer itself. +	 * +	 * The function tracer always reports the address of where the +	 * mcount call was, but the stack will hold the return address. +	 */ +	if (fentry) +		ip = parent_ip; +	else +		ip += MCOUNT_INSN_SIZE; + +	check_stack(ip, &stack);   out:  	per_cpu(trace_active, cpu)--; @@ -322,7 +382,7 @@ static const struct file_operations stack_trace_filter_fops = {  	.open = stack_trace_filter_open,  	.read = seq_read,  	.write = ftrace_filter_write, -	.llseek = ftrace_regex_lseek, +	.llseek = ftrace_filter_lseek,  	.release = ftrace_regex_release,  }; @@ -371,6 +431,8 @@ static __init int stack_trace_init(void)  	struct dentry *d_tracer;  	d_tracer = tracing_init_dentry(); +	if (!d_tracer) +		return 0;  	trace_create_file("stack_max_size", 0644, d_tracer,  			&max_stack_size, &stack_max_size_fops); | 
