diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/trace/trace.c | 178 |
1 files changed, 154 insertions, 24 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 135af323608b..e29edee1542a 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -1285,22 +1285,71 @@ void tracing_reset_all_online_cpus(void) | |||
1285 | } | 1285 | } |
1286 | } | 1286 | } |
1287 | 1287 | ||
1288 | #define SAVED_CMDLINES 128 | 1288 | #define SAVED_CMDLINES_DEFAULT 128 |
1289 | #define NO_CMDLINE_MAP UINT_MAX | 1289 | #define NO_CMDLINE_MAP UINT_MAX |
1290 | static unsigned map_pid_to_cmdline[PID_MAX_DEFAULT+1]; | ||
1291 | static unsigned map_cmdline_to_pid[SAVED_CMDLINES]; | ||
1292 | static char saved_cmdlines[SAVED_CMDLINES][TASK_COMM_LEN]; | ||
1293 | static int cmdline_idx; | ||
1294 | static arch_spinlock_t trace_cmdline_lock = __ARCH_SPIN_LOCK_UNLOCKED; | 1290 | static arch_spinlock_t trace_cmdline_lock = __ARCH_SPIN_LOCK_UNLOCKED; |
1291 | struct saved_cmdlines_buffer { | ||
1292 | unsigned map_pid_to_cmdline[PID_MAX_DEFAULT+1]; | ||
1293 | unsigned *map_cmdline_to_pid; | ||
1294 | unsigned cmdline_num; | ||
1295 | int cmdline_idx; | ||
1296 | char *saved_cmdlines; | ||
1297 | }; | ||
1298 | static struct saved_cmdlines_buffer *savedcmd; | ||
1295 | 1299 | ||
1296 | /* temporary disable recording */ | 1300 | /* temporary disable recording */ |
1297 | static atomic_t trace_record_cmdline_disabled __read_mostly; | 1301 | static atomic_t trace_record_cmdline_disabled __read_mostly; |
1298 | 1302 | ||
1299 | static void trace_init_cmdlines(void) | 1303 | static inline char *get_saved_cmdlines(int idx) |
1304 | { | ||
1305 | return &savedcmd->saved_cmdlines[idx * TASK_COMM_LEN]; | ||
1306 | } | ||
1307 | |||
1308 | static inline void set_cmdline(int idx, const char *cmdline) | ||
1300 | { | 1309 | { |
1301 | memset(&map_pid_to_cmdline, NO_CMDLINE_MAP, sizeof(map_pid_to_cmdline)); | 1310 | memcpy(get_saved_cmdlines(idx), cmdline, TASK_COMM_LEN); |
1302 | memset(&map_cmdline_to_pid, NO_CMDLINE_MAP, sizeof(map_cmdline_to_pid)); | 1311 | } |
1303 | cmdline_idx = 0; | 1312 | |
1313 | static int allocate_cmdlines_buffer(unsigned int val, | ||
1314 | struct saved_cmdlines_buffer *s) | ||
1315 | { | ||
1316 | s->map_cmdline_to_pid = kmalloc(val * sizeof(*s->map_cmdline_to_pid), | ||
1317 | GFP_KERNEL); | ||
1318 | if (!s->map_cmdline_to_pid) | ||
1319 | return -ENOMEM; | ||
1320 | |||
1321 | s->saved_cmdlines = kmalloc(val * TASK_COMM_LEN, GFP_KERNEL); | ||
1322 | if (!s->saved_cmdlines) { | ||
1323 | kfree(s->map_cmdline_to_pid); | ||
1324 | return -ENOMEM; | ||
1325 | } | ||
1326 | |||
1327 | s->cmdline_idx = 0; | ||
1328 | s->cmdline_num = val; | ||
1329 | memset(&s->map_pid_to_cmdline, NO_CMDLINE_MAP, | ||
1330 | sizeof(s->map_pid_to_cmdline)); | ||
1331 | memset(s->map_cmdline_to_pid, NO_CMDLINE_MAP, | ||
1332 | val * sizeof(*s->map_cmdline_to_pid)); | ||
1333 | |||
1334 | return 0; | ||
1335 | } | ||
1336 | |||
1337 | static int trace_create_savedcmd(void) | ||
1338 | { | ||
1339 | int ret; | ||
1340 | |||
1341 | savedcmd = kmalloc(sizeof(struct saved_cmdlines_buffer), GFP_KERNEL); | ||
1342 | if (!savedcmd) | ||
1343 | return -ENOMEM; | ||
1344 | |||
1345 | ret = allocate_cmdlines_buffer(SAVED_CMDLINES_DEFAULT, savedcmd); | ||
1346 | if (ret < 0) { | ||
1347 | kfree(savedcmd); | ||
1348 | savedcmd = NULL; | ||
1349 | return -ENOMEM; | ||
1350 | } | ||
1351 | |||
1352 | return 0; | ||
1304 | } | 1353 | } |
1305 | 1354 | ||
1306 | int is_tracing_stopped(void) | 1355 | int is_tracing_stopped(void) |
@@ -1457,9 +1506,9 @@ static int trace_save_cmdline(struct task_struct *tsk) | |||
1457 | if (!arch_spin_trylock(&trace_cmdline_lock)) | 1506 | if (!arch_spin_trylock(&trace_cmdline_lock)) |
1458 | return 0; | 1507 | return 0; |
1459 | 1508 | ||
1460 | idx = map_pid_to_cmdline[tsk->pid]; | 1509 | idx = savedcmd->map_pid_to_cmdline[tsk->pid]; |
1461 | if (idx == NO_CMDLINE_MAP) { | 1510 | if (idx == NO_CMDLINE_MAP) { |
1462 | idx = (cmdline_idx + 1) % SAVED_CMDLINES; | 1511 | idx = (savedcmd->cmdline_idx + 1) % savedcmd->cmdline_num; |
1463 | 1512 | ||
1464 | /* | 1513 | /* |
1465 | * Check whether the cmdline buffer at idx has a pid | 1514 | * Check whether the cmdline buffer at idx has a pid |
@@ -1467,17 +1516,17 @@ static int trace_save_cmdline(struct task_struct *tsk) | |||
1467 | * need to clear the map_pid_to_cmdline. Otherwise we | 1516 | * need to clear the map_pid_to_cmdline. Otherwise we |
1468 | * would read the new comm for the old pid. | 1517 | * would read the new comm for the old pid. |
1469 | */ | 1518 | */ |
1470 | pid = map_cmdline_to_pid[idx]; | 1519 | pid = savedcmd->map_cmdline_to_pid[idx]; |
1471 | if (pid != NO_CMDLINE_MAP) | 1520 | if (pid != NO_CMDLINE_MAP) |
1472 | map_pid_to_cmdline[pid] = NO_CMDLINE_MAP; | 1521 | savedcmd->map_pid_to_cmdline[pid] = NO_CMDLINE_MAP; |
1473 | 1522 | ||
1474 | map_cmdline_to_pid[idx] = tsk->pid; | 1523 | savedcmd->map_cmdline_to_pid[idx] = tsk->pid; |
1475 | map_pid_to_cmdline[tsk->pid] = idx; | 1524 | savedcmd->map_pid_to_cmdline[tsk->pid] = idx; |
1476 | 1525 | ||
1477 | cmdline_idx = idx; | 1526 | savedcmd->cmdline_idx = idx; |
1478 | } | 1527 | } |
1479 | 1528 | ||
1480 | memcpy(&saved_cmdlines[idx], tsk->comm, TASK_COMM_LEN); | 1529 | set_cmdline(idx, tsk->comm); |
1481 | 1530 | ||
1482 | arch_spin_unlock(&trace_cmdline_lock); | 1531 | arch_spin_unlock(&trace_cmdline_lock); |
1483 | 1532 | ||
@@ -1503,9 +1552,9 @@ static void __trace_find_cmdline(int pid, char comm[]) | |||
1503 | return; | 1552 | return; |
1504 | } | 1553 | } |
1505 | 1554 | ||
1506 | map = map_pid_to_cmdline[pid]; | 1555 | map = savedcmd->map_pid_to_cmdline[pid]; |
1507 | if (map != NO_CMDLINE_MAP) | 1556 | if (map != NO_CMDLINE_MAP) |
1508 | strcpy(comm, saved_cmdlines[map]); | 1557 | strcpy(comm, get_saved_cmdlines(map)); |
1509 | else | 1558 | else |
1510 | strcpy(comm, "<...>"); | 1559 | strcpy(comm, "<...>"); |
1511 | } | 1560 | } |
@@ -3593,6 +3642,7 @@ static const char readme_msg[] = | |||
3593 | " trace_options\t\t- Set format or modify how tracing happens\n" | 3642 | " trace_options\t\t- Set format or modify how tracing happens\n" |
3594 | "\t\t\t Disable an option by adding a suffix 'no' to the\n" | 3643 | "\t\t\t Disable an option by adding a suffix 'no' to the\n" |
3595 | "\t\t\t option name\n" | 3644 | "\t\t\t option name\n" |
3645 | " saved_cmdlines_size\t- echo command number in here to store comm-pid list\n" | ||
3596 | #ifdef CONFIG_DYNAMIC_FTRACE | 3646 | #ifdef CONFIG_DYNAMIC_FTRACE |
3597 | "\n available_filter_functions - list of functions that can be filtered on\n" | 3647 | "\n available_filter_functions - list of functions that can be filtered on\n" |
3598 | " set_ftrace_filter\t- echo function name in here to only trace these\n" | 3648 | " set_ftrace_filter\t- echo function name in here to only trace these\n" |
@@ -3715,7 +3765,8 @@ static void *saved_cmdlines_next(struct seq_file *m, void *v, loff_t *pos) | |||
3715 | 3765 | ||
3716 | (*pos)++; | 3766 | (*pos)++; |
3717 | 3767 | ||
3718 | for (; ptr < &map_cmdline_to_pid[SAVED_CMDLINES]; ptr++) { | 3768 | for (; ptr < &savedcmd->map_cmdline_to_pid[savedcmd->cmdline_num]; |
3769 | ptr++) { | ||
3719 | if (*ptr == -1 || *ptr == NO_CMDLINE_MAP) | 3770 | if (*ptr == -1 || *ptr == NO_CMDLINE_MAP) |
3720 | continue; | 3771 | continue; |
3721 | 3772 | ||
@@ -3733,7 +3784,7 @@ static void *saved_cmdlines_start(struct seq_file *m, loff_t *pos) | |||
3733 | preempt_disable(); | 3784 | preempt_disable(); |
3734 | arch_spin_lock(&trace_cmdline_lock); | 3785 | arch_spin_lock(&trace_cmdline_lock); |
3735 | 3786 | ||
3736 | v = &map_cmdline_to_pid[0]; | 3787 | v = &savedcmd->map_cmdline_to_pid[0]; |
3737 | while (l <= *pos) { | 3788 | while (l <= *pos) { |
3738 | v = saved_cmdlines_next(m, v, &l); | 3789 | v = saved_cmdlines_next(m, v, &l); |
3739 | if (!v) | 3790 | if (!v) |
@@ -3782,6 +3833,79 @@ static const struct file_operations tracing_saved_cmdlines_fops = { | |||
3782 | }; | 3833 | }; |
3783 | 3834 | ||
3784 | static ssize_t | 3835 | static ssize_t |
3836 | tracing_saved_cmdlines_size_read(struct file *filp, char __user *ubuf, | ||
3837 | size_t cnt, loff_t *ppos) | ||
3838 | { | ||
3839 | char buf[64]; | ||
3840 | int r; | ||
3841 | |||
3842 | arch_spin_lock(&trace_cmdline_lock); | ||
3843 | r = sprintf(buf, "%u\n", savedcmd->cmdline_num); | ||
3844 | arch_spin_unlock(&trace_cmdline_lock); | ||
3845 | |||
3846 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); | ||
3847 | } | ||
3848 | |||
3849 | static void free_saved_cmdlines_buffer(struct saved_cmdlines_buffer *s) | ||
3850 | { | ||
3851 | kfree(s->saved_cmdlines); | ||
3852 | kfree(s->map_cmdline_to_pid); | ||
3853 | kfree(s); | ||
3854 | } | ||
3855 | |||
3856 | static int tracing_resize_saved_cmdlines(unsigned int val) | ||
3857 | { | ||
3858 | struct saved_cmdlines_buffer *s, *savedcmd_temp; | ||
3859 | |||
3860 | s = kmalloc(sizeof(struct saved_cmdlines_buffer), GFP_KERNEL); | ||
3861 | if (!s) | ||
3862 | return -ENOMEM; | ||
3863 | |||
3864 | if (allocate_cmdlines_buffer(val, s) < 0) { | ||
3865 | kfree(s); | ||
3866 | return -ENOMEM; | ||
3867 | } | ||
3868 | |||
3869 | arch_spin_lock(&trace_cmdline_lock); | ||
3870 | savedcmd_temp = savedcmd; | ||
3871 | savedcmd = s; | ||
3872 | arch_spin_unlock(&trace_cmdline_lock); | ||
3873 | free_saved_cmdlines_buffer(savedcmd_temp); | ||
3874 | |||
3875 | return 0; | ||
3876 | } | ||
3877 | |||
3878 | static ssize_t | ||
3879 | tracing_saved_cmdlines_size_write(struct file *filp, const char __user *ubuf, | ||
3880 | size_t cnt, loff_t *ppos) | ||
3881 | { | ||
3882 | unsigned long val; | ||
3883 | int ret; | ||
3884 | |||
3885 | ret = kstrtoul_from_user(ubuf, cnt, 10, &val); | ||
3886 | if (ret) | ||
3887 | return ret; | ||
3888 | |||
3889 | /* must have at least 1 entry or less than PID_MAX_DEFAULT */ | ||
3890 | if (!val || val > PID_MAX_DEFAULT) | ||
3891 | return -EINVAL; | ||
3892 | |||
3893 | ret = tracing_resize_saved_cmdlines((unsigned int)val); | ||
3894 | if (ret < 0) | ||
3895 | return ret; | ||
3896 | |||
3897 | *ppos += cnt; | ||
3898 | |||
3899 | return cnt; | ||
3900 | } | ||
3901 | |||
3902 | static const struct file_operations tracing_saved_cmdlines_size_fops = { | ||
3903 | .open = tracing_open_generic, | ||
3904 | .read = tracing_saved_cmdlines_size_read, | ||
3905 | .write = tracing_saved_cmdlines_size_write, | ||
3906 | }; | ||
3907 | |||
3908 | static ssize_t | ||
3785 | tracing_set_trace_read(struct file *filp, char __user *ubuf, | 3909 | tracing_set_trace_read(struct file *filp, char __user *ubuf, |
3786 | size_t cnt, loff_t *ppos) | 3910 | size_t cnt, loff_t *ppos) |
3787 | { | 3911 | { |
@@ -6375,6 +6499,9 @@ static __init int tracer_init_debugfs(void) | |||
6375 | trace_create_file("saved_cmdlines", 0444, d_tracer, | 6499 | trace_create_file("saved_cmdlines", 0444, d_tracer, |
6376 | NULL, &tracing_saved_cmdlines_fops); | 6500 | NULL, &tracing_saved_cmdlines_fops); |
6377 | 6501 | ||
6502 | trace_create_file("saved_cmdlines_size", 0644, d_tracer, | ||
6503 | NULL, &tracing_saved_cmdlines_size_fops); | ||
6504 | |||
6378 | #ifdef CONFIG_DYNAMIC_FTRACE | 6505 | #ifdef CONFIG_DYNAMIC_FTRACE |
6379 | trace_create_file("dyn_ftrace_total_info", 0444, d_tracer, | 6506 | trace_create_file("dyn_ftrace_total_info", 0444, d_tracer, |
6380 | &ftrace_update_tot_cnt, &tracing_dyn_info_fops); | 6507 | &ftrace_update_tot_cnt, &tracing_dyn_info_fops); |
@@ -6611,18 +6738,19 @@ __init static int tracer_alloc_buffers(void) | |||
6611 | if (!temp_buffer) | 6738 | if (!temp_buffer) |
6612 | goto out_free_cpumask; | 6739 | goto out_free_cpumask; |
6613 | 6740 | ||
6741 | if (trace_create_savedcmd() < 0) | ||
6742 | goto out_free_temp_buffer; | ||
6743 | |||
6614 | /* TODO: make the number of buffers hot pluggable with CPUS */ | 6744 | /* TODO: make the number of buffers hot pluggable with CPUS */ |
6615 | if (allocate_trace_buffers(&global_trace, ring_buf_size) < 0) { | 6745 | if (allocate_trace_buffers(&global_trace, ring_buf_size) < 0) { |
6616 | printk(KERN_ERR "tracer: failed to allocate ring buffer!\n"); | 6746 | printk(KERN_ERR "tracer: failed to allocate ring buffer!\n"); |
6617 | WARN_ON(1); | 6747 | WARN_ON(1); |
6618 | goto out_free_temp_buffer; | 6748 | goto out_free_savedcmd; |
6619 | } | 6749 | } |
6620 | 6750 | ||
6621 | if (global_trace.buffer_disabled) | 6751 | if (global_trace.buffer_disabled) |
6622 | tracing_off(); | 6752 | tracing_off(); |
6623 | 6753 | ||
6624 | trace_init_cmdlines(); | ||
6625 | |||
6626 | if (trace_boot_clock) { | 6754 | if (trace_boot_clock) { |
6627 | ret = tracing_set_clock(&global_trace, trace_boot_clock); | 6755 | ret = tracing_set_clock(&global_trace, trace_boot_clock); |
6628 | if (ret < 0) | 6756 | if (ret < 0) |
@@ -6668,6 +6796,8 @@ __init static int tracer_alloc_buffers(void) | |||
6668 | 6796 | ||
6669 | return 0; | 6797 | return 0; |
6670 | 6798 | ||
6799 | out_free_savedcmd: | ||
6800 | free_saved_cmdlines_buffer(savedcmd); | ||
6671 | out_free_temp_buffer: | 6801 | out_free_temp_buffer: |
6672 | ring_buffer_free(temp_buffer); | 6802 | ring_buffer_free(temp_buffer); |
6673 | out_free_cpumask: | 6803 | out_free_cpumask: |