diff options
Diffstat (limited to 'kernel/trace/trace_events.c')
| -rw-r--r-- | kernel/trace/trace_events.c | 178 |
1 files changed, 147 insertions, 31 deletions
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index aa08be69a1b6..78b1ed230177 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c | |||
| @@ -17,6 +17,8 @@ | |||
| 17 | #include <linux/ctype.h> | 17 | #include <linux/ctype.h> |
| 18 | #include <linux/delay.h> | 18 | #include <linux/delay.h> |
| 19 | 19 | ||
| 20 | #include <asm/setup.h> | ||
| 21 | |||
| 20 | #include "trace_output.h" | 22 | #include "trace_output.h" |
| 21 | 23 | ||
| 22 | #define TRACE_SYSTEM "TRACE_SYSTEM" | 24 | #define TRACE_SYSTEM "TRACE_SYSTEM" |
| @@ -25,8 +27,9 @@ DEFINE_MUTEX(event_mutex); | |||
| 25 | 27 | ||
| 26 | LIST_HEAD(ftrace_events); | 28 | LIST_HEAD(ftrace_events); |
| 27 | 29 | ||
| 28 | int trace_define_field(struct ftrace_event_call *call, char *type, | 30 | int trace_define_field(struct ftrace_event_call *call, const char *type, |
| 29 | char *name, int offset, int size, int is_signed) | 31 | const char *name, int offset, int size, int is_signed, |
| 32 | int filter_type) | ||
| 30 | { | 33 | { |
| 31 | struct ftrace_event_field *field; | 34 | struct ftrace_event_field *field; |
| 32 | 35 | ||
| @@ -42,9 +45,15 @@ int trace_define_field(struct ftrace_event_call *call, char *type, | |||
| 42 | if (!field->type) | 45 | if (!field->type) |
| 43 | goto err; | 46 | goto err; |
| 44 | 47 | ||
| 48 | if (filter_type == FILTER_OTHER) | ||
| 49 | field->filter_type = filter_assign_type(type); | ||
| 50 | else | ||
| 51 | field->filter_type = filter_type; | ||
| 52 | |||
| 45 | field->offset = offset; | 53 | field->offset = offset; |
| 46 | field->size = size; | 54 | field->size = size; |
| 47 | field->is_signed = is_signed; | 55 | field->is_signed = is_signed; |
| 56 | |||
| 48 | list_add(&field->link, &call->fields); | 57 | list_add(&field->link, &call->fields); |
| 49 | 58 | ||
| 50 | return 0; | 59 | return 0; |
| @@ -60,6 +69,29 @@ err: | |||
| 60 | } | 69 | } |
| 61 | EXPORT_SYMBOL_GPL(trace_define_field); | 70 | EXPORT_SYMBOL_GPL(trace_define_field); |
| 62 | 71 | ||
| 72 | #define __common_field(type, item) \ | ||
| 73 | ret = trace_define_field(call, #type, "common_" #item, \ | ||
| 74 | offsetof(typeof(ent), item), \ | ||
| 75 | sizeof(ent.item), \ | ||
| 76 | is_signed_type(type), FILTER_OTHER); \ | ||
| 77 | if (ret) \ | ||
| 78 | return ret; | ||
| 79 | |||
| 80 | int trace_define_common_fields(struct ftrace_event_call *call) | ||
| 81 | { | ||
| 82 | int ret; | ||
| 83 | struct trace_entry ent; | ||
| 84 | |||
| 85 | __common_field(unsigned short, type); | ||
| 86 | __common_field(unsigned char, flags); | ||
| 87 | __common_field(unsigned char, preempt_count); | ||
| 88 | __common_field(int, pid); | ||
| 89 | __common_field(int, tgid); | ||
| 90 | |||
| 91 | return ret; | ||
| 92 | } | ||
| 93 | EXPORT_SYMBOL_GPL(trace_define_common_fields); | ||
| 94 | |||
| 63 | #ifdef CONFIG_MODULES | 95 | #ifdef CONFIG_MODULES |
| 64 | 96 | ||
| 65 | static void trace_destroy_fields(struct ftrace_event_call *call) | 97 | static void trace_destroy_fields(struct ftrace_event_call *call) |
| @@ -84,14 +116,14 @@ static void ftrace_event_enable_disable(struct ftrace_event_call *call, | |||
| 84 | if (call->enabled) { | 116 | if (call->enabled) { |
| 85 | call->enabled = 0; | 117 | call->enabled = 0; |
| 86 | tracing_stop_cmdline_record(); | 118 | tracing_stop_cmdline_record(); |
| 87 | call->unregfunc(); | 119 | call->unregfunc(call->data); |
| 88 | } | 120 | } |
| 89 | break; | 121 | break; |
| 90 | case 1: | 122 | case 1: |
| 91 | if (!call->enabled) { | 123 | if (!call->enabled) { |
| 92 | call->enabled = 1; | 124 | call->enabled = 1; |
| 93 | tracing_start_cmdline_record(); | 125 | tracing_start_cmdline_record(); |
| 94 | call->regfunc(); | 126 | call->regfunc(call->data); |
| 95 | } | 127 | } |
| 96 | break; | 128 | break; |
| 97 | } | 129 | } |
| @@ -300,10 +332,18 @@ t_next(struct seq_file *m, void *v, loff_t *pos) | |||
| 300 | 332 | ||
| 301 | static void *t_start(struct seq_file *m, loff_t *pos) | 333 | static void *t_start(struct seq_file *m, loff_t *pos) |
| 302 | { | 334 | { |
| 335 | struct ftrace_event_call *call = NULL; | ||
| 336 | loff_t l; | ||
| 337 | |||
| 303 | mutex_lock(&event_mutex); | 338 | mutex_lock(&event_mutex); |
| 304 | if (*pos == 0) | 339 | |
| 305 | m->private = ftrace_events.next; | 340 | m->private = ftrace_events.next; |
| 306 | return t_next(m, NULL, pos); | 341 | for (l = 0; l <= *pos; ) { |
| 342 | call = t_next(m, NULL, &l); | ||
| 343 | if (!call) | ||
| 344 | break; | ||
| 345 | } | ||
| 346 | return call; | ||
| 307 | } | 347 | } |
| 308 | 348 | ||
| 309 | static void * | 349 | static void * |
| @@ -332,10 +372,18 @@ s_next(struct seq_file *m, void *v, loff_t *pos) | |||
| 332 | 372 | ||
| 333 | static void *s_start(struct seq_file *m, loff_t *pos) | 373 | static void *s_start(struct seq_file *m, loff_t *pos) |
| 334 | { | 374 | { |
| 375 | struct ftrace_event_call *call = NULL; | ||
| 376 | loff_t l; | ||
| 377 | |||
| 335 | mutex_lock(&event_mutex); | 378 | mutex_lock(&event_mutex); |
| 336 | if (*pos == 0) | 379 | |
| 337 | m->private = ftrace_events.next; | 380 | m->private = ftrace_events.next; |
| 338 | return s_next(m, NULL, pos); | 381 | for (l = 0; l <= *pos; ) { |
| 382 | call = s_next(m, NULL, &l); | ||
| 383 | if (!call) | ||
| 384 | break; | ||
| 385 | } | ||
| 386 | return call; | ||
| 339 | } | 387 | } |
| 340 | 388 | ||
| 341 | static int t_show(struct seq_file *m, void *v) | 389 | static int t_show(struct seq_file *m, void *v) |
| @@ -360,7 +408,7 @@ ftrace_event_seq_open(struct inode *inode, struct file *file) | |||
| 360 | const struct seq_operations *seq_ops; | 408 | const struct seq_operations *seq_ops; |
| 361 | 409 | ||
| 362 | if ((file->f_mode & FMODE_WRITE) && | 410 | if ((file->f_mode & FMODE_WRITE) && |
| 363 | !(file->f_flags & O_APPEND)) | 411 | (file->f_flags & O_TRUNC)) |
| 364 | ftrace_clear_events(); | 412 | ftrace_clear_events(); |
| 365 | 413 | ||
| 366 | seq_ops = inode->i_private; | 414 | seq_ops = inode->i_private; |
| @@ -558,7 +606,7 @@ event_format_read(struct file *filp, char __user *ubuf, size_t cnt, | |||
| 558 | trace_seq_printf(s, "format:\n"); | 606 | trace_seq_printf(s, "format:\n"); |
| 559 | trace_write_header(s); | 607 | trace_write_header(s); |
| 560 | 608 | ||
| 561 | r = call->show_format(s); | 609 | r = call->show_format(call, s); |
| 562 | if (!r) { | 610 | if (!r) { |
| 563 | /* | 611 | /* |
| 564 | * ug! The format output is bigger than a PAGE!! | 612 | * ug! The format output is bigger than a PAGE!! |
| @@ -833,8 +881,10 @@ event_subsystem_dir(const char *name, struct dentry *d_events) | |||
| 833 | 881 | ||
| 834 | /* First see if we did not already create this dir */ | 882 | /* First see if we did not already create this dir */ |
| 835 | list_for_each_entry(system, &event_subsystems, list) { | 883 | list_for_each_entry(system, &event_subsystems, list) { |
| 836 | if (strcmp(system->name, name) == 0) | 884 | if (strcmp(system->name, name) == 0) { |
| 885 | system->nr_events++; | ||
| 837 | return system->entry; | 886 | return system->entry; |
| 887 | } | ||
| 838 | } | 888 | } |
| 839 | 889 | ||
| 840 | /* need to create new entry */ | 890 | /* need to create new entry */ |
| @@ -853,6 +903,7 @@ event_subsystem_dir(const char *name, struct dentry *d_events) | |||
| 853 | return d_events; | 903 | return d_events; |
| 854 | } | 904 | } |
| 855 | 905 | ||
| 906 | system->nr_events = 1; | ||
| 856 | system->name = kstrdup(name, GFP_KERNEL); | 907 | system->name = kstrdup(name, GFP_KERNEL); |
| 857 | if (!system->name) { | 908 | if (!system->name) { |
| 858 | debugfs_remove(system->entry); | 909 | debugfs_remove(system->entry); |
| @@ -904,15 +955,6 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events, | |||
| 904 | if (strcmp(call->system, TRACE_SYSTEM) != 0) | 955 | if (strcmp(call->system, TRACE_SYSTEM) != 0) |
| 905 | d_events = event_subsystem_dir(call->system, d_events); | 956 | d_events = event_subsystem_dir(call->system, d_events); |
| 906 | 957 | ||
| 907 | if (call->raw_init) { | ||
| 908 | ret = call->raw_init(); | ||
| 909 | if (ret < 0) { | ||
| 910 | pr_warning("Could not initialize trace point" | ||
| 911 | " events/%s\n", call->name); | ||
| 912 | return ret; | ||
| 913 | } | ||
| 914 | } | ||
| 915 | |||
| 916 | call->dir = debugfs_create_dir(call->name, d_events); | 958 | call->dir = debugfs_create_dir(call->name, d_events); |
| 917 | if (!call->dir) { | 959 | if (!call->dir) { |
| 918 | pr_warning("Could not create debugfs " | 960 | pr_warning("Could not create debugfs " |
| @@ -924,12 +966,12 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events, | |||
| 924 | entry = trace_create_file("enable", 0644, call->dir, call, | 966 | entry = trace_create_file("enable", 0644, call->dir, call, |
| 925 | enable); | 967 | enable); |
| 926 | 968 | ||
| 927 | if (call->id) | 969 | if (call->id && call->profile_enable) |
| 928 | entry = trace_create_file("id", 0444, call->dir, call, | 970 | entry = trace_create_file("id", 0444, call->dir, call, |
| 929 | id); | 971 | id); |
| 930 | 972 | ||
| 931 | if (call->define_fields) { | 973 | if (call->define_fields) { |
| 932 | ret = call->define_fields(); | 974 | ret = call->define_fields(call); |
| 933 | if (ret < 0) { | 975 | if (ret < 0) { |
| 934 | pr_warning("Could not initialize trace point" | 976 | pr_warning("Could not initialize trace point" |
| 935 | " events/%s\n", call->name); | 977 | " events/%s\n", call->name); |
| @@ -971,6 +1013,32 @@ struct ftrace_module_file_ops { | |||
| 971 | struct file_operations filter; | 1013 | struct file_operations filter; |
| 972 | }; | 1014 | }; |
| 973 | 1015 | ||
| 1016 | static void remove_subsystem_dir(const char *name) | ||
| 1017 | { | ||
| 1018 | struct event_subsystem *system; | ||
| 1019 | |||
| 1020 | if (strcmp(name, TRACE_SYSTEM) == 0) | ||
| 1021 | return; | ||
| 1022 | |||
| 1023 | list_for_each_entry(system, &event_subsystems, list) { | ||
| 1024 | if (strcmp(system->name, name) == 0) { | ||
| 1025 | if (!--system->nr_events) { | ||
| 1026 | struct event_filter *filter = system->filter; | ||
| 1027 | |||
| 1028 | debugfs_remove_recursive(system->entry); | ||
| 1029 | list_del(&system->list); | ||
| 1030 | if (filter) { | ||
| 1031 | kfree(filter->filter_string); | ||
| 1032 | kfree(filter); | ||
| 1033 | } | ||
| 1034 | kfree(system->name); | ||
| 1035 | kfree(system); | ||
| 1036 | } | ||
| 1037 | break; | ||
| 1038 | } | ||
| 1039 | } | ||
| 1040 | } | ||
| 1041 | |||
| 974 | static struct ftrace_module_file_ops * | 1042 | static struct ftrace_module_file_ops * |
| 975 | trace_create_file_ops(struct module *mod) | 1043 | trace_create_file_ops(struct module *mod) |
| 976 | { | 1044 | { |
| @@ -1011,6 +1079,7 @@ static void trace_module_add_events(struct module *mod) | |||
| 1011 | struct ftrace_module_file_ops *file_ops = NULL; | 1079 | struct ftrace_module_file_ops *file_ops = NULL; |
| 1012 | struct ftrace_event_call *call, *start, *end; | 1080 | struct ftrace_event_call *call, *start, *end; |
| 1013 | struct dentry *d_events; | 1081 | struct dentry *d_events; |
| 1082 | int ret; | ||
| 1014 | 1083 | ||
| 1015 | start = mod->trace_events; | 1084 | start = mod->trace_events; |
| 1016 | end = mod->trace_events + mod->num_trace_events; | 1085 | end = mod->trace_events + mod->num_trace_events; |
| @@ -1026,7 +1095,15 @@ static void trace_module_add_events(struct module *mod) | |||
| 1026 | /* The linker may leave blanks */ | 1095 | /* The linker may leave blanks */ |
| 1027 | if (!call->name) | 1096 | if (!call->name) |
| 1028 | continue; | 1097 | continue; |
| 1029 | 1098 | if (call->raw_init) { | |
| 1099 | ret = call->raw_init(); | ||
| 1100 | if (ret < 0) { | ||
| 1101 | if (ret != -ENOSYS) | ||
| 1102 | pr_warning("Could not initialize trace " | ||
| 1103 | "point events/%s\n", call->name); | ||
| 1104 | continue; | ||
| 1105 | } | ||
| 1106 | } | ||
| 1030 | /* | 1107 | /* |
| 1031 | * This module has events, create file ops for this module | 1108 | * This module has events, create file ops for this module |
| 1032 | * if not already done. | 1109 | * if not already done. |
| @@ -1061,6 +1138,7 @@ static void trace_module_remove_events(struct module *mod) | |||
| 1061 | list_del(&call->list); | 1138 | list_del(&call->list); |
| 1062 | trace_destroy_fields(call); | 1139 | trace_destroy_fields(call); |
| 1063 | destroy_preds(call); | 1140 | destroy_preds(call); |
| 1141 | remove_subsystem_dir(call->system); | ||
| 1064 | } | 1142 | } |
| 1065 | } | 1143 | } |
| 1066 | 1144 | ||
| @@ -1117,6 +1195,18 @@ struct notifier_block trace_module_nb = { | |||
| 1117 | extern struct ftrace_event_call __start_ftrace_events[]; | 1195 | extern struct ftrace_event_call __start_ftrace_events[]; |
| 1118 | extern struct ftrace_event_call __stop_ftrace_events[]; | 1196 | extern struct ftrace_event_call __stop_ftrace_events[]; |
| 1119 | 1197 | ||
| 1198 | static char bootup_event_buf[COMMAND_LINE_SIZE] __initdata; | ||
| 1199 | |||
| 1200 | static __init int setup_trace_event(char *str) | ||
| 1201 | { | ||
| 1202 | strlcpy(bootup_event_buf, str, COMMAND_LINE_SIZE); | ||
| 1203 | ring_buffer_expanded = 1; | ||
| 1204 | tracing_selftest_disabled = 1; | ||
| 1205 | |||
| 1206 | return 1; | ||
| 1207 | } | ||
| 1208 | __setup("trace_event=", setup_trace_event); | ||
| 1209 | |||
| 1120 | static __init int event_trace_init(void) | 1210 | static __init int event_trace_init(void) |
| 1121 | { | 1211 | { |
| 1122 | struct ftrace_event_call *call; | 1212 | struct ftrace_event_call *call; |
| @@ -1124,6 +1214,8 @@ static __init int event_trace_init(void) | |||
| 1124 | struct dentry *entry; | 1214 | struct dentry *entry; |
| 1125 | struct dentry *d_events; | 1215 | struct dentry *d_events; |
| 1126 | int ret; | 1216 | int ret; |
| 1217 | char *buf = bootup_event_buf; | ||
| 1218 | char *token; | ||
| 1127 | 1219 | ||
| 1128 | d_tracer = tracing_init_dentry(); | 1220 | d_tracer = tracing_init_dentry(); |
| 1129 | if (!d_tracer) | 1221 | if (!d_tracer) |
| @@ -1163,12 +1255,34 @@ static __init int event_trace_init(void) | |||
| 1163 | /* The linker may leave blanks */ | 1255 | /* The linker may leave blanks */ |
| 1164 | if (!call->name) | 1256 | if (!call->name) |
| 1165 | continue; | 1257 | continue; |
| 1258 | if (call->raw_init) { | ||
| 1259 | ret = call->raw_init(); | ||
| 1260 | if (ret < 0) { | ||
| 1261 | if (ret != -ENOSYS) | ||
| 1262 | pr_warning("Could not initialize trace " | ||
| 1263 | "point events/%s\n", call->name); | ||
| 1264 | continue; | ||
| 1265 | } | ||
| 1266 | } | ||
| 1166 | list_add(&call->list, &ftrace_events); | 1267 | list_add(&call->list, &ftrace_events); |
| 1167 | event_create_dir(call, d_events, &ftrace_event_id_fops, | 1268 | event_create_dir(call, d_events, &ftrace_event_id_fops, |
| 1168 | &ftrace_enable_fops, &ftrace_event_filter_fops, | 1269 | &ftrace_enable_fops, &ftrace_event_filter_fops, |
| 1169 | &ftrace_event_format_fops); | 1270 | &ftrace_event_format_fops); |
| 1170 | } | 1271 | } |
| 1171 | 1272 | ||
| 1273 | while (true) { | ||
| 1274 | token = strsep(&buf, ","); | ||
| 1275 | |||
| 1276 | if (!token) | ||
| 1277 | break; | ||
| 1278 | if (!*token) | ||
| 1279 | continue; | ||
| 1280 | |||
| 1281 | ret = ftrace_set_clr_event(token, 1); | ||
| 1282 | if (ret) | ||
| 1283 | pr_warning("Failed to enable trace event: %s\n", token); | ||
| 1284 | } | ||
| 1285 | |||
| 1172 | ret = register_module_notifier(&trace_module_nb); | 1286 | ret = register_module_notifier(&trace_module_nb); |
| 1173 | if (ret) | 1287 | if (ret) |
| 1174 | pr_warning("Failed to register trace events module notifier\n"); | 1288 | pr_warning("Failed to register trace events module notifier\n"); |
| @@ -1324,6 +1438,7 @@ static void | |||
| 1324 | function_test_events_call(unsigned long ip, unsigned long parent_ip) | 1438 | function_test_events_call(unsigned long ip, unsigned long parent_ip) |
| 1325 | { | 1439 | { |
| 1326 | struct ring_buffer_event *event; | 1440 | struct ring_buffer_event *event; |
| 1441 | struct ring_buffer *buffer; | ||
| 1327 | struct ftrace_entry *entry; | 1442 | struct ftrace_entry *entry; |
| 1328 | unsigned long flags; | 1443 | unsigned long flags; |
| 1329 | long disabled; | 1444 | long disabled; |
| @@ -1341,7 +1456,8 @@ function_test_events_call(unsigned long ip, unsigned long parent_ip) | |||
| 1341 | 1456 | ||
| 1342 | local_save_flags(flags); | 1457 | local_save_flags(flags); |
| 1343 | 1458 | ||
| 1344 | event = trace_current_buffer_lock_reserve(TRACE_FN, sizeof(*entry), | 1459 | event = trace_current_buffer_lock_reserve(&buffer, |
| 1460 | TRACE_FN, sizeof(*entry), | ||
| 1345 | flags, pc); | 1461 | flags, pc); |
| 1346 | if (!event) | 1462 | if (!event) |
| 1347 | goto out; | 1463 | goto out; |
| @@ -1349,7 +1465,7 @@ function_test_events_call(unsigned long ip, unsigned long parent_ip) | |||
| 1349 | entry->ip = ip; | 1465 | entry->ip = ip; |
| 1350 | entry->parent_ip = parent_ip; | 1466 | entry->parent_ip = parent_ip; |
| 1351 | 1467 | ||
| 1352 | trace_nowake_buffer_unlock_commit(event, flags, pc); | 1468 | trace_nowake_buffer_unlock_commit(buffer, event, flags, pc); |
| 1353 | 1469 | ||
| 1354 | out: | 1470 | out: |
| 1355 | atomic_dec(&per_cpu(test_event_disable, cpu)); | 1471 | atomic_dec(&per_cpu(test_event_disable, cpu)); |
| @@ -1376,10 +1492,10 @@ static __init void event_trace_self_test_with_function(void) | |||
| 1376 | 1492 | ||
| 1377 | static __init int event_trace_self_tests_init(void) | 1493 | static __init int event_trace_self_tests_init(void) |
| 1378 | { | 1494 | { |
| 1379 | 1495 | if (!tracing_selftest_disabled) { | |
| 1380 | event_trace_self_tests(); | 1496 | event_trace_self_tests(); |
| 1381 | 1497 | event_trace_self_test_with_function(); | |
| 1382 | event_trace_self_test_with_function(); | 1498 | } |
| 1383 | 1499 | ||
| 1384 | return 0; | 1500 | return 0; |
| 1385 | } | 1501 | } |
