diff options
Diffstat (limited to 'kernel/trace/trace_output.c')
| -rw-r--r-- | kernel/trace/trace_output.c | 407 | 
1 files changed, 338 insertions, 69 deletions
diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index 02272baa220..f3dad80c20b 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -14,7 +14,7 @@  /* must be a power of 2 */  #define EVENT_HASHSIZE	128 -DECLARE_RWSEM(trace_event_mutex); +DECLARE_RWSEM(trace_event_sem);  static struct hlist_head event_hash[EVENT_HASHSIZE] __read_mostly; @@ -37,6 +37,22 @@ int trace_print_seq(struct seq_file *m, struct trace_seq *s)  	return ret;  } +enum print_line_t trace_print_bputs_msg_only(struct trace_iterator *iter) +{ +	struct trace_seq *s = &iter->seq; +	struct trace_entry *entry = iter->ent; +	struct bputs_entry *field; +	int ret; + +	trace_assign_type(field, entry); + +	ret = trace_seq_puts(s, field->str); +	if (!ret) +		return TRACE_TYPE_PARTIAL_LINE; + +	return TRACE_TYPE_HANDLED; +} +  enum print_line_t trace_print_bprintk_msg_only(struct trace_iterator *iter)  {  	struct trace_seq *s = &iter->seq; @@ -62,7 +78,7 @@ enum print_line_t trace_print_printk_msg_only(struct trace_iterator *iter)  	trace_assign_type(field, entry); -	ret = trace_seq_printf(s, "%s", field->buf); +	ret = trace_seq_puts(s, field->buf);  	if (!ret)  		return TRACE_TYPE_PARTIAL_LINE; @@ -110,6 +126,34 @@ trace_seq_printf(struct trace_seq *s, const char *fmt, ...)  EXPORT_SYMBOL_GPL(trace_seq_printf);  /** + * trace_seq_bitmask - put a list of longs as a bitmask print output + * @s:		trace sequence descriptor + * @maskp:	points to an array of unsigned longs that represent a bitmask + * @nmaskbits:	The number of bits that are valid in @maskp + * + * It returns 0 if the trace oversizes the buffer's free + * space, 1 otherwise. + * + * Writes a ASCII representation of a bitmask string into @s. + */ +int +trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp, +		  int nmaskbits) +{ +	int len = (PAGE_SIZE - 1) - s->len; +	int ret; + +	if (s->full || !len) +		return 0; + +	ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits); +	s->len += ret; + +	return 1; +} +EXPORT_SYMBOL_GPL(trace_seq_bitmask); + +/**   * trace_seq_vprintf - sequence printing of trace information   * @s: trace sequence descriptor   * @fmt: printf format string @@ -264,7 +308,7 @@ void *trace_seq_reserve(struct trace_seq *s, size_t len)  	return ret;  } -int trace_seq_path(struct trace_seq *s, struct path *path) +int trace_seq_path(struct trace_seq *s, const struct path *path)  {  	unsigned char *p; @@ -300,7 +344,7 @@ ftrace_print_flags_seq(struct trace_seq *p, const char *delim,  	unsigned long mask;  	const char *str;  	const char *ret = p->buffer + p->len; -	int i; +	int i, first = 1;  	for (i = 0;  flag_array[i].name && flags; i++) { @@ -310,14 +354,16 @@ ftrace_print_flags_seq(struct trace_seq *p, const char *delim,  		str = flag_array[i].name;  		flags &= ~mask; -		if (p->len && delim) +		if (!first && delim)  			trace_seq_puts(p, delim); +		else +			first = 0;  		trace_seq_puts(p, str);  	}  	/* check for left over flags */  	if (flags) { -		if (p->len && delim) +		if (!first && delim)  			trace_seq_puts(p, delim);  		trace_seq_printf(p, "0x%lx", flags);  	} @@ -344,7 +390,7 @@ ftrace_print_symbols_seq(struct trace_seq *p, unsigned long val,  		break;  	} -	if (!p->len) +	if (ret == (const char *)(p->buffer + p->len))  		trace_seq_printf(p, "0x%lx", val);  	trace_seq_putc(p, 0); @@ -353,6 +399,46 @@ ftrace_print_symbols_seq(struct trace_seq *p, unsigned long val,  }  EXPORT_SYMBOL(ftrace_print_symbols_seq); +#if BITS_PER_LONG == 32 +const char * +ftrace_print_symbols_seq_u64(struct trace_seq *p, unsigned long long val, +			 const struct trace_print_flags_u64 *symbol_array) +{ +	int i; +	const char *ret = p->buffer + p->len; + +	for (i = 0;  symbol_array[i].name; i++) { + +		if (val != symbol_array[i].mask) +			continue; + +		trace_seq_puts(p, symbol_array[i].name); +		break; +	} + +	if (ret == (const char *)(p->buffer + p->len)) +		trace_seq_printf(p, "0x%llx", val); + +	trace_seq_putc(p, 0); + +	return ret; +} +EXPORT_SYMBOL(ftrace_print_symbols_seq_u64); +#endif + +const char * +ftrace_print_bitmask_seq(struct trace_seq *p, void *bitmask_ptr, +			 unsigned int bitmask_size) +{ +	const char *ret = p->buffer + p->len; + +	trace_seq_bitmask(p, bitmask_ptr, bitmask_size * 8); +	trace_seq_putc(p, 0); + +	return ret; +} +EXPORT_SYMBOL_GPL(ftrace_print_bitmask_seq); +  const char *  ftrace_print_hex_seq(struct trace_seq *p, const unsigned char *buf, int buf_len)  { @@ -368,6 +454,63 @@ ftrace_print_hex_seq(struct trace_seq *p, const unsigned char *buf, int buf_len)  }  EXPORT_SYMBOL(ftrace_print_hex_seq); +int ftrace_raw_output_prep(struct trace_iterator *iter, +			   struct trace_event *trace_event) +{ +	struct ftrace_event_call *event; +	struct trace_seq *s = &iter->seq; +	struct trace_seq *p = &iter->tmp_seq; +	struct trace_entry *entry; +	int ret; + +	event = container_of(trace_event, struct ftrace_event_call, event); +	entry = iter->ent; + +	if (entry->type != event->event.type) { +		WARN_ON_ONCE(1); +		return TRACE_TYPE_UNHANDLED; +	} + +	trace_seq_init(p); +	ret = trace_seq_printf(s, "%s: ", ftrace_event_name(event)); +	if (!ret) +		return TRACE_TYPE_PARTIAL_LINE; + +	return 0; +} +EXPORT_SYMBOL(ftrace_raw_output_prep); + +static int ftrace_output_raw(struct trace_iterator *iter, char *name, +			     char *fmt, va_list ap) +{ +	struct trace_seq *s = &iter->seq; +	int ret; + +	ret = trace_seq_printf(s, "%s: ", name); +	if (!ret) +		return TRACE_TYPE_PARTIAL_LINE; + +	ret = trace_seq_vprintf(s, fmt, ap); + +	if (!ret) +		return TRACE_TYPE_PARTIAL_LINE; + +	return TRACE_TYPE_HANDLED; +} + +int ftrace_output_call(struct trace_iterator *iter, char *name, char *fmt, ...) +{ +	va_list ap; +	int ret; + +	va_start(ap, fmt); +	ret = ftrace_output_raw(iter, name, fmt, ap); +	va_end(ap); + +	return ret; +} +EXPORT_SYMBOL_GPL(ftrace_output_call); +  #ifdef CONFIG_KRETPROBES  static inline const char *kretprobed(const char *name)  { @@ -487,14 +630,14 @@ seq_print_userip_objs(const struct userstack_entry *entry, struct trace_seq *s,  			if (ret)  				ret = trace_seq_puts(s, "??");  			if (ret) -				ret = trace_seq_puts(s, "\n"); +				ret = trace_seq_putc(s, '\n');  			continue;  		}  		if (!ret)  			break;  		if (ret)  			ret = seq_print_user_ip(s, mm, ip, sym_flags); -		ret = trace_seq_puts(s, "\n"); +		ret = trace_seq_putc(s, '\n');  	}  	if (mm) @@ -508,7 +651,7 @@ seq_print_ip_sym(struct trace_seq *s, unsigned long ip, unsigned long sym_flags)  	int ret;  	if (!ip) -		return trace_seq_printf(s, "0"); +		return trace_seq_putc(s, '0');  	if (sym_flags & TRACE_ITER_SYM_OFFSET)  		ret = seq_print_sym_offset(s, "%s", ip); @@ -529,24 +672,49 @@ seq_print_ip_sym(struct trace_seq *s, unsigned long ip, unsigned long sym_flags)   * @entry: The trace entry field from the ring buffer   *   * Prints the generic fields of irqs off, in hard or softirq, preempt - * count and lock depth. + * count.   */  int trace_print_lat_fmt(struct trace_seq *s, struct trace_entry *entry)  { -	int hardirq, softirq; +	char hardsoft_irq; +	char need_resched; +	char irqs_off; +	int hardirq; +	int softirq;  	int ret;  	hardirq = entry->flags & TRACE_FLAG_HARDIRQ;  	softirq = entry->flags & TRACE_FLAG_SOFTIRQ; +	irqs_off = +		(entry->flags & TRACE_FLAG_IRQS_OFF) ? 'd' : +		(entry->flags & TRACE_FLAG_IRQS_NOSUPPORT) ? 'X' : +		'.'; + +	switch (entry->flags & (TRACE_FLAG_NEED_RESCHED | +				TRACE_FLAG_PREEMPT_RESCHED)) { +	case TRACE_FLAG_NEED_RESCHED | TRACE_FLAG_PREEMPT_RESCHED: +		need_resched = 'N'; +		break; +	case TRACE_FLAG_NEED_RESCHED: +		need_resched = 'n'; +		break; +	case TRACE_FLAG_PREEMPT_RESCHED: +		need_resched = 'p'; +		break; +	default: +		need_resched = '.'; +		break; +	} + +	hardsoft_irq = +		(hardirq && softirq) ? 'H' : +		hardirq ? 'h' : +		softirq ? 's' : +		'.'; +  	if (!trace_seq_printf(s, "%c%c%c", -			      (entry->flags & TRACE_FLAG_IRQS_OFF) ? 'd' : -				(entry->flags & TRACE_FLAG_IRQS_NOSUPPORT) ? -				  'X' : '.', -			      (entry->flags & TRACE_FLAG_NEED_RESCHED) ? -				'N' : '.', -			      (hardirq && softirq) ? 'H' : -				hardirq ? 'h' : softirq ? 's' : '.')) +			      irqs_off, need_resched, hardsoft_irq))  		return 0;  	if (entry->preempt_count) @@ -554,13 +722,7 @@ int trace_print_lat_fmt(struct trace_seq *s, struct trace_entry *entry)  	else  		ret = trace_seq_putc(s, '.'); -	if (!ret) -		return 0; - -	if (entry->lock_depth < 0) -		return trace_seq_putc(s, '.'); - -	return trace_seq_printf(s, "%d", entry->lock_depth); +	return ret;  }  static int @@ -577,68 +739,113 @@ lat_print_generic(struct trace_seq *s, struct trace_entry *entry, int cpu)  	return trace_print_lat_fmt(s, entry);  } -static unsigned long preempt_mark_thresh = 100; +static unsigned long preempt_mark_thresh_us = 100;  static int -lat_print_timestamp(struct trace_seq *s, u64 abs_usecs, -		    unsigned long rel_usecs) +lat_print_timestamp(struct trace_iterator *iter, u64 next_ts)  { -	return trace_seq_printf(s, " %4lldus%c: ", abs_usecs, -				rel_usecs > preempt_mark_thresh ? '!' : -				  rel_usecs > 1 ? '+' : ' '); +	unsigned long verbose = trace_flags & TRACE_ITER_VERBOSE; +	unsigned long in_ns = iter->iter_flags & TRACE_FILE_TIME_IN_NS; +	unsigned long long abs_ts = iter->ts - iter->trace_buffer->time_start; +	unsigned long long rel_ts = next_ts - iter->ts; +	struct trace_seq *s = &iter->seq; + +	if (in_ns) { +		abs_ts = ns2usecs(abs_ts); +		rel_ts = ns2usecs(rel_ts); +	} + +	if (verbose && in_ns) { +		unsigned long abs_usec = do_div(abs_ts, USEC_PER_MSEC); +		unsigned long abs_msec = (unsigned long)abs_ts; +		unsigned long rel_usec = do_div(rel_ts, USEC_PER_MSEC); +		unsigned long rel_msec = (unsigned long)rel_ts; + +		return trace_seq_printf( +				s, "[%08llx] %ld.%03ldms (+%ld.%03ldms): ", +				ns2usecs(iter->ts), +				abs_msec, abs_usec, +				rel_msec, rel_usec); +	} else if (verbose && !in_ns) { +		return trace_seq_printf( +				s, "[%016llx] %lld (+%lld): ", +				iter->ts, abs_ts, rel_ts); +	} else if (!verbose && in_ns) { +		return trace_seq_printf( +				s, " %4lldus%c: ", +				abs_ts, +				rel_ts > preempt_mark_thresh_us ? '!' : +				  rel_ts > 1 ? '+' : ' '); +	} else { /* !verbose && !in_ns */ +		return trace_seq_printf(s, " %4lld: ", abs_ts); +	}  }  int trace_print_context(struct trace_iterator *iter)  {  	struct trace_seq *s = &iter->seq;  	struct trace_entry *entry = iter->ent; -	unsigned long long t = ns2usecs(iter->ts); -	unsigned long usec_rem = do_div(t, USEC_PER_SEC); -	unsigned long secs = (unsigned long)t; +	unsigned long long t; +	unsigned long secs, usec_rem;  	char comm[TASK_COMM_LEN]; +	int ret;  	trace_find_cmdline(entry->pid, comm); -	return trace_seq_printf(s, "%16s-%-5d [%03d] %5lu.%06lu: ", -				comm, entry->pid, iter->cpu, secs, usec_rem); +	ret = trace_seq_printf(s, "%16s-%-5d [%03d] ", +			       comm, entry->pid, iter->cpu); +	if (!ret) +		return 0; + +	if (trace_flags & TRACE_ITER_IRQ_INFO) { +		ret = trace_print_lat_fmt(s, entry); +		if (!ret) +			return 0; +	} + +	if (iter->iter_flags & TRACE_FILE_TIME_IN_NS) { +		t = ns2usecs(iter->ts); +		usec_rem = do_div(t, USEC_PER_SEC); +		secs = (unsigned long)t; +		return trace_seq_printf(s, " %5lu.%06lu: ", secs, usec_rem); +	} else +		return trace_seq_printf(s, " %12llu: ", iter->ts);  }  int trace_print_lat_context(struct trace_iterator *iter)  {  	u64 next_ts;  	int ret; +	/* trace_find_next_entry will reset ent_size */ +	int ent_size = iter->ent_size;  	struct trace_seq *s = &iter->seq;  	struct trace_entry *entry = iter->ent,  			   *next_entry = trace_find_next_entry(iter, NULL,  							       &next_ts);  	unsigned long verbose = (trace_flags & TRACE_ITER_VERBOSE); -	unsigned long abs_usecs = ns2usecs(iter->ts - iter->tr->time_start); -	unsigned long rel_usecs; + +	/* Restore the original ent_size */ +	iter->ent_size = ent_size;  	if (!next_entry)  		next_ts = iter->ts; -	rel_usecs = ns2usecs(next_ts - iter->ts);  	if (verbose) {  		char comm[TASK_COMM_LEN];  		trace_find_cmdline(entry->pid, comm); -		ret = trace_seq_printf(s, "%16s %5d %3d %d %08x %08lx [%08llx]" -				       " %ld.%03ldms (+%ld.%03ldms): ", comm, -				       entry->pid, iter->cpu, entry->flags, -				       entry->preempt_count, iter->idx, -				       ns2usecs(iter->ts), -				       abs_usecs / USEC_PER_MSEC, -				       abs_usecs % USEC_PER_MSEC, -				       rel_usecs / USEC_PER_MSEC, -				       rel_usecs % USEC_PER_MSEC); +		ret = trace_seq_printf( +				s, "%16s %5d %3d %d %08x %08lx ", +				comm, entry->pid, iter->cpu, entry->flags, +				entry->preempt_count, iter->idx);  	} else {  		ret = lat_print_generic(s, entry, iter->cpu); -		if (ret) -			ret = lat_print_timestamp(s, abs_usecs, rel_usecs);  	} +	if (ret) +		ret = lat_print_timestamp(iter, next_ts); +  	return ret;  } @@ -661,12 +868,11 @@ static int task_state_char(unsigned long state)  struct trace_event *ftrace_find_event(int type)  {  	struct trace_event *event; -	struct hlist_node *n;  	unsigned key;  	key = type & (EVENT_HASHSIZE - 1); -	hlist_for_each_entry(event, n, &event_hash[key], node) { +	hlist_for_each_entry(event, &event_hash[key], node) {  		if (event->type == type)  			return event;  	} @@ -706,12 +912,12 @@ static int trace_search_list(struct list_head **list)  void trace_event_read_lock(void)  { -	down_read(&trace_event_mutex); +	down_read(&trace_event_sem);  }  void trace_event_read_unlock(void)  { -	up_read(&trace_event_mutex); +	up_read(&trace_event_sem);  }  /** @@ -734,7 +940,7 @@ int register_ftrace_event(struct trace_event *event)  	unsigned key;  	int ret = 0; -	down_write(&trace_event_mutex); +	down_write(&trace_event_sem);  	if (WARN_ON(!event))  		goto out; @@ -789,14 +995,14 @@ int register_ftrace_event(struct trace_event *event)  	ret = event->type;   out: -	up_write(&trace_event_mutex); +	up_write(&trace_event_sem);  	return ret;  }  EXPORT_SYMBOL_GPL(register_ftrace_event);  /* - * Used by module code with the trace_event_mutex held for write. + * Used by module code with the trace_event_sem held for write.   */  int __unregister_ftrace_event(struct trace_event *event)  { @@ -811,9 +1017,9 @@ int __unregister_ftrace_event(struct trace_event *event)   */  int unregister_ftrace_event(struct trace_event *event)  { -	down_write(&trace_event_mutex); +	down_write(&trace_event_sem);  	__unregister_ftrace_event(event); -	up_write(&trace_event_mutex); +	up_write(&trace_event_sem);  	return 0;  } @@ -826,6 +1032,9 @@ EXPORT_SYMBOL_GPL(unregister_ftrace_event);  enum print_line_t trace_nop_print(struct trace_iterator *iter, int flags,  				  struct trace_event *event)  { +	if (!trace_seq_printf(&iter->seq, "type: %d\n", iter->ent->type)) +		return TRACE_TYPE_PARTIAL_LINE; +  	return TRACE_TYPE_HANDLED;  } @@ -842,14 +1051,14 @@ static enum print_line_t trace_fn_trace(struct trace_iterator *iter, int flags,  		goto partial;  	if ((flags & TRACE_ITER_PRINT_PARENT) && field->parent_ip) { -		if (!trace_seq_printf(s, " <-")) +		if (!trace_seq_puts(s, " <-"))  			goto partial;  		if (!seq_print_ip_sym(s,  				      field->parent_ip,  				      flags))  			goto partial;  	} -	if (!trace_seq_printf(s, "\n")) +	if (!trace_seq_putc(s, '\n'))  		goto partial;  	return TRACE_TYPE_HANDLED; @@ -1073,21 +1282,22 @@ static enum print_line_t trace_stack_print(struct trace_iterator *iter,  {  	struct stack_entry *field;  	struct trace_seq *s = &iter->seq; -	int i; +	unsigned long *p; +	unsigned long *end;  	trace_assign_type(field, iter->ent); +	end = (unsigned long *)((long)iter->ent + iter->ent_size);  	if (!trace_seq_puts(s, "<stack trace>\n"))  		goto partial; -	for (i = 0; i < FTRACE_STACK_ENTRIES; i++) { -		if (!field->caller[i] || (field->caller[i] == ULONG_MAX)) -			break; + +	for (p = field->caller; p && *p != ULONG_MAX && p < end; p++) {  		if (!trace_seq_puts(s, " => "))  			goto partial; -		if (!seq_print_ip_sym(s, field->caller[i], flags)) +		if (!seq_print_ip_sym(s, *p, flags))  			goto partial; -		if (!trace_seq_puts(s, "\n")) +		if (!trace_seq_putc(s, '\n'))  			goto partial;  	} @@ -1136,6 +1346,64 @@ static struct trace_event trace_user_stack_event = {  	.funcs		= &trace_user_stack_funcs,  }; +/* TRACE_BPUTS */ +static enum print_line_t +trace_bputs_print(struct trace_iterator *iter, int flags, +		   struct trace_event *event) +{ +	struct trace_entry *entry = iter->ent; +	struct trace_seq *s = &iter->seq; +	struct bputs_entry *field; + +	trace_assign_type(field, entry); + +	if (!seq_print_ip_sym(s, field->ip, flags)) +		goto partial; + +	if (!trace_seq_puts(s, ": ")) +		goto partial; + +	if (!trace_seq_puts(s, field->str)) +		goto partial; + +	return TRACE_TYPE_HANDLED; + + partial: +	return TRACE_TYPE_PARTIAL_LINE; +} + + +static enum print_line_t +trace_bputs_raw(struct trace_iterator *iter, int flags, +		struct trace_event *event) +{ +	struct bputs_entry *field; +	struct trace_seq *s = &iter->seq; + +	trace_assign_type(field, iter->ent); + +	if (!trace_seq_printf(s, ": %lx : ", field->ip)) +		goto partial; + +	if (!trace_seq_puts(s, field->str)) +		goto partial; + +	return TRACE_TYPE_HANDLED; + + partial: +	return TRACE_TYPE_PARTIAL_LINE; +} + +static struct trace_event_functions trace_bputs_funcs = { +	.trace		= trace_bputs_print, +	.raw		= trace_bputs_raw, +}; + +static struct trace_event trace_bputs_event = { +	.type		= TRACE_BPUTS, +	.funcs		= &trace_bputs_funcs, +}; +  /* TRACE_BPRINT */  static enum print_line_t  trace_bprint_print(struct trace_iterator *iter, int flags, @@ -1248,6 +1516,7 @@ static struct trace_event *events[] __initdata = {  	&trace_wake_event,  	&trace_stack_event,  	&trace_user_stack_event, +	&trace_bputs_event,  	&trace_bprint_event,  	&trace_print_event,  	NULL @@ -1271,4 +1540,4 @@ __init static int init_events(void)  	return 0;  } -device_initcall(init_events); +early_initcall(init_events);  | 
