diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-02-17 05:31:01 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-02-17 05:31:01 -0500 |
commit | f492d3f8385a98f828e8220d14492337dc29e07b (patch) | |
tree | 31f22c83f7e45388955aacb45c425d7cb6a4d78c /kernel | |
parent | c4e2b432d5b57e2faaeea048079b31c243079647 (diff) | |
parent | e110e3d1eaa0f9628918be67ddd32e8ad65a2871 (diff) |
Merge branch 'tip/tracing/ftrace' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-trace into tracing/ftrace
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/trace/ftrace.c | 929 | ||||
-rw-r--r-- | kernel/trace/ring_buffer.c | 9 | ||||
-rw-r--r-- | kernel/trace/trace_functions.c | 163 |
3 files changed, 904 insertions, 197 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 1796e018fbff..6533c1d20155 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/sysctl.h> | 27 | #include <linux/sysctl.h> |
28 | #include <linux/ctype.h> | 28 | #include <linux/ctype.h> |
29 | #include <linux/list.h> | 29 | #include <linux/list.h> |
30 | #include <linux/hash.h> | ||
30 | 31 | ||
31 | #include <asm/ftrace.h> | 32 | #include <asm/ftrace.h> |
32 | 33 | ||
@@ -44,14 +45,14 @@ | |||
44 | ftrace_kill(); \ | 45 | ftrace_kill(); \ |
45 | } while (0) | 46 | } while (0) |
46 | 47 | ||
48 | /* hash bits for specific function selection */ | ||
49 | #define FTRACE_HASH_BITS 7 | ||
50 | #define FTRACE_FUNC_HASHSIZE (1 << FTRACE_HASH_BITS) | ||
51 | |||
47 | /* ftrace_enabled is a method to turn ftrace on or off */ | 52 | /* ftrace_enabled is a method to turn ftrace on or off */ |
48 | int ftrace_enabled __read_mostly; | 53 | int ftrace_enabled __read_mostly; |
49 | static int last_ftrace_enabled; | 54 | static int last_ftrace_enabled; |
50 | 55 | ||
51 | /* set when tracing only a pid */ | ||
52 | struct pid *ftrace_pid_trace; | ||
53 | static struct pid * const ftrace_swapper_pid = &init_struct_pid; | ||
54 | |||
55 | /* Quick disabling of function tracer. */ | 56 | /* Quick disabling of function tracer. */ |
56 | int function_trace_stop; | 57 | int function_trace_stop; |
57 | 58 | ||
@@ -61,9 +62,7 @@ int function_trace_stop; | |||
61 | */ | 62 | */ |
62 | static int ftrace_disabled __read_mostly; | 63 | static int ftrace_disabled __read_mostly; |
63 | 64 | ||
64 | static DEFINE_SPINLOCK(ftrace_lock); | 65 | static DEFINE_MUTEX(ftrace_lock); |
65 | static DEFINE_MUTEX(ftrace_sysctl_lock); | ||
66 | static DEFINE_MUTEX(ftrace_start_lock); | ||
67 | 66 | ||
68 | static struct ftrace_ops ftrace_list_end __read_mostly = | 67 | static struct ftrace_ops ftrace_list_end __read_mostly = |
69 | { | 68 | { |
@@ -134,9 +133,6 @@ static void ftrace_test_stop_func(unsigned long ip, unsigned long parent_ip) | |||
134 | 133 | ||
135 | static int __register_ftrace_function(struct ftrace_ops *ops) | 134 | static int __register_ftrace_function(struct ftrace_ops *ops) |
136 | { | 135 | { |
137 | /* should not be called from interrupt context */ | ||
138 | spin_lock(&ftrace_lock); | ||
139 | |||
140 | ops->next = ftrace_list; | 136 | ops->next = ftrace_list; |
141 | /* | 137 | /* |
142 | * We are entering ops into the ftrace_list but another | 138 | * We are entering ops into the ftrace_list but another |
@@ -172,18 +168,12 @@ static int __register_ftrace_function(struct ftrace_ops *ops) | |||
172 | #endif | 168 | #endif |
173 | } | 169 | } |
174 | 170 | ||
175 | spin_unlock(&ftrace_lock); | ||
176 | |||
177 | return 0; | 171 | return 0; |
178 | } | 172 | } |
179 | 173 | ||
180 | static int __unregister_ftrace_function(struct ftrace_ops *ops) | 174 | static int __unregister_ftrace_function(struct ftrace_ops *ops) |
181 | { | 175 | { |
182 | struct ftrace_ops **p; | 176 | struct ftrace_ops **p; |
183 | int ret = 0; | ||
184 | |||
185 | /* should not be called from interrupt context */ | ||
186 | spin_lock(&ftrace_lock); | ||
187 | 177 | ||
188 | /* | 178 | /* |
189 | * If we are removing the last function, then simply point | 179 | * If we are removing the last function, then simply point |
@@ -192,17 +182,15 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) | |||
192 | if (ftrace_list == ops && ops->next == &ftrace_list_end) { | 182 | if (ftrace_list == ops && ops->next == &ftrace_list_end) { |
193 | ftrace_trace_function = ftrace_stub; | 183 | ftrace_trace_function = ftrace_stub; |
194 | ftrace_list = &ftrace_list_end; | 184 | ftrace_list = &ftrace_list_end; |
195 | goto out; | 185 | return 0; |
196 | } | 186 | } |
197 | 187 | ||
198 | for (p = &ftrace_list; *p != &ftrace_list_end; p = &(*p)->next) | 188 | for (p = &ftrace_list; *p != &ftrace_list_end; p = &(*p)->next) |
199 | if (*p == ops) | 189 | if (*p == ops) |
200 | break; | 190 | break; |
201 | 191 | ||
202 | if (*p != ops) { | 192 | if (*p != ops) |
203 | ret = -1; | 193 | return -1; |
204 | goto out; | ||
205 | } | ||
206 | 194 | ||
207 | *p = (*p)->next; | 195 | *p = (*p)->next; |
208 | 196 | ||
@@ -223,18 +211,14 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) | |||
223 | } | 211 | } |
224 | } | 212 | } |
225 | 213 | ||
226 | out: | 214 | return 0; |
227 | spin_unlock(&ftrace_lock); | ||
228 | |||
229 | return ret; | ||
230 | } | 215 | } |
231 | 216 | ||
232 | static void ftrace_update_pid_func(void) | 217 | static void ftrace_update_pid_func(void) |
233 | { | 218 | { |
234 | ftrace_func_t func; | 219 | ftrace_func_t func; |
235 | 220 | ||
236 | /* should not be called from interrupt context */ | 221 | mutex_lock(&ftrace_lock); |
237 | spin_lock(&ftrace_lock); | ||
238 | 222 | ||
239 | if (ftrace_trace_function == ftrace_stub) | 223 | if (ftrace_trace_function == ftrace_stub) |
240 | goto out; | 224 | goto out; |
@@ -256,7 +240,7 @@ static void ftrace_update_pid_func(void) | |||
256 | #endif | 240 | #endif |
257 | 241 | ||
258 | out: | 242 | out: |
259 | spin_unlock(&ftrace_lock); | 243 | mutex_unlock(&ftrace_lock); |
260 | } | 244 | } |
261 | 245 | ||
262 | #ifdef CONFIG_DYNAMIC_FTRACE | 246 | #ifdef CONFIG_DYNAMIC_FTRACE |
@@ -264,6 +248,21 @@ static void ftrace_update_pid_func(void) | |||
264 | # error Dynamic ftrace depends on MCOUNT_RECORD | 248 | # error Dynamic ftrace depends on MCOUNT_RECORD |
265 | #endif | 249 | #endif |
266 | 250 | ||
251 | /* set when tracing only a pid */ | ||
252 | struct pid *ftrace_pid_trace; | ||
253 | static struct pid * const ftrace_swapper_pid = &init_struct_pid; | ||
254 | static struct hlist_head ftrace_func_hash[FTRACE_FUNC_HASHSIZE] __read_mostly; | ||
255 | |||
256 | struct ftrace_func_hook { | ||
257 | struct hlist_node node; | ||
258 | struct ftrace_hook_ops *ops; | ||
259 | unsigned long flags; | ||
260 | unsigned long ip; | ||
261 | void *data; | ||
262 | struct rcu_head rcu; | ||
263 | }; | ||
264 | |||
265 | |||
267 | enum { | 266 | enum { |
268 | FTRACE_ENABLE_CALLS = (1 << 0), | 267 | FTRACE_ENABLE_CALLS = (1 << 0), |
269 | FTRACE_DISABLE_CALLS = (1 << 1), | 268 | FTRACE_DISABLE_CALLS = (1 << 1), |
@@ -297,6 +296,19 @@ static struct ftrace_page *ftrace_pages; | |||
297 | 296 | ||
298 | static struct dyn_ftrace *ftrace_free_records; | 297 | static struct dyn_ftrace *ftrace_free_records; |
299 | 298 | ||
299 | /* | ||
300 | * This is a double for. Do not use 'break' to break out of the loop, | ||
301 | * you must use a goto. | ||
302 | */ | ||
303 | #define do_for_each_ftrace_rec(pg, rec) \ | ||
304 | for (pg = ftrace_pages_start; pg; pg = pg->next) { \ | ||
305 | int _____i; \ | ||
306 | for (_____i = 0; _____i < pg->index; _____i++) { \ | ||
307 | rec = &pg->records[_____i]; | ||
308 | |||
309 | #define while_for_each_ftrace_rec() \ | ||
310 | } \ | ||
311 | } | ||
300 | 312 | ||
301 | #ifdef CONFIG_KPROBES | 313 | #ifdef CONFIG_KPROBES |
302 | 314 | ||
@@ -341,23 +353,16 @@ void ftrace_release(void *start, unsigned long size) | |||
341 | struct ftrace_page *pg; | 353 | struct ftrace_page *pg; |
342 | unsigned long s = (unsigned long)start; | 354 | unsigned long s = (unsigned long)start; |
343 | unsigned long e = s + size; | 355 | unsigned long e = s + size; |
344 | int i; | ||
345 | 356 | ||
346 | if (ftrace_disabled || !start) | 357 | if (ftrace_disabled || !start) |
347 | return; | 358 | return; |
348 | 359 | ||
349 | /* should not be called from interrupt context */ | 360 | mutex_lock(&ftrace_lock); |
350 | spin_lock(&ftrace_lock); | 361 | do_for_each_ftrace_rec(pg, rec) { |
351 | 362 | if ((rec->ip >= s) && (rec->ip < e)) | |
352 | for (pg = ftrace_pages_start; pg; pg = pg->next) { | 363 | ftrace_free_rec(rec); |
353 | for (i = 0; i < pg->index; i++) { | 364 | } while_for_each_ftrace_rec(); |
354 | rec = &pg->records[i]; | 365 | mutex_unlock(&ftrace_lock); |
355 | |||
356 | if ((rec->ip >= s) && (rec->ip < e)) | ||
357 | ftrace_free_rec(rec); | ||
358 | } | ||
359 | } | ||
360 | spin_unlock(&ftrace_lock); | ||
361 | } | 366 | } |
362 | 367 | ||
363 | static struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip) | 368 | static struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip) |
@@ -523,41 +528,37 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable) | |||
523 | 528 | ||
524 | static void ftrace_replace_code(int enable) | 529 | static void ftrace_replace_code(int enable) |
525 | { | 530 | { |
526 | int i, failed; | 531 | int failed; |
527 | struct dyn_ftrace *rec; | 532 | struct dyn_ftrace *rec; |
528 | struct ftrace_page *pg; | 533 | struct ftrace_page *pg; |
529 | 534 | ||
530 | for (pg = ftrace_pages_start; pg; pg = pg->next) { | 535 | do_for_each_ftrace_rec(pg, rec) { |
531 | for (i = 0; i < pg->index; i++) { | 536 | /* |
532 | rec = &pg->records[i]; | 537 | * Skip over free records and records that have |
533 | 538 | * failed. | |
534 | /* | 539 | */ |
535 | * Skip over free records and records that have | 540 | if (rec->flags & FTRACE_FL_FREE || |
536 | * failed. | 541 | rec->flags & FTRACE_FL_FAILED) |
537 | */ | 542 | continue; |
538 | if (rec->flags & FTRACE_FL_FREE || | ||
539 | rec->flags & FTRACE_FL_FAILED) | ||
540 | continue; | ||
541 | 543 | ||
542 | /* ignore updates to this record's mcount site */ | 544 | /* ignore updates to this record's mcount site */ |
543 | if (get_kprobe((void *)rec->ip)) { | 545 | if (get_kprobe((void *)rec->ip)) { |
544 | freeze_record(rec); | 546 | freeze_record(rec); |
545 | continue; | 547 | continue; |
546 | } else { | 548 | } else { |
547 | unfreeze_record(rec); | 549 | unfreeze_record(rec); |
548 | } | 550 | } |
549 | 551 | ||
550 | failed = __ftrace_replace_code(rec, enable); | 552 | failed = __ftrace_replace_code(rec, enable); |
551 | if (failed && (rec->flags & FTRACE_FL_CONVERTED)) { | 553 | if (failed && (rec->flags & FTRACE_FL_CONVERTED)) { |
552 | rec->flags |= FTRACE_FL_FAILED; | 554 | rec->flags |= FTRACE_FL_FAILED; |
553 | if ((system_state == SYSTEM_BOOTING) || | 555 | if ((system_state == SYSTEM_BOOTING) || |
554 | !core_kernel_text(rec->ip)) { | 556 | !core_kernel_text(rec->ip)) { |
555 | ftrace_free_rec(rec); | 557 | ftrace_free_rec(rec); |
556 | } else | 558 | } else |
557 | ftrace_bug(failed, rec->ip); | 559 | ftrace_bug(failed, rec->ip); |
558 | } | ||
559 | } | 560 | } |
560 | } | 561 | } while_for_each_ftrace_rec(); |
561 | } | 562 | } |
562 | 563 | ||
563 | static int | 564 | static int |
@@ -623,13 +624,10 @@ static void ftrace_startup(int command) | |||
623 | if (unlikely(ftrace_disabled)) | 624 | if (unlikely(ftrace_disabled)) |
624 | return; | 625 | return; |
625 | 626 | ||
626 | mutex_lock(&ftrace_start_lock); | ||
627 | ftrace_start_up++; | 627 | ftrace_start_up++; |
628 | command |= FTRACE_ENABLE_CALLS; | 628 | command |= FTRACE_ENABLE_CALLS; |
629 | 629 | ||
630 | ftrace_startup_enable(command); | 630 | ftrace_startup_enable(command); |
631 | |||
632 | mutex_unlock(&ftrace_start_lock); | ||
633 | } | 631 | } |
634 | 632 | ||
635 | static void ftrace_shutdown(int command) | 633 | static void ftrace_shutdown(int command) |
@@ -637,7 +635,6 @@ static void ftrace_shutdown(int command) | |||
637 | if (unlikely(ftrace_disabled)) | 635 | if (unlikely(ftrace_disabled)) |
638 | return; | 636 | return; |
639 | 637 | ||
640 | mutex_lock(&ftrace_start_lock); | ||
641 | ftrace_start_up--; | 638 | ftrace_start_up--; |
642 | if (!ftrace_start_up) | 639 | if (!ftrace_start_up) |
643 | command |= FTRACE_DISABLE_CALLS; | 640 | command |= FTRACE_DISABLE_CALLS; |
@@ -648,11 +645,9 @@ static void ftrace_shutdown(int command) | |||
648 | } | 645 | } |
649 | 646 | ||
650 | if (!command || !ftrace_enabled) | 647 | if (!command || !ftrace_enabled) |
651 | goto out; | 648 | return; |
652 | 649 | ||
653 | ftrace_run_update_code(command); | 650 | ftrace_run_update_code(command); |
654 | out: | ||
655 | mutex_unlock(&ftrace_start_lock); | ||
656 | } | 651 | } |
657 | 652 | ||
658 | static void ftrace_startup_sysctl(void) | 653 | static void ftrace_startup_sysctl(void) |
@@ -662,7 +657,6 @@ static void ftrace_startup_sysctl(void) | |||
662 | if (unlikely(ftrace_disabled)) | 657 | if (unlikely(ftrace_disabled)) |
663 | return; | 658 | return; |
664 | 659 | ||
665 | mutex_lock(&ftrace_start_lock); | ||
666 | /* Force update next time */ | 660 | /* Force update next time */ |
667 | saved_ftrace_func = NULL; | 661 | saved_ftrace_func = NULL; |
668 | /* ftrace_start_up is true if we want ftrace running */ | 662 | /* ftrace_start_up is true if we want ftrace running */ |
@@ -670,7 +664,6 @@ static void ftrace_startup_sysctl(void) | |||
670 | command |= FTRACE_ENABLE_CALLS; | 664 | command |= FTRACE_ENABLE_CALLS; |
671 | 665 | ||
672 | ftrace_run_update_code(command); | 666 | ftrace_run_update_code(command); |
673 | mutex_unlock(&ftrace_start_lock); | ||
674 | } | 667 | } |
675 | 668 | ||
676 | static void ftrace_shutdown_sysctl(void) | 669 | static void ftrace_shutdown_sysctl(void) |
@@ -680,13 +673,11 @@ static void ftrace_shutdown_sysctl(void) | |||
680 | if (unlikely(ftrace_disabled)) | 673 | if (unlikely(ftrace_disabled)) |
681 | return; | 674 | return; |
682 | 675 | ||
683 | mutex_lock(&ftrace_start_lock); | ||
684 | /* ftrace_start_up is true if ftrace is running */ | 676 | /* ftrace_start_up is true if ftrace is running */ |
685 | if (ftrace_start_up) | 677 | if (ftrace_start_up) |
686 | command |= FTRACE_DISABLE_CALLS; | 678 | command |= FTRACE_DISABLE_CALLS; |
687 | 679 | ||
688 | ftrace_run_update_code(command); | 680 | ftrace_run_update_code(command); |
689 | mutex_unlock(&ftrace_start_lock); | ||
690 | } | 681 | } |
691 | 682 | ||
692 | static cycle_t ftrace_update_time; | 683 | static cycle_t ftrace_update_time; |
@@ -773,12 +764,15 @@ enum { | |||
773 | FTRACE_ITER_CONT = (1 << 1), | 764 | FTRACE_ITER_CONT = (1 << 1), |
774 | FTRACE_ITER_NOTRACE = (1 << 2), | 765 | FTRACE_ITER_NOTRACE = (1 << 2), |
775 | FTRACE_ITER_FAILURES = (1 << 3), | 766 | FTRACE_ITER_FAILURES = (1 << 3), |
767 | FTRACE_ITER_PRINTALL = (1 << 4), | ||
768 | FTRACE_ITER_HASH = (1 << 5), | ||
776 | }; | 769 | }; |
777 | 770 | ||
778 | #define FTRACE_BUFF_MAX (KSYM_SYMBOL_LEN+4) /* room for wildcards */ | 771 | #define FTRACE_BUFF_MAX (KSYM_SYMBOL_LEN+4) /* room for wildcards */ |
779 | 772 | ||
780 | struct ftrace_iterator { | 773 | struct ftrace_iterator { |
781 | struct ftrace_page *pg; | 774 | struct ftrace_page *pg; |
775 | int hidx; | ||
782 | int idx; | 776 | int idx; |
783 | unsigned flags; | 777 | unsigned flags; |
784 | unsigned char buffer[FTRACE_BUFF_MAX+1]; | 778 | unsigned char buffer[FTRACE_BUFF_MAX+1]; |
@@ -787,15 +781,89 @@ struct ftrace_iterator { | |||
787 | }; | 781 | }; |
788 | 782 | ||
789 | static void * | 783 | static void * |
784 | t_hash_next(struct seq_file *m, void *v, loff_t *pos) | ||
785 | { | ||
786 | struct ftrace_iterator *iter = m->private; | ||
787 | struct hlist_node *hnd = v; | ||
788 | struct hlist_head *hhd; | ||
789 | |||
790 | WARN_ON(!(iter->flags & FTRACE_ITER_HASH)); | ||
791 | |||
792 | (*pos)++; | ||
793 | |||
794 | retry: | ||
795 | if (iter->hidx >= FTRACE_FUNC_HASHSIZE) | ||
796 | return NULL; | ||
797 | |||
798 | hhd = &ftrace_func_hash[iter->hidx]; | ||
799 | |||
800 | if (hlist_empty(hhd)) { | ||
801 | iter->hidx++; | ||
802 | hnd = NULL; | ||
803 | goto retry; | ||
804 | } | ||
805 | |||
806 | if (!hnd) | ||
807 | hnd = hhd->first; | ||
808 | else { | ||
809 | hnd = hnd->next; | ||
810 | if (!hnd) { | ||
811 | iter->hidx++; | ||
812 | goto retry; | ||
813 | } | ||
814 | } | ||
815 | |||
816 | return hnd; | ||
817 | } | ||
818 | |||
819 | static void *t_hash_start(struct seq_file *m, loff_t *pos) | ||
820 | { | ||
821 | struct ftrace_iterator *iter = m->private; | ||
822 | void *p = NULL; | ||
823 | |||
824 | iter->flags |= FTRACE_ITER_HASH; | ||
825 | |||
826 | return t_hash_next(m, p, pos); | ||
827 | } | ||
828 | |||
829 | static int t_hash_show(struct seq_file *m, void *v) | ||
830 | { | ||
831 | struct ftrace_func_hook *rec; | ||
832 | struct hlist_node *hnd = v; | ||
833 | char str[KSYM_SYMBOL_LEN]; | ||
834 | |||
835 | rec = hlist_entry(hnd, struct ftrace_func_hook, node); | ||
836 | |||
837 | if (rec->ops->print) | ||
838 | return rec->ops->print(m, rec->ip, rec->ops, rec->data); | ||
839 | |||
840 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); | ||
841 | seq_printf(m, "%s:", str); | ||
842 | |||
843 | kallsyms_lookup((unsigned long)rec->ops->func, NULL, NULL, NULL, str); | ||
844 | seq_printf(m, "%s", str); | ||
845 | |||
846 | if (rec->data) | ||
847 | seq_printf(m, ":%p", rec->data); | ||
848 | seq_putc(m, '\n'); | ||
849 | |||
850 | return 0; | ||
851 | } | ||
852 | |||
853 | static void * | ||
790 | t_next(struct seq_file *m, void *v, loff_t *pos) | 854 | t_next(struct seq_file *m, void *v, loff_t *pos) |
791 | { | 855 | { |
792 | struct ftrace_iterator *iter = m->private; | 856 | struct ftrace_iterator *iter = m->private; |
793 | struct dyn_ftrace *rec = NULL; | 857 | struct dyn_ftrace *rec = NULL; |
794 | 858 | ||
859 | if (iter->flags & FTRACE_ITER_HASH) | ||
860 | return t_hash_next(m, v, pos); | ||
861 | |||
795 | (*pos)++; | 862 | (*pos)++; |
796 | 863 | ||
797 | /* should not be called from interrupt context */ | 864 | if (iter->flags & FTRACE_ITER_PRINTALL) |
798 | spin_lock(&ftrace_lock); | 865 | return NULL; |
866 | |||
799 | retry: | 867 | retry: |
800 | if (iter->idx >= iter->pg->index) { | 868 | if (iter->idx >= iter->pg->index) { |
801 | if (iter->pg->next) { | 869 | if (iter->pg->next) { |
@@ -824,7 +892,6 @@ t_next(struct seq_file *m, void *v, loff_t *pos) | |||
824 | goto retry; | 892 | goto retry; |
825 | } | 893 | } |
826 | } | 894 | } |
827 | spin_unlock(&ftrace_lock); | ||
828 | 895 | ||
829 | return rec; | 896 | return rec; |
830 | } | 897 | } |
@@ -834,6 +901,23 @@ static void *t_start(struct seq_file *m, loff_t *pos) | |||
834 | struct ftrace_iterator *iter = m->private; | 901 | struct ftrace_iterator *iter = m->private; |
835 | void *p = NULL; | 902 | void *p = NULL; |
836 | 903 | ||
904 | mutex_lock(&ftrace_lock); | ||
905 | /* | ||
906 | * For set_ftrace_filter reading, if we have the filter | ||
907 | * off, we can short cut and just print out that all | ||
908 | * functions are enabled. | ||
909 | */ | ||
910 | if (iter->flags & FTRACE_ITER_FILTER && !ftrace_filtered) { | ||
911 | if (*pos > 0) | ||
912 | return t_hash_start(m, pos); | ||
913 | iter->flags |= FTRACE_ITER_PRINTALL; | ||
914 | (*pos)++; | ||
915 | return iter; | ||
916 | } | ||
917 | |||
918 | if (iter->flags & FTRACE_ITER_HASH) | ||
919 | return t_hash_start(m, pos); | ||
920 | |||
837 | if (*pos > 0) { | 921 | if (*pos > 0) { |
838 | if (iter->idx < 0) | 922 | if (iter->idx < 0) |
839 | return p; | 923 | return p; |
@@ -843,18 +927,31 @@ static void *t_start(struct seq_file *m, loff_t *pos) | |||
843 | 927 | ||
844 | p = t_next(m, p, pos); | 928 | p = t_next(m, p, pos); |
845 | 929 | ||
930 | if (!p) | ||
931 | return t_hash_start(m, pos); | ||
932 | |||
846 | return p; | 933 | return p; |
847 | } | 934 | } |
848 | 935 | ||
849 | static void t_stop(struct seq_file *m, void *p) | 936 | static void t_stop(struct seq_file *m, void *p) |
850 | { | 937 | { |
938 | mutex_unlock(&ftrace_lock); | ||
851 | } | 939 | } |
852 | 940 | ||
853 | static int t_show(struct seq_file *m, void *v) | 941 | static int t_show(struct seq_file *m, void *v) |
854 | { | 942 | { |
943 | struct ftrace_iterator *iter = m->private; | ||
855 | struct dyn_ftrace *rec = v; | 944 | struct dyn_ftrace *rec = v; |
856 | char str[KSYM_SYMBOL_LEN]; | 945 | char str[KSYM_SYMBOL_LEN]; |
857 | 946 | ||
947 | if (iter->flags & FTRACE_ITER_HASH) | ||
948 | return t_hash_show(m, v); | ||
949 | |||
950 | if (iter->flags & FTRACE_ITER_PRINTALL) { | ||
951 | seq_printf(m, "#### all functions enabled ####\n"); | ||
952 | return 0; | ||
953 | } | ||
954 | |||
858 | if (!rec) | 955 | if (!rec) |
859 | return 0; | 956 | return 0; |
860 | 957 | ||
@@ -933,23 +1030,16 @@ static void ftrace_filter_reset(int enable) | |||
933 | struct ftrace_page *pg; | 1030 | struct ftrace_page *pg; |
934 | struct dyn_ftrace *rec; | 1031 | struct dyn_ftrace *rec; |
935 | unsigned long type = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; | 1032 | unsigned long type = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; |
936 | unsigned i; | ||
937 | 1033 | ||
938 | /* should not be called from interrupt context */ | 1034 | mutex_lock(&ftrace_lock); |
939 | spin_lock(&ftrace_lock); | ||
940 | if (enable) | 1035 | if (enable) |
941 | ftrace_filtered = 0; | 1036 | ftrace_filtered = 0; |
942 | pg = ftrace_pages_start; | 1037 | do_for_each_ftrace_rec(pg, rec) { |
943 | while (pg) { | 1038 | if (rec->flags & FTRACE_FL_FAILED) |
944 | for (i = 0; i < pg->index; i++) { | 1039 | continue; |
945 | rec = &pg->records[i]; | 1040 | rec->flags &= ~type; |
946 | if (rec->flags & FTRACE_FL_FAILED) | 1041 | } while_for_each_ftrace_rec(); |
947 | continue; | 1042 | mutex_unlock(&ftrace_lock); |
948 | rec->flags &= ~type; | ||
949 | } | ||
950 | pg = pg->next; | ||
951 | } | ||
952 | spin_unlock(&ftrace_lock); | ||
953 | } | 1043 | } |
954 | 1044 | ||
955 | static int | 1045 | static int |
@@ -1030,86 +1120,533 @@ enum { | |||
1030 | MATCH_END_ONLY, | 1120 | MATCH_END_ONLY, |
1031 | }; | 1121 | }; |
1032 | 1122 | ||
1033 | static void | 1123 | /* |
1034 | ftrace_match(unsigned char *buff, int len, int enable) | 1124 | * (static function - no need for kernel doc) |
1125 | * | ||
1126 | * Pass in a buffer containing a glob and this function will | ||
1127 | * set search to point to the search part of the buffer and | ||
1128 | * return the type of search it is (see enum above). | ||
1129 | * This does modify buff. | ||
1130 | * | ||
1131 | * Returns enum type. | ||
1132 | * search returns the pointer to use for comparison. | ||
1133 | * not returns 1 if buff started with a '!' | ||
1134 | * 0 otherwise. | ||
1135 | */ | ||
1136 | static int | ||
1137 | ftrace_setup_glob(char *buff, int len, char **search, int *not) | ||
1035 | { | 1138 | { |
1036 | char str[KSYM_SYMBOL_LEN]; | ||
1037 | char *search = NULL; | ||
1038 | struct ftrace_page *pg; | ||
1039 | struct dyn_ftrace *rec; | ||
1040 | int type = MATCH_FULL; | 1139 | int type = MATCH_FULL; |
1041 | unsigned long flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; | 1140 | int i; |
1042 | unsigned i, match = 0, search_len = 0; | ||
1043 | int not = 0; | ||
1044 | 1141 | ||
1045 | if (buff[0] == '!') { | 1142 | if (buff[0] == '!') { |
1046 | not = 1; | 1143 | *not = 1; |
1047 | buff++; | 1144 | buff++; |
1048 | len--; | 1145 | len--; |
1049 | } | 1146 | } else |
1147 | *not = 0; | ||
1148 | |||
1149 | *search = buff; | ||
1050 | 1150 | ||
1051 | for (i = 0; i < len; i++) { | 1151 | for (i = 0; i < len; i++) { |
1052 | if (buff[i] == '*') { | 1152 | if (buff[i] == '*') { |
1053 | if (!i) { | 1153 | if (!i) { |
1054 | search = buff + i + 1; | 1154 | *search = buff + 1; |
1055 | type = MATCH_END_ONLY; | 1155 | type = MATCH_END_ONLY; |
1056 | search_len = len - (i + 1); | ||
1057 | } else { | 1156 | } else { |
1058 | if (type == MATCH_END_ONLY) { | 1157 | if (type == MATCH_END_ONLY) |
1059 | type = MATCH_MIDDLE_ONLY; | 1158 | type = MATCH_MIDDLE_ONLY; |
1060 | } else { | 1159 | else |
1061 | match = i; | ||
1062 | type = MATCH_FRONT_ONLY; | 1160 | type = MATCH_FRONT_ONLY; |
1063 | } | ||
1064 | buff[i] = 0; | 1161 | buff[i] = 0; |
1065 | break; | 1162 | break; |
1066 | } | 1163 | } |
1067 | } | 1164 | } |
1068 | } | 1165 | } |
1069 | 1166 | ||
1070 | /* should not be called from interrupt context */ | 1167 | return type; |
1071 | spin_lock(&ftrace_lock); | 1168 | } |
1072 | if (enable) | 1169 | |
1073 | ftrace_filtered = 1; | 1170 | static int ftrace_match(char *str, char *regex, int len, int type) |
1074 | pg = ftrace_pages_start; | 1171 | { |
1075 | while (pg) { | 1172 | int matched = 0; |
1076 | for (i = 0; i < pg->index; i++) { | 1173 | char *ptr; |
1077 | int matched = 0; | 1174 | |
1078 | char *ptr; | 1175 | switch (type) { |
1079 | 1176 | case MATCH_FULL: | |
1080 | rec = &pg->records[i]; | 1177 | if (strcmp(str, regex) == 0) |
1081 | if (rec->flags & FTRACE_FL_FAILED) | 1178 | matched = 1; |
1179 | break; | ||
1180 | case MATCH_FRONT_ONLY: | ||
1181 | if (strncmp(str, regex, len) == 0) | ||
1182 | matched = 1; | ||
1183 | break; | ||
1184 | case MATCH_MIDDLE_ONLY: | ||
1185 | if (strstr(str, regex)) | ||
1186 | matched = 1; | ||
1187 | break; | ||
1188 | case MATCH_END_ONLY: | ||
1189 | ptr = strstr(str, regex); | ||
1190 | if (ptr && (ptr[len] == 0)) | ||
1191 | matched = 1; | ||
1192 | break; | ||
1193 | } | ||
1194 | |||
1195 | return matched; | ||
1196 | } | ||
1197 | |||
1198 | static int | ||
1199 | ftrace_match_record(struct dyn_ftrace *rec, char *regex, int len, int type) | ||
1200 | { | ||
1201 | char str[KSYM_SYMBOL_LEN]; | ||
1202 | |||
1203 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); | ||
1204 | return ftrace_match(str, regex, len, type); | ||
1205 | } | ||
1206 | |||
1207 | static void ftrace_match_records(char *buff, int len, int enable) | ||
1208 | { | ||
1209 | char *search; | ||
1210 | struct ftrace_page *pg; | ||
1211 | struct dyn_ftrace *rec; | ||
1212 | int type; | ||
1213 | unsigned long flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; | ||
1214 | unsigned search_len; | ||
1215 | int not; | ||
1216 | |||
1217 | type = ftrace_setup_glob(buff, len, &search, ¬); | ||
1218 | |||
1219 | search_len = strlen(search); | ||
1220 | |||
1221 | mutex_lock(&ftrace_lock); | ||
1222 | do_for_each_ftrace_rec(pg, rec) { | ||
1223 | |||
1224 | if (rec->flags & FTRACE_FL_FAILED) | ||
1225 | continue; | ||
1226 | |||
1227 | if (ftrace_match_record(rec, search, search_len, type)) { | ||
1228 | if (not) | ||
1229 | rec->flags &= ~flag; | ||
1230 | else | ||
1231 | rec->flags |= flag; | ||
1232 | } | ||
1233 | /* | ||
1234 | * Only enable filtering if we have a function that | ||
1235 | * is filtered on. | ||
1236 | */ | ||
1237 | if (enable && (rec->flags & FTRACE_FL_FILTER)) | ||
1238 | ftrace_filtered = 1; | ||
1239 | } while_for_each_ftrace_rec(); | ||
1240 | mutex_unlock(&ftrace_lock); | ||
1241 | } | ||
1242 | |||
1243 | static int | ||
1244 | ftrace_match_module_record(struct dyn_ftrace *rec, char *mod, | ||
1245 | char *regex, int len, int type) | ||
1246 | { | ||
1247 | char str[KSYM_SYMBOL_LEN]; | ||
1248 | char *modname; | ||
1249 | |||
1250 | kallsyms_lookup(rec->ip, NULL, NULL, &modname, str); | ||
1251 | |||
1252 | if (!modname || strcmp(modname, mod)) | ||
1253 | return 0; | ||
1254 | |||
1255 | /* blank search means to match all funcs in the mod */ | ||
1256 | if (len) | ||
1257 | return ftrace_match(str, regex, len, type); | ||
1258 | else | ||
1259 | return 1; | ||
1260 | } | ||
1261 | |||
1262 | static void ftrace_match_module_records(char *buff, char *mod, int enable) | ||
1263 | { | ||
1264 | char *search = buff; | ||
1265 | struct ftrace_page *pg; | ||
1266 | struct dyn_ftrace *rec; | ||
1267 | int type = MATCH_FULL; | ||
1268 | unsigned long flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; | ||
1269 | unsigned search_len = 0; | ||
1270 | int not = 0; | ||
1271 | |||
1272 | /* blank or '*' mean the same */ | ||
1273 | if (strcmp(buff, "*") == 0) | ||
1274 | buff[0] = 0; | ||
1275 | |||
1276 | /* handle the case of 'dont filter this module' */ | ||
1277 | if (strcmp(buff, "!") == 0 || strcmp(buff, "!*") == 0) { | ||
1278 | buff[0] = 0; | ||
1279 | not = 1; | ||
1280 | } | ||
1281 | |||
1282 | if (strlen(buff)) { | ||
1283 | type = ftrace_setup_glob(buff, strlen(buff), &search, ¬); | ||
1284 | search_len = strlen(search); | ||
1285 | } | ||
1286 | |||
1287 | mutex_lock(&ftrace_lock); | ||
1288 | do_for_each_ftrace_rec(pg, rec) { | ||
1289 | |||
1290 | if (rec->flags & FTRACE_FL_FAILED) | ||
1291 | continue; | ||
1292 | |||
1293 | if (ftrace_match_module_record(rec, mod, | ||
1294 | search, search_len, type)) { | ||
1295 | if (not) | ||
1296 | rec->flags &= ~flag; | ||
1297 | else | ||
1298 | rec->flags |= flag; | ||
1299 | } | ||
1300 | if (enable && (rec->flags & FTRACE_FL_FILTER)) | ||
1301 | ftrace_filtered = 1; | ||
1302 | |||
1303 | } while_for_each_ftrace_rec(); | ||
1304 | mutex_unlock(&ftrace_lock); | ||
1305 | } | ||
1306 | |||
1307 | /* | ||
1308 | * We register the module command as a template to show others how | ||
1309 | * to register the a command as well. | ||
1310 | */ | ||
1311 | |||
1312 | static int | ||
1313 | ftrace_mod_callback(char *func, char *cmd, char *param, int enable) | ||
1314 | { | ||
1315 | char *mod; | ||
1316 | |||
1317 | /* | ||
1318 | * cmd == 'mod' because we only registered this func | ||
1319 | * for the 'mod' ftrace_func_command. | ||
1320 | * But if you register one func with multiple commands, | ||
1321 | * you can tell which command was used by the cmd | ||
1322 | * parameter. | ||
1323 | */ | ||
1324 | |||
1325 | /* we must have a module name */ | ||
1326 | if (!param) | ||
1327 | return -EINVAL; | ||
1328 | |||
1329 | mod = strsep(¶m, ":"); | ||
1330 | if (!strlen(mod)) | ||
1331 | return -EINVAL; | ||
1332 | |||
1333 | ftrace_match_module_records(func, mod, enable); | ||
1334 | return 0; | ||
1335 | } | ||
1336 | |||
1337 | static struct ftrace_func_command ftrace_mod_cmd = { | ||
1338 | .name = "mod", | ||
1339 | .func = ftrace_mod_callback, | ||
1340 | }; | ||
1341 | |||
1342 | static int __init ftrace_mod_cmd_init(void) | ||
1343 | { | ||
1344 | return register_ftrace_command(&ftrace_mod_cmd); | ||
1345 | } | ||
1346 | device_initcall(ftrace_mod_cmd_init); | ||
1347 | |||
1348 | static void | ||
1349 | function_trace_hook_call(unsigned long ip, unsigned long parent_ip) | ||
1350 | { | ||
1351 | struct ftrace_func_hook *entry; | ||
1352 | struct hlist_head *hhd; | ||
1353 | struct hlist_node *n; | ||
1354 | unsigned long key; | ||
1355 | int resched; | ||
1356 | |||
1357 | key = hash_long(ip, FTRACE_HASH_BITS); | ||
1358 | |||
1359 | hhd = &ftrace_func_hash[key]; | ||
1360 | |||
1361 | if (hlist_empty(hhd)) | ||
1362 | return; | ||
1363 | |||
1364 | /* | ||
1365 | * Disable preemption for these calls to prevent a RCU grace | ||
1366 | * period. This syncs the hash iteration and freeing of items | ||
1367 | * on the hash. rcu_read_lock is too dangerous here. | ||
1368 | */ | ||
1369 | resched = ftrace_preempt_disable(); | ||
1370 | hlist_for_each_entry_rcu(entry, n, hhd, node) { | ||
1371 | if (entry->ip == ip) | ||
1372 | entry->ops->func(ip, parent_ip, &entry->data); | ||
1373 | } | ||
1374 | ftrace_preempt_enable(resched); | ||
1375 | } | ||
1376 | |||
1377 | static struct ftrace_ops trace_hook_ops __read_mostly = | ||
1378 | { | ||
1379 | .func = function_trace_hook_call, | ||
1380 | }; | ||
1381 | |||
1382 | static int ftrace_hook_registered; | ||
1383 | |||
1384 | static void __enable_ftrace_function_hook(void) | ||
1385 | { | ||
1386 | int i; | ||
1387 | |||
1388 | if (ftrace_hook_registered) | ||
1389 | return; | ||
1390 | |||
1391 | for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) { | ||
1392 | struct hlist_head *hhd = &ftrace_func_hash[i]; | ||
1393 | if (hhd->first) | ||
1394 | break; | ||
1395 | } | ||
1396 | /* Nothing registered? */ | ||
1397 | if (i == FTRACE_FUNC_HASHSIZE) | ||
1398 | return; | ||
1399 | |||
1400 | __register_ftrace_function(&trace_hook_ops); | ||
1401 | ftrace_startup(0); | ||
1402 | ftrace_hook_registered = 1; | ||
1403 | } | ||
1404 | |||
1405 | static void __disable_ftrace_function_hook(void) | ||
1406 | { | ||
1407 | int i; | ||
1408 | |||
1409 | if (!ftrace_hook_registered) | ||
1410 | return; | ||
1411 | |||
1412 | for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) { | ||
1413 | struct hlist_head *hhd = &ftrace_func_hash[i]; | ||
1414 | if (hhd->first) | ||
1415 | return; | ||
1416 | } | ||
1417 | |||
1418 | /* no more funcs left */ | ||
1419 | __unregister_ftrace_function(&trace_hook_ops); | ||
1420 | ftrace_shutdown(0); | ||
1421 | ftrace_hook_registered = 0; | ||
1422 | } | ||
1423 | |||
1424 | |||
1425 | static void ftrace_free_entry_rcu(struct rcu_head *rhp) | ||
1426 | { | ||
1427 | struct ftrace_func_hook *entry = | ||
1428 | container_of(rhp, struct ftrace_func_hook, rcu); | ||
1429 | |||
1430 | if (entry->ops->free) | ||
1431 | entry->ops->free(&entry->data); | ||
1432 | kfree(entry); | ||
1433 | } | ||
1434 | |||
1435 | |||
1436 | int | ||
1437 | register_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops, | ||
1438 | void *data) | ||
1439 | { | ||
1440 | struct ftrace_func_hook *entry; | ||
1441 | struct ftrace_page *pg; | ||
1442 | struct dyn_ftrace *rec; | ||
1443 | unsigned long key; | ||
1444 | int type, len, not; | ||
1445 | int count = 0; | ||
1446 | char *search; | ||
1447 | |||
1448 | type = ftrace_setup_glob(glob, strlen(glob), &search, ¬); | ||
1449 | len = strlen(search); | ||
1450 | |||
1451 | /* we do not support '!' for function hooks */ | ||
1452 | if (WARN_ON(not)) | ||
1453 | return -EINVAL; | ||
1454 | |||
1455 | mutex_lock(&ftrace_lock); | ||
1456 | do_for_each_ftrace_rec(pg, rec) { | ||
1457 | |||
1458 | if (rec->flags & FTRACE_FL_FAILED) | ||
1459 | continue; | ||
1460 | |||
1461 | if (!ftrace_match_record(rec, search, len, type)) | ||
1462 | continue; | ||
1463 | |||
1464 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); | ||
1465 | if (!entry) { | ||
1466 | /* If we did not hook to any, then return error */ | ||
1467 | if (!count) | ||
1468 | count = -ENOMEM; | ||
1469 | goto out_unlock; | ||
1470 | } | ||
1471 | |||
1472 | count++; | ||
1473 | |||
1474 | entry->data = data; | ||
1475 | |||
1476 | /* | ||
1477 | * The caller might want to do something special | ||
1478 | * for each function we find. We call the callback | ||
1479 | * to give the caller an opportunity to do so. | ||
1480 | */ | ||
1481 | if (ops->callback) { | ||
1482 | if (ops->callback(rec->ip, &entry->data) < 0) { | ||
1483 | /* caller does not like this func */ | ||
1484 | kfree(entry); | ||
1082 | continue; | 1485 | continue; |
1083 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); | ||
1084 | switch (type) { | ||
1085 | case MATCH_FULL: | ||
1086 | if (strcmp(str, buff) == 0) | ||
1087 | matched = 1; | ||
1088 | break; | ||
1089 | case MATCH_FRONT_ONLY: | ||
1090 | if (memcmp(str, buff, match) == 0) | ||
1091 | matched = 1; | ||
1092 | break; | ||
1093 | case MATCH_MIDDLE_ONLY: | ||
1094 | if (strstr(str, search)) | ||
1095 | matched = 1; | ||
1096 | break; | ||
1097 | case MATCH_END_ONLY: | ||
1098 | ptr = strstr(str, search); | ||
1099 | if (ptr && (ptr[search_len] == 0)) | ||
1100 | matched = 1; | ||
1101 | break; | ||
1102 | } | 1486 | } |
1103 | if (matched) { | 1487 | } |
1104 | if (not) | 1488 | |
1105 | rec->flags &= ~flag; | 1489 | entry->ops = ops; |
1106 | else | 1490 | entry->ip = rec->ip; |
1107 | rec->flags |= flag; | 1491 | |
1492 | key = hash_long(entry->ip, FTRACE_HASH_BITS); | ||
1493 | hlist_add_head_rcu(&entry->node, &ftrace_func_hash[key]); | ||
1494 | |||
1495 | } while_for_each_ftrace_rec(); | ||
1496 | __enable_ftrace_function_hook(); | ||
1497 | |||
1498 | out_unlock: | ||
1499 | mutex_unlock(&ftrace_lock); | ||
1500 | |||
1501 | return count; | ||
1502 | } | ||
1503 | |||
1504 | enum { | ||
1505 | HOOK_TEST_FUNC = 1, | ||
1506 | HOOK_TEST_DATA = 2 | ||
1507 | }; | ||
1508 | |||
1509 | static void | ||
1510 | __unregister_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops, | ||
1511 | void *data, int flags) | ||
1512 | { | ||
1513 | struct ftrace_func_hook *entry; | ||
1514 | struct hlist_node *n, *tmp; | ||
1515 | char str[KSYM_SYMBOL_LEN]; | ||
1516 | int type = MATCH_FULL; | ||
1517 | int i, len = 0; | ||
1518 | char *search; | ||
1519 | |||
1520 | if (glob && (strcmp(glob, "*") || !strlen(glob))) | ||
1521 | glob = NULL; | ||
1522 | else { | ||
1523 | int not; | ||
1524 | |||
1525 | type = ftrace_setup_glob(glob, strlen(glob), &search, ¬); | ||
1526 | len = strlen(search); | ||
1527 | |||
1528 | /* we do not support '!' for function hooks */ | ||
1529 | if (WARN_ON(not)) | ||
1530 | return; | ||
1531 | } | ||
1532 | |||
1533 | mutex_lock(&ftrace_lock); | ||
1534 | for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) { | ||
1535 | struct hlist_head *hhd = &ftrace_func_hash[i]; | ||
1536 | |||
1537 | hlist_for_each_entry_safe(entry, n, tmp, hhd, node) { | ||
1538 | |||
1539 | /* break up if statements for readability */ | ||
1540 | if ((flags & HOOK_TEST_FUNC) && entry->ops != ops) | ||
1541 | continue; | ||
1542 | |||
1543 | if ((flags & HOOK_TEST_DATA) && entry->data != data) | ||
1544 | continue; | ||
1545 | |||
1546 | /* do this last, since it is the most expensive */ | ||
1547 | if (glob) { | ||
1548 | kallsyms_lookup(entry->ip, NULL, NULL, | ||
1549 | NULL, str); | ||
1550 | if (!ftrace_match(str, glob, len, type)) | ||
1551 | continue; | ||
1108 | } | 1552 | } |
1553 | |||
1554 | hlist_del(&entry->node); | ||
1555 | call_rcu(&entry->rcu, ftrace_free_entry_rcu); | ||
1109 | } | 1556 | } |
1110 | pg = pg->next; | ||
1111 | } | 1557 | } |
1112 | spin_unlock(&ftrace_lock); | 1558 | __disable_ftrace_function_hook(); |
1559 | mutex_unlock(&ftrace_lock); | ||
1560 | } | ||
1561 | |||
1562 | void | ||
1563 | unregister_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops, | ||
1564 | void *data) | ||
1565 | { | ||
1566 | __unregister_ftrace_function_hook(glob, ops, data, | ||
1567 | HOOK_TEST_FUNC | HOOK_TEST_DATA); | ||
1568 | } | ||
1569 | |||
1570 | void | ||
1571 | unregister_ftrace_function_hook_func(char *glob, struct ftrace_hook_ops *ops) | ||
1572 | { | ||
1573 | __unregister_ftrace_function_hook(glob, ops, NULL, HOOK_TEST_FUNC); | ||
1574 | } | ||
1575 | |||
1576 | void unregister_ftrace_function_hook_all(char *glob) | ||
1577 | { | ||
1578 | __unregister_ftrace_function_hook(glob, NULL, NULL, 0); | ||
1579 | } | ||
1580 | |||
1581 | static LIST_HEAD(ftrace_commands); | ||
1582 | static DEFINE_MUTEX(ftrace_cmd_mutex); | ||
1583 | |||
1584 | int register_ftrace_command(struct ftrace_func_command *cmd) | ||
1585 | { | ||
1586 | struct ftrace_func_command *p; | ||
1587 | int ret = 0; | ||
1588 | |||
1589 | mutex_lock(&ftrace_cmd_mutex); | ||
1590 | list_for_each_entry(p, &ftrace_commands, list) { | ||
1591 | if (strcmp(cmd->name, p->name) == 0) { | ||
1592 | ret = -EBUSY; | ||
1593 | goto out_unlock; | ||
1594 | } | ||
1595 | } | ||
1596 | list_add(&cmd->list, &ftrace_commands); | ||
1597 | out_unlock: | ||
1598 | mutex_unlock(&ftrace_cmd_mutex); | ||
1599 | |||
1600 | return ret; | ||
1601 | } | ||
1602 | |||
1603 | int unregister_ftrace_command(struct ftrace_func_command *cmd) | ||
1604 | { | ||
1605 | struct ftrace_func_command *p, *n; | ||
1606 | int ret = -ENODEV; | ||
1607 | |||
1608 | mutex_lock(&ftrace_cmd_mutex); | ||
1609 | list_for_each_entry_safe(p, n, &ftrace_commands, list) { | ||
1610 | if (strcmp(cmd->name, p->name) == 0) { | ||
1611 | ret = 0; | ||
1612 | list_del_init(&p->list); | ||
1613 | goto out_unlock; | ||
1614 | } | ||
1615 | } | ||
1616 | out_unlock: | ||
1617 | mutex_unlock(&ftrace_cmd_mutex); | ||
1618 | |||
1619 | return ret; | ||
1620 | } | ||
1621 | |||
1622 | static int ftrace_process_regex(char *buff, int len, int enable) | ||
1623 | { | ||
1624 | struct ftrace_func_command *p; | ||
1625 | char *func, *command, *next = buff; | ||
1626 | int ret = -EINVAL; | ||
1627 | |||
1628 | func = strsep(&next, ":"); | ||
1629 | |||
1630 | if (!next) { | ||
1631 | ftrace_match_records(func, len, enable); | ||
1632 | return 0; | ||
1633 | } | ||
1634 | |||
1635 | /* command found */ | ||
1636 | |||
1637 | command = strsep(&next, ":"); | ||
1638 | |||
1639 | mutex_lock(&ftrace_cmd_mutex); | ||
1640 | list_for_each_entry(p, &ftrace_commands, list) { | ||
1641 | if (strcmp(p->name, command) == 0) { | ||
1642 | ret = p->func(func, command, next, enable); | ||
1643 | goto out_unlock; | ||
1644 | } | ||
1645 | } | ||
1646 | out_unlock: | ||
1647 | mutex_unlock(&ftrace_cmd_mutex); | ||
1648 | |||
1649 | return ret; | ||
1113 | } | 1650 | } |
1114 | 1651 | ||
1115 | static ssize_t | 1652 | static ssize_t |
@@ -1179,7 +1716,10 @@ ftrace_regex_write(struct file *file, const char __user *ubuf, | |||
1179 | if (isspace(ch)) { | 1716 | if (isspace(ch)) { |
1180 | iter->filtered++; | 1717 | iter->filtered++; |
1181 | iter->buffer[iter->buffer_idx] = 0; | 1718 | iter->buffer[iter->buffer_idx] = 0; |
1182 | ftrace_match(iter->buffer, iter->buffer_idx, enable); | 1719 | ret = ftrace_process_regex(iter->buffer, |
1720 | iter->buffer_idx, enable); | ||
1721 | if (ret) | ||
1722 | goto out; | ||
1183 | iter->buffer_idx = 0; | 1723 | iter->buffer_idx = 0; |
1184 | } else | 1724 | } else |
1185 | iter->flags |= FTRACE_ITER_CONT; | 1725 | iter->flags |= FTRACE_ITER_CONT; |
@@ -1218,7 +1758,7 @@ ftrace_set_regex(unsigned char *buf, int len, int reset, int enable) | |||
1218 | if (reset) | 1758 | if (reset) |
1219 | ftrace_filter_reset(enable); | 1759 | ftrace_filter_reset(enable); |
1220 | if (buf) | 1760 | if (buf) |
1221 | ftrace_match(buf, len, enable); | 1761 | ftrace_match_records(buf, len, enable); |
1222 | mutex_unlock(&ftrace_regex_lock); | 1762 | mutex_unlock(&ftrace_regex_lock); |
1223 | } | 1763 | } |
1224 | 1764 | ||
@@ -1268,15 +1808,13 @@ ftrace_regex_release(struct inode *inode, struct file *file, int enable) | |||
1268 | if (iter->buffer_idx) { | 1808 | if (iter->buffer_idx) { |
1269 | iter->filtered++; | 1809 | iter->filtered++; |
1270 | iter->buffer[iter->buffer_idx] = 0; | 1810 | iter->buffer[iter->buffer_idx] = 0; |
1271 | ftrace_match(iter->buffer, iter->buffer_idx, enable); | 1811 | ftrace_match_records(iter->buffer, iter->buffer_idx, enable); |
1272 | } | 1812 | } |
1273 | 1813 | ||
1274 | mutex_lock(&ftrace_sysctl_lock); | 1814 | mutex_lock(&ftrace_lock); |
1275 | mutex_lock(&ftrace_start_lock); | ||
1276 | if (ftrace_start_up && ftrace_enabled) | 1815 | if (ftrace_start_up && ftrace_enabled) |
1277 | ftrace_run_update_code(FTRACE_ENABLE_CALLS); | 1816 | ftrace_run_update_code(FTRACE_ENABLE_CALLS); |
1278 | mutex_unlock(&ftrace_start_lock); | 1817 | mutex_unlock(&ftrace_lock); |
1279 | mutex_unlock(&ftrace_sysctl_lock); | ||
1280 | 1818 | ||
1281 | kfree(iter); | 1819 | kfree(iter); |
1282 | mutex_unlock(&ftrace_regex_lock); | 1820 | mutex_unlock(&ftrace_regex_lock); |
@@ -1429,36 +1967,33 @@ ftrace_set_func(unsigned long *array, int idx, char *buffer) | |||
1429 | struct dyn_ftrace *rec; | 1967 | struct dyn_ftrace *rec; |
1430 | struct ftrace_page *pg; | 1968 | struct ftrace_page *pg; |
1431 | int found = 0; | 1969 | int found = 0; |
1432 | int i, j; | 1970 | int j; |
1433 | 1971 | ||
1434 | if (ftrace_disabled) | 1972 | if (ftrace_disabled) |
1435 | return -ENODEV; | 1973 | return -ENODEV; |
1436 | 1974 | ||
1437 | /* should not be called from interrupt context */ | 1975 | mutex_lock(&ftrace_lock); |
1438 | spin_lock(&ftrace_lock); | 1976 | do_for_each_ftrace_rec(pg, rec) { |
1439 | |||
1440 | for (pg = ftrace_pages_start; pg; pg = pg->next) { | ||
1441 | for (i = 0; i < pg->index; i++) { | ||
1442 | rec = &pg->records[i]; | ||
1443 | 1977 | ||
1444 | if (rec->flags & (FTRACE_FL_FAILED | FTRACE_FL_FREE)) | 1978 | if (rec->flags & (FTRACE_FL_FAILED | FTRACE_FL_FREE)) |
1445 | continue; | 1979 | continue; |
1446 | 1980 | ||
1447 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); | 1981 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); |
1448 | if (strcmp(str, buffer) == 0) { | 1982 | if (strcmp(str, buffer) == 0) { |
1449 | found = 1; | 1983 | /* Return 1 if we add it to the array */ |
1450 | for (j = 0; j < idx; j++) | 1984 | found = 1; |
1451 | if (array[j] == rec->ip) { | 1985 | for (j = 0; j < idx; j++) |
1452 | found = 0; | 1986 | if (array[j] == rec->ip) { |
1453 | break; | 1987 | found = 0; |
1454 | } | 1988 | break; |
1455 | if (found) | 1989 | } |
1456 | array[idx] = rec->ip; | 1990 | if (found) |
1457 | break; | 1991 | array[idx] = rec->ip; |
1458 | } | 1992 | goto out; |
1459 | } | 1993 | } |
1460 | } | 1994 | } while_for_each_ftrace_rec(); |
1461 | spin_unlock(&ftrace_lock); | 1995 | out: |
1996 | mutex_unlock(&ftrace_lock); | ||
1462 | 1997 | ||
1463 | return found ? 0 : -EINVAL; | 1998 | return found ? 0 : -EINVAL; |
1464 | } | 1999 | } |
@@ -1596,7 +2131,7 @@ static int ftrace_convert_nops(struct module *mod, | |||
1596 | unsigned long addr; | 2131 | unsigned long addr; |
1597 | unsigned long flags; | 2132 | unsigned long flags; |
1598 | 2133 | ||
1599 | mutex_lock(&ftrace_start_lock); | 2134 | mutex_lock(&ftrace_lock); |
1600 | p = start; | 2135 | p = start; |
1601 | while (p < end) { | 2136 | while (p < end) { |
1602 | addr = ftrace_call_adjust(*p++); | 2137 | addr = ftrace_call_adjust(*p++); |
@@ -1615,7 +2150,7 @@ static int ftrace_convert_nops(struct module *mod, | |||
1615 | local_irq_save(flags); | 2150 | local_irq_save(flags); |
1616 | ftrace_update_code(mod); | 2151 | ftrace_update_code(mod); |
1617 | local_irq_restore(flags); | 2152 | local_irq_restore(flags); |
1618 | mutex_unlock(&ftrace_start_lock); | 2153 | mutex_unlock(&ftrace_lock); |
1619 | 2154 | ||
1620 | return 0; | 2155 | return 0; |
1621 | } | 2156 | } |
@@ -1788,7 +2323,7 @@ ftrace_pid_write(struct file *filp, const char __user *ubuf, | |||
1788 | if (ret < 0) | 2323 | if (ret < 0) |
1789 | return ret; | 2324 | return ret; |
1790 | 2325 | ||
1791 | mutex_lock(&ftrace_start_lock); | 2326 | mutex_lock(&ftrace_lock); |
1792 | if (val < 0) { | 2327 | if (val < 0) { |
1793 | /* disable pid tracing */ | 2328 | /* disable pid tracing */ |
1794 | if (!ftrace_pid_trace) | 2329 | if (!ftrace_pid_trace) |
@@ -1827,7 +2362,7 @@ ftrace_pid_write(struct file *filp, const char __user *ubuf, | |||
1827 | ftrace_startup_enable(0); | 2362 | ftrace_startup_enable(0); |
1828 | 2363 | ||
1829 | out: | 2364 | out: |
1830 | mutex_unlock(&ftrace_start_lock); | 2365 | mutex_unlock(&ftrace_lock); |
1831 | 2366 | ||
1832 | return cnt; | 2367 | return cnt; |
1833 | } | 2368 | } |
@@ -1890,12 +2425,12 @@ int register_ftrace_function(struct ftrace_ops *ops) | |||
1890 | if (unlikely(ftrace_disabled)) | 2425 | if (unlikely(ftrace_disabled)) |
1891 | return -1; | 2426 | return -1; |
1892 | 2427 | ||
1893 | mutex_lock(&ftrace_sysctl_lock); | 2428 | mutex_lock(&ftrace_lock); |
1894 | 2429 | ||
1895 | ret = __register_ftrace_function(ops); | 2430 | ret = __register_ftrace_function(ops); |
1896 | ftrace_startup(0); | 2431 | ftrace_startup(0); |
1897 | 2432 | ||
1898 | mutex_unlock(&ftrace_sysctl_lock); | 2433 | mutex_unlock(&ftrace_lock); |
1899 | return ret; | 2434 | return ret; |
1900 | } | 2435 | } |
1901 | 2436 | ||
@@ -1909,10 +2444,10 @@ int unregister_ftrace_function(struct ftrace_ops *ops) | |||
1909 | { | 2444 | { |
1910 | int ret; | 2445 | int ret; |
1911 | 2446 | ||
1912 | mutex_lock(&ftrace_sysctl_lock); | 2447 | mutex_lock(&ftrace_lock); |
1913 | ret = __unregister_ftrace_function(ops); | 2448 | ret = __unregister_ftrace_function(ops); |
1914 | ftrace_shutdown(0); | 2449 | ftrace_shutdown(0); |
1915 | mutex_unlock(&ftrace_sysctl_lock); | 2450 | mutex_unlock(&ftrace_lock); |
1916 | 2451 | ||
1917 | return ret; | 2452 | return ret; |
1918 | } | 2453 | } |
@@ -1927,7 +2462,7 @@ ftrace_enable_sysctl(struct ctl_table *table, int write, | |||
1927 | if (unlikely(ftrace_disabled)) | 2462 | if (unlikely(ftrace_disabled)) |
1928 | return -ENODEV; | 2463 | return -ENODEV; |
1929 | 2464 | ||
1930 | mutex_lock(&ftrace_sysctl_lock); | 2465 | mutex_lock(&ftrace_lock); |
1931 | 2466 | ||
1932 | ret = proc_dointvec(table, write, file, buffer, lenp, ppos); | 2467 | ret = proc_dointvec(table, write, file, buffer, lenp, ppos); |
1933 | 2468 | ||
@@ -1956,7 +2491,7 @@ ftrace_enable_sysctl(struct ctl_table *table, int write, | |||
1956 | } | 2491 | } |
1957 | 2492 | ||
1958 | out: | 2493 | out: |
1959 | mutex_unlock(&ftrace_sysctl_lock); | 2494 | mutex_unlock(&ftrace_lock); |
1960 | return ret; | 2495 | return ret; |
1961 | } | 2496 | } |
1962 | 2497 | ||
@@ -2068,7 +2603,7 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc, | |||
2068 | { | 2603 | { |
2069 | int ret = 0; | 2604 | int ret = 0; |
2070 | 2605 | ||
2071 | mutex_lock(&ftrace_sysctl_lock); | 2606 | mutex_lock(&ftrace_lock); |
2072 | 2607 | ||
2073 | ftrace_suspend_notifier.notifier_call = ftrace_suspend_notifier_call; | 2608 | ftrace_suspend_notifier.notifier_call = ftrace_suspend_notifier_call; |
2074 | register_pm_notifier(&ftrace_suspend_notifier); | 2609 | register_pm_notifier(&ftrace_suspend_notifier); |
@@ -2086,13 +2621,13 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc, | |||
2086 | ftrace_startup(FTRACE_START_FUNC_RET); | 2621 | ftrace_startup(FTRACE_START_FUNC_RET); |
2087 | 2622 | ||
2088 | out: | 2623 | out: |
2089 | mutex_unlock(&ftrace_sysctl_lock); | 2624 | mutex_unlock(&ftrace_lock); |
2090 | return ret; | 2625 | return ret; |
2091 | } | 2626 | } |
2092 | 2627 | ||
2093 | void unregister_ftrace_graph(void) | 2628 | void unregister_ftrace_graph(void) |
2094 | { | 2629 | { |
2095 | mutex_lock(&ftrace_sysctl_lock); | 2630 | mutex_lock(&ftrace_lock); |
2096 | 2631 | ||
2097 | atomic_dec(&ftrace_graph_active); | 2632 | atomic_dec(&ftrace_graph_active); |
2098 | ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub; | 2633 | ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub; |
@@ -2100,7 +2635,7 @@ void unregister_ftrace_graph(void) | |||
2100 | ftrace_shutdown(FTRACE_STOP_FUNC_RET); | 2635 | ftrace_shutdown(FTRACE_STOP_FUNC_RET); |
2101 | unregister_pm_notifier(&ftrace_suspend_notifier); | 2636 | unregister_pm_notifier(&ftrace_suspend_notifier); |
2102 | 2637 | ||
2103 | mutex_unlock(&ftrace_sysctl_lock); | 2638 | mutex_unlock(&ftrace_lock); |
2104 | } | 2639 | } |
2105 | 2640 | ||
2106 | /* Allocate a return stack for newly created task */ | 2641 | /* Allocate a return stack for newly created task */ |
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 2b4626ce95d6..8f19f1aa42b0 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c | |||
@@ -98,6 +98,15 @@ void tracing_off_permanent(void) | |||
98 | set_bit(RB_BUFFERS_DISABLED_BIT, &ring_buffer_flags); | 98 | set_bit(RB_BUFFERS_DISABLED_BIT, &ring_buffer_flags); |
99 | } | 99 | } |
100 | 100 | ||
101 | /** | ||
102 | * tracing_is_on - show state of ring buffers enabled | ||
103 | */ | ||
104 | int tracing_is_on(void) | ||
105 | { | ||
106 | return ring_buffer_flags == RB_BUFFERS_ON; | ||
107 | } | ||
108 | EXPORT_SYMBOL_GPL(tracing_is_on); | ||
109 | |||
101 | #include "trace.h" | 110 | #include "trace.h" |
102 | 111 | ||
103 | /* Up this if you want to test the TIME_EXTENTS and normalization */ | 112 | /* Up this if you want to test the TIME_EXTENTS and normalization */ |
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c index 36bf9568ccd9..f520aa419dff 100644 --- a/kernel/trace/trace_functions.c +++ b/kernel/trace/trace_functions.c | |||
@@ -9,6 +9,7 @@ | |||
9 | * Copyright (C) 2004-2006 Ingo Molnar | 9 | * Copyright (C) 2004-2006 Ingo Molnar |
10 | * Copyright (C) 2004 William Lee Irwin III | 10 | * Copyright (C) 2004 William Lee Irwin III |
11 | */ | 11 | */ |
12 | #include <linux/ring_buffer.h> | ||
12 | #include <linux/debugfs.h> | 13 | #include <linux/debugfs.h> |
13 | #include <linux/uaccess.h> | 14 | #include <linux/uaccess.h> |
14 | #include <linux/ftrace.h> | 15 | #include <linux/ftrace.h> |
@@ -231,9 +232,171 @@ static struct tracer function_trace __read_mostly = | |||
231 | #endif | 232 | #endif |
232 | }; | 233 | }; |
233 | 234 | ||
235 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
236 | static void | ||
237 | ftrace_traceon(unsigned long ip, unsigned long parent_ip, void **data) | ||
238 | { | ||
239 | long *count = (long *)data; | ||
240 | |||
241 | if (tracing_is_on()) | ||
242 | return; | ||
243 | |||
244 | if (!*count) | ||
245 | return; | ||
246 | |||
247 | if (*count != -1) | ||
248 | (*count)--; | ||
249 | |||
250 | tracing_on(); | ||
251 | } | ||
252 | |||
253 | static void | ||
254 | ftrace_traceoff(unsigned long ip, unsigned long parent_ip, void **data) | ||
255 | { | ||
256 | long *count = (long *)data; | ||
257 | |||
258 | if (!tracing_is_on()) | ||
259 | return; | ||
260 | |||
261 | if (!*count) | ||
262 | return; | ||
263 | |||
264 | if (*count != -1) | ||
265 | (*count)--; | ||
266 | |||
267 | tracing_off(); | ||
268 | } | ||
269 | |||
270 | static int | ||
271 | ftrace_trace_onoff_print(struct seq_file *m, unsigned long ip, | ||
272 | struct ftrace_hook_ops *ops, void *data); | ||
273 | |||
274 | static struct ftrace_hook_ops traceon_hook_ops = { | ||
275 | .func = ftrace_traceon, | ||
276 | .print = ftrace_trace_onoff_print, | ||
277 | }; | ||
278 | |||
279 | static struct ftrace_hook_ops traceoff_hook_ops = { | ||
280 | .func = ftrace_traceoff, | ||
281 | .print = ftrace_trace_onoff_print, | ||
282 | }; | ||
283 | |||
284 | static int | ||
285 | ftrace_trace_onoff_print(struct seq_file *m, unsigned long ip, | ||
286 | struct ftrace_hook_ops *ops, void *data) | ||
287 | { | ||
288 | char str[KSYM_SYMBOL_LEN]; | ||
289 | long count = (long)data; | ||
290 | |||
291 | kallsyms_lookup(ip, NULL, NULL, NULL, str); | ||
292 | seq_printf(m, "%s:", str); | ||
293 | |||
294 | if (ops == &traceon_hook_ops) | ||
295 | seq_printf(m, "traceon"); | ||
296 | else | ||
297 | seq_printf(m, "traceoff"); | ||
298 | |||
299 | if (count != -1) | ||
300 | seq_printf(m, ":count=%ld", count); | ||
301 | seq_putc(m, '\n'); | ||
302 | |||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | static int | ||
307 | ftrace_trace_onoff_unreg(char *glob, char *cmd, char *param) | ||
308 | { | ||
309 | struct ftrace_hook_ops *ops; | ||
310 | |||
311 | /* we register both traceon and traceoff to this callback */ | ||
312 | if (strcmp(cmd, "traceon") == 0) | ||
313 | ops = &traceon_hook_ops; | ||
314 | else | ||
315 | ops = &traceoff_hook_ops; | ||
316 | |||
317 | unregister_ftrace_function_hook_func(glob, ops); | ||
318 | |||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | static int | ||
323 | ftrace_trace_onoff_callback(char *glob, char *cmd, char *param, int enable) | ||
324 | { | ||
325 | struct ftrace_hook_ops *ops; | ||
326 | void *count = (void *)-1; | ||
327 | char *number; | ||
328 | int ret; | ||
329 | |||
330 | /* hash funcs only work with set_ftrace_filter */ | ||
331 | if (!enable) | ||
332 | return -EINVAL; | ||
333 | |||
334 | if (glob[0] == '!') | ||
335 | return ftrace_trace_onoff_unreg(glob+1, cmd, param); | ||
336 | |||
337 | /* we register both traceon and traceoff to this callback */ | ||
338 | if (strcmp(cmd, "traceon") == 0) | ||
339 | ops = &traceon_hook_ops; | ||
340 | else | ||
341 | ops = &traceoff_hook_ops; | ||
342 | |||
343 | if (!param) | ||
344 | goto out_reg; | ||
345 | |||
346 | number = strsep(¶m, ":"); | ||
347 | |||
348 | if (!strlen(number)) | ||
349 | goto out_reg; | ||
350 | |||
351 | /* | ||
352 | * We use the callback data field (which is a pointer) | ||
353 | * as our counter. | ||
354 | */ | ||
355 | ret = strict_strtoul(number, 0, (unsigned long *)&count); | ||
356 | if (ret) | ||
357 | return ret; | ||
358 | |||
359 | out_reg: | ||
360 | ret = register_ftrace_function_hook(glob, ops, count); | ||
361 | |||
362 | return ret; | ||
363 | } | ||
364 | |||
365 | static struct ftrace_func_command ftrace_traceon_cmd = { | ||
366 | .name = "traceon", | ||
367 | .func = ftrace_trace_onoff_callback, | ||
368 | }; | ||
369 | |||
370 | static struct ftrace_func_command ftrace_traceoff_cmd = { | ||
371 | .name = "traceoff", | ||
372 | .func = ftrace_trace_onoff_callback, | ||
373 | }; | ||
374 | |||
375 | static int __init init_func_cmd_traceon(void) | ||
376 | { | ||
377 | int ret; | ||
378 | |||
379 | ret = register_ftrace_command(&ftrace_traceoff_cmd); | ||
380 | if (ret) | ||
381 | return ret; | ||
382 | |||
383 | ret = register_ftrace_command(&ftrace_traceon_cmd); | ||
384 | if (ret) | ||
385 | unregister_ftrace_command(&ftrace_traceoff_cmd); | ||
386 | return ret; | ||
387 | } | ||
388 | #else | ||
389 | static inline int init_func_cmd_traceon(void) | ||
390 | { | ||
391 | return 0; | ||
392 | } | ||
393 | #endif /* CONFIG_DYNAMIC_FTRACE */ | ||
394 | |||
234 | static __init int init_function_trace(void) | 395 | static __init int init_function_trace(void) |
235 | { | 396 | { |
397 | init_func_cmd_traceon(); | ||
236 | return register_tracer(&function_trace); | 398 | return register_tracer(&function_trace); |
237 | } | 399 | } |
238 | 400 | ||
239 | device_initcall(init_function_trace); | 401 | device_initcall(init_function_trace); |
402 | |||