diff options
Diffstat (limited to 'kernel/trace/trace_events.c')
-rw-r--r-- | kernel/trace/trace_events.c | 288 |
1 files changed, 187 insertions, 101 deletions
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index d128f65778e6..c697c7043349 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/uaccess.h> | 15 | #include <linux/uaccess.h> |
16 | #include <linux/module.h> | 16 | #include <linux/module.h> |
17 | #include <linux/ctype.h> | 17 | #include <linux/ctype.h> |
18 | #include <linux/slab.h> | ||
18 | #include <linux/delay.h> | 19 | #include <linux/delay.h> |
19 | 20 | ||
20 | #include <asm/setup.h> | 21 | #include <asm/setup.h> |
@@ -60,10 +61,8 @@ int trace_define_field(struct ftrace_event_call *call, const char *type, | |||
60 | return 0; | 61 | return 0; |
61 | 62 | ||
62 | err: | 63 | err: |
63 | if (field) { | 64 | if (field) |
64 | kfree(field->name); | 65 | kfree(field->name); |
65 | kfree(field->type); | ||
66 | } | ||
67 | kfree(field); | 66 | kfree(field); |
68 | 67 | ||
69 | return -ENOMEM; | 68 | return -ENOMEM; |
@@ -78,7 +77,7 @@ EXPORT_SYMBOL_GPL(trace_define_field); | |||
78 | if (ret) \ | 77 | if (ret) \ |
79 | return ret; | 78 | return ret; |
80 | 79 | ||
81 | int trace_define_common_fields(struct ftrace_event_call *call) | 80 | static int trace_define_common_fields(struct ftrace_event_call *call) |
82 | { | 81 | { |
83 | int ret; | 82 | int ret; |
84 | struct trace_entry ent; | 83 | struct trace_entry ent; |
@@ -91,11 +90,8 @@ int trace_define_common_fields(struct ftrace_event_call *call) | |||
91 | 90 | ||
92 | return ret; | 91 | return ret; |
93 | } | 92 | } |
94 | EXPORT_SYMBOL_GPL(trace_define_common_fields); | ||
95 | |||
96 | #ifdef CONFIG_MODULES | ||
97 | 93 | ||
98 | static void trace_destroy_fields(struct ftrace_event_call *call) | 94 | void trace_destroy_fields(struct ftrace_event_call *call) |
99 | { | 95 | { |
100 | struct ftrace_event_field *field, *next; | 96 | struct ftrace_event_field *field, *next; |
101 | 97 | ||
@@ -107,27 +103,49 @@ static void trace_destroy_fields(struct ftrace_event_call *call) | |||
107 | } | 103 | } |
108 | } | 104 | } |
109 | 105 | ||
110 | #endif /* CONFIG_MODULES */ | 106 | int trace_event_raw_init(struct ftrace_event_call *call) |
107 | { | ||
108 | int id; | ||
111 | 109 | ||
112 | static void ftrace_event_enable_disable(struct ftrace_event_call *call, | 110 | id = register_ftrace_event(call->event); |
111 | if (!id) | ||
112 | return -ENODEV; | ||
113 | call->id = id; | ||
114 | INIT_LIST_HEAD(&call->fields); | ||
115 | |||
116 | return 0; | ||
117 | } | ||
118 | EXPORT_SYMBOL_GPL(trace_event_raw_init); | ||
119 | |||
120 | static int ftrace_event_enable_disable(struct ftrace_event_call *call, | ||
113 | int enable) | 121 | int enable) |
114 | { | 122 | { |
123 | int ret = 0; | ||
124 | |||
115 | switch (enable) { | 125 | switch (enable) { |
116 | case 0: | 126 | case 0: |
117 | if (call->enabled) { | 127 | if (call->enabled) { |
118 | call->enabled = 0; | 128 | call->enabled = 0; |
119 | tracing_stop_cmdline_record(); | 129 | tracing_stop_cmdline_record(); |
120 | call->unregfunc(call->data); | 130 | call->unregfunc(call); |
121 | } | 131 | } |
122 | break; | 132 | break; |
123 | case 1: | 133 | case 1: |
124 | if (!call->enabled) { | 134 | if (!call->enabled) { |
125 | call->enabled = 1; | ||
126 | tracing_start_cmdline_record(); | 135 | tracing_start_cmdline_record(); |
127 | call->regfunc(call->data); | 136 | ret = call->regfunc(call); |
137 | if (ret) { | ||
138 | tracing_stop_cmdline_record(); | ||
139 | pr_info("event trace: Could not enable event " | ||
140 | "%s\n", call->name); | ||
141 | break; | ||
142 | } | ||
143 | call->enabled = 1; | ||
128 | } | 144 | } |
129 | break; | 145 | break; |
130 | } | 146 | } |
147 | |||
148 | return ret; | ||
131 | } | 149 | } |
132 | 150 | ||
133 | static void ftrace_clear_events(void) | 151 | static void ftrace_clear_events(void) |
@@ -406,7 +424,7 @@ event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, | |||
406 | case 0: | 424 | case 0: |
407 | case 1: | 425 | case 1: |
408 | mutex_lock(&event_mutex); | 426 | mutex_lock(&event_mutex); |
409 | ftrace_event_enable_disable(call, val); | 427 | ret = ftrace_event_enable_disable(call, val); |
410 | mutex_unlock(&event_mutex); | 428 | mutex_unlock(&event_mutex); |
411 | break; | 429 | break; |
412 | 430 | ||
@@ -416,7 +434,7 @@ event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, | |||
416 | 434 | ||
417 | *ppos += cnt; | 435 | *ppos += cnt; |
418 | 436 | ||
419 | return cnt; | 437 | return ret ? ret : cnt; |
420 | } | 438 | } |
421 | 439 | ||
422 | static ssize_t | 440 | static ssize_t |
@@ -501,41 +519,16 @@ out: | |||
501 | return ret; | 519 | return ret; |
502 | } | 520 | } |
503 | 521 | ||
504 | extern char *__bad_type_size(void); | ||
505 | |||
506 | #undef FIELD | ||
507 | #define FIELD(type, name) \ | ||
508 | sizeof(type) != sizeof(field.name) ? __bad_type_size() : \ | ||
509 | #type, "common_" #name, offsetof(typeof(field), name), \ | ||
510 | sizeof(field.name) | ||
511 | |||
512 | static int trace_write_header(struct trace_seq *s) | ||
513 | { | ||
514 | struct trace_entry field; | ||
515 | |||
516 | /* struct trace_entry */ | ||
517 | return trace_seq_printf(s, | ||
518 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n" | ||
519 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n" | ||
520 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n" | ||
521 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n" | ||
522 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n" | ||
523 | "\n", | ||
524 | FIELD(unsigned short, type), | ||
525 | FIELD(unsigned char, flags), | ||
526 | FIELD(unsigned char, preempt_count), | ||
527 | FIELD(int, pid), | ||
528 | FIELD(int, lock_depth)); | ||
529 | } | ||
530 | |||
531 | static ssize_t | 522 | static ssize_t |
532 | event_format_read(struct file *filp, char __user *ubuf, size_t cnt, | 523 | event_format_read(struct file *filp, char __user *ubuf, size_t cnt, |
533 | loff_t *ppos) | 524 | loff_t *ppos) |
534 | { | 525 | { |
535 | struct ftrace_event_call *call = filp->private_data; | 526 | struct ftrace_event_call *call = filp->private_data; |
527 | struct ftrace_event_field *field; | ||
536 | struct trace_seq *s; | 528 | struct trace_seq *s; |
529 | int common_field_count = 5; | ||
537 | char *buf; | 530 | char *buf; |
538 | int r; | 531 | int r = 0; |
539 | 532 | ||
540 | if (*ppos) | 533 | if (*ppos) |
541 | return 0; | 534 | return 0; |
@@ -546,14 +539,48 @@ event_format_read(struct file *filp, char __user *ubuf, size_t cnt, | |||
546 | 539 | ||
547 | trace_seq_init(s); | 540 | trace_seq_init(s); |
548 | 541 | ||
549 | /* If any of the first writes fail, so will the show_format. */ | ||
550 | |||
551 | trace_seq_printf(s, "name: %s\n", call->name); | 542 | trace_seq_printf(s, "name: %s\n", call->name); |
552 | trace_seq_printf(s, "ID: %d\n", call->id); | 543 | trace_seq_printf(s, "ID: %d\n", call->id); |
553 | trace_seq_printf(s, "format:\n"); | 544 | trace_seq_printf(s, "format:\n"); |
554 | trace_write_header(s); | ||
555 | 545 | ||
556 | r = call->show_format(call, s); | 546 | list_for_each_entry_reverse(field, &call->fields, link) { |
547 | /* | ||
548 | * Smartly shows the array type(except dynamic array). | ||
549 | * Normal: | ||
550 | * field:TYPE VAR | ||
551 | * If TYPE := TYPE[LEN], it is shown: | ||
552 | * field:TYPE VAR[LEN] | ||
553 | */ | ||
554 | const char *array_descriptor = strchr(field->type, '['); | ||
555 | |||
556 | if (!strncmp(field->type, "__data_loc", 10)) | ||
557 | array_descriptor = NULL; | ||
558 | |||
559 | if (!array_descriptor) { | ||
560 | r = trace_seq_printf(s, "\tfield:%s %s;\toffset:%u;" | ||
561 | "\tsize:%u;\tsigned:%d;\n", | ||
562 | field->type, field->name, field->offset, | ||
563 | field->size, !!field->is_signed); | ||
564 | } else { | ||
565 | r = trace_seq_printf(s, "\tfield:%.*s %s%s;\toffset:%u;" | ||
566 | "\tsize:%u;\tsigned:%d;\n", | ||
567 | (int)(array_descriptor - field->type), | ||
568 | field->type, field->name, | ||
569 | array_descriptor, field->offset, | ||
570 | field->size, !!field->is_signed); | ||
571 | } | ||
572 | |||
573 | if (--common_field_count == 0) | ||
574 | r = trace_seq_printf(s, "\n"); | ||
575 | |||
576 | if (!r) | ||
577 | break; | ||
578 | } | ||
579 | |||
580 | if (r) | ||
581 | r = trace_seq_printf(s, "\nprint fmt: %s\n", | ||
582 | call->print_fmt); | ||
583 | |||
557 | if (!r) { | 584 | if (!r) { |
558 | /* | 585 | /* |
559 | * ug! The format output is bigger than a PAGE!! | 586 | * ug! The format output is bigger than a PAGE!! |
@@ -878,9 +905,9 @@ event_subsystem_dir(const char *name, struct dentry *d_events) | |||
878 | "'%s/filter' entry\n", name); | 905 | "'%s/filter' entry\n", name); |
879 | } | 906 | } |
880 | 907 | ||
881 | entry = trace_create_file("enable", 0644, system->entry, | 908 | trace_create_file("enable", 0644, system->entry, |
882 | (void *)system->name, | 909 | (void *)system->name, |
883 | &ftrace_system_enable_fops); | 910 | &ftrace_system_enable_fops); |
884 | 911 | ||
885 | return system->entry; | 912 | return system->entry; |
886 | } | 913 | } |
@@ -892,7 +919,6 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events, | |||
892 | const struct file_operations *filter, | 919 | const struct file_operations *filter, |
893 | const struct file_operations *format) | 920 | const struct file_operations *format) |
894 | { | 921 | { |
895 | struct dentry *entry; | ||
896 | int ret; | 922 | int ret; |
897 | 923 | ||
898 | /* | 924 | /* |
@@ -910,55 +936,72 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events, | |||
910 | } | 936 | } |
911 | 937 | ||
912 | if (call->regfunc) | 938 | if (call->regfunc) |
913 | entry = trace_create_file("enable", 0644, call->dir, call, | 939 | trace_create_file("enable", 0644, call->dir, call, |
914 | enable); | 940 | enable); |
915 | 941 | ||
916 | if (call->id && call->profile_enable) | 942 | if (call->id && call->perf_event_enable) |
917 | entry = trace_create_file("id", 0444, call->dir, call, | 943 | trace_create_file("id", 0444, call->dir, call, |
918 | id); | 944 | id); |
919 | 945 | ||
920 | if (call->define_fields) { | 946 | if (call->define_fields) { |
921 | ret = call->define_fields(call); | 947 | ret = trace_define_common_fields(call); |
948 | if (!ret) | ||
949 | ret = call->define_fields(call); | ||
922 | if (ret < 0) { | 950 | if (ret < 0) { |
923 | pr_warning("Could not initialize trace point" | 951 | pr_warning("Could not initialize trace point" |
924 | " events/%s\n", call->name); | 952 | " events/%s\n", call->name); |
925 | return ret; | 953 | return ret; |
926 | } | 954 | } |
927 | entry = trace_create_file("filter", 0644, call->dir, call, | 955 | trace_create_file("filter", 0644, call->dir, call, |
928 | filter); | 956 | filter); |
929 | } | 957 | } |
930 | 958 | ||
931 | /* A trace may not want to export its format */ | 959 | trace_create_file("format", 0444, call->dir, call, |
932 | if (!call->show_format) | 960 | format); |
933 | return 0; | ||
934 | |||
935 | entry = trace_create_file("format", 0444, call->dir, call, | ||
936 | format); | ||
937 | 961 | ||
938 | return 0; | 962 | return 0; |
939 | } | 963 | } |
940 | 964 | ||
941 | #define for_each_event(event, start, end) \ | 965 | static int __trace_add_event_call(struct ftrace_event_call *call) |
942 | for (event = start; \ | 966 | { |
943 | (unsigned long)event < (unsigned long)end; \ | 967 | struct dentry *d_events; |
944 | event++) | 968 | int ret; |
945 | 969 | ||
946 | #ifdef CONFIG_MODULES | 970 | if (!call->name) |
971 | return -EINVAL; | ||
947 | 972 | ||
948 | static LIST_HEAD(ftrace_module_file_list); | 973 | if (call->raw_init) { |
974 | ret = call->raw_init(call); | ||
975 | if (ret < 0) { | ||
976 | if (ret != -ENOSYS) | ||
977 | pr_warning("Could not initialize trace " | ||
978 | "events/%s\n", call->name); | ||
979 | return ret; | ||
980 | } | ||
981 | } | ||
949 | 982 | ||
950 | /* | 983 | d_events = event_trace_events_dir(); |
951 | * Modules must own their file_operations to keep up with | 984 | if (!d_events) |
952 | * reference counting. | 985 | return -ENOENT; |
953 | */ | 986 | |
954 | struct ftrace_module_file_ops { | 987 | ret = event_create_dir(call, d_events, &ftrace_event_id_fops, |
955 | struct list_head list; | 988 | &ftrace_enable_fops, &ftrace_event_filter_fops, |
956 | struct module *mod; | 989 | &ftrace_event_format_fops); |
957 | struct file_operations id; | 990 | if (!ret) |
958 | struct file_operations enable; | 991 | list_add(&call->list, &ftrace_events); |
959 | struct file_operations format; | 992 | |
960 | struct file_operations filter; | 993 | return ret; |
961 | }; | 994 | } |
995 | |||
996 | /* Add an additional event_call dynamically */ | ||
997 | int trace_add_event_call(struct ftrace_event_call *call) | ||
998 | { | ||
999 | int ret; | ||
1000 | mutex_lock(&event_mutex); | ||
1001 | ret = __trace_add_event_call(call); | ||
1002 | mutex_unlock(&event_mutex); | ||
1003 | return ret; | ||
1004 | } | ||
962 | 1005 | ||
963 | static void remove_subsystem_dir(const char *name) | 1006 | static void remove_subsystem_dir(const char *name) |
964 | { | 1007 | { |
@@ -986,6 +1029,53 @@ static void remove_subsystem_dir(const char *name) | |||
986 | } | 1029 | } |
987 | } | 1030 | } |
988 | 1031 | ||
1032 | /* | ||
1033 | * Must be called under locking both of event_mutex and trace_event_mutex. | ||
1034 | */ | ||
1035 | static void __trace_remove_event_call(struct ftrace_event_call *call) | ||
1036 | { | ||
1037 | ftrace_event_enable_disable(call, 0); | ||
1038 | if (call->event) | ||
1039 | __unregister_ftrace_event(call->event); | ||
1040 | debugfs_remove_recursive(call->dir); | ||
1041 | list_del(&call->list); | ||
1042 | trace_destroy_fields(call); | ||
1043 | destroy_preds(call); | ||
1044 | remove_subsystem_dir(call->system); | ||
1045 | } | ||
1046 | |||
1047 | /* Remove an event_call */ | ||
1048 | void trace_remove_event_call(struct ftrace_event_call *call) | ||
1049 | { | ||
1050 | mutex_lock(&event_mutex); | ||
1051 | down_write(&trace_event_mutex); | ||
1052 | __trace_remove_event_call(call); | ||
1053 | up_write(&trace_event_mutex); | ||
1054 | mutex_unlock(&event_mutex); | ||
1055 | } | ||
1056 | |||
1057 | #define for_each_event(event, start, end) \ | ||
1058 | for (event = start; \ | ||
1059 | (unsigned long)event < (unsigned long)end; \ | ||
1060 | event++) | ||
1061 | |||
1062 | #ifdef CONFIG_MODULES | ||
1063 | |||
1064 | static LIST_HEAD(ftrace_module_file_list); | ||
1065 | |||
1066 | /* | ||
1067 | * Modules must own their file_operations to keep up with | ||
1068 | * reference counting. | ||
1069 | */ | ||
1070 | struct ftrace_module_file_ops { | ||
1071 | struct list_head list; | ||
1072 | struct module *mod; | ||
1073 | struct file_operations id; | ||
1074 | struct file_operations enable; | ||
1075 | struct file_operations format; | ||
1076 | struct file_operations filter; | ||
1077 | }; | ||
1078 | |||
989 | static struct ftrace_module_file_ops * | 1079 | static struct ftrace_module_file_ops * |
990 | trace_create_file_ops(struct module *mod) | 1080 | trace_create_file_ops(struct module *mod) |
991 | { | 1081 | { |
@@ -1043,7 +1133,7 @@ static void trace_module_add_events(struct module *mod) | |||
1043 | if (!call->name) | 1133 | if (!call->name) |
1044 | continue; | 1134 | continue; |
1045 | if (call->raw_init) { | 1135 | if (call->raw_init) { |
1046 | ret = call->raw_init(); | 1136 | ret = call->raw_init(call); |
1047 | if (ret < 0) { | 1137 | if (ret < 0) { |
1048 | if (ret != -ENOSYS) | 1138 | if (ret != -ENOSYS) |
1049 | pr_warning("Could not initialize trace " | 1139 | pr_warning("Could not initialize trace " |
@@ -1061,10 +1151,11 @@ static void trace_module_add_events(struct module *mod) | |||
1061 | return; | 1151 | return; |
1062 | } | 1152 | } |
1063 | call->mod = mod; | 1153 | call->mod = mod; |
1064 | list_add(&call->list, &ftrace_events); | 1154 | ret = event_create_dir(call, d_events, |
1065 | event_create_dir(call, d_events, | 1155 | &file_ops->id, &file_ops->enable, |
1066 | &file_ops->id, &file_ops->enable, | 1156 | &file_ops->filter, &file_ops->format); |
1067 | &file_ops->filter, &file_ops->format); | 1157 | if (!ret) |
1158 | list_add(&call->list, &ftrace_events); | ||
1068 | } | 1159 | } |
1069 | } | 1160 | } |
1070 | 1161 | ||
@@ -1078,14 +1169,7 @@ static void trace_module_remove_events(struct module *mod) | |||
1078 | list_for_each_entry_safe(call, p, &ftrace_events, list) { | 1169 | list_for_each_entry_safe(call, p, &ftrace_events, list) { |
1079 | if (call->mod == mod) { | 1170 | if (call->mod == mod) { |
1080 | found = true; | 1171 | found = true; |
1081 | ftrace_event_enable_disable(call, 0); | 1172 | __trace_remove_event_call(call); |
1082 | if (call->event) | ||
1083 | __unregister_ftrace_event(call->event); | ||
1084 | debugfs_remove_recursive(call->dir); | ||
1085 | list_del(&call->list); | ||
1086 | trace_destroy_fields(call); | ||
1087 | destroy_preds(call); | ||
1088 | remove_subsystem_dir(call->system); | ||
1089 | } | 1173 | } |
1090 | } | 1174 | } |
1091 | 1175 | ||
@@ -1203,7 +1287,7 @@ static __init int event_trace_init(void) | |||
1203 | if (!call->name) | 1287 | if (!call->name) |
1204 | continue; | 1288 | continue; |
1205 | if (call->raw_init) { | 1289 | if (call->raw_init) { |
1206 | ret = call->raw_init(); | 1290 | ret = call->raw_init(call); |
1207 | if (ret < 0) { | 1291 | if (ret < 0) { |
1208 | if (ret != -ENOSYS) | 1292 | if (ret != -ENOSYS) |
1209 | pr_warning("Could not initialize trace " | 1293 | pr_warning("Could not initialize trace " |
@@ -1211,10 +1295,12 @@ static __init int event_trace_init(void) | |||
1211 | continue; | 1295 | continue; |
1212 | } | 1296 | } |
1213 | } | 1297 | } |
1214 | list_add(&call->list, &ftrace_events); | 1298 | ret = event_create_dir(call, d_events, &ftrace_event_id_fops, |
1215 | event_create_dir(call, d_events, &ftrace_event_id_fops, | 1299 | &ftrace_enable_fops, |
1216 | &ftrace_enable_fops, &ftrace_event_filter_fops, | 1300 | &ftrace_event_filter_fops, |
1217 | &ftrace_event_format_fops); | 1301 | &ftrace_event_format_fops); |
1302 | if (!ret) | ||
1303 | list_add(&call->list, &ftrace_events); | ||
1218 | } | 1304 | } |
1219 | 1305 | ||
1220 | while (true) { | 1306 | while (true) { |