455 lines · 10.1 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * unlikely profiler * * Copyright (C) 2008 Steven Rostedt <srostedt@redhat.com> */#include <linux/kallsyms.h>#include <linux/seq_file.h>#include <linux/spinlock.h>#include <linux/irqflags.h>#include <linux/uaccess.h>#include <linux/module.h>#include <linux/ftrace.h>#include <linux/hash.h>#include <linux/fs.h>#include <asm/local.h> #include "trace.h"#include "trace_stat.h"#include "trace_output.h" #ifdef CONFIG_BRANCH_TRACER static struct tracer branch_trace;static int branch_tracing_enabled __read_mostly;static DEFINE_MUTEX(branch_tracing_mutex); static struct trace_array *branch_tracer; static voidprobe_likely_condition(struct ftrace_likely_data *f, int val, int expect){	struct trace_event_call *call = &event_branch;	struct trace_array *tr = branch_tracer;	struct trace_array_cpu *data;	struct ring_buffer_event *event;	struct trace_branch *entry;	struct ring_buffer *buffer;	unsigned long flags;	int pc;	const char *p; 	if (current->trace_recursion & TRACE_BRANCH_BIT)		return; 	/*	 * I would love to save just the ftrace_likely_data pointer, but	 * this code can also be used by modules. Ugly things can happen	 * if the module is unloaded, and then we go and read the	 * pointer.  This is slower, but much safer.	 */ 	if (unlikely(!tr))		return; 	raw_local_irq_save(flags);	current->trace_recursion |= TRACE_BRANCH_BIT;	data = this_cpu_ptr(tr->trace_buffer.data);	if (atomic_read(&data->disabled))		goto out; 	pc = preempt_count();	buffer = tr->trace_buffer.buffer;	event = trace_buffer_lock_reserve(buffer, TRACE_BRANCH,					  sizeof(*entry), flags, pc);	if (!event)		goto out; 	entry	= ring_buffer_event_data(event); 	/* Strip off the path, only save the file */	p = f->data.file + strlen(f->data.file);	while (p >= f->data.file && *p != '/')		p--;	p++; 	strncpy(entry->func, f->data.func, TRACE_FUNC_SIZE);	strncpy(entry->file, p, TRACE_FILE_SIZE);	entry->func[TRACE_FUNC_SIZE] = 0;	entry->file[TRACE_FILE_SIZE] = 0;	entry->constant = f->constant;	entry->line = f->data.line;	entry->correct = val == expect; 	if (!call_filter_check_discard(call, entry, buffer, event))		trace_buffer_unlock_commit_nostack(buffer, event);  out:	current->trace_recursion &= ~TRACE_BRANCH_BIT;	raw_local_irq_restore(flags);} static inlinevoid trace_likely_condition(struct ftrace_likely_data *f, int val, int expect){	if (!branch_tracing_enabled)		return; 	probe_likely_condition(f, val, expect);} int enable_branch_tracing(struct trace_array *tr){	mutex_lock(&branch_tracing_mutex);	branch_tracer = tr;	/*	 * Must be seen before enabling. The reader is a condition	 * where we do not need a matching rmb()	 */	smp_wmb();	branch_tracing_enabled++;	mutex_unlock(&branch_tracing_mutex); 	return 0;} void disable_branch_tracing(void){	mutex_lock(&branch_tracing_mutex); 	if (!branch_tracing_enabled)		goto out_unlock; 	branch_tracing_enabled--;  out_unlock:	mutex_unlock(&branch_tracing_mutex);} static int branch_trace_init(struct trace_array *tr){	return enable_branch_tracing(tr);} static void branch_trace_reset(struct trace_array *tr){	disable_branch_tracing();} static enum print_line_t trace_branch_print(struct trace_iterator *iter,					    int flags, struct trace_event *event){	struct trace_branch *field; 	trace_assign_type(field, iter->ent); 	trace_seq_printf(&iter->seq, "[%s] %s:%s:%d\n",			 field->correct ? "  ok  " : " MISS ",			 field->func,			 field->file,			 field->line); 	return trace_handle_return(&iter->seq);} static void branch_print_header(struct seq_file *s){	seq_puts(s, "#           TASK-PID    CPU#    TIMESTAMP  CORRECT"		    "  FUNC:FILE:LINE\n"		    "#              | |       |          |         |   "		    "    |\n");} static struct trace_event_functions trace_branch_funcs = {	.trace		= trace_branch_print,}; static struct trace_event trace_branch_event = {	.type		= TRACE_BRANCH,	.funcs		= &trace_branch_funcs,}; static struct tracer branch_trace __read_mostly ={	.name		= "branch",	.init		= branch_trace_init,	.reset		= branch_trace_reset,#ifdef CONFIG_FTRACE_SELFTEST	.selftest	= trace_selftest_startup_branch,#endif /* CONFIG_FTRACE_SELFTEST */	.print_header	= branch_print_header,}; __init static int init_branch_tracer(void){	int ret; 	ret = register_trace_event(&trace_branch_event);	if (!ret) {		printk(KERN_WARNING "Warning: could not register "				    "branch events\n");		return 1;	}	return register_tracer(&branch_trace);}core_initcall(init_branch_tracer); #elsestatic inlinevoid trace_likely_condition(struct ftrace_likely_data *f, int val, int expect){}#endif /* CONFIG_BRANCH_TRACER */ void ftrace_likely_update(struct ftrace_likely_data *f, int val,			  int expect, int is_constant){	unsigned long flags = user_access_save(); 	/* A constant is always correct */	if (is_constant) {		f->constant++;		val = expect;	}	/*	 * I would love to have a trace point here instead, but the	 * trace point code is so inundated with unlikely and likely	 * conditions that the recursive nightmare that exists is too	 * much to try to get working. At least for now.	 */	trace_likely_condition(f, val, expect); 	/* FIXME: Make this atomic! */	if (val == expect)		f->data.correct++;	else		f->data.incorrect++; 	user_access_restore(flags);}EXPORT_SYMBOL(ftrace_likely_update); extern unsigned long __start_annotated_branch_profile[];extern unsigned long __stop_annotated_branch_profile[]; static int annotated_branch_stat_headers(struct seq_file *m){	seq_puts(m, " correct incorrect  % "		    "       Function                "		    "  File              Line\n"		    " ------- ---------  - "		    "       --------                "		    "  ----              ----\n");	return 0;} static inline long get_incorrect_percent(struct ftrace_branch_data *p){	long percent; 	if (p->correct) {		percent = p->incorrect * 100;		percent /= p->correct + p->incorrect;	} else		percent = p->incorrect ? 100 : -1; 	return percent;} static const char *branch_stat_process_file(struct ftrace_branch_data *p){	const char *f; 	/* Only print the file, not the path */	f = p->file + strlen(p->file);	while (f >= p->file && *f != '/')		f--;	return ++f;} static void branch_stat_show(struct seq_file *m,			     struct ftrace_branch_data *p, const char *f){	long percent; 	/*	 * The miss is overlayed on correct, and hit on incorrect.	 */	percent = get_incorrect_percent(p); 	if (percent < 0)		seq_puts(m, "  X ");	else		seq_printf(m, "%3ld ", percent); 	seq_printf(m, "%-30.30s %-20.20s %d\n", p->func, f, p->line);} static int branch_stat_show_normal(struct seq_file *m,				   struct ftrace_branch_data *p, const char *f){	seq_printf(m, "%8lu %8lu ",  p->correct, p->incorrect);	branch_stat_show(m, p, f);	return 0;} static int annotate_branch_stat_show(struct seq_file *m, void *v){	struct ftrace_likely_data *p = v;	const char *f;	int l; 	f = branch_stat_process_file(&p->data); 	if (!p->constant)		return branch_stat_show_normal(m, &p->data, f); 	l = snprintf(NULL, 0, "/%lu", p->constant);	l = l > 8 ? 0 : 8 - l; 	seq_printf(m, "%8lu/%lu %*lu ",		   p->data.correct, p->constant, l, p->data.incorrect);	branch_stat_show(m, &p->data, f);	return 0;} static void *annotated_branch_stat_start(struct tracer_stat *trace){	return __start_annotated_branch_profile;} static void *annotated_branch_stat_next(void *v, int idx){	struct ftrace_likely_data *p = v; 	++p; 	if ((void *)p >= (void *)__stop_annotated_branch_profile)		return NULL; 	return p;} static int annotated_branch_stat_cmp(void *p1, void *p2){	struct ftrace_branch_data *a = p1;	struct ftrace_branch_data *b = p2; 	long percent_a, percent_b; 	percent_a = get_incorrect_percent(a);	percent_b = get_incorrect_percent(b); 	if (percent_a < percent_b)		return -1;	if (percent_a > percent_b)		return 1; 	if (a->incorrect < b->incorrect)		return -1;	if (a->incorrect > b->incorrect)		return 1; 	/*	 * Since the above shows worse (incorrect) cases	 * first, we continue that by showing best (correct)	 * cases last.	 */	if (a->correct > b->correct)		return -1;	if (a->correct < b->correct)		return 1; 	return 0;} static struct tracer_stat annotated_branch_stats = {	.name = "branch_annotated",	.stat_start = annotated_branch_stat_start,	.stat_next = annotated_branch_stat_next,	.stat_cmp = annotated_branch_stat_cmp,	.stat_headers = annotated_branch_stat_headers,	.stat_show = annotate_branch_stat_show}; __init static int init_annotated_branch_stats(void){	int ret; 	ret = register_stat_tracer(&annotated_branch_stats);	if (!ret) {		printk(KERN_WARNING "Warning: could not register "				    "annotated branches stats\n");		return 1;	}	return 0;}fs_initcall(init_annotated_branch_stats); #ifdef CONFIG_PROFILE_ALL_BRANCHES extern unsigned long __start_branch_profile[];extern unsigned long __stop_branch_profile[]; static int all_branch_stat_headers(struct seq_file *m){	seq_puts(m, "   miss      hit    % "		    "       Function                "		    "  File              Line\n"		    " ------- ---------  - "		    "       --------                "		    "  ----              ----\n");	return 0;} static void *all_branch_stat_start(struct tracer_stat *trace){	return __start_branch_profile;} static void *all_branch_stat_next(void *v, int idx){	struct ftrace_branch_data *p = v; 	++p; 	if ((void *)p >= (void *)__stop_branch_profile)		return NULL; 	return p;} static int all_branch_stat_show(struct seq_file *m, void *v){	struct ftrace_branch_data *p = v;	const char *f; 	f = branch_stat_process_file(p);	return branch_stat_show_normal(m, p, f);} static struct tracer_stat all_branch_stats = {	.name = "branch_all",	.stat_start = all_branch_stat_start,	.stat_next = all_branch_stat_next,	.stat_headers = all_branch_stat_headers,	.stat_show = all_branch_stat_show}; __init static int all_annotated_branch_stats(void){	int ret; 	ret = register_stat_tracer(&all_branch_stats);	if (!ret) {		printk(KERN_WARNING "Warning: could not register "				    "all branches stats\n");		return 1;	}	return 0;}fs_initcall(all_annotated_branch_stats);#endif /* CONFIG_PROFILE_ALL_BRANCHES */