diff options
Diffstat (limited to 'kernel/trace/trace_events.c')
-rw-r--r-- | kernel/trace/trace_events.c | 228 |
1 files changed, 155 insertions, 73 deletions
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index d128f65778e6..189b09baf4fb 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c | |||
@@ -78,7 +78,7 @@ EXPORT_SYMBOL_GPL(trace_define_field); | |||
78 | if (ret) \ | 78 | if (ret) \ |
79 | return ret; | 79 | return ret; |
80 | 80 | ||
81 | int trace_define_common_fields(struct ftrace_event_call *call) | 81 | static int trace_define_common_fields(struct ftrace_event_call *call) |
82 | { | 82 | { |
83 | int ret; | 83 | int ret; |
84 | struct trace_entry ent; | 84 | struct trace_entry ent; |
@@ -91,11 +91,8 @@ int trace_define_common_fields(struct ftrace_event_call *call) | |||
91 | 91 | ||
92 | return ret; | 92 | return ret; |
93 | } | 93 | } |
94 | EXPORT_SYMBOL_GPL(trace_define_common_fields); | ||
95 | 94 | ||
96 | #ifdef CONFIG_MODULES | 95 | void trace_destroy_fields(struct ftrace_event_call *call) |
97 | |||
98 | static void trace_destroy_fields(struct ftrace_event_call *call) | ||
99 | { | 96 | { |
100 | struct ftrace_event_field *field, *next; | 97 | struct ftrace_event_field *field, *next; |
101 | 98 | ||
@@ -107,27 +104,49 @@ static void trace_destroy_fields(struct ftrace_event_call *call) | |||
107 | } | 104 | } |
108 | } | 105 | } |
109 | 106 | ||
110 | #endif /* CONFIG_MODULES */ | 107 | int trace_event_raw_init(struct ftrace_event_call *call) |
108 | { | ||
109 | int id; | ||
110 | |||
111 | id = register_ftrace_event(call->event); | ||
112 | if (!id) | ||
113 | return -ENODEV; | ||
114 | call->id = id; | ||
115 | INIT_LIST_HEAD(&call->fields); | ||
116 | |||
117 | return 0; | ||
118 | } | ||
119 | EXPORT_SYMBOL_GPL(trace_event_raw_init); | ||
111 | 120 | ||
112 | static void ftrace_event_enable_disable(struct ftrace_event_call *call, | 121 | static int ftrace_event_enable_disable(struct ftrace_event_call *call, |
113 | int enable) | 122 | int enable) |
114 | { | 123 | { |
124 | int ret = 0; | ||
125 | |||
115 | switch (enable) { | 126 | switch (enable) { |
116 | case 0: | 127 | case 0: |
117 | if (call->enabled) { | 128 | if (call->enabled) { |
118 | call->enabled = 0; | 129 | call->enabled = 0; |
119 | tracing_stop_cmdline_record(); | 130 | tracing_stop_cmdline_record(); |
120 | call->unregfunc(call->data); | 131 | call->unregfunc(call); |
121 | } | 132 | } |
122 | break; | 133 | break; |
123 | case 1: | 134 | case 1: |
124 | if (!call->enabled) { | 135 | if (!call->enabled) { |
125 | call->enabled = 1; | ||
126 | tracing_start_cmdline_record(); | 136 | tracing_start_cmdline_record(); |
127 | call->regfunc(call->data); | 137 | ret = call->regfunc(call); |
138 | if (ret) { | ||
139 | tracing_stop_cmdline_record(); | ||
140 | pr_info("event trace: Could not enable event " | ||
141 | "%s\n", call->name); | ||
142 | break; | ||
143 | } | ||
144 | call->enabled = 1; | ||
128 | } | 145 | } |
129 | break; | 146 | break; |
130 | } | 147 | } |
148 | |||
149 | return ret; | ||
131 | } | 150 | } |
132 | 151 | ||
133 | static void ftrace_clear_events(void) | 152 | static void ftrace_clear_events(void) |
@@ -406,7 +425,7 @@ event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, | |||
406 | case 0: | 425 | case 0: |
407 | case 1: | 426 | case 1: |
408 | mutex_lock(&event_mutex); | 427 | mutex_lock(&event_mutex); |
409 | ftrace_event_enable_disable(call, val); | 428 | ret = ftrace_event_enable_disable(call, val); |
410 | mutex_unlock(&event_mutex); | 429 | mutex_unlock(&event_mutex); |
411 | break; | 430 | break; |
412 | 431 | ||
@@ -416,7 +435,7 @@ event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, | |||
416 | 435 | ||
417 | *ppos += cnt; | 436 | *ppos += cnt; |
418 | 437 | ||
419 | return cnt; | 438 | return ret ? ret : cnt; |
420 | } | 439 | } |
421 | 440 | ||
422 | static ssize_t | 441 | static ssize_t |
@@ -507,7 +526,7 @@ extern char *__bad_type_size(void); | |||
507 | #define FIELD(type, name) \ | 526 | #define FIELD(type, name) \ |
508 | sizeof(type) != sizeof(field.name) ? __bad_type_size() : \ | 527 | sizeof(type) != sizeof(field.name) ? __bad_type_size() : \ |
509 | #type, "common_" #name, offsetof(typeof(field), name), \ | 528 | #type, "common_" #name, offsetof(typeof(field), name), \ |
510 | sizeof(field.name) | 529 | sizeof(field.name), is_signed_type(type) |
511 | 530 | ||
512 | static int trace_write_header(struct trace_seq *s) | 531 | static int trace_write_header(struct trace_seq *s) |
513 | { | 532 | { |
@@ -515,17 +534,17 @@ static int trace_write_header(struct trace_seq *s) | |||
515 | 534 | ||
516 | /* struct trace_entry */ | 535 | /* struct trace_entry */ |
517 | return trace_seq_printf(s, | 536 | return trace_seq_printf(s, |
518 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n" | 537 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\tsigned:%u;\n" |
519 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n" | 538 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\tsigned:%u;\n" |
520 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n" | 539 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\tsigned:%u;\n" |
521 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n" | 540 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\tsigned:%u;\n" |
522 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n" | 541 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\tsigned:%u;\n" |
523 | "\n", | 542 | "\n", |
524 | FIELD(unsigned short, type), | 543 | FIELD(unsigned short, type), |
525 | FIELD(unsigned char, flags), | 544 | FIELD(unsigned char, flags), |
526 | FIELD(unsigned char, preempt_count), | 545 | FIELD(unsigned char, preempt_count), |
527 | FIELD(int, pid), | 546 | FIELD(int, pid), |
528 | FIELD(int, lock_depth)); | 547 | FIELD(int, lock_depth)); |
529 | } | 548 | } |
530 | 549 | ||
531 | static ssize_t | 550 | static ssize_t |
@@ -878,9 +897,9 @@ event_subsystem_dir(const char *name, struct dentry *d_events) | |||
878 | "'%s/filter' entry\n", name); | 897 | "'%s/filter' entry\n", name); |
879 | } | 898 | } |
880 | 899 | ||
881 | entry = trace_create_file("enable", 0644, system->entry, | 900 | trace_create_file("enable", 0644, system->entry, |
882 | (void *)system->name, | 901 | (void *)system->name, |
883 | &ftrace_system_enable_fops); | 902 | &ftrace_system_enable_fops); |
884 | 903 | ||
885 | return system->entry; | 904 | return system->entry; |
886 | } | 905 | } |
@@ -892,7 +911,6 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events, | |||
892 | const struct file_operations *filter, | 911 | const struct file_operations *filter, |
893 | const struct file_operations *format) | 912 | const struct file_operations *format) |
894 | { | 913 | { |
895 | struct dentry *entry; | ||
896 | int ret; | 914 | int ret; |
897 | 915 | ||
898 | /* | 916 | /* |
@@ -910,55 +928,76 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events, | |||
910 | } | 928 | } |
911 | 929 | ||
912 | if (call->regfunc) | 930 | if (call->regfunc) |
913 | entry = trace_create_file("enable", 0644, call->dir, call, | 931 | trace_create_file("enable", 0644, call->dir, call, |
914 | enable); | 932 | enable); |
915 | 933 | ||
916 | if (call->id && call->profile_enable) | 934 | if (call->id && call->profile_enable) |
917 | entry = trace_create_file("id", 0444, call->dir, call, | 935 | trace_create_file("id", 0444, call->dir, call, |
918 | id); | 936 | id); |
919 | 937 | ||
920 | if (call->define_fields) { | 938 | if (call->define_fields) { |
921 | ret = call->define_fields(call); | 939 | ret = trace_define_common_fields(call); |
940 | if (!ret) | ||
941 | ret = call->define_fields(call); | ||
922 | if (ret < 0) { | 942 | if (ret < 0) { |
923 | pr_warning("Could not initialize trace point" | 943 | pr_warning("Could not initialize trace point" |
924 | " events/%s\n", call->name); | 944 | " events/%s\n", call->name); |
925 | return ret; | 945 | return ret; |
926 | } | 946 | } |
927 | entry = trace_create_file("filter", 0644, call->dir, call, | 947 | trace_create_file("filter", 0644, call->dir, call, |
928 | filter); | 948 | filter); |
929 | } | 949 | } |
930 | 950 | ||
931 | /* A trace may not want to export its format */ | 951 | /* A trace may not want to export its format */ |
932 | if (!call->show_format) | 952 | if (!call->show_format) |
933 | return 0; | 953 | return 0; |
934 | 954 | ||
935 | entry = trace_create_file("format", 0444, call->dir, call, | 955 | trace_create_file("format", 0444, call->dir, call, |
936 | format); | 956 | format); |
937 | 957 | ||
938 | return 0; | 958 | return 0; |
939 | } | 959 | } |
940 | 960 | ||
941 | #define for_each_event(event, start, end) \ | 961 | static int __trace_add_event_call(struct ftrace_event_call *call) |
942 | for (event = start; \ | 962 | { |
943 | (unsigned long)event < (unsigned long)end; \ | 963 | struct dentry *d_events; |
944 | event++) | 964 | int ret; |
945 | 965 | ||
946 | #ifdef CONFIG_MODULES | 966 | if (!call->name) |
967 | return -EINVAL; | ||
947 | 968 | ||
948 | static LIST_HEAD(ftrace_module_file_list); | 969 | if (call->raw_init) { |
970 | ret = call->raw_init(call); | ||
971 | if (ret < 0) { | ||
972 | if (ret != -ENOSYS) | ||
973 | pr_warning("Could not initialize trace " | ||
974 | "events/%s\n", call->name); | ||
975 | return ret; | ||
976 | } | ||
977 | } | ||
949 | 978 | ||
950 | /* | 979 | d_events = event_trace_events_dir(); |
951 | * Modules must own their file_operations to keep up with | 980 | if (!d_events) |
952 | * reference counting. | 981 | return -ENOENT; |
953 | */ | 982 | |
954 | struct ftrace_module_file_ops { | 983 | ret = event_create_dir(call, d_events, &ftrace_event_id_fops, |
955 | struct list_head list; | 984 | &ftrace_enable_fops, &ftrace_event_filter_fops, |
956 | struct module *mod; | 985 | &ftrace_event_format_fops); |
957 | struct file_operations id; | 986 | if (!ret) |
958 | struct file_operations enable; | 987 | list_add(&call->list, &ftrace_events); |
959 | struct file_operations format; | 988 | |
960 | struct file_operations filter; | 989 | return ret; |
961 | }; | 990 | } |
991 | |||
992 | /* Add an additional event_call dynamically */ | ||
993 | int trace_add_event_call(struct ftrace_event_call *call) | ||
994 | { | ||
995 | int ret; | ||
996 | mutex_lock(&event_mutex); | ||
997 | ret = __trace_add_event_call(call); | ||
998 | mutex_unlock(&event_mutex); | ||
999 | return ret; | ||
1000 | } | ||
962 | 1001 | ||
963 | static void remove_subsystem_dir(const char *name) | 1002 | static void remove_subsystem_dir(const char *name) |
964 | { | 1003 | { |
@@ -986,6 +1025,53 @@ static void remove_subsystem_dir(const char *name) | |||
986 | } | 1025 | } |
987 | } | 1026 | } |
988 | 1027 | ||
1028 | /* | ||
1029 | * Must be called under locking both of event_mutex and trace_event_mutex. | ||
1030 | */ | ||
1031 | static void __trace_remove_event_call(struct ftrace_event_call *call) | ||
1032 | { | ||
1033 | ftrace_event_enable_disable(call, 0); | ||
1034 | if (call->event) | ||
1035 | __unregister_ftrace_event(call->event); | ||
1036 | debugfs_remove_recursive(call->dir); | ||
1037 | list_del(&call->list); | ||
1038 | trace_destroy_fields(call); | ||
1039 | destroy_preds(call); | ||
1040 | remove_subsystem_dir(call->system); | ||
1041 | } | ||
1042 | |||
1043 | /* Remove an event_call */ | ||
1044 | void trace_remove_event_call(struct ftrace_event_call *call) | ||
1045 | { | ||
1046 | mutex_lock(&event_mutex); | ||
1047 | down_write(&trace_event_mutex); | ||
1048 | __trace_remove_event_call(call); | ||
1049 | up_write(&trace_event_mutex); | ||
1050 | mutex_unlock(&event_mutex); | ||
1051 | } | ||
1052 | |||
1053 | #define for_each_event(event, start, end) \ | ||
1054 | for (event = start; \ | ||
1055 | (unsigned long)event < (unsigned long)end; \ | ||
1056 | event++) | ||
1057 | |||
1058 | #ifdef CONFIG_MODULES | ||
1059 | |||
1060 | static LIST_HEAD(ftrace_module_file_list); | ||
1061 | |||
1062 | /* | ||
1063 | * Modules must own their file_operations to keep up with | ||
1064 | * reference counting. | ||
1065 | */ | ||
1066 | struct ftrace_module_file_ops { | ||
1067 | struct list_head list; | ||
1068 | struct module *mod; | ||
1069 | struct file_operations id; | ||
1070 | struct file_operations enable; | ||
1071 | struct file_operations format; | ||
1072 | struct file_operations filter; | ||
1073 | }; | ||
1074 | |||
989 | static struct ftrace_module_file_ops * | 1075 | static struct ftrace_module_file_ops * |
990 | trace_create_file_ops(struct module *mod) | 1076 | trace_create_file_ops(struct module *mod) |
991 | { | 1077 | { |
@@ -1043,7 +1129,7 @@ static void trace_module_add_events(struct module *mod) | |||
1043 | if (!call->name) | 1129 | if (!call->name) |
1044 | continue; | 1130 | continue; |
1045 | if (call->raw_init) { | 1131 | if (call->raw_init) { |
1046 | ret = call->raw_init(); | 1132 | ret = call->raw_init(call); |
1047 | if (ret < 0) { | 1133 | if (ret < 0) { |
1048 | if (ret != -ENOSYS) | 1134 | if (ret != -ENOSYS) |
1049 | pr_warning("Could not initialize trace " | 1135 | pr_warning("Could not initialize trace " |
@@ -1061,10 +1147,11 @@ static void trace_module_add_events(struct module *mod) | |||
1061 | return; | 1147 | return; |
1062 | } | 1148 | } |
1063 | call->mod = mod; | 1149 | call->mod = mod; |
1064 | list_add(&call->list, &ftrace_events); | 1150 | ret = event_create_dir(call, d_events, |
1065 | event_create_dir(call, d_events, | 1151 | &file_ops->id, &file_ops->enable, |
1066 | &file_ops->id, &file_ops->enable, | 1152 | &file_ops->filter, &file_ops->format); |
1067 | &file_ops->filter, &file_ops->format); | 1153 | if (!ret) |
1154 | list_add(&call->list, &ftrace_events); | ||
1068 | } | 1155 | } |
1069 | } | 1156 | } |
1070 | 1157 | ||
@@ -1078,14 +1165,7 @@ static void trace_module_remove_events(struct module *mod) | |||
1078 | list_for_each_entry_safe(call, p, &ftrace_events, list) { | 1165 | list_for_each_entry_safe(call, p, &ftrace_events, list) { |
1079 | if (call->mod == mod) { | 1166 | if (call->mod == mod) { |
1080 | found = true; | 1167 | found = true; |
1081 | ftrace_event_enable_disable(call, 0); | 1168 | __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 | } | 1169 | } |
1090 | } | 1170 | } |
1091 | 1171 | ||
@@ -1203,7 +1283,7 @@ static __init int event_trace_init(void) | |||
1203 | if (!call->name) | 1283 | if (!call->name) |
1204 | continue; | 1284 | continue; |
1205 | if (call->raw_init) { | 1285 | if (call->raw_init) { |
1206 | ret = call->raw_init(); | 1286 | ret = call->raw_init(call); |
1207 | if (ret < 0) { | 1287 | if (ret < 0) { |
1208 | if (ret != -ENOSYS) | 1288 | if (ret != -ENOSYS) |
1209 | pr_warning("Could not initialize trace " | 1289 | pr_warning("Could not initialize trace " |
@@ -1211,10 +1291,12 @@ static __init int event_trace_init(void) | |||
1211 | continue; | 1291 | continue; |
1212 | } | 1292 | } |
1213 | } | 1293 | } |
1214 | list_add(&call->list, &ftrace_events); | 1294 | ret = event_create_dir(call, d_events, &ftrace_event_id_fops, |
1215 | event_create_dir(call, d_events, &ftrace_event_id_fops, | 1295 | &ftrace_enable_fops, |
1216 | &ftrace_enable_fops, &ftrace_event_filter_fops, | 1296 | &ftrace_event_filter_fops, |
1217 | &ftrace_event_format_fops); | 1297 | &ftrace_event_format_fops); |
1298 | if (!ret) | ||
1299 | list_add(&call->list, &ftrace_events); | ||
1218 | } | 1300 | } |
1219 | 1301 | ||
1220 | while (true) { | 1302 | while (true) { |