diff options
Diffstat (limited to 'kernel/trace/ftrace.c')
-rw-r--r-- | kernel/trace/ftrace.c | 581 |
1 files changed, 577 insertions, 4 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index f1ed080406c3..678e3d6caf85 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -33,7 +33,8 @@ | |||
33 | 33 | ||
34 | #include <asm/ftrace.h> | 34 | #include <asm/ftrace.h> |
35 | 35 | ||
36 | #include "trace.h" | 36 | #include "trace_output.h" |
37 | #include "trace_stat.h" | ||
37 | 38 | ||
38 | #define FTRACE_WARN_ON(cond) \ | 39 | #define FTRACE_WARN_ON(cond) \ |
39 | do { \ | 40 | do { \ |
@@ -68,7 +69,7 @@ static DEFINE_MUTEX(ftrace_lock); | |||
68 | 69 | ||
69 | static struct ftrace_ops ftrace_list_end __read_mostly = | 70 | static struct ftrace_ops ftrace_list_end __read_mostly = |
70 | { | 71 | { |
71 | .func = ftrace_stub, | 72 | .func = ftrace_stub, |
72 | }; | 73 | }; |
73 | 74 | ||
74 | static struct ftrace_ops *ftrace_list __read_mostly = &ftrace_list_end; | 75 | static struct ftrace_ops *ftrace_list __read_mostly = &ftrace_list_end; |
@@ -240,6 +241,576 @@ static void ftrace_update_pid_func(void) | |||
240 | #endif | 241 | #endif |
241 | } | 242 | } |
242 | 243 | ||
244 | #ifdef CONFIG_FUNCTION_PROFILER | ||
245 | struct ftrace_profile { | ||
246 | struct hlist_node node; | ||
247 | unsigned long ip; | ||
248 | unsigned long counter; | ||
249 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
250 | unsigned long long time; | ||
251 | #endif | ||
252 | }; | ||
253 | |||
254 | struct ftrace_profile_page { | ||
255 | struct ftrace_profile_page *next; | ||
256 | unsigned long index; | ||
257 | struct ftrace_profile records[]; | ||
258 | }; | ||
259 | |||
260 | struct ftrace_profile_stat { | ||
261 | atomic_t disabled; | ||
262 | struct hlist_head *hash; | ||
263 | struct ftrace_profile_page *pages; | ||
264 | struct ftrace_profile_page *start; | ||
265 | struct tracer_stat stat; | ||
266 | }; | ||
267 | |||
268 | #define PROFILE_RECORDS_SIZE \ | ||
269 | (PAGE_SIZE - offsetof(struct ftrace_profile_page, records)) | ||
270 | |||
271 | #define PROFILES_PER_PAGE \ | ||
272 | (PROFILE_RECORDS_SIZE / sizeof(struct ftrace_profile)) | ||
273 | |||
274 | static int ftrace_profile_bits __read_mostly; | ||
275 | static int ftrace_profile_enabled __read_mostly; | ||
276 | |||
277 | /* ftrace_profile_lock - synchronize the enable and disable of the profiler */ | ||
278 | static DEFINE_MUTEX(ftrace_profile_lock); | ||
279 | |||
280 | static DEFINE_PER_CPU(struct ftrace_profile_stat, ftrace_profile_stats); | ||
281 | |||
282 | #define FTRACE_PROFILE_HASH_SIZE 1024 /* must be power of 2 */ | ||
283 | |||
284 | static void * | ||
285 | function_stat_next(void *v, int idx) | ||
286 | { | ||
287 | struct ftrace_profile *rec = v; | ||
288 | struct ftrace_profile_page *pg; | ||
289 | |||
290 | pg = (struct ftrace_profile_page *)((unsigned long)rec & PAGE_MASK); | ||
291 | |||
292 | again: | ||
293 | rec++; | ||
294 | if ((void *)rec >= (void *)&pg->records[pg->index]) { | ||
295 | pg = pg->next; | ||
296 | if (!pg) | ||
297 | return NULL; | ||
298 | rec = &pg->records[0]; | ||
299 | if (!rec->counter) | ||
300 | goto again; | ||
301 | } | ||
302 | |||
303 | return rec; | ||
304 | } | ||
305 | |||
306 | static void *function_stat_start(struct tracer_stat *trace) | ||
307 | { | ||
308 | struct ftrace_profile_stat *stat = | ||
309 | container_of(trace, struct ftrace_profile_stat, stat); | ||
310 | |||
311 | if (!stat || !stat->start) | ||
312 | return NULL; | ||
313 | |||
314 | return function_stat_next(&stat->start->records[0], 0); | ||
315 | } | ||
316 | |||
317 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
318 | /* function graph compares on total time */ | ||
319 | static int function_stat_cmp(void *p1, void *p2) | ||
320 | { | ||
321 | struct ftrace_profile *a = p1; | ||
322 | struct ftrace_profile *b = p2; | ||
323 | |||
324 | if (a->time < b->time) | ||
325 | return -1; | ||
326 | if (a->time > b->time) | ||
327 | return 1; | ||
328 | else | ||
329 | return 0; | ||
330 | } | ||
331 | #else | ||
332 | /* not function graph compares against hits */ | ||
333 | static int function_stat_cmp(void *p1, void *p2) | ||
334 | { | ||
335 | struct ftrace_profile *a = p1; | ||
336 | struct ftrace_profile *b = p2; | ||
337 | |||
338 | if (a->counter < b->counter) | ||
339 | return -1; | ||
340 | if (a->counter > b->counter) | ||
341 | return 1; | ||
342 | else | ||
343 | return 0; | ||
344 | } | ||
345 | #endif | ||
346 | |||
347 | static int function_stat_headers(struct seq_file *m) | ||
348 | { | ||
349 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
350 | seq_printf(m, " Function " | ||
351 | "Hit Time Avg\n" | ||
352 | " -------- " | ||
353 | "--- ---- ---\n"); | ||
354 | #else | ||
355 | seq_printf(m, " Function Hit\n" | ||
356 | " -------- ---\n"); | ||
357 | #endif | ||
358 | return 0; | ||
359 | } | ||
360 | |||
361 | static int function_stat_show(struct seq_file *m, void *v) | ||
362 | { | ||
363 | struct ftrace_profile *rec = v; | ||
364 | char str[KSYM_SYMBOL_LEN]; | ||
365 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
366 | static DEFINE_MUTEX(mutex); | ||
367 | static struct trace_seq s; | ||
368 | unsigned long long avg; | ||
369 | #endif | ||
370 | |||
371 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); | ||
372 | seq_printf(m, " %-30.30s %10lu", str, rec->counter); | ||
373 | |||
374 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
375 | seq_printf(m, " "); | ||
376 | avg = rec->time; | ||
377 | do_div(avg, rec->counter); | ||
378 | |||
379 | mutex_lock(&mutex); | ||
380 | trace_seq_init(&s); | ||
381 | trace_print_graph_duration(rec->time, &s); | ||
382 | trace_seq_puts(&s, " "); | ||
383 | trace_print_graph_duration(avg, &s); | ||
384 | trace_print_seq(m, &s); | ||
385 | mutex_unlock(&mutex); | ||
386 | #endif | ||
387 | seq_putc(m, '\n'); | ||
388 | |||
389 | return 0; | ||
390 | } | ||
391 | |||
392 | static void ftrace_profile_reset(struct ftrace_profile_stat *stat) | ||
393 | { | ||
394 | struct ftrace_profile_page *pg; | ||
395 | |||
396 | pg = stat->pages = stat->start; | ||
397 | |||
398 | while (pg) { | ||
399 | memset(pg->records, 0, PROFILE_RECORDS_SIZE); | ||
400 | pg->index = 0; | ||
401 | pg = pg->next; | ||
402 | } | ||
403 | |||
404 | memset(stat->hash, 0, | ||
405 | FTRACE_PROFILE_HASH_SIZE * sizeof(struct hlist_head)); | ||
406 | } | ||
407 | |||
408 | int ftrace_profile_pages_init(struct ftrace_profile_stat *stat) | ||
409 | { | ||
410 | struct ftrace_profile_page *pg; | ||
411 | int functions; | ||
412 | int pages; | ||
413 | int i; | ||
414 | |||
415 | /* If we already allocated, do nothing */ | ||
416 | if (stat->pages) | ||
417 | return 0; | ||
418 | |||
419 | stat->pages = (void *)get_zeroed_page(GFP_KERNEL); | ||
420 | if (!stat->pages) | ||
421 | return -ENOMEM; | ||
422 | |||
423 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
424 | functions = ftrace_update_tot_cnt; | ||
425 | #else | ||
426 | /* | ||
427 | * We do not know the number of functions that exist because | ||
428 | * dynamic tracing is what counts them. With past experience | ||
429 | * we have around 20K functions. That should be more than enough. | ||
430 | * It is highly unlikely we will execute every function in | ||
431 | * the kernel. | ||
432 | */ | ||
433 | functions = 20000; | ||
434 | #endif | ||
435 | |||
436 | pg = stat->start = stat->pages; | ||
437 | |||
438 | pages = DIV_ROUND_UP(functions, PROFILES_PER_PAGE); | ||
439 | |||
440 | for (i = 0; i < pages; i++) { | ||
441 | pg->next = (void *)get_zeroed_page(GFP_KERNEL); | ||
442 | if (!pg->next) | ||
443 | goto out_free; | ||
444 | pg = pg->next; | ||
445 | } | ||
446 | |||
447 | return 0; | ||
448 | |||
449 | out_free: | ||
450 | pg = stat->start; | ||
451 | while (pg) { | ||
452 | unsigned long tmp = (unsigned long)pg; | ||
453 | |||
454 | pg = pg->next; | ||
455 | free_page(tmp); | ||
456 | } | ||
457 | |||
458 | free_page((unsigned long)stat->pages); | ||
459 | stat->pages = NULL; | ||
460 | stat->start = NULL; | ||
461 | |||
462 | return -ENOMEM; | ||
463 | } | ||
464 | |||
465 | static int ftrace_profile_init_cpu(int cpu) | ||
466 | { | ||
467 | struct ftrace_profile_stat *stat; | ||
468 | int size; | ||
469 | |||
470 | stat = &per_cpu(ftrace_profile_stats, cpu); | ||
471 | |||
472 | if (stat->hash) { | ||
473 | /* If the profile is already created, simply reset it */ | ||
474 | ftrace_profile_reset(stat); | ||
475 | return 0; | ||
476 | } | ||
477 | |||
478 | /* | ||
479 | * We are profiling all functions, but usually only a few thousand | ||
480 | * functions are hit. We'll make a hash of 1024 items. | ||
481 | */ | ||
482 | size = FTRACE_PROFILE_HASH_SIZE; | ||
483 | |||
484 | stat->hash = kzalloc(sizeof(struct hlist_head) * size, GFP_KERNEL); | ||
485 | |||
486 | if (!stat->hash) | ||
487 | return -ENOMEM; | ||
488 | |||
489 | if (!ftrace_profile_bits) { | ||
490 | size--; | ||
491 | |||
492 | for (; size; size >>= 1) | ||
493 | ftrace_profile_bits++; | ||
494 | } | ||
495 | |||
496 | /* Preallocate the function profiling pages */ | ||
497 | if (ftrace_profile_pages_init(stat) < 0) { | ||
498 | kfree(stat->hash); | ||
499 | stat->hash = NULL; | ||
500 | return -ENOMEM; | ||
501 | } | ||
502 | |||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | static int ftrace_profile_init(void) | ||
507 | { | ||
508 | int cpu; | ||
509 | int ret = 0; | ||
510 | |||
511 | for_each_online_cpu(cpu) { | ||
512 | ret = ftrace_profile_init_cpu(cpu); | ||
513 | if (ret) | ||
514 | break; | ||
515 | } | ||
516 | |||
517 | return ret; | ||
518 | } | ||
519 | |||
520 | /* interrupts must be disabled */ | ||
521 | static struct ftrace_profile * | ||
522 | ftrace_find_profiled_func(struct ftrace_profile_stat *stat, unsigned long ip) | ||
523 | { | ||
524 | struct ftrace_profile *rec; | ||
525 | struct hlist_head *hhd; | ||
526 | struct hlist_node *n; | ||
527 | unsigned long key; | ||
528 | |||
529 | key = hash_long(ip, ftrace_profile_bits); | ||
530 | hhd = &stat->hash[key]; | ||
531 | |||
532 | if (hlist_empty(hhd)) | ||
533 | return NULL; | ||
534 | |||
535 | hlist_for_each_entry_rcu(rec, n, hhd, node) { | ||
536 | if (rec->ip == ip) | ||
537 | return rec; | ||
538 | } | ||
539 | |||
540 | return NULL; | ||
541 | } | ||
542 | |||
543 | static void ftrace_add_profile(struct ftrace_profile_stat *stat, | ||
544 | struct ftrace_profile *rec) | ||
545 | { | ||
546 | unsigned long key; | ||
547 | |||
548 | key = hash_long(rec->ip, ftrace_profile_bits); | ||
549 | hlist_add_head_rcu(&rec->node, &stat->hash[key]); | ||
550 | } | ||
551 | |||
552 | /* | ||
553 | * The memory is already allocated, this simply finds a new record to use. | ||
554 | */ | ||
555 | static struct ftrace_profile * | ||
556 | ftrace_profile_alloc(struct ftrace_profile_stat *stat, unsigned long ip) | ||
557 | { | ||
558 | struct ftrace_profile *rec = NULL; | ||
559 | |||
560 | /* prevent recursion (from NMIs) */ | ||
561 | if (atomic_inc_return(&stat->disabled) != 1) | ||
562 | goto out; | ||
563 | |||
564 | /* | ||
565 | * Try to find the function again since an NMI | ||
566 | * could have added it | ||
567 | */ | ||
568 | rec = ftrace_find_profiled_func(stat, ip); | ||
569 | if (rec) | ||
570 | goto out; | ||
571 | |||
572 | if (stat->pages->index == PROFILES_PER_PAGE) { | ||
573 | if (!stat->pages->next) | ||
574 | goto out; | ||
575 | stat->pages = stat->pages->next; | ||
576 | } | ||
577 | |||
578 | rec = &stat->pages->records[stat->pages->index++]; | ||
579 | rec->ip = ip; | ||
580 | ftrace_add_profile(stat, rec); | ||
581 | |||
582 | out: | ||
583 | atomic_dec(&stat->disabled); | ||
584 | |||
585 | return rec; | ||
586 | } | ||
587 | |||
588 | static void | ||
589 | function_profile_call(unsigned long ip, unsigned long parent_ip) | ||
590 | { | ||
591 | struct ftrace_profile_stat *stat; | ||
592 | struct ftrace_profile *rec; | ||
593 | unsigned long flags; | ||
594 | |||
595 | if (!ftrace_profile_enabled) | ||
596 | return; | ||
597 | |||
598 | local_irq_save(flags); | ||
599 | |||
600 | stat = &__get_cpu_var(ftrace_profile_stats); | ||
601 | if (!stat->hash) | ||
602 | goto out; | ||
603 | |||
604 | rec = ftrace_find_profiled_func(stat, ip); | ||
605 | if (!rec) { | ||
606 | rec = ftrace_profile_alloc(stat, ip); | ||
607 | if (!rec) | ||
608 | goto out; | ||
609 | } | ||
610 | |||
611 | rec->counter++; | ||
612 | out: | ||
613 | local_irq_restore(flags); | ||
614 | } | ||
615 | |||
616 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
617 | static int profile_graph_entry(struct ftrace_graph_ent *trace) | ||
618 | { | ||
619 | function_profile_call(trace->func, 0); | ||
620 | return 1; | ||
621 | } | ||
622 | |||
623 | static void profile_graph_return(struct ftrace_graph_ret *trace) | ||
624 | { | ||
625 | struct ftrace_profile_stat *stat; | ||
626 | unsigned long long calltime; | ||
627 | struct ftrace_profile *rec; | ||
628 | unsigned long flags; | ||
629 | |||
630 | local_irq_save(flags); | ||
631 | stat = &__get_cpu_var(ftrace_profile_stats); | ||
632 | if (!stat->hash) | ||
633 | goto out; | ||
634 | |||
635 | calltime = trace->rettime - trace->calltime; | ||
636 | |||
637 | if (!(trace_flags & TRACE_ITER_GRAPH_TIME)) { | ||
638 | int index; | ||
639 | |||
640 | index = trace->depth; | ||
641 | |||
642 | /* Append this call time to the parent time to subtract */ | ||
643 | if (index) | ||
644 | current->ret_stack[index - 1].subtime += calltime; | ||
645 | |||
646 | if (current->ret_stack[index].subtime < calltime) | ||
647 | calltime -= current->ret_stack[index].subtime; | ||
648 | else | ||
649 | calltime = 0; | ||
650 | } | ||
651 | |||
652 | rec = ftrace_find_profiled_func(stat, trace->func); | ||
653 | if (rec) | ||
654 | rec->time += calltime; | ||
655 | |||
656 | out: | ||
657 | local_irq_restore(flags); | ||
658 | } | ||
659 | |||
660 | static int register_ftrace_profiler(void) | ||
661 | { | ||
662 | return register_ftrace_graph(&profile_graph_return, | ||
663 | &profile_graph_entry); | ||
664 | } | ||
665 | |||
666 | static void unregister_ftrace_profiler(void) | ||
667 | { | ||
668 | unregister_ftrace_graph(); | ||
669 | } | ||
670 | #else | ||
671 | static struct ftrace_ops ftrace_profile_ops __read_mostly = | ||
672 | { | ||
673 | .func = function_profile_call, | ||
674 | }; | ||
675 | |||
676 | static int register_ftrace_profiler(void) | ||
677 | { | ||
678 | return register_ftrace_function(&ftrace_profile_ops); | ||
679 | } | ||
680 | |||
681 | static void unregister_ftrace_profiler(void) | ||
682 | { | ||
683 | unregister_ftrace_function(&ftrace_profile_ops); | ||
684 | } | ||
685 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | ||
686 | |||
687 | static ssize_t | ||
688 | ftrace_profile_write(struct file *filp, const char __user *ubuf, | ||
689 | size_t cnt, loff_t *ppos) | ||
690 | { | ||
691 | unsigned long val; | ||
692 | char buf[64]; /* big enough to hold a number */ | ||
693 | int ret; | ||
694 | |||
695 | if (cnt >= sizeof(buf)) | ||
696 | return -EINVAL; | ||
697 | |||
698 | if (copy_from_user(&buf, ubuf, cnt)) | ||
699 | return -EFAULT; | ||
700 | |||
701 | buf[cnt] = 0; | ||
702 | |||
703 | ret = strict_strtoul(buf, 10, &val); | ||
704 | if (ret < 0) | ||
705 | return ret; | ||
706 | |||
707 | val = !!val; | ||
708 | |||
709 | mutex_lock(&ftrace_profile_lock); | ||
710 | if (ftrace_profile_enabled ^ val) { | ||
711 | if (val) { | ||
712 | ret = ftrace_profile_init(); | ||
713 | if (ret < 0) { | ||
714 | cnt = ret; | ||
715 | goto out; | ||
716 | } | ||
717 | |||
718 | ret = register_ftrace_profiler(); | ||
719 | if (ret < 0) { | ||
720 | cnt = ret; | ||
721 | goto out; | ||
722 | } | ||
723 | ftrace_profile_enabled = 1; | ||
724 | } else { | ||
725 | ftrace_profile_enabled = 0; | ||
726 | unregister_ftrace_profiler(); | ||
727 | } | ||
728 | } | ||
729 | out: | ||
730 | mutex_unlock(&ftrace_profile_lock); | ||
731 | |||
732 | filp->f_pos += cnt; | ||
733 | |||
734 | return cnt; | ||
735 | } | ||
736 | |||
737 | static ssize_t | ||
738 | ftrace_profile_read(struct file *filp, char __user *ubuf, | ||
739 | size_t cnt, loff_t *ppos) | ||
740 | { | ||
741 | char buf[64]; /* big enough to hold a number */ | ||
742 | int r; | ||
743 | |||
744 | r = sprintf(buf, "%u\n", ftrace_profile_enabled); | ||
745 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); | ||
746 | } | ||
747 | |||
748 | static const struct file_operations ftrace_profile_fops = { | ||
749 | .open = tracing_open_generic, | ||
750 | .read = ftrace_profile_read, | ||
751 | .write = ftrace_profile_write, | ||
752 | }; | ||
753 | |||
754 | /* used to initialize the real stat files */ | ||
755 | static struct tracer_stat function_stats __initdata = { | ||
756 | .name = "functions", | ||
757 | .stat_start = function_stat_start, | ||
758 | .stat_next = function_stat_next, | ||
759 | .stat_cmp = function_stat_cmp, | ||
760 | .stat_headers = function_stat_headers, | ||
761 | .stat_show = function_stat_show | ||
762 | }; | ||
763 | |||
764 | static void ftrace_profile_debugfs(struct dentry *d_tracer) | ||
765 | { | ||
766 | struct ftrace_profile_stat *stat; | ||
767 | struct dentry *entry; | ||
768 | char *name; | ||
769 | int ret; | ||
770 | int cpu; | ||
771 | |||
772 | for_each_possible_cpu(cpu) { | ||
773 | stat = &per_cpu(ftrace_profile_stats, cpu); | ||
774 | |||
775 | /* allocate enough for function name + cpu number */ | ||
776 | name = kmalloc(32, GFP_KERNEL); | ||
777 | if (!name) { | ||
778 | /* | ||
779 | * The files created are permanent, if something happens | ||
780 | * we still do not free memory. | ||
781 | */ | ||
782 | kfree(stat); | ||
783 | WARN(1, | ||
784 | "Could not allocate stat file for cpu %d\n", | ||
785 | cpu); | ||
786 | return; | ||
787 | } | ||
788 | stat->stat = function_stats; | ||
789 | snprintf(name, 32, "function%d", cpu); | ||
790 | stat->stat.name = name; | ||
791 | ret = register_stat_tracer(&stat->stat); | ||
792 | if (ret) { | ||
793 | WARN(1, | ||
794 | "Could not register function stat for cpu %d\n", | ||
795 | cpu); | ||
796 | kfree(name); | ||
797 | return; | ||
798 | } | ||
799 | } | ||
800 | |||
801 | entry = debugfs_create_file("function_profile_enabled", 0644, | ||
802 | d_tracer, NULL, &ftrace_profile_fops); | ||
803 | if (!entry) | ||
804 | pr_warning("Could not create debugfs " | ||
805 | "'function_profile_enabled' entry\n"); | ||
806 | } | ||
807 | |||
808 | #else /* CONFIG_FUNCTION_PROFILER */ | ||
809 | static void ftrace_profile_debugfs(struct dentry *d_tracer) | ||
810 | { | ||
811 | } | ||
812 | #endif /* CONFIG_FUNCTION_PROFILER */ | ||
813 | |||
243 | /* set when tracing only a pid */ | 814 | /* set when tracing only a pid */ |
244 | struct pid *ftrace_pid_trace; | 815 | struct pid *ftrace_pid_trace; |
245 | static struct pid * const ftrace_swapper_pid = &init_struct_pid; | 816 | static struct pid * const ftrace_swapper_pid = &init_struct_pid; |
@@ -261,7 +832,6 @@ struct ftrace_func_probe { | |||
261 | struct rcu_head rcu; | 832 | struct rcu_head rcu; |
262 | }; | 833 | }; |
263 | 834 | ||
264 | |||
265 | enum { | 835 | enum { |
266 | FTRACE_ENABLE_CALLS = (1 << 0), | 836 | FTRACE_ENABLE_CALLS = (1 << 0), |
267 | FTRACE_DISABLE_CALLS = (1 << 1), | 837 | FTRACE_DISABLE_CALLS = (1 << 1), |
@@ -1408,7 +1978,7 @@ function_trace_probe_call(unsigned long ip, unsigned long parent_ip) | |||
1408 | 1978 | ||
1409 | static struct ftrace_ops trace_probe_ops __read_mostly = | 1979 | static struct ftrace_ops trace_probe_ops __read_mostly = |
1410 | { | 1980 | { |
1411 | .func = function_trace_probe_call, | 1981 | .func = function_trace_probe_call, |
1412 | }; | 1982 | }; |
1413 | 1983 | ||
1414 | static int ftrace_probe_registered; | 1984 | static int ftrace_probe_registered; |
@@ -2430,6 +3000,9 @@ static __init int ftrace_init_debugfs(void) | |||
2430 | if (!entry) | 3000 | if (!entry) |
2431 | pr_warning("Could not create debugfs " | 3001 | pr_warning("Could not create debugfs " |
2432 | "'set_ftrace_pid' entry\n"); | 3002 | "'set_ftrace_pid' entry\n"); |
3003 | |||
3004 | ftrace_profile_debugfs(d_tracer); | ||
3005 | |||
2433 | return 0; | 3006 | return 0; |
2434 | } | 3007 | } |
2435 | fs_initcall(ftrace_init_debugfs); | 3008 | fs_initcall(ftrace_init_debugfs); |