Merge branch 'tracing-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6] / kernel / trace / trace_sched_switch.c
1 /*
2  * trace context switch
3  *
4  * Copyright (C) 2007 Steven Rostedt <srostedt@redhat.com>
5  *
6  */
7 #include <linux/module.h>
8 #include <linux/fs.h>
9 #include <linux/debugfs.h>
10 #include <linux/kallsyms.h>
11 #include <linux/uaccess.h>
12 #include <linux/ftrace.h>
13 #include <trace/sched.h>
14
15 #include "trace.h"
16
17 static struct trace_array       *ctx_trace;
18 static int __read_mostly        tracer_enabled;
19 static int                      sched_ref;
20 static DEFINE_MUTEX(sched_register_mutex);
21 static int                      sched_stopped;
22
23 static void
24 probe_sched_switch(struct rq *__rq, struct task_struct *prev,
25                         struct task_struct *next)
26 {
27         struct trace_array_cpu *data;
28         unsigned long flags;
29         int cpu;
30         int pc;
31
32         if (!sched_ref || sched_stopped)
33                 return;
34
35         tracing_record_cmdline(prev);
36         tracing_record_cmdline(next);
37
38         if (!tracer_enabled)
39                 return;
40
41         pc = preempt_count();
42         local_irq_save(flags);
43         cpu = raw_smp_processor_id();
44         data = ctx_trace->data[cpu];
45
46         if (likely(!atomic_read(&data->disabled)))
47                 tracing_sched_switch_trace(ctx_trace, prev, next, flags, pc);
48
49         local_irq_restore(flags);
50 }
51
52 static void
53 probe_sched_wakeup(struct rq *__rq, struct task_struct *wakee, int success)
54 {
55         struct trace_array_cpu *data;
56         unsigned long flags;
57         int cpu, pc;
58
59         if (!likely(tracer_enabled))
60                 return;
61
62         pc = preempt_count();
63         tracing_record_cmdline(current);
64
65         local_irq_save(flags);
66         cpu = raw_smp_processor_id();
67         data = ctx_trace->data[cpu];
68
69         if (likely(!atomic_read(&data->disabled)))
70                 tracing_sched_wakeup_trace(ctx_trace, wakee, current,
71                                            flags, pc);
72
73         local_irq_restore(flags);
74 }
75
76 static int tracing_sched_register(void)
77 {
78         int ret;
79
80         ret = register_trace_sched_wakeup(probe_sched_wakeup);
81         if (ret) {
82                 pr_info("wakeup trace: Couldn't activate tracepoint"
83                         " probe to kernel_sched_wakeup\n");
84                 return ret;
85         }
86
87         ret = register_trace_sched_wakeup_new(probe_sched_wakeup);
88         if (ret) {
89                 pr_info("wakeup trace: Couldn't activate tracepoint"
90                         " probe to kernel_sched_wakeup_new\n");
91                 goto fail_deprobe;
92         }
93
94         ret = register_trace_sched_switch(probe_sched_switch);
95         if (ret) {
96                 pr_info("sched trace: Couldn't activate tracepoint"
97                         " probe to kernel_sched_switch\n");
98                 goto fail_deprobe_wake_new;
99         }
100
101         return ret;
102 fail_deprobe_wake_new:
103         unregister_trace_sched_wakeup_new(probe_sched_wakeup);
104 fail_deprobe:
105         unregister_trace_sched_wakeup(probe_sched_wakeup);
106         return ret;
107 }
108
109 static void tracing_sched_unregister(void)
110 {
111         unregister_trace_sched_switch(probe_sched_switch);
112         unregister_trace_sched_wakeup_new(probe_sched_wakeup);
113         unregister_trace_sched_wakeup(probe_sched_wakeup);
114 }
115
116 static void tracing_start_sched_switch(void)
117 {
118         mutex_lock(&sched_register_mutex);
119         if (!(sched_ref++))
120                 tracing_sched_register();
121         mutex_unlock(&sched_register_mutex);
122 }
123
124 static void tracing_stop_sched_switch(void)
125 {
126         mutex_lock(&sched_register_mutex);
127         if (!(--sched_ref))
128                 tracing_sched_unregister();
129         mutex_unlock(&sched_register_mutex);
130 }
131
132 void tracing_start_cmdline_record(void)
133 {
134         tracing_start_sched_switch();
135 }
136
137 void tracing_stop_cmdline_record(void)
138 {
139         tracing_stop_sched_switch();
140 }
141
142 /**
143  * tracing_start_sched_switch_record - start tracing context switches
144  *
145  * Turns on context switch tracing for a tracer.
146  */
147 void tracing_start_sched_switch_record(void)
148 {
149         if (unlikely(!ctx_trace)) {
150                 WARN_ON(1);
151                 return;
152         }
153
154         tracing_start_sched_switch();
155
156         mutex_lock(&sched_register_mutex);
157         tracer_enabled++;
158         mutex_unlock(&sched_register_mutex);
159 }
160
161 /**
162  * tracing_stop_sched_switch_record - start tracing context switches
163  *
164  * Turns off context switch tracing for a tracer.
165  */
166 void tracing_stop_sched_switch_record(void)
167 {
168         mutex_lock(&sched_register_mutex);
169         tracer_enabled--;
170         WARN_ON(tracer_enabled < 0);
171         mutex_unlock(&sched_register_mutex);
172
173         tracing_stop_sched_switch();
174 }
175
176 /**
177  * tracing_sched_switch_assign_trace - assign a trace array for ctx switch
178  * @tr: trace array pointer to assign
179  *
180  * Some tracers might want to record the context switches in their
181  * trace. This function lets those tracers assign the trace array
182  * to use.
183  */
184 void tracing_sched_switch_assign_trace(struct trace_array *tr)
185 {
186         ctx_trace = tr;
187 }
188
189 static void stop_sched_trace(struct trace_array *tr)
190 {
191         tracing_stop_sched_switch_record();
192 }
193
194 static int sched_switch_trace_init(struct trace_array *tr)
195 {
196         ctx_trace = tr;
197         tracing_reset_online_cpus(tr);
198         tracing_start_sched_switch_record();
199         return 0;
200 }
201
202 static void sched_switch_trace_reset(struct trace_array *tr)
203 {
204         if (sched_ref)
205                 stop_sched_trace(tr);
206 }
207
208 static void sched_switch_trace_start(struct trace_array *tr)
209 {
210         sched_stopped = 0;
211 }
212
213 static void sched_switch_trace_stop(struct trace_array *tr)
214 {
215         sched_stopped = 1;
216 }
217
218 static struct tracer sched_switch_trace __read_mostly =
219 {
220         .name           = "sched_switch",
221         .init           = sched_switch_trace_init,
222         .reset          = sched_switch_trace_reset,
223         .start          = sched_switch_trace_start,
224         .stop           = sched_switch_trace_stop,
225         .wait_pipe      = poll_wait_pipe,
226 #ifdef CONFIG_FTRACE_SELFTEST
227         .selftest    = trace_selftest_startup_sched_switch,
228 #endif
229 };
230
231 __init static int init_sched_switch_trace(void)
232 {
233         return register_tracer(&sched_switch_trace);
234 }
235 device_initcall(init_sched_switch_trace);
236