Commit 2bcd521a authored by Steven Rostedt's avatar Steven Rostedt Committed by Ingo Molnar

trace: profile all if conditionals

Impact: feature to profile if statements

This patch adds a branch profiler for all if () statements.
The results will be found in:

  /debugfs/tracing/profile_branch

For example:

   miss      hit    %        Function                  File              Line
 ------- ---------  -        --------                  ----              ----
       0        1 100 x86_64_start_reservations      head64.c             127
       0        1 100 copy_bootdata                  head64.c             69
       1        0   0 x86_64_start_kernel            head64.c             111
      32        0   0 set_intr_gate                  desc.h               319
       1        0   0 reserve_ebda_region            head.c               51
       1        0   0 reserve_ebda_region            head.c               47
       0        1 100 reserve_ebda_region            head.c               42
       0        0   X maxcpus                        main.c               165

Miss means the branch was not taken. Hit means the branch was taken.
The percent is the percentage the branch was taken.

This adds a significant amount of overhead and should only be used
by those analyzing their system.
Signed-off-by: default avatarSteven Rostedt <srostedt@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent bac28bfe
...@@ -53,6 +53,14 @@ ...@@ -53,6 +53,14 @@
#define LIKELY_PROFILE() #define LIKELY_PROFILE()
#endif #endif
#ifdef CONFIG_PROFILE_ALL_BRANCHES
#define BRANCH_PROFILE() VMLINUX_SYMBOL(__start_branch_profile) = .; \
*(_ftrace_branch) \
VMLINUX_SYMBOL(__stop_branch_profile) = .;
#else
#define BRANCH_PROFILE()
#endif
/* .data section */ /* .data section */
#define DATA_DATA \ #define DATA_DATA \
*(.data) \ *(.data) \
...@@ -72,7 +80,8 @@ ...@@ -72,7 +80,8 @@
VMLINUX_SYMBOL(__start___tracepoints) = .; \ VMLINUX_SYMBOL(__start___tracepoints) = .; \
*(__tracepoints) \ *(__tracepoints) \
VMLINUX_SYMBOL(__stop___tracepoints) = .; \ VMLINUX_SYMBOL(__stop___tracepoints) = .; \
LIKELY_PROFILE() LIKELY_PROFILE() \
BRANCH_PROFILE()
#define RO_DATA(align) \ #define RO_DATA(align) \
. = ALIGN((align)); \ . = ALIGN((align)); \
......
...@@ -63,8 +63,16 @@ struct ftrace_branch_data { ...@@ -63,8 +63,16 @@ struct ftrace_branch_data {
const char *func; const char *func;
const char *file; const char *file;
unsigned line; unsigned line;
union {
struct {
unsigned long correct; unsigned long correct;
unsigned long incorrect; unsigned long incorrect;
};
struct {
unsigned long miss;
unsigned long hit;
};
};
}; };
/* /*
...@@ -103,6 +111,32 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect); ...@@ -103,6 +111,32 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect);
# ifndef unlikely # ifndef unlikely
# define unlikely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 0)) # define unlikely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 0))
# endif # endif
#ifdef CONFIG_PROFILE_ALL_BRANCHES
/*
* "Define 'is'", Bill Clinton
* "Define 'if'", Steven Rostedt
*/
#define if(cond) if (__builtin_constant_p((cond)) ? !!(cond) : \
({ \
int ______r; \
static struct ftrace_branch_data \
__attribute__((__aligned__(4))) \
__attribute__((section("_ftrace_branch"))) \
______f = { \
.func = __func__, \
.file = __FILE__, \
.line = __LINE__, \
}; \
______r = !!(cond); \
if (______r) \
______f.hit++; \
else \
______f.miss++; \
______r; \
}))
#endif /* CONFIG_PROFILE_ALL_BRANCHES */
#else #else
# define likely(x) __builtin_expect(!!(x), 1) # define likely(x) __builtin_expect(!!(x), 1)
# define unlikely(x) __builtin_expect(!!(x), 0) # define unlikely(x) __builtin_expect(!!(x), 0)
......
...@@ -173,6 +173,22 @@ config TRACE_BRANCH_PROFILING ...@@ -173,6 +173,22 @@ config TRACE_BRANCH_PROFILING
Say N if unsure. Say N if unsure.
config PROFILE_ALL_BRANCHES
bool "Profile all if conditionals"
depends on TRACE_BRANCH_PROFILING
help
This tracer profiles all branch conditions. Every if ()
taken in the kernel is recorded whether it hit or miss.
The results will be displayed in:
/debugfs/tracing/profile_branch
This configuration, when enabled, will impose a great overhead
on the system. This should only be enabled when the system
is to be analyzed
Say N if unsure.
config TRACING_BRANCHES config TRACING_BRANCHES
bool bool
help help
......
...@@ -185,6 +185,7 @@ EXPORT_SYMBOL(ftrace_likely_update); ...@@ -185,6 +185,7 @@ EXPORT_SYMBOL(ftrace_likely_update);
struct ftrace_pointer { struct ftrace_pointer {
void *start; void *start;
void *stop; void *stop;
int hit;
}; };
static void * static void *
...@@ -223,13 +224,17 @@ static void t_stop(struct seq_file *m, void *p) ...@@ -223,13 +224,17 @@ static void t_stop(struct seq_file *m, void *p)
static int t_show(struct seq_file *m, void *v) static int t_show(struct seq_file *m, void *v)
{ {
struct ftrace_pointer *fp = m->private;
struct ftrace_branch_data *p = v; struct ftrace_branch_data *p = v;
const char *f; const char *f;
long percent; long percent;
if (v == (void *)1) { if (v == (void *)1) {
seq_printf(m, " correct incorrect %% " if (fp->hit)
" Function " seq_printf(m, " miss hit %% ");
else
seq_printf(m, " correct incorrect %% ");
seq_printf(m, " Function "
" File Line\n" " File Line\n"
" ------- --------- - " " ------- --------- - "
" -------- " " -------- "
...@@ -243,6 +248,9 @@ static int t_show(struct seq_file *m, void *v) ...@@ -243,6 +248,9 @@ static int t_show(struct seq_file *m, void *v)
f--; f--;
f++; f++;
/*
* The miss is overlayed on correct, and hit on incorrect.
*/
if (p->correct) { if (p->correct) {
percent = p->incorrect * 100; percent = p->incorrect * 100;
percent /= p->correct + p->incorrect; percent /= p->correct + p->incorrect;
...@@ -284,6 +292,18 @@ static const struct file_operations tracing_branch_fops = { ...@@ -284,6 +292,18 @@ static const struct file_operations tracing_branch_fops = {
.llseek = seq_lseek, .llseek = seq_lseek,
}; };
#ifdef CONFIG_PROFILE_ALL_BRANCHES
extern unsigned long __start_branch_profile[];
extern unsigned long __stop_branch_profile[];
static struct ftrace_pointer ftrace_branch_pos = {
.start = __start_branch_profile,
.stop = __stop_branch_profile,
.hit = 1,
};
#endif /* CONFIG_PROFILE_ALL_BRANCHES */
extern unsigned long __start_annotated_branch_profile[]; extern unsigned long __start_annotated_branch_profile[];
extern unsigned long __stop_annotated_branch_profile[]; extern unsigned long __stop_annotated_branch_profile[];
...@@ -306,6 +326,15 @@ static __init int ftrace_branch_init(void) ...@@ -306,6 +326,15 @@ static __init int ftrace_branch_init(void)
pr_warning("Could not create debugfs " pr_warning("Could not create debugfs "
"'profile_annotatet_branch' entry\n"); "'profile_annotatet_branch' entry\n");
#ifdef CONFIG_PROFILE_ALL_BRANCHES
entry = debugfs_create_file("profile_branch", 0444, d_tracer,
&ftrace_branch_pos,
&tracing_branch_fops);
if (!entry)
pr_warning("Could not create debugfs"
" 'profile_branch' entry\n");
#endif
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment