diff options
| author | jolsa@redhat.com <jolsa@redhat.com> | 2009-10-13 16:33:52 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2009-10-14 02:13:53 -0400 |
| commit | 756d17ee7ee4fbc8238bdf97100af63e6ac441ef (patch) | |
| tree | d0b794f4964be5689fc1bd64302969405000e18b | |
| parent | 194ec34184869f0de1cf255c924fc5299e1b3d27 (diff) | |
tracing: Support multiple pids in set_pid_ftrace file
Adding the possibility to set more than 1 pid in the set_pid_ftrace
file, thus allowing to trace more than 1 independent processes.
Usage:
sh-4.0# echo 284 > ./set_ftrace_pid
sh-4.0# cat ./set_ftrace_pid
284
sh-4.0# echo 1 >> ./set_ftrace_pid
sh-4.0# echo 0 >> ./set_ftrace_pid
sh-4.0# cat ./set_ftrace_pid
swapper tasks
1
284
sh-4.0# echo 4 > ./set_ftrace_pid
sh-4.0# cat ./set_ftrace_pid
4
sh-4.0# echo > ./set_ftrace_pid
sh-4.0# cat ./set_ftrace_pid
no pid
sh-4.0#
Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <20091013203425.565454612@goodmis.org>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
| -rw-r--r-- | kernel/trace/ftrace.c | 234 | ||||
| -rw-r--r-- | kernel/trace/trace.h | 4 |
2 files changed, 167 insertions, 71 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 45c965919cff..0c799d1af702 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
| @@ -60,6 +60,13 @@ static int last_ftrace_enabled; | |||
| 60 | /* Quick disabling of function tracer. */ | 60 | /* Quick disabling of function tracer. */ |
| 61 | int function_trace_stop; | 61 | int function_trace_stop; |
| 62 | 62 | ||
| 63 | /* List for set_ftrace_pid's pids. */ | ||
| 64 | LIST_HEAD(ftrace_pids); | ||
| 65 | struct ftrace_pid { | ||
| 66 | struct list_head list; | ||
| 67 | struct pid *pid; | ||
| 68 | }; | ||
| 69 | |||
| 63 | /* | 70 | /* |
| 64 | * ftrace_disabled is set when an anomaly is discovered. | 71 | * ftrace_disabled is set when an anomaly is discovered. |
| 65 | * ftrace_disabled is much stronger than ftrace_enabled. | 72 | * ftrace_disabled is much stronger than ftrace_enabled. |
| @@ -159,7 +166,7 @@ static int __register_ftrace_function(struct ftrace_ops *ops) | |||
| 159 | else | 166 | else |
| 160 | func = ftrace_list_func; | 167 | func = ftrace_list_func; |
| 161 | 168 | ||
| 162 | if (ftrace_pid_trace) { | 169 | if (!list_empty(&ftrace_pids)) { |
| 163 | set_ftrace_pid_function(func); | 170 | set_ftrace_pid_function(func); |
| 164 | func = ftrace_pid_func; | 171 | func = ftrace_pid_func; |
| 165 | } | 172 | } |
| @@ -207,7 +214,7 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) | |||
| 207 | if (ftrace_list->next == &ftrace_list_end) { | 214 | if (ftrace_list->next == &ftrace_list_end) { |
| 208 | ftrace_func_t func = ftrace_list->func; | 215 | ftrace_func_t func = ftrace_list->func; |
| 209 | 216 | ||
| 210 | if (ftrace_pid_trace) { | 217 | if (!list_empty(&ftrace_pids)) { |
| 211 | set_ftrace_pid_function(func); | 218 | set_ftrace_pid_function(func); |
| 212 | func = ftrace_pid_func; | 219 | func = ftrace_pid_func; |
| 213 | } | 220 | } |
| @@ -235,7 +242,7 @@ static void ftrace_update_pid_func(void) | |||
| 235 | func = __ftrace_trace_function; | 242 | func = __ftrace_trace_function; |
| 236 | #endif | 243 | #endif |
| 237 | 244 | ||
| 238 | if (ftrace_pid_trace) { | 245 | if (!list_empty(&ftrace_pids)) { |
| 239 | set_ftrace_pid_function(func); | 246 | set_ftrace_pid_function(func); |
| 240 | func = ftrace_pid_func; | 247 | func = ftrace_pid_func; |
| 241 | } else { | 248 | } else { |
| @@ -825,8 +832,6 @@ static __init void ftrace_profile_debugfs(struct dentry *d_tracer) | |||
| 825 | } | 832 | } |
| 826 | #endif /* CONFIG_FUNCTION_PROFILER */ | 833 | #endif /* CONFIG_FUNCTION_PROFILER */ |
| 827 | 834 | ||
| 828 | /* set when tracing only a pid */ | ||
| 829 | struct pid *ftrace_pid_trace; | ||
| 830 | static struct pid * const ftrace_swapper_pid = &init_struct_pid; | 835 | static struct pid * const ftrace_swapper_pid = &init_struct_pid; |
| 831 | 836 | ||
| 832 | #ifdef CONFIG_DYNAMIC_FTRACE | 837 | #ifdef CONFIG_DYNAMIC_FTRACE |
| @@ -2758,23 +2763,6 @@ static inline void ftrace_startup_enable(int command) { } | |||
| 2758 | # define ftrace_shutdown_sysctl() do { } while (0) | 2763 | # define ftrace_shutdown_sysctl() do { } while (0) |
| 2759 | #endif /* CONFIG_DYNAMIC_FTRACE */ | 2764 | #endif /* CONFIG_DYNAMIC_FTRACE */ |
| 2760 | 2765 | ||
| 2761 | static ssize_t | ||
| 2762 | ftrace_pid_read(struct file *file, char __user *ubuf, | ||
| 2763 | size_t cnt, loff_t *ppos) | ||
| 2764 | { | ||
| 2765 | char buf[64]; | ||
| 2766 | int r; | ||
| 2767 | |||
| 2768 | if (ftrace_pid_trace == ftrace_swapper_pid) | ||
| 2769 | r = sprintf(buf, "swapper tasks\n"); | ||
| 2770 | else if (ftrace_pid_trace) | ||
| 2771 | r = sprintf(buf, "%u\n", pid_vnr(ftrace_pid_trace)); | ||
| 2772 | else | ||
| 2773 | r = sprintf(buf, "no pid\n"); | ||
| 2774 | |||
| 2775 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); | ||
| 2776 | } | ||
| 2777 | |||
| 2778 | static void clear_ftrace_swapper(void) | 2766 | static void clear_ftrace_swapper(void) |
| 2779 | { | 2767 | { |
| 2780 | struct task_struct *p; | 2768 | struct task_struct *p; |
| @@ -2825,14 +2813,12 @@ static void set_ftrace_pid(struct pid *pid) | |||
| 2825 | rcu_read_unlock(); | 2813 | rcu_read_unlock(); |
| 2826 | } | 2814 | } |
| 2827 | 2815 | ||
| 2828 | static void clear_ftrace_pid_task(struct pid **pid) | 2816 | static void clear_ftrace_pid_task(struct pid *pid) |
| 2829 | { | 2817 | { |
| 2830 | if (*pid == ftrace_swapper_pid) | 2818 | if (pid == ftrace_swapper_pid) |
| 2831 | clear_ftrace_swapper(); | 2819 | clear_ftrace_swapper(); |
| 2832 | else | 2820 | else |
| 2833 | clear_ftrace_pid(*pid); | 2821 | clear_ftrace_pid(pid); |
| 2834 | |||
| 2835 | *pid = NULL; | ||
| 2836 | } | 2822 | } |
| 2837 | 2823 | ||
| 2838 | static void set_ftrace_pid_task(struct pid *pid) | 2824 | static void set_ftrace_pid_task(struct pid *pid) |
| @@ -2843,11 +2829,140 @@ static void set_ftrace_pid_task(struct pid *pid) | |||
| 2843 | set_ftrace_pid(pid); | 2829 | set_ftrace_pid(pid); |
| 2844 | } | 2830 | } |
| 2845 | 2831 | ||
| 2832 | static int ftrace_pid_add(int p) | ||
| 2833 | { | ||
| 2834 | struct pid *pid; | ||
| 2835 | struct ftrace_pid *fpid; | ||
| 2836 | int ret = -EINVAL; | ||
| 2837 | |||
| 2838 | mutex_lock(&ftrace_lock); | ||
| 2839 | |||
| 2840 | if (!p) | ||
| 2841 | pid = ftrace_swapper_pid; | ||
| 2842 | else | ||
| 2843 | pid = find_get_pid(p); | ||
| 2844 | |||
| 2845 | if (!pid) | ||
| 2846 | goto out; | ||
| 2847 | |||
| 2848 | ret = 0; | ||
| 2849 | |||
| 2850 | list_for_each_entry(fpid, &ftrace_pids, list) | ||
| 2851 | if (fpid->pid == pid) | ||
| 2852 | goto out_put; | ||
| 2853 | |||
| 2854 | ret = -ENOMEM; | ||
| 2855 | |||
| 2856 | fpid = kmalloc(sizeof(*fpid), GFP_KERNEL); | ||
| 2857 | if (!fpid) | ||
| 2858 | goto out_put; | ||
| 2859 | |||
| 2860 | list_add(&fpid->list, &ftrace_pids); | ||
| 2861 | fpid->pid = pid; | ||
| 2862 | |||
| 2863 | set_ftrace_pid_task(pid); | ||
| 2864 | |||
| 2865 | ftrace_update_pid_func(); | ||
| 2866 | ftrace_startup_enable(0); | ||
| 2867 | |||
| 2868 | mutex_unlock(&ftrace_lock); | ||
| 2869 | return 0; | ||
| 2870 | |||
| 2871 | out_put: | ||
| 2872 | if (pid != ftrace_swapper_pid) | ||
| 2873 | put_pid(pid); | ||
| 2874 | |||
| 2875 | out: | ||
| 2876 | mutex_unlock(&ftrace_lock); | ||
| 2877 | return ret; | ||
| 2878 | } | ||
| 2879 | |||
| 2880 | static void ftrace_pid_reset(void) | ||
| 2881 | { | ||
| 2882 | struct ftrace_pid *fpid, *safe; | ||
| 2883 | |||
| 2884 | mutex_lock(&ftrace_lock); | ||
| 2885 | list_for_each_entry_safe(fpid, safe, &ftrace_pids, list) { | ||
| 2886 | struct pid *pid = fpid->pid; | ||
| 2887 | |||
| 2888 | clear_ftrace_pid_task(pid); | ||
| 2889 | |||
| 2890 | list_del(&fpid->list); | ||
| 2891 | kfree(fpid); | ||
| 2892 | } | ||
| 2893 | |||
| 2894 | ftrace_update_pid_func(); | ||
| 2895 | ftrace_startup_enable(0); | ||
| 2896 | |||
| 2897 | mutex_unlock(&ftrace_lock); | ||
| 2898 | } | ||
| 2899 | |||
| 2900 | static void *fpid_start(struct seq_file *m, loff_t *pos) | ||
| 2901 | { | ||
| 2902 | mutex_lock(&ftrace_lock); | ||
| 2903 | |||
| 2904 | if (list_empty(&ftrace_pids) && (!*pos)) | ||
| 2905 | return (void *) 1; | ||
| 2906 | |||
| 2907 | return seq_list_start(&ftrace_pids, *pos); | ||
| 2908 | } | ||
| 2909 | |||
| 2910 | static void *fpid_next(struct seq_file *m, void *v, loff_t *pos) | ||
| 2911 | { | ||
| 2912 | if (v == (void *)1) | ||
| 2913 | return NULL; | ||
| 2914 | |||
| 2915 | return seq_list_next(v, &ftrace_pids, pos); | ||
| 2916 | } | ||
| 2917 | |||
| 2918 | static void fpid_stop(struct seq_file *m, void *p) | ||
| 2919 | { | ||
| 2920 | mutex_unlock(&ftrace_lock); | ||
| 2921 | } | ||
| 2922 | |||
| 2923 | static int fpid_show(struct seq_file *m, void *v) | ||
| 2924 | { | ||
| 2925 | const struct ftrace_pid *fpid = list_entry(v, struct ftrace_pid, list); | ||
| 2926 | |||
| 2927 | if (v == (void *)1) { | ||
| 2928 | seq_printf(m, "no pid\n"); | ||
| 2929 | return 0; | ||
| 2930 | } | ||
| 2931 | |||
| 2932 | if (fpid->pid == ftrace_swapper_pid) | ||
| 2933 | seq_printf(m, "swapper tasks\n"); | ||
| 2934 | else | ||
| 2935 | seq_printf(m, "%u\n", pid_vnr(fpid->pid)); | ||
| 2936 | |||
| 2937 | return 0; | ||
| 2938 | } | ||
| 2939 | |||
| 2940 | static const struct seq_operations ftrace_pid_sops = { | ||
| 2941 | .start = fpid_start, | ||
| 2942 | .next = fpid_next, | ||
| 2943 | .stop = fpid_stop, | ||
| 2944 | .show = fpid_show, | ||
| 2945 | }; | ||
| 2946 | |||
| 2947 | static int | ||
| 2948 | ftrace_pid_open(struct inode *inode, struct file *file) | ||
| 2949 | { | ||
| 2950 | int ret = 0; | ||
| 2951 | |||
| 2952 | if ((file->f_mode & FMODE_WRITE) && | ||
| 2953 | (file->f_flags & O_TRUNC)) | ||
| 2954 | ftrace_pid_reset(); | ||
| 2955 | |||
| 2956 | if (file->f_mode & FMODE_READ) | ||
| 2957 | ret = seq_open(file, &ftrace_pid_sops); | ||
| 2958 | |||
| 2959 | return ret; | ||
| 2960 | } | ||
| 2961 | |||
| 2846 | static ssize_t | 2962 | static ssize_t |
| 2847 | ftrace_pid_write(struct file *filp, const char __user *ubuf, | 2963 | ftrace_pid_write(struct file *filp, const char __user *ubuf, |
| 2848 | size_t cnt, loff_t *ppos) | 2964 | size_t cnt, loff_t *ppos) |
| 2849 | { | 2965 | { |
| 2850 | struct pid *pid; | ||
| 2851 | char buf[64]; | 2966 | char buf[64]; |
| 2852 | long val; | 2967 | long val; |
| 2853 | int ret; | 2968 | int ret; |
| @@ -2860,57 +2975,38 @@ ftrace_pid_write(struct file *filp, const char __user *ubuf, | |||
| 2860 | 2975 | ||
| 2861 | buf[cnt] = 0; | 2976 | buf[cnt] = 0; |
| 2862 | 2977 | ||
| 2978 | /* | ||
| 2979 | * Allow "echo > set_ftrace_pid" or "echo -n '' > set_ftrace_pid" | ||
| 2980 | * to clean the filter quietly. | ||
| 2981 | */ | ||
| 2982 | strstrip(buf); | ||
| 2983 | if (strlen(buf) == 0) | ||
| 2984 | return 1; | ||
| 2985 | |||
| 2863 | ret = strict_strtol(buf, 10, &val); | 2986 | ret = strict_strtol(buf, 10, &val); |
| 2864 | if (ret < 0) | 2987 | if (ret < 0) |
| 2865 | return ret; | 2988 | return ret; |
| 2866 | 2989 | ||
| 2867 | mutex_lock(&ftrace_lock); | 2990 | ret = ftrace_pid_add(val); |
| 2868 | if (val < 0) { | ||
| 2869 | /* disable pid tracing */ | ||
| 2870 | if (!ftrace_pid_trace) | ||
| 2871 | goto out; | ||
| 2872 | |||
| 2873 | clear_ftrace_pid_task(&ftrace_pid_trace); | ||
| 2874 | |||
| 2875 | } else { | ||
| 2876 | /* swapper task is special */ | ||
| 2877 | if (!val) { | ||
| 2878 | pid = ftrace_swapper_pid; | ||
| 2879 | if (pid == ftrace_pid_trace) | ||
| 2880 | goto out; | ||
| 2881 | } else { | ||
| 2882 | pid = find_get_pid(val); | ||
| 2883 | |||
| 2884 | if (pid == ftrace_pid_trace) { | ||
| 2885 | put_pid(pid); | ||
| 2886 | goto out; | ||
| 2887 | } | ||
| 2888 | } | ||
| 2889 | |||
| 2890 | if (ftrace_pid_trace) | ||
| 2891 | clear_ftrace_pid_task(&ftrace_pid_trace); | ||
| 2892 | |||
| 2893 | if (!pid) | ||
| 2894 | goto out; | ||
| 2895 | |||
| 2896 | ftrace_pid_trace = pid; | ||
| 2897 | |||
| 2898 | set_ftrace_pid_task(ftrace_pid_trace); | ||
| 2899 | } | ||
| 2900 | 2991 | ||
| 2901 | /* update the function call */ | 2992 | return ret ? ret : cnt; |
| 2902 | ftrace_update_pid_func(); | 2993 | } |
| 2903 | ftrace_startup_enable(0); | ||
| 2904 | 2994 | ||
| 2905 | out: | 2995 | static int |
| 2906 | mutex_unlock(&ftrace_lock); | 2996 | ftrace_pid_release(struct inode *inode, struct file *file) |
| 2997 | { | ||
| 2998 | if (file->f_mode & FMODE_READ) | ||
| 2999 | seq_release(inode, file); | ||
| 2907 | 3000 | ||
| 2908 | return cnt; | 3001 | return 0; |
| 2909 | } | 3002 | } |
| 2910 | 3003 | ||
| 2911 | static const struct file_operations ftrace_pid_fops = { | 3004 | static const struct file_operations ftrace_pid_fops = { |
| 2912 | .read = ftrace_pid_read, | 3005 | .open = ftrace_pid_open, |
| 2913 | .write = ftrace_pid_write, | 3006 | .write = ftrace_pid_write, |
| 3007 | .read = seq_read, | ||
| 3008 | .llseek = seq_lseek, | ||
| 3009 | .release = ftrace_pid_release, | ||
| 2914 | }; | 3010 | }; |
| 2915 | 3011 | ||
| 2916 | static __init int ftrace_init_debugfs(void) | 3012 | static __init int ftrace_init_debugfs(void) |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index f22a7ac32380..acef8b4636f0 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
| @@ -496,12 +496,12 @@ print_graph_function(struct trace_iterator *iter) | |||
| 496 | } | 496 | } |
| 497 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | 497 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ |
| 498 | 498 | ||
| 499 | extern struct pid *ftrace_pid_trace; | 499 | extern struct list_head ftrace_pids; |
| 500 | 500 | ||
| 501 | #ifdef CONFIG_FUNCTION_TRACER | 501 | #ifdef CONFIG_FUNCTION_TRACER |
| 502 | static inline int ftrace_trace_task(struct task_struct *task) | 502 | static inline int ftrace_trace_task(struct task_struct *task) |
| 503 | { | 503 | { |
| 504 | if (!ftrace_pid_trace) | 504 | if (list_empty(&ftrace_pids)) |
| 505 | return 1; | 505 | return 1; |
| 506 | 506 | ||
| 507 | return test_tsk_trace_trace(task); | 507 | return test_tsk_trace_trace(task); |
