diff options
| -rw-r--r-- | include/asm-generic/vmlinux.lds.h | 11 | ||||
| -rw-r--r-- | include/linux/compiler.h | 38 | ||||
| -rw-r--r-- | kernel/trace/Kconfig | 16 | ||||
| -rw-r--r-- | kernel/trace/trace_branch.c | 33 |
4 files changed, 93 insertions, 5 deletions
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 8bccb49981e5..eba835a2c2cd 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h | |||
| @@ -53,6 +53,14 @@ | |||
| 53 | #define LIKELY_PROFILE() | 53 | #define LIKELY_PROFILE() |
| 54 | #endif | 54 | #endif |
| 55 | 55 | ||
| 56 | #ifdef CONFIG_PROFILE_ALL_BRANCHES | ||
| 57 | #define BRANCH_PROFILE() VMLINUX_SYMBOL(__start_branch_profile) = .; \ | ||
| 58 | *(_ftrace_branch) \ | ||
| 59 | VMLINUX_SYMBOL(__stop_branch_profile) = .; | ||
| 60 | #else | ||
| 61 | #define BRANCH_PROFILE() | ||
| 62 | #endif | ||
| 63 | |||
| 56 | /* .data section */ | 64 | /* .data section */ |
| 57 | #define DATA_DATA \ | 65 | #define DATA_DATA \ |
| 58 | *(.data) \ | 66 | *(.data) \ |
| @@ -72,7 +80,8 @@ | |||
| 72 | VMLINUX_SYMBOL(__start___tracepoints) = .; \ | 80 | VMLINUX_SYMBOL(__start___tracepoints) = .; \ |
| 73 | *(__tracepoints) \ | 81 | *(__tracepoints) \ |
| 74 | VMLINUX_SYMBOL(__stop___tracepoints) = .; \ | 82 | VMLINUX_SYMBOL(__stop___tracepoints) = .; \ |
| 75 | LIKELY_PROFILE() | 83 | LIKELY_PROFILE() \ |
| 84 | BRANCH_PROFILE() | ||
| 76 | 85 | ||
| 77 | #define RO_DATA(align) \ | 86 | #define RO_DATA(align) \ |
| 78 | . = ALIGN((align)); \ | 87 | . = ALIGN((align)); \ |
diff --git a/include/linux/compiler.h b/include/linux/compiler.h index 0628a2013fae..ea7c6be354b7 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h | |||
| @@ -63,8 +63,16 @@ struct ftrace_branch_data { | |||
| 63 | const char *func; | 63 | const char *func; |
| 64 | const char *file; | 64 | const char *file; |
| 65 | unsigned line; | 65 | unsigned line; |
| 66 | unsigned long correct; | 66 | union { |
| 67 | unsigned long incorrect; | 67 | struct { |
| 68 | unsigned long correct; | ||
| 69 | unsigned long incorrect; | ||
| 70 | }; | ||
| 71 | struct { | ||
| 72 | unsigned long miss; | ||
| 73 | unsigned long hit; | ||
| 74 | }; | ||
| 75 | }; | ||
| 68 | }; | 76 | }; |
| 69 | 77 | ||
| 70 | /* | 78 | /* |
| @@ -103,6 +111,32 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect); | |||
| 103 | # ifndef unlikely | 111 | # ifndef unlikely |
| 104 | # define unlikely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 0)) | 112 | # define unlikely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 0)) |
| 105 | # endif | 113 | # endif |
| 114 | |||
| 115 | #ifdef CONFIG_PROFILE_ALL_BRANCHES | ||
| 116 | /* | ||
| 117 | * "Define 'is'", Bill Clinton | ||
| 118 | * "Define 'if'", Steven Rostedt | ||
| 119 | */ | ||
| 120 | #define if(cond) if (__builtin_constant_p((cond)) ? !!(cond) : \ | ||
| 121 | ({ \ | ||
| 122 | int ______r; \ | ||
| 123 | static struct ftrace_branch_data \ | ||
| 124 | __attribute__((__aligned__(4))) \ | ||
| 125 | __attribute__((section("_ftrace_branch"))) \ | ||
| 126 | ______f = { \ | ||
| 127 | .func = __func__, \ | ||
| 128 | .file = __FILE__, \ | ||
| 129 | .line = __LINE__, \ | ||
| 130 | }; \ | ||
| 131 | ______r = !!(cond); \ | ||
| 132 | if (______r) \ | ||
| 133 | ______f.hit++; \ | ||
| 134 | else \ | ||
| 135 | ______f.miss++; \ | ||
| 136 | ______r; \ | ||
| 137 | })) | ||
| 138 | #endif /* CONFIG_PROFILE_ALL_BRANCHES */ | ||
| 139 | |||
| 106 | #else | 140 | #else |
| 107 | # define likely(x) __builtin_expect(!!(x), 1) | 141 | # define likely(x) __builtin_expect(!!(x), 1) |
| 108 | # define unlikely(x) __builtin_expect(!!(x), 0) | 142 | # define unlikely(x) __builtin_expect(!!(x), 0) |
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 7e3548705708..61e8cca6ff45 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig | |||
| @@ -173,6 +173,22 @@ config TRACE_BRANCH_PROFILING | |||
| 173 | 173 | ||
| 174 | Say N if unsure. | 174 | Say N if unsure. |
| 175 | 175 | ||
| 176 | config PROFILE_ALL_BRANCHES | ||
| 177 | bool "Profile all if conditionals" | ||
| 178 | depends on TRACE_BRANCH_PROFILING | ||
| 179 | help | ||
| 180 | This tracer profiles all branch conditions. Every if () | ||
| 181 | taken in the kernel is recorded whether it hit or miss. | ||
| 182 | The results will be displayed in: | ||
| 183 | |||
| 184 | /debugfs/tracing/profile_branch | ||
| 185 | |||
| 186 | This configuration, when enabled, will impose a great overhead | ||
| 187 | on the system. This should only be enabled when the system | ||
| 188 | is to be analyzed | ||
| 189 | |||
| 190 | Say N if unsure. | ||
| 191 | |||
| 176 | config TRACING_BRANCHES | 192 | config TRACING_BRANCHES |
| 177 | bool | 193 | bool |
| 178 | help | 194 | help |
diff --git a/kernel/trace/trace_branch.c b/kernel/trace/trace_branch.c index 142acb3b4e00..85792aec64d2 100644 --- a/kernel/trace/trace_branch.c +++ b/kernel/trace/trace_branch.c | |||
| @@ -185,6 +185,7 @@ EXPORT_SYMBOL(ftrace_likely_update); | |||
| 185 | struct ftrace_pointer { | 185 | struct ftrace_pointer { |
| 186 | void *start; | 186 | void *start; |
| 187 | void *stop; | 187 | void *stop; |
| 188 | int hit; | ||
| 188 | }; | 189 | }; |
| 189 | 190 | ||
| 190 | static void * | 191 | static void * |
| @@ -223,13 +224,17 @@ static void t_stop(struct seq_file *m, void *p) | |||
| 223 | 224 | ||
| 224 | static int t_show(struct seq_file *m, void *v) | 225 | static int t_show(struct seq_file *m, void *v) |
| 225 | { | 226 | { |
| 227 | struct ftrace_pointer *fp = m->private; | ||
| 226 | struct ftrace_branch_data *p = v; | 228 | struct ftrace_branch_data *p = v; |
| 227 | const char *f; | 229 | const char *f; |
| 228 | long percent; | 230 | long percent; |
| 229 | 231 | ||
| 230 | if (v == (void *)1) { | 232 | if (v == (void *)1) { |
| 231 | seq_printf(m, " correct incorrect %% " | 233 | if (fp->hit) |
| 232 | " Function " | 234 | seq_printf(m, " miss hit %% "); |
| 235 | else | ||
| 236 | seq_printf(m, " correct incorrect %% "); | ||
| 237 | seq_printf(m, " Function " | ||
| 233 | " File Line\n" | 238 | " File Line\n" |
| 234 | " ------- --------- - " | 239 | " ------- --------- - " |
| 235 | " -------- " | 240 | " -------- " |
| @@ -243,6 +248,9 @@ static int t_show(struct seq_file *m, void *v) | |||
| 243 | f--; | 248 | f--; |
| 244 | f++; | 249 | f++; |
| 245 | 250 | ||
| 251 | /* | ||
| 252 | * The miss is overlayed on correct, and hit on incorrect. | ||
| 253 | */ | ||
| 246 | if (p->correct) { | 254 | if (p->correct) { |
| 247 | percent = p->incorrect * 100; | 255 | percent = p->incorrect * 100; |
| 248 | percent /= p->correct + p->incorrect; | 256 | percent /= p->correct + p->incorrect; |
| @@ -284,6 +292,18 @@ static const struct file_operations tracing_branch_fops = { | |||
| 284 | .llseek = seq_lseek, | 292 | .llseek = seq_lseek, |
| 285 | }; | 293 | }; |
| 286 | 294 | ||
| 295 | #ifdef CONFIG_PROFILE_ALL_BRANCHES | ||
| 296 | extern unsigned long __start_branch_profile[]; | ||
| 297 | extern unsigned long __stop_branch_profile[]; | ||
| 298 | |||
| 299 | static struct ftrace_pointer ftrace_branch_pos = { | ||
| 300 | .start = __start_branch_profile, | ||
| 301 | .stop = __stop_branch_profile, | ||
| 302 | .hit = 1, | ||
| 303 | }; | ||
| 304 | |||
| 305 | #endif /* CONFIG_PROFILE_ALL_BRANCHES */ | ||
| 306 | |||
| 287 | extern unsigned long __start_annotated_branch_profile[]; | 307 | extern unsigned long __start_annotated_branch_profile[]; |
| 288 | extern unsigned long __stop_annotated_branch_profile[]; | 308 | extern unsigned long __stop_annotated_branch_profile[]; |
| 289 | 309 | ||
| @@ -306,6 +326,15 @@ static __init int ftrace_branch_init(void) | |||
| 306 | pr_warning("Could not create debugfs " | 326 | pr_warning("Could not create debugfs " |
| 307 | "'profile_annotatet_branch' entry\n"); | 327 | "'profile_annotatet_branch' entry\n"); |
| 308 | 328 | ||
| 329 | #ifdef CONFIG_PROFILE_ALL_BRANCHES | ||
| 330 | entry = debugfs_create_file("profile_branch", 0444, d_tracer, | ||
| 331 | &ftrace_branch_pos, | ||
| 332 | &tracing_branch_fops); | ||
| 333 | if (!entry) | ||
| 334 | pr_warning("Could not create debugfs" | ||
| 335 | " 'profile_branch' entry\n"); | ||
| 336 | #endif | ||
| 337 | |||
| 309 | return 0; | 338 | return 0; |
| 310 | } | 339 | } |
| 311 | 340 | ||
