aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace/ftrace.c
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2008-05-12 15:20:43 -0400
committerThomas Gleixner <tglx@linutronix.de>2008-05-23 14:38:41 -0400
commit5072c59fd45e9976d02ee6f18c7336ef97623cbc (patch)
treed7c9d48ed518246b958ee7759f285bc22445feb8 /kernel/trace/ftrace.c
parentd61f82d06672f57fca410da6f7fffd15867db622 (diff)
ftrace: add filter select functions to trace
This patch adds two files to the debugfs system: /debugfs/tracing/available_filter_functions and /debugfs/tracing/set_ftrace_filter The available_filter_functions lists all functions that has been recorded by the ftraced that has called the ftrace_record_ip function. This is to allow users to see what functions have been converted to nops and can be enabled for tracing. To enable functions, simply echo the names (whitespace delimited) into set_ftrace_filter. Simple wildcards are also allowed. echo 'scheduler' > /debugfs/tracing/set_ftrace_filter Will have only the scheduler be activated when tracing is enabled. echo 'sched_*' > /debugfs/tracing/set_ftrace_filter Will have only the functions starting with 'sched_' be activated. echo '*lock' > /debugfs/tracing/set_ftrace_filter Will have only functions ending with 'lock' be activated. echo '*lock*' > /debugfs/tracing/set_ftrace_filter Will have only functions with 'lock' in its name be activated. Note: 'sched*lock' will not work. The only wildcards that are allowed is an asterisk and the beginning and or end of the string passed in. Multiple names can be passed in with whitespace delimited: echo 'scheduler *lock *acpi*' > /debugfs/tracing/set_ftrace_filter is also the same as: echo 'scheduler' > /debugfs/tracing/set_ftrace_filter echo '*lock' >> /debugfs/tracing/set_ftrace_filter echo '*acpi*' >> /debugfs/tracing/set_ftrace_filter Appending does just that. It appends to the list. To disable all filters simply echo an empty line in: echo > /debugfs/tracing/set_ftrace_filter Signed-off-by: Steven Rostedt <srostedt@redhat.com> Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel/trace/ftrace.c')
-rw-r--r--kernel/trace/ftrace.c527
1 files changed, 510 insertions, 17 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 88544f9bc0e..97d5cb7b7e7 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -16,12 +16,15 @@
16#include <linux/stop_machine.h> 16#include <linux/stop_machine.h>
17#include <linux/clocksource.h> 17#include <linux/clocksource.h>
18#include <linux/kallsyms.h> 18#include <linux/kallsyms.h>
19#include <linux/seq_file.h>
20#include <linux/debugfs.h>
19#include <linux/kthread.h> 21#include <linux/kthread.h>
20#include <linux/hardirq.h> 22#include <linux/hardirq.h>
21#include <linux/ftrace.h> 23#include <linux/ftrace.h>
22#include <linux/module.h> 24#include <linux/uaccess.h>
23#include <linux/sysctl.h> 25#include <linux/sysctl.h>
24#include <linux/hash.h> 26#include <linux/hash.h>
27#include <linux/ctype.h>
25#include <linux/list.h> 28#include <linux/list.h>
26 29
27#include "trace.h" 30#include "trace.h"
@@ -151,12 +154,15 @@ enum {
151 FTRACE_DISABLE_MCOUNT = (1 << 4), 154 FTRACE_DISABLE_MCOUNT = (1 << 4),
152}; 155};
153 156
157static int ftrace_filtered;
158
154static struct hlist_head ftrace_hash[FTRACE_HASHSIZE]; 159static struct hlist_head ftrace_hash[FTRACE_HASHSIZE];
155 160
156static DEFINE_PER_CPU(int, ftrace_shutdown_disable_cpu); 161static DEFINE_PER_CPU(int, ftrace_shutdown_disable_cpu);
157 162
158static DEFINE_SPINLOCK(ftrace_shutdown_lock); 163static DEFINE_SPINLOCK(ftrace_shutdown_lock);
159static DEFINE_MUTEX(ftraced_lock); 164static DEFINE_MUTEX(ftraced_lock);
165static DEFINE_MUTEX(ftrace_filter_lock);
160 166
161struct ftrace_page { 167struct ftrace_page {
162 struct ftrace_page *next; 168 struct ftrace_page *next;
@@ -282,16 +288,82 @@ ftrace_record_ip(unsigned long ip)
282#define FTRACE_ADDR ((long)(&ftrace_caller)) 288#define FTRACE_ADDR ((long)(&ftrace_caller))
283#define MCOUNT_ADDR ((long)(&mcount)) 289#define MCOUNT_ADDR ((long)(&mcount))
284 290
285static void notrace ftrace_replace_code(int saved) 291static void notrace
292__ftrace_replace_code(struct dyn_ftrace *rec,
293 unsigned char *old, unsigned char *new, int enable)
294{
295 unsigned long ip;
296 int failed;
297
298 ip = rec->ip;
299
300 if (ftrace_filtered && enable) {
301 unsigned long fl;
302 /*
303 * If filtering is on:
304 *
305 * If this record is set to be filtered and
306 * is enabled then do nothing.
307 *
308 * If this record is set to be filtered and
309 * it is not enabled, enable it.
310 *
311 * If this record is not set to be filtered
312 * and it is not enabled do nothing.
313 *
314 * If this record is not set to be filtered and
315 * it is enabled, disable it.
316 */
317 fl = rec->flags & (FTRACE_FL_FILTER | FTRACE_FL_ENABLED);
318
319 if ((fl == (FTRACE_FL_FILTER | FTRACE_FL_ENABLED)) ||
320 (fl == 0))
321 return;
322
323 /*
324 * If it is enabled disable it,
325 * otherwise enable it!
326 */
327 if (fl == FTRACE_FL_ENABLED) {
328 /* swap new and old */
329 new = old;
330 old = ftrace_call_replace(ip, FTRACE_ADDR);
331 rec->flags &= ~FTRACE_FL_ENABLED;
332 } else {
333 new = ftrace_call_replace(ip, FTRACE_ADDR);
334 rec->flags |= FTRACE_FL_ENABLED;
335 }
336 } else {
337
338 if (enable)
339 new = ftrace_call_replace(ip, FTRACE_ADDR);
340 else
341 old = ftrace_call_replace(ip, FTRACE_ADDR);
342
343 if (enable) {
344 if (rec->flags & FTRACE_FL_ENABLED)
345 return;
346 rec->flags |= FTRACE_FL_ENABLED;
347 } else {
348 if (!(rec->flags & FTRACE_FL_ENABLED))
349 return;
350 rec->flags &= ~FTRACE_FL_ENABLED;
351 }
352 }
353
354 failed = ftrace_modify_code(ip, old, new);
355 if (failed)
356 rec->flags |= FTRACE_FL_FAILED;
357}
358
359static void notrace ftrace_replace_code(int enable)
286{ 360{
287 unsigned char *new = NULL, *old = NULL; 361 unsigned char *new = NULL, *old = NULL;
288 struct dyn_ftrace *rec; 362 struct dyn_ftrace *rec;
289 struct ftrace_page *pg; 363 struct ftrace_page *pg;
290 unsigned long ip;
291 int failed;
292 int i; 364 int i;
293 365
294 if (saved) 366 if (enable)
295 old = ftrace_nop_replace(); 367 old = ftrace_nop_replace();
296 else 368 else
297 new = ftrace_nop_replace(); 369 new = ftrace_nop_replace();
@@ -304,16 +376,7 @@ static void notrace ftrace_replace_code(int saved)
304 if (rec->flags & FTRACE_FL_FAILED) 376 if (rec->flags & FTRACE_FL_FAILED)
305 continue; 377 continue;
306 378
307 ip = rec->ip; 379 __ftrace_replace_code(rec, old, new, enable);
308
309 if (saved)
310 new = ftrace_call_replace(ip, FTRACE_ADDR);
311 else
312 old = ftrace_call_replace(ip, FTRACE_ADDR);
313
314 failed = ftrace_modify_code(ip, old, new);
315 if (failed)
316 rec->flags |= FTRACE_FL_FAILED;
317 } 380 }
318 } 381 }
319} 382}
@@ -580,6 +643,436 @@ static int __init ftrace_dyn_table_alloc(void)
580 return 0; 643 return 0;
581} 644}
582 645
646enum {
647 FTRACE_ITER_FILTER = (1 << 0),
648 FTRACE_ITER_CONT = (1 << 1),
649};
650
651#define FTRACE_BUFF_MAX (KSYM_SYMBOL_LEN+4) /* room for wildcards */
652
653struct ftrace_iterator {
654 loff_t pos;
655 struct ftrace_page *pg;
656 unsigned idx;
657 unsigned flags;
658 unsigned char buffer[FTRACE_BUFF_MAX+1];
659 unsigned buffer_idx;
660 unsigned filtered;
661};
662
663static void notrace *
664t_next(struct seq_file *m, void *v, loff_t *pos)
665{
666 struct ftrace_iterator *iter = m->private;
667 struct dyn_ftrace *rec = NULL;
668
669 (*pos)++;
670
671 retry:
672 if (iter->idx >= iter->pg->index) {
673 if (iter->pg->next) {
674 iter->pg = iter->pg->next;
675 iter->idx = 0;
676 goto retry;
677 }
678 } else {
679 rec = &iter->pg->records[iter->idx++];
680 if ((rec->flags & FTRACE_FL_FAILED) ||
681 ((iter->flags & FTRACE_ITER_FILTER) &&
682 !(rec->flags & FTRACE_FL_FILTER))) {
683 rec = NULL;
684 goto retry;
685 }
686 }
687
688 iter->pos = *pos;
689
690 return rec;
691}
692
693static void *t_start(struct seq_file *m, loff_t *pos)
694{
695 struct ftrace_iterator *iter = m->private;
696 void *p = NULL;
697 loff_t l = -1;
698
699 if (*pos != iter->pos) {
700 for (p = t_next(m, p, &l); p && l < *pos; p = t_next(m, p, &l))
701 ;
702 } else {
703 l = *pos;
704 p = t_next(m, p, &l);
705 }
706
707 return p;
708}
709
710static void t_stop(struct seq_file *m, void *p)
711{
712}
713
714static int t_show(struct seq_file *m, void *v)
715{
716 struct dyn_ftrace *rec = v;
717 char str[KSYM_SYMBOL_LEN];
718
719 if (!rec)
720 return 0;
721
722 kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
723
724 seq_printf(m, "%s\n", str);
725
726 return 0;
727}
728
729static struct seq_operations show_ftrace_seq_ops = {
730 .start = t_start,
731 .next = t_next,
732 .stop = t_stop,
733 .show = t_show,
734};
735
736static int notrace
737ftrace_avail_open(struct inode *inode, struct file *file)
738{
739 struct ftrace_iterator *iter;
740 int ret;
741
742 iter = kzalloc(sizeof(*iter), GFP_KERNEL);
743 if (!iter)
744 return -ENOMEM;
745
746 iter->pg = ftrace_pages_start;
747 iter->pos = -1;
748
749 ret = seq_open(file, &show_ftrace_seq_ops);
750 if (!ret) {
751 struct seq_file *m = file->private_data;
752 m->private = iter;
753 } else
754 kfree(iter);
755
756 return ret;
757}
758
759int ftrace_avail_release(struct inode *inode, struct file *file)
760{
761 struct seq_file *m = (struct seq_file *)file->private_data;
762 struct ftrace_iterator *iter = m->private;
763
764 seq_release(inode, file);
765 kfree(iter);
766 return 0;
767}
768
769static void notrace ftrace_filter_reset(void)
770{
771 struct ftrace_page *pg;
772 struct dyn_ftrace *rec;
773 unsigned i;
774
775 /* keep kstop machine from running */
776 preempt_disable();
777 ftrace_filtered = 0;
778 pg = ftrace_pages_start;
779 while (pg) {
780 for (i = 0; i < pg->index; i++) {
781 rec = &pg->records[i];
782 if (rec->flags & FTRACE_FL_FAILED)
783 continue;
784 rec->flags &= ~FTRACE_FL_FILTER;
785 }
786 pg = pg->next;
787 }
788 preempt_enable();
789}
790
791static int notrace
792ftrace_filter_open(struct inode *inode, struct file *file)
793{
794 struct ftrace_iterator *iter;
795 int ret = 0;
796
797 iter = kzalloc(sizeof(*iter), GFP_KERNEL);
798 if (!iter)
799 return -ENOMEM;
800
801 mutex_lock(&ftrace_filter_lock);
802 if ((file->f_mode & FMODE_WRITE) &&
803 !(file->f_flags & O_APPEND))
804 ftrace_filter_reset();
805
806 if (file->f_mode & FMODE_READ) {
807 iter->pg = ftrace_pages_start;
808 iter->pos = -1;
809 iter->flags = FTRACE_ITER_FILTER;
810
811 ret = seq_open(file, &show_ftrace_seq_ops);
812 if (!ret) {
813 struct seq_file *m = file->private_data;
814 m->private = iter;
815 } else
816 kfree(iter);
817 } else
818 file->private_data = iter;
819 mutex_unlock(&ftrace_filter_lock);
820
821 return ret;
822}
823
824static ssize_t notrace
825ftrace_filter_read(struct file *file, char __user *ubuf,
826 size_t cnt, loff_t *ppos)
827{
828 if (file->f_mode & FMODE_READ)
829 return seq_read(file, ubuf, cnt, ppos);
830 else
831 return -EPERM;
832}
833
834static loff_t notrace
835ftrace_filter_lseek(struct file *file, loff_t offset, int origin)
836{
837 loff_t ret;
838
839 if (file->f_mode & FMODE_READ)
840 ret = seq_lseek(file, offset, origin);
841 else
842 file->f_pos = ret = 1;
843
844 return ret;
845}
846
847enum {
848 MATCH_FULL,
849 MATCH_FRONT_ONLY,
850 MATCH_MIDDLE_ONLY,
851 MATCH_END_ONLY,
852};
853
854static void notrace
855ftrace_match(unsigned char *buff, int len)
856{
857 char str[KSYM_SYMBOL_LEN];
858 char *search = NULL;
859 struct ftrace_page *pg;
860 struct dyn_ftrace *rec;
861 int type = MATCH_FULL;
862 unsigned i, match = 0, search_len = 0;
863
864 for (i = 0; i < len; i++) {
865 if (buff[i] == '*') {
866 if (!i) {
867 search = buff + i + 1;
868 type = MATCH_END_ONLY;
869 search_len = len - (i + 1);
870 } else {
871 if (type == MATCH_END_ONLY) {
872 type = MATCH_MIDDLE_ONLY;
873 } else {
874 match = i;
875 type = MATCH_FRONT_ONLY;
876 }
877 buff[i] = 0;
878 break;
879 }
880 }
881 }
882
883 /* keep kstop machine from running */
884 preempt_disable();
885 ftrace_filtered = 1;
886 pg = ftrace_pages_start;
887 while (pg) {
888 for (i = 0; i < pg->index; i++) {
889 int matched = 0;
890 char *ptr;
891
892 rec = &pg->records[i];
893 if (rec->flags & FTRACE_FL_FAILED)
894 continue;
895 kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
896 switch (type) {
897 case MATCH_FULL:
898 if (strcmp(str, buff) == 0)
899 matched = 1;
900 break;
901 case MATCH_FRONT_ONLY:
902 if (memcmp(str, buff, match) == 0)
903 matched = 1;
904 break;
905 case MATCH_MIDDLE_ONLY:
906 if (strstr(str, search))
907 matched = 1;
908 break;
909 case MATCH_END_ONLY:
910 ptr = strstr(str, search);
911 if (ptr && (ptr[search_len] == 0))
912 matched = 1;
913 break;
914 }
915 if (matched)
916 rec->flags |= FTRACE_FL_FILTER;
917 }
918 pg = pg->next;
919 }
920 preempt_enable();
921}
922
923static ssize_t notrace
924ftrace_filter_write(struct file *file, const char __user *ubuf,
925 size_t cnt, loff_t *ppos)
926{
927 struct ftrace_iterator *iter;
928 char ch;
929 size_t read = 0;
930 ssize_t ret;
931
932 if (!cnt || cnt < 0)
933 return 0;
934
935 mutex_lock(&ftrace_filter_lock);
936
937 if (file->f_mode & FMODE_READ) {
938 struct seq_file *m = file->private_data;
939 iter = m->private;
940 } else
941 iter = file->private_data;
942
943 if (!*ppos) {
944 iter->flags &= ~FTRACE_ITER_CONT;
945 iter->buffer_idx = 0;
946 }
947
948 ret = get_user(ch, ubuf++);
949 if (ret)
950 goto out;
951 read++;
952 cnt--;
953
954 if (!(iter->flags & ~FTRACE_ITER_CONT)) {
955 /* skip white space */
956 while (cnt && isspace(ch)) {
957 ret = get_user(ch, ubuf++);
958 if (ret)
959 goto out;
960 read++;
961 cnt--;
962 }
963
964
965 if (isspace(ch)) {
966 file->f_pos += read;
967 ret = read;
968 goto out;
969 }
970
971 iter->buffer_idx = 0;
972 }
973
974 while (cnt && !isspace(ch)) {
975 if (iter->buffer_idx < FTRACE_BUFF_MAX)
976 iter->buffer[iter->buffer_idx++] = ch;
977 else {
978 ret = -EINVAL;
979 goto out;
980 }
981 ret = get_user(ch, ubuf++);
982 if (ret)
983 goto out;
984 read++;
985 cnt--;
986 }
987
988 if (isspace(ch)) {
989 iter->filtered++;
990 iter->buffer[iter->buffer_idx] = 0;
991 ftrace_match(iter->buffer, iter->buffer_idx);
992 iter->buffer_idx = 0;
993 } else
994 iter->flags |= FTRACE_ITER_CONT;
995
996
997 file->f_pos += read;
998
999 ret = read;
1000 out:
1001 mutex_unlock(&ftrace_filter_lock);
1002
1003 return ret;
1004}
1005
1006static int notrace
1007ftrace_filter_release(struct inode *inode, struct file *file)
1008{
1009 struct seq_file *m = (struct seq_file *)file->private_data;
1010 struct ftrace_iterator *iter;
1011
1012 mutex_lock(&ftrace_filter_lock);
1013 if (file->f_mode & FMODE_READ) {
1014 iter = m->private;
1015
1016 seq_release(inode, file);
1017 } else
1018 iter = file->private_data;
1019
1020 if (iter->buffer_idx) {
1021 iter->filtered++;
1022 iter->buffer[iter->buffer_idx] = 0;
1023 ftrace_match(iter->buffer, iter->buffer_idx);
1024 }
1025
1026 mutex_lock(&ftrace_sysctl_lock);
1027 mutex_lock(&ftraced_lock);
1028 if (iter->filtered && ftraced_suspend && ftrace_enabled)
1029 ftrace_run_update_code(FTRACE_ENABLE_CALLS);
1030 mutex_unlock(&ftraced_lock);
1031 mutex_unlock(&ftrace_sysctl_lock);
1032
1033 kfree(iter);
1034 mutex_unlock(&ftrace_filter_lock);
1035 return 0;
1036}
1037
1038static struct file_operations ftrace_avail_fops = {
1039 .open = ftrace_avail_open,
1040 .read = seq_read,
1041 .llseek = seq_lseek,
1042 .release = ftrace_avail_release,
1043};
1044
1045static struct file_operations ftrace_filter_fops = {
1046 .open = ftrace_filter_open,
1047 .read = ftrace_filter_read,
1048 .write = ftrace_filter_write,
1049 .llseek = ftrace_filter_lseek,
1050 .release = ftrace_filter_release,
1051};
1052
1053static __init int ftrace_init_debugfs(void)
1054{
1055 struct dentry *d_tracer;
1056 struct dentry *entry;
1057
1058 d_tracer = tracing_init_dentry();
1059
1060 entry = debugfs_create_file("available_filter_functions", 0444,
1061 d_tracer, NULL, &ftrace_avail_fops);
1062 if (!entry)
1063 pr_warning("Could not create debugfs "
1064 "'available_filter_functions' entry\n");
1065
1066 entry = debugfs_create_file("set_ftrace_filter", 0644, d_tracer,
1067 NULL, &ftrace_filter_fops);
1068 if (!entry)
1069 pr_warning("Could not create debugfs "
1070 "'set_ftrace_filter' entry\n");
1071 return 0;
1072}
1073
1074fs_initcall(ftrace_init_debugfs);
1075
583static int __init notrace ftrace_dynamic_init(void) 1076static int __init notrace ftrace_dynamic_init(void)
584{ 1077{
585 struct task_struct *p; 1078 struct task_struct *p;
@@ -657,14 +1150,14 @@ int unregister_ftrace_function(struct ftrace_ops *ops)
657 1150
658notrace int 1151notrace int
659ftrace_enable_sysctl(struct ctl_table *table, int write, 1152ftrace_enable_sysctl(struct ctl_table *table, int write,
660 struct file *filp, void __user *buffer, size_t *lenp, 1153 struct file *file, void __user *buffer, size_t *lenp,
661 loff_t *ppos) 1154 loff_t *ppos)
662{ 1155{
663 int ret; 1156 int ret;
664 1157
665 mutex_lock(&ftrace_sysctl_lock); 1158 mutex_lock(&ftrace_sysctl_lock);
666 1159
667 ret = proc_dointvec(table, write, filp, buffer, lenp, ppos); 1160 ret = proc_dointvec(table, write, file, buffer, lenp, ppos);
668 1161
669 if (ret || !write || (last_ftrace_enabled == ftrace_enabled)) 1162 if (ret || !write || (last_ftrace_enabled == ftrace_enabled))
670 goto out; 1163 goto out;