aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace/trace_ksym.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/trace/trace_ksym.c')
-rw-r--r--kernel/trace/trace_ksym.c188
1 files changed, 78 insertions, 110 deletions
diff --git a/kernel/trace/trace_ksym.c b/kernel/trace/trace_ksym.c
index acb87d4a4ac1..94103cdcf9d8 100644
--- a/kernel/trace/trace_ksym.c
+++ b/kernel/trace/trace_ksym.c
@@ -26,12 +26,13 @@
26#include <linux/fs.h> 26#include <linux/fs.h>
27 27
28#include "trace_output.h" 28#include "trace_output.h"
29#include "trace_stat.h"
30#include "trace.h" 29#include "trace.h"
31 30
32#include <linux/hw_breakpoint.h> 31#include <linux/hw_breakpoint.h>
33#include <asm/hw_breakpoint.h> 32#include <asm/hw_breakpoint.h>
34 33
34#include <asm/atomic.h>
35
35/* 36/*
36 * For now, let us restrict the no. of symbols traced simultaneously to number 37 * For now, let us restrict the no. of symbols traced simultaneously to number
37 * of available hardware breakpoint registers. 38 * of available hardware breakpoint registers.
@@ -44,7 +45,7 @@ struct trace_ksym {
44 struct perf_event **ksym_hbp; 45 struct perf_event **ksym_hbp;
45 struct perf_event_attr attr; 46 struct perf_event_attr attr;
46#ifdef CONFIG_PROFILE_KSYM_TRACER 47#ifdef CONFIG_PROFILE_KSYM_TRACER
47 unsigned long counter; 48 atomic64_t counter;
48#endif 49#endif
49 struct hlist_node ksym_hlist; 50 struct hlist_node ksym_hlist;
50}; 51};
@@ -69,9 +70,8 @@ void ksym_collect_stats(unsigned long hbp_hit_addr)
69 70
70 rcu_read_lock(); 71 rcu_read_lock();
71 hlist_for_each_entry_rcu(entry, node, &ksym_filter_head, ksym_hlist) { 72 hlist_for_each_entry_rcu(entry, node, &ksym_filter_head, ksym_hlist) {
72 if ((entry->attr.bp_addr == hbp_hit_addr) && 73 if (entry->attr.bp_addr == hbp_hit_addr) {
73 (entry->counter <= MAX_UL_INT)) { 74 atomic64_inc(&entry->counter);
74 entry->counter++;
75 break; 75 break;
76 } 76 }
77 } 77 }
@@ -197,7 +197,6 @@ int process_new_ksym_entry(char *ksymname, int op, unsigned long addr)
197 entry->attr.bp_addr = addr; 197 entry->attr.bp_addr = addr;
198 entry->attr.bp_len = HW_BREAKPOINT_LEN_4; 198 entry->attr.bp_len = HW_BREAKPOINT_LEN_4;
199 199
200 ret = -EAGAIN;
201 entry->ksym_hbp = register_wide_hw_breakpoint(&entry->attr, 200 entry->ksym_hbp = register_wide_hw_breakpoint(&entry->attr,
202 ksym_hbp_handler); 201 ksym_hbp_handler);
203 202
@@ -236,7 +235,8 @@ static ssize_t ksym_trace_filter_read(struct file *filp, char __user *ubuf,
236 mutex_lock(&ksym_tracer_mutex); 235 mutex_lock(&ksym_tracer_mutex);
237 236
238 hlist_for_each_entry(entry, node, &ksym_filter_head, ksym_hlist) { 237 hlist_for_each_entry(entry, node, &ksym_filter_head, ksym_hlist) {
239 ret = trace_seq_printf(s, "%pS:", (void *)entry->attr.bp_addr); 238 ret = trace_seq_printf(s, "%pS:",
239 (void *)(unsigned long)entry->attr.bp_addr);
240 if (entry->attr.bp_type == HW_BREAKPOINT_R) 240 if (entry->attr.bp_type == HW_BREAKPOINT_R)
241 ret = trace_seq_puts(s, "r--\n"); 241 ret = trace_seq_puts(s, "r--\n");
242 else if (entry->attr.bp_type == HW_BREAKPOINT_W) 242 else if (entry->attr.bp_type == HW_BREAKPOINT_W)
@@ -278,21 +278,20 @@ static ssize_t ksym_trace_filter_write(struct file *file,
278{ 278{
279 struct trace_ksym *entry; 279 struct trace_ksym *entry;
280 struct hlist_node *node; 280 struct hlist_node *node;
281 char *input_string, *ksymname = NULL; 281 char *buf, *input_string, *ksymname = NULL;
282 unsigned long ksym_addr = 0; 282 unsigned long ksym_addr = 0;
283 int ret, op, changed = 0; 283 int ret, op, changed = 0;
284 284
285 input_string = kzalloc(count + 1, GFP_KERNEL); 285 buf = kzalloc(count + 1, GFP_KERNEL);
286 if (!input_string) 286 if (!buf)
287 return -ENOMEM; 287 return -ENOMEM;
288 288
289 if (copy_from_user(input_string, buffer, count)) { 289 ret = -EFAULT;
290 kfree(input_string); 290 if (copy_from_user(buf, buffer, count))
291 return -EFAULT; 291 goto out;
292 }
293 input_string[count] = '\0';
294 292
295 strstrip(input_string); 293 buf[count] = '\0';
294 input_string = strstrip(buf);
296 295
297 /* 296 /*
298 * Clear all breakpoints if: 297 * Clear all breakpoints if:
@@ -303,15 +302,13 @@ static ssize_t ksym_trace_filter_write(struct file *file,
303 if (!input_string[0] || !strcmp(input_string, "0") || 302 if (!input_string[0] || !strcmp(input_string, "0") ||
304 !strcmp(input_string, "*:---")) { 303 !strcmp(input_string, "*:---")) {
305 __ksym_trace_reset(); 304 __ksym_trace_reset();
306 kfree(input_string); 305 ret = 0;
307 return count; 306 goto out;
308 } 307 }
309 308
310 ret = op = parse_ksym_trace_str(input_string, &ksymname, &ksym_addr); 309 ret = op = parse_ksym_trace_str(input_string, &ksymname, &ksym_addr);
311 if (ret < 0) { 310 if (ret < 0)
312 kfree(input_string); 311 goto out;
313 return ret;
314 }
315 312
316 mutex_lock(&ksym_tracer_mutex); 313 mutex_lock(&ksym_tracer_mutex);
317 314
@@ -322,7 +319,7 @@ static ssize_t ksym_trace_filter_write(struct file *file,
322 if (entry->attr.bp_type != op) 319 if (entry->attr.bp_type != op)
323 changed = 1; 320 changed = 1;
324 else 321 else
325 goto out; 322 goto out_unlock;
326 break; 323 break;
327 } 324 }
328 } 325 }
@@ -337,28 +334,24 @@ static ssize_t ksym_trace_filter_write(struct file *file,
337 if (IS_ERR(entry->ksym_hbp)) 334 if (IS_ERR(entry->ksym_hbp))
338 ret = PTR_ERR(entry->ksym_hbp); 335 ret = PTR_ERR(entry->ksym_hbp);
339 else 336 else
340 goto out; 337 goto out_unlock;
341 } 338 }
342 /* Error or "symbol:---" case: drop it */ 339 /* Error or "symbol:---" case: drop it */
343 ksym_filter_entry_count--; 340 ksym_filter_entry_count--;
344 hlist_del_rcu(&(entry->ksym_hlist)); 341 hlist_del_rcu(&(entry->ksym_hlist));
345 synchronize_rcu(); 342 synchronize_rcu();
346 kfree(entry); 343 kfree(entry);
347 goto out; 344 goto out_unlock;
348 } else { 345 } else {
349 /* Check for malformed request: (4) */ 346 /* Check for malformed request: (4) */
350 if (op == 0) 347 if (op)
351 goto out; 348 ret = process_new_ksym_entry(ksymname, op, ksym_addr);
352 ret = process_new_ksym_entry(ksymname, op, ksym_addr);
353 } 349 }
354out: 350out_unlock:
355 mutex_unlock(&ksym_tracer_mutex); 351 mutex_unlock(&ksym_tracer_mutex);
356 352out:
357 kfree(input_string); 353 kfree(buf);
358 354 return !ret ? count : ret;
359 if (!ret)
360 ret = count;
361 return ret;
362} 355}
363 356
364static const struct file_operations ksym_tracing_fops = { 357static const struct file_operations ksym_tracing_fops = {
@@ -450,102 +443,77 @@ struct tracer ksym_tracer __read_mostly =
450 .print_line = ksym_trace_output 443 .print_line = ksym_trace_output
451}; 444};
452 445
453__init static int init_ksym_trace(void)
454{
455 struct dentry *d_tracer;
456 struct dentry *entry;
457
458 d_tracer = tracing_init_dentry();
459 ksym_filter_entry_count = 0;
460
461 entry = debugfs_create_file("ksym_trace_filter", 0644, d_tracer,
462 NULL, &ksym_tracing_fops);
463 if (!entry)
464 pr_warning("Could not create debugfs "
465 "'ksym_trace_filter' file\n");
466
467 return register_tracer(&ksym_tracer);
468}
469device_initcall(init_ksym_trace);
470
471
472#ifdef CONFIG_PROFILE_KSYM_TRACER 446#ifdef CONFIG_PROFILE_KSYM_TRACER
473static int ksym_tracer_stat_headers(struct seq_file *m) 447static int ksym_profile_show(struct seq_file *m, void *v)
474{ 448{
449 struct hlist_node *node;
450 struct trace_ksym *entry;
451 int access_type = 0;
452 char fn_name[KSYM_NAME_LEN];
453
475 seq_puts(m, " Access Type "); 454 seq_puts(m, " Access Type ");
476 seq_puts(m, " Symbol Counter\n"); 455 seq_puts(m, " Symbol Counter\n");
477 seq_puts(m, " ----------- "); 456 seq_puts(m, " ----------- ");
478 seq_puts(m, " ------ -------\n"); 457 seq_puts(m, " ------ -------\n");
479 return 0;
480}
481 458
482static int ksym_tracer_stat_show(struct seq_file *m, void *v) 459 rcu_read_lock();
483{ 460 hlist_for_each_entry_rcu(entry, node, &ksym_filter_head, ksym_hlist) {
484 struct hlist_node *stat = v;
485 struct trace_ksym *entry;
486 int access_type = 0;
487 char fn_name[KSYM_NAME_LEN];
488 461
489 entry = hlist_entry(stat, struct trace_ksym, ksym_hlist); 462 access_type = entry->attr.bp_type;
490 463
491 access_type = entry->attr.bp_type; 464 switch (access_type) {
465 case HW_BREAKPOINT_R:
466 seq_puts(m, " R ");
467 break;
468 case HW_BREAKPOINT_W:
469 seq_puts(m, " W ");
470 break;
471 case HW_BREAKPOINT_R | HW_BREAKPOINT_W:
472 seq_puts(m, " RW ");
473 break;
474 default:
475 seq_puts(m, " NA ");
476 }
492 477
493 switch (access_type) { 478 if (lookup_symbol_name(entry->attr.bp_addr, fn_name) >= 0)
494 case HW_BREAKPOINT_R: 479 seq_printf(m, " %-36s", fn_name);
495 seq_puts(m, " R "); 480 else
496 break; 481 seq_printf(m, " %-36s", "<NA>");
497 case HW_BREAKPOINT_W: 482 seq_printf(m, " %15llu\n",
498 seq_puts(m, " W "); 483 (unsigned long long)atomic64_read(&entry->counter));
499 break;
500 case HW_BREAKPOINT_R | HW_BREAKPOINT_W:
501 seq_puts(m, " RW ");
502 break;
503 default:
504 seq_puts(m, " NA ");
505 } 484 }
506 485 rcu_read_unlock();
507 if (lookup_symbol_name(entry->attr.bp_addr, fn_name) >= 0)
508 seq_printf(m, " %-36s", fn_name);
509 else
510 seq_printf(m, " %-36s", "<NA>");
511 seq_printf(m, " %15lu\n", entry->counter);
512 486
513 return 0; 487 return 0;
514} 488}
515 489
516static void *ksym_tracer_stat_start(struct tracer_stat *trace) 490static int ksym_profile_open(struct inode *node, struct file *file)
517{ 491{
518 return ksym_filter_head.first; 492 return single_open(file, ksym_profile_show, NULL);
519} 493}
520 494
521static void * 495static const struct file_operations ksym_profile_fops = {
522ksym_tracer_stat_next(void *v, int idx) 496 .open = ksym_profile_open,
523{ 497 .read = seq_read,
524 struct hlist_node *stat = v; 498 .llseek = seq_lseek,
525 499 .release = single_release,
526 return stat->next;
527}
528
529static struct tracer_stat ksym_tracer_stats = {
530 .name = "ksym_tracer",
531 .stat_start = ksym_tracer_stat_start,
532 .stat_next = ksym_tracer_stat_next,
533 .stat_headers = ksym_tracer_stat_headers,
534 .stat_show = ksym_tracer_stat_show
535}; 500};
501#endif /* CONFIG_PROFILE_KSYM_TRACER */
536 502
537__init static int ksym_tracer_stat_init(void) 503__init static int init_ksym_trace(void)
538{ 504{
539 int ret; 505 struct dentry *d_tracer;
540 506
541 ret = register_stat_tracer(&ksym_tracer_stats); 507 d_tracer = tracing_init_dentry();
542 if (ret) {
543 printk(KERN_WARNING "Warning: could not register "
544 "ksym tracer stats\n");
545 return 1;
546 }
547 508
548 return 0; 509 trace_create_file("ksym_trace_filter", 0644, d_tracer,
510 NULL, &ksym_tracing_fops);
511
512#ifdef CONFIG_PROFILE_KSYM_TRACER
513 trace_create_file("ksym_profile", 0444, d_tracer,
514 NULL, &ksym_profile_fops);
515#endif
516
517 return register_tracer(&ksym_tracer);
549} 518}
550fs_initcall(ksym_tracer_stat_init); 519device_initcall(init_ksym_trace);
551#endif /* CONFIG_PROFILE_KSYM_TRACER */