diff options
Diffstat (limited to 'kernel/trace/trace_branch.c')
| -rw-r--r-- | kernel/trace/trace_branch.c | 278 |
1 files changed, 158 insertions, 120 deletions
diff --git a/kernel/trace/trace_branch.c b/kernel/trace/trace_branch.c index 6c00feb3bac7..ad8c22efff41 100644 --- a/kernel/trace/trace_branch.c +++ b/kernel/trace/trace_branch.c | |||
| @@ -14,12 +14,17 @@ | |||
| 14 | #include <linux/hash.h> | 14 | #include <linux/hash.h> |
| 15 | #include <linux/fs.h> | 15 | #include <linux/fs.h> |
| 16 | #include <asm/local.h> | 16 | #include <asm/local.h> |
| 17 | |||
| 17 | #include "trace.h" | 18 | #include "trace.h" |
| 19 | #include "trace_stat.h" | ||
| 20 | #include "trace_output.h" | ||
| 18 | 21 | ||
| 19 | #ifdef CONFIG_BRANCH_TRACER | 22 | #ifdef CONFIG_BRANCH_TRACER |
| 20 | 23 | ||
| 24 | static struct tracer branch_trace; | ||
| 21 | static int branch_tracing_enabled __read_mostly; | 25 | static int branch_tracing_enabled __read_mostly; |
| 22 | static DEFINE_MUTEX(branch_tracing_mutex); | 26 | static DEFINE_MUTEX(branch_tracing_mutex); |
| 27 | |||
| 23 | static struct trace_array *branch_tracer; | 28 | static struct trace_array *branch_tracer; |
| 24 | 29 | ||
| 25 | static void | 30 | static void |
| @@ -28,7 +33,7 @@ probe_likely_condition(struct ftrace_branch_data *f, int val, int expect) | |||
| 28 | struct trace_array *tr = branch_tracer; | 33 | struct trace_array *tr = branch_tracer; |
| 29 | struct ring_buffer_event *event; | 34 | struct ring_buffer_event *event; |
| 30 | struct trace_branch *entry; | 35 | struct trace_branch *entry; |
| 31 | unsigned long flags, irq_flags; | 36 | unsigned long flags; |
| 32 | int cpu, pc; | 37 | int cpu, pc; |
| 33 | const char *p; | 38 | const char *p; |
| 34 | 39 | ||
| @@ -47,15 +52,13 @@ probe_likely_condition(struct ftrace_branch_data *f, int val, int expect) | |||
| 47 | if (atomic_inc_return(&tr->data[cpu]->disabled) != 1) | 52 | if (atomic_inc_return(&tr->data[cpu]->disabled) != 1) |
| 48 | goto out; | 53 | goto out; |
| 49 | 54 | ||
| 50 | event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry), | 55 | pc = preempt_count(); |
| 51 | &irq_flags); | 56 | event = trace_buffer_lock_reserve(tr, TRACE_BRANCH, |
| 57 | sizeof(*entry), flags, pc); | ||
| 52 | if (!event) | 58 | if (!event) |
| 53 | goto out; | 59 | goto out; |
| 54 | 60 | ||
| 55 | pc = preempt_count(); | ||
| 56 | entry = ring_buffer_event_data(event); | 61 | entry = ring_buffer_event_data(event); |
| 57 | tracing_generic_entry_update(&entry->ent, flags, pc); | ||
| 58 | entry->ent.type = TRACE_BRANCH; | ||
| 59 | 62 | ||
| 60 | /* Strip off the path, only save the file */ | 63 | /* Strip off the path, only save the file */ |
| 61 | p = f->file + strlen(f->file); | 64 | p = f->file + strlen(f->file); |
| @@ -70,7 +73,7 @@ probe_likely_condition(struct ftrace_branch_data *f, int val, int expect) | |||
| 70 | entry->line = f->line; | 73 | entry->line = f->line; |
| 71 | entry->correct = val == expect; | 74 | entry->correct = val == expect; |
| 72 | 75 | ||
| 73 | ring_buffer_unlock_commit(tr->buffer, event, irq_flags); | 76 | ring_buffer_unlock_commit(tr->buffer, event); |
| 74 | 77 | ||
| 75 | out: | 78 | out: |
| 76 | atomic_dec(&tr->data[cpu]->disabled); | 79 | atomic_dec(&tr->data[cpu]->disabled); |
| @@ -88,8 +91,6 @@ void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect) | |||
| 88 | 91 | ||
| 89 | int enable_branch_tracing(struct trace_array *tr) | 92 | int enable_branch_tracing(struct trace_array *tr) |
| 90 | { | 93 | { |
| 91 | int ret = 0; | ||
| 92 | |||
| 93 | mutex_lock(&branch_tracing_mutex); | 94 | mutex_lock(&branch_tracing_mutex); |
| 94 | branch_tracer = tr; | 95 | branch_tracer = tr; |
| 95 | /* | 96 | /* |
| @@ -100,7 +101,7 @@ int enable_branch_tracing(struct trace_array *tr) | |||
| 100 | branch_tracing_enabled++; | 101 | branch_tracing_enabled++; |
| 101 | mutex_unlock(&branch_tracing_mutex); | 102 | mutex_unlock(&branch_tracing_mutex); |
| 102 | 103 | ||
| 103 | return ret; | 104 | return 0; |
| 104 | } | 105 | } |
| 105 | 106 | ||
| 106 | void disable_branch_tracing(void) | 107 | void disable_branch_tracing(void) |
| @@ -128,11 +129,6 @@ static void stop_branch_trace(struct trace_array *tr) | |||
| 128 | 129 | ||
| 129 | static int branch_trace_init(struct trace_array *tr) | 130 | static int branch_trace_init(struct trace_array *tr) |
| 130 | { | 131 | { |
| 131 | int cpu; | ||
| 132 | |||
| 133 | for_each_online_cpu(cpu) | ||
| 134 | tracing_reset(tr, cpu); | ||
| 135 | |||
| 136 | start_branch_trace(tr); | 132 | start_branch_trace(tr); |
| 137 | return 0; | 133 | return 0; |
| 138 | } | 134 | } |
| @@ -142,22 +138,53 @@ static void branch_trace_reset(struct trace_array *tr) | |||
| 142 | stop_branch_trace(tr); | 138 | stop_branch_trace(tr); |
| 143 | } | 139 | } |
| 144 | 140 | ||
| 145 | struct tracer branch_trace __read_mostly = | 141 | static enum print_line_t trace_branch_print(struct trace_iterator *iter, |
| 142 | int flags) | ||
| 143 | { | ||
| 144 | struct trace_branch *field; | ||
| 145 | |||
| 146 | trace_assign_type(field, iter->ent); | ||
| 147 | |||
| 148 | if (trace_seq_printf(&iter->seq, "[%s] %s:%s:%d\n", | ||
| 149 | field->correct ? " ok " : " MISS ", | ||
| 150 | field->func, | ||
| 151 | field->file, | ||
| 152 | field->line)) | ||
| 153 | return TRACE_TYPE_PARTIAL_LINE; | ||
| 154 | |||
| 155 | return TRACE_TYPE_HANDLED; | ||
| 156 | } | ||
| 157 | |||
| 158 | |||
| 159 | static struct trace_event trace_branch_event = { | ||
| 160 | .type = TRACE_BRANCH, | ||
| 161 | .trace = trace_branch_print, | ||
| 162 | }; | ||
| 163 | |||
| 164 | static struct tracer branch_trace __read_mostly = | ||
| 146 | { | 165 | { |
| 147 | .name = "branch", | 166 | .name = "branch", |
| 148 | .init = branch_trace_init, | 167 | .init = branch_trace_init, |
| 149 | .reset = branch_trace_reset, | 168 | .reset = branch_trace_reset, |
| 150 | #ifdef CONFIG_FTRACE_SELFTEST | 169 | #ifdef CONFIG_FTRACE_SELFTEST |
| 151 | .selftest = trace_selftest_startup_branch, | 170 | .selftest = trace_selftest_startup_branch, |
| 152 | #endif | 171 | #endif /* CONFIG_FTRACE_SELFTEST */ |
| 153 | }; | 172 | }; |
| 154 | 173 | ||
| 155 | __init static int init_branch_trace(void) | 174 | __init static int init_branch_tracer(void) |
| 156 | { | 175 | { |
| 176 | int ret; | ||
| 177 | |||
| 178 | ret = register_ftrace_event(&trace_branch_event); | ||
| 179 | if (!ret) { | ||
| 180 | printk(KERN_WARNING "Warning: could not register " | ||
| 181 | "branch events\n"); | ||
| 182 | return 1; | ||
| 183 | } | ||
| 157 | return register_tracer(&branch_trace); | 184 | return register_tracer(&branch_trace); |
| 158 | } | 185 | } |
| 186 | device_initcall(init_branch_tracer); | ||
| 159 | 187 | ||
| 160 | device_initcall(init_branch_trace); | ||
| 161 | #else | 188 | #else |
| 162 | static inline | 189 | static inline |
| 163 | void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect) | 190 | void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect) |
| @@ -183,66 +210,39 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect) | |||
| 183 | } | 210 | } |
| 184 | EXPORT_SYMBOL(ftrace_likely_update); | 211 | EXPORT_SYMBOL(ftrace_likely_update); |
| 185 | 212 | ||
| 186 | struct ftrace_pointer { | 213 | extern unsigned long __start_annotated_branch_profile[]; |
| 187 | void *start; | 214 | extern unsigned long __stop_annotated_branch_profile[]; |
| 188 | void *stop; | ||
| 189 | int hit; | ||
| 190 | }; | ||
| 191 | 215 | ||
| 192 | static void * | 216 | static int annotated_branch_stat_headers(struct seq_file *m) |
| 193 | t_next(struct seq_file *m, void *v, loff_t *pos) | ||
| 194 | { | 217 | { |
| 195 | const struct ftrace_pointer *f = m->private; | 218 | seq_printf(m, " correct incorrect %% "); |
| 196 | struct ftrace_branch_data *p = v; | 219 | seq_printf(m, " Function " |
| 197 | 220 | " File Line\n" | |
| 198 | (*pos)++; | 221 | " ------- --------- - " |
| 199 | 222 | " -------- " | |
| 200 | if (v == (void *)1) | 223 | " ---- ----\n"); |
| 201 | return f->start; | 224 | return 0; |
| 202 | |||
| 203 | ++p; | ||
| 204 | |||
| 205 | if ((void *)p >= (void *)f->stop) | ||
| 206 | return NULL; | ||
| 207 | |||
| 208 | return p; | ||
| 209 | } | 225 | } |
| 210 | 226 | ||
| 211 | static void *t_start(struct seq_file *m, loff_t *pos) | 227 | static inline long get_incorrect_percent(struct ftrace_branch_data *p) |
| 212 | { | 228 | { |
| 213 | void *t = (void *)1; | 229 | long percent; |
| 214 | loff_t l = 0; | ||
| 215 | |||
| 216 | for (; t && l < *pos; t = t_next(m, t, &l)) | ||
| 217 | ; | ||
| 218 | 230 | ||
| 219 | return t; | 231 | if (p->correct) { |
| 220 | } | 232 | percent = p->incorrect * 100; |
| 233 | percent /= p->correct + p->incorrect; | ||
| 234 | } else | ||
| 235 | percent = p->incorrect ? 100 : -1; | ||
| 221 | 236 | ||
| 222 | static void t_stop(struct seq_file *m, void *p) | 237 | return percent; |
| 223 | { | ||
| 224 | } | 238 | } |
| 225 | 239 | ||
| 226 | static int t_show(struct seq_file *m, void *v) | 240 | static int branch_stat_show(struct seq_file *m, void *v) |
| 227 | { | 241 | { |
| 228 | const struct ftrace_pointer *fp = m->private; | ||
| 229 | struct ftrace_branch_data *p = v; | 242 | struct ftrace_branch_data *p = v; |
| 230 | const char *f; | 243 | const char *f; |
| 231 | long percent; | 244 | long percent; |
| 232 | 245 | ||
| 233 | if (v == (void *)1) { | ||
| 234 | if (fp->hit) | ||
| 235 | seq_printf(m, " miss hit %% "); | ||
| 236 | else | ||
| 237 | seq_printf(m, " correct incorrect %% "); | ||
| 238 | seq_printf(m, " Function " | ||
| 239 | " File Line\n" | ||
| 240 | " ------- --------- - " | ||
| 241 | " -------- " | ||
| 242 | " ---- ----\n"); | ||
| 243 | return 0; | ||
| 244 | } | ||
| 245 | |||
| 246 | /* Only print the file, not the path */ | 246 | /* Only print the file, not the path */ |
| 247 | f = p->file + strlen(p->file); | 247 | f = p->file + strlen(p->file); |
| 248 | while (f >= p->file && *f != '/') | 248 | while (f >= p->file && *f != '/') |
| @@ -252,11 +252,7 @@ static int t_show(struct seq_file *m, void *v) | |||
| 252 | /* | 252 | /* |
| 253 | * The miss is overlayed on correct, and hit on incorrect. | 253 | * The miss is overlayed on correct, and hit on incorrect. |
| 254 | */ | 254 | */ |
| 255 | if (p->correct) { | 255 | percent = get_incorrect_percent(p); |
| 256 | percent = p->incorrect * 100; | ||
| 257 | percent /= p->correct + p->incorrect; | ||
| 258 | } else | ||
| 259 | percent = p->incorrect ? 100 : -1; | ||
| 260 | 256 | ||
| 261 | seq_printf(m, "%8lu %8lu ", p->correct, p->incorrect); | 257 | seq_printf(m, "%8lu %8lu ", p->correct, p->incorrect); |
| 262 | if (percent < 0) | 258 | if (percent < 0) |
| @@ -267,76 +263,118 @@ static int t_show(struct seq_file *m, void *v) | |||
| 267 | return 0; | 263 | return 0; |
| 268 | } | 264 | } |
| 269 | 265 | ||
| 270 | static struct seq_operations tracing_likely_seq_ops = { | 266 | static void *annotated_branch_stat_start(void) |
| 271 | .start = t_start, | 267 | { |
| 272 | .next = t_next, | 268 | return __start_annotated_branch_profile; |
| 273 | .stop = t_stop, | 269 | } |
| 274 | .show = t_show, | 270 | |
| 271 | static void * | ||
| 272 | annotated_branch_stat_next(void *v, int idx) | ||
| 273 | { | ||
| 274 | struct ftrace_branch_data *p = v; | ||
| 275 | |||
| 276 | ++p; | ||
| 277 | |||
| 278 | if ((void *)p >= (void *)__stop_annotated_branch_profile) | ||
| 279 | return NULL; | ||
| 280 | |||
| 281 | return p; | ||
| 282 | } | ||
| 283 | |||
| 284 | static int annotated_branch_stat_cmp(void *p1, void *p2) | ||
| 285 | { | ||
| 286 | struct ftrace_branch_data *a = p1; | ||
| 287 | struct ftrace_branch_data *b = p2; | ||
| 288 | |||
| 289 | long percent_a, percent_b; | ||
| 290 | |||
| 291 | percent_a = get_incorrect_percent(a); | ||
| 292 | percent_b = get_incorrect_percent(b); | ||
| 293 | |||
| 294 | if (percent_a < percent_b) | ||
| 295 | return -1; | ||
| 296 | if (percent_a > percent_b) | ||
| 297 | return 1; | ||
| 298 | else | ||
| 299 | return 0; | ||
| 300 | } | ||
| 301 | |||
| 302 | static struct tracer_stat annotated_branch_stats = { | ||
| 303 | .name = "branch_annotated", | ||
| 304 | .stat_start = annotated_branch_stat_start, | ||
| 305 | .stat_next = annotated_branch_stat_next, | ||
| 306 | .stat_cmp = annotated_branch_stat_cmp, | ||
| 307 | .stat_headers = annotated_branch_stat_headers, | ||
| 308 | .stat_show = branch_stat_show | ||
| 275 | }; | 309 | }; |
| 276 | 310 | ||
| 277 | static int tracing_branch_open(struct inode *inode, struct file *file) | 311 | __init static int init_annotated_branch_stats(void) |
| 278 | { | 312 | { |
| 279 | int ret; | 313 | int ret; |
| 280 | 314 | ||
| 281 | ret = seq_open(file, &tracing_likely_seq_ops); | 315 | ret = register_stat_tracer(&annotated_branch_stats); |
| 282 | if (!ret) { | 316 | if (!ret) { |
| 283 | struct seq_file *m = file->private_data; | 317 | printk(KERN_WARNING "Warning: could not register " |
| 284 | m->private = (void *)inode->i_private; | 318 | "annotated branches stats\n"); |
| 319 | return 1; | ||
| 285 | } | 320 | } |
| 286 | 321 | return 0; | |
| 287 | return ret; | ||
| 288 | } | 322 | } |
| 289 | 323 | fs_initcall(init_annotated_branch_stats); | |
| 290 | static const struct file_operations tracing_branch_fops = { | ||
| 291 | .open = tracing_branch_open, | ||
| 292 | .read = seq_read, | ||
| 293 | .llseek = seq_lseek, | ||
| 294 | }; | ||
| 295 | 324 | ||
| 296 | #ifdef CONFIG_PROFILE_ALL_BRANCHES | 325 | #ifdef CONFIG_PROFILE_ALL_BRANCHES |
| 326 | |||
| 297 | extern unsigned long __start_branch_profile[]; | 327 | extern unsigned long __start_branch_profile[]; |
| 298 | extern unsigned long __stop_branch_profile[]; | 328 | extern unsigned long __stop_branch_profile[]; |
| 299 | 329 | ||
| 300 | static const struct ftrace_pointer ftrace_branch_pos = { | 330 | static int all_branch_stat_headers(struct seq_file *m) |
| 301 | .start = __start_branch_profile, | 331 | { |
| 302 | .stop = __stop_branch_profile, | 332 | seq_printf(m, " miss hit %% "); |
| 303 | .hit = 1, | 333 | seq_printf(m, " Function " |
| 304 | }; | 334 | " File Line\n" |
| 335 | " ------- --------- - " | ||
| 336 | " -------- " | ||
| 337 | " ---- ----\n"); | ||
| 338 | return 0; | ||
| 339 | } | ||
| 305 | 340 | ||
| 306 | #endif /* CONFIG_PROFILE_ALL_BRANCHES */ | 341 | static void *all_branch_stat_start(void) |
| 342 | { | ||
| 343 | return __start_branch_profile; | ||
| 344 | } | ||
| 307 | 345 | ||
| 308 | extern unsigned long __start_annotated_branch_profile[]; | 346 | static void * |
| 309 | extern unsigned long __stop_annotated_branch_profile[]; | 347 | all_branch_stat_next(void *v, int idx) |
| 348 | { | ||
| 349 | struct ftrace_branch_data *p = v; | ||
| 310 | 350 | ||
| 311 | static const struct ftrace_pointer ftrace_annotated_branch_pos = { | 351 | ++p; |
| 312 | .start = __start_annotated_branch_profile, | ||
| 313 | .stop = __stop_annotated_branch_profile, | ||
| 314 | }; | ||
| 315 | 352 | ||
| 316 | static __init int ftrace_branch_init(void) | 353 | if ((void *)p >= (void *)__stop_branch_profile) |
| 317 | { | 354 | return NULL; |
| 318 | struct dentry *d_tracer; | ||
| 319 | struct dentry *entry; | ||
| 320 | 355 | ||
| 321 | d_tracer = tracing_init_dentry(); | 356 | return p; |
| 357 | } | ||
| 322 | 358 | ||
| 323 | entry = debugfs_create_file("profile_annotated_branch", 0444, d_tracer, | 359 | static struct tracer_stat all_branch_stats = { |
| 324 | (void *)&ftrace_annotated_branch_pos, | 360 | .name = "branch_all", |
| 325 | &tracing_branch_fops); | 361 | .stat_start = all_branch_stat_start, |
| 326 | if (!entry) | 362 | .stat_next = all_branch_stat_next, |
| 327 | pr_warning("Could not create debugfs " | 363 | .stat_headers = all_branch_stat_headers, |
| 328 | "'profile_annotatet_branch' entry\n"); | 364 | .stat_show = branch_stat_show |
| 365 | }; | ||
| 329 | 366 | ||
| 330 | #ifdef CONFIG_PROFILE_ALL_BRANCHES | 367 | __init static int all_annotated_branch_stats(void) |
| 331 | entry = debugfs_create_file("profile_branch", 0444, d_tracer, | 368 | { |
| 332 | (void *)&ftrace_branch_pos, | 369 | int ret; |
| 333 | &tracing_branch_fops); | ||
| 334 | if (!entry) | ||
| 335 | pr_warning("Could not create debugfs" | ||
| 336 | " 'profile_branch' entry\n"); | ||
| 337 | #endif | ||
| 338 | 370 | ||
| 371 | ret = register_stat_tracer(&all_branch_stats); | ||
| 372 | if (!ret) { | ||
| 373 | printk(KERN_WARNING "Warning: could not register " | ||
| 374 | "all branches stats\n"); | ||
| 375 | return 1; | ||
| 376 | } | ||
| 339 | return 0; | 377 | return 0; |
| 340 | } | 378 | } |
| 341 | 379 | fs_initcall(all_annotated_branch_stats); | |
| 342 | device_initcall(ftrace_branch_init); | 380 | #endif /* CONFIG_PROFILE_ALL_BRANCHES */ |
