4 * Copyright (C) 2007 Steven Rostedt <srostedt@redhat.com>
7 #include <linux/module.h>
9 #include <linux/debugfs.h>
10 #include <linux/kallsyms.h>
11 #include <linux/uaccess.h>
12 #include <linux/marker.h>
13 #include <linux/ftrace.h>
17 static struct trace_array *ctx_trace;
18 static int __read_mostly tracer_enabled;
19 static atomic_t sched_ref;
22 sched_switch_func(void *private, void *__rq, struct task_struct *prev,
23 struct task_struct *next)
25 struct trace_array **ptr = private;
26 struct trace_array *tr = *ptr;
27 struct trace_array_cpu *data;
35 local_irq_save(flags);
36 cpu = raw_smp_processor_id();
38 disabled = atomic_inc_return(&data->disabled);
40 if (likely(disabled == 1))
41 tracing_sched_switch_trace(tr, data, prev, next, flags);
43 atomic_dec(&data->disabled);
44 local_irq_restore(flags);
48 sched_switch_callback(void *probe_data, void *call_data,
49 const char *format, va_list *args)
51 struct task_struct *prev;
52 struct task_struct *next;
55 if (!atomic_read(&sched_ref))
58 /* skip prev_pid %d next_pid %d prev_state %ld */
59 (void)va_arg(*args, int);
60 (void)va_arg(*args, int);
61 (void)va_arg(*args, long);
62 __rq = va_arg(*args, typeof(__rq));
63 prev = va_arg(*args, typeof(prev));
64 next = va_arg(*args, typeof(next));
66 tracing_record_cmdline(prev);
69 * If tracer_switch_func only points to the local
70 * switch func, it still needs the ptr passed to it.
72 sched_switch_func(probe_data, __rq, prev, next);
76 wakeup_func(void *private, void *__rq, struct task_struct *wakee, struct
79 struct trace_array **ptr = private;
80 struct trace_array *tr = *ptr;
81 struct trace_array_cpu *data;
89 tracing_record_cmdline(curr);
91 local_irq_save(flags);
92 cpu = raw_smp_processor_id();
94 disabled = atomic_inc_return(&data->disabled);
96 if (likely(disabled == 1))
97 tracing_sched_wakeup_trace(tr, data, wakee, curr, flags);
99 atomic_dec(&data->disabled);
100 local_irq_restore(flags);
104 wake_up_callback(void *probe_data, void *call_data,
105 const char *format, va_list *args)
107 struct task_struct *curr;
108 struct task_struct *task;
111 if (likely(!tracer_enabled))
114 /* Skip pid %d state %ld */
115 (void)va_arg(*args, int);
116 (void)va_arg(*args, long);
117 /* now get the meat: "rq %p task %p rq->curr %p" */
118 __rq = va_arg(*args, typeof(__rq));
119 task = va_arg(*args, typeof(task));
120 curr = va_arg(*args, typeof(curr));
122 tracing_record_cmdline(task);
123 tracing_record_cmdline(curr);
125 wakeup_func(probe_data, __rq, task, curr);
128 static void sched_switch_reset(struct trace_array *tr)
132 tr->time_start = ftrace_now(tr->cpu);
134 for_each_online_cpu(cpu)
135 tracing_reset(tr->data[cpu]);
138 static int tracing_sched_register(void)
142 ret = marker_probe_register("kernel_sched_wakeup",
143 "pid %d state %ld ## rq %p task %p rq->curr %p",
147 pr_info("wakeup trace: Couldn't add marker"
148 " probe to kernel_sched_wakeup\n");
152 ret = marker_probe_register("kernel_sched_wakeup_new",
153 "pid %d state %ld ## rq %p task %p rq->curr %p",
157 pr_info("wakeup trace: Couldn't add marker"
158 " probe to kernel_sched_wakeup_new\n");
162 ret = marker_probe_register("kernel_sched_schedule",
163 "prev_pid %d next_pid %d prev_state %ld "
164 "## rq %p prev %p next %p",
165 sched_switch_callback,
168 pr_info("sched trace: Couldn't add marker"
169 " probe to kernel_sched_schedule\n");
170 goto fail_deprobe_wake_new;
174 fail_deprobe_wake_new:
175 marker_probe_unregister("kernel_sched_wakeup_new",
179 marker_probe_unregister("kernel_sched_wakeup",
185 static void tracing_sched_unregister(void)
187 marker_probe_unregister("kernel_sched_schedule",
188 sched_switch_callback,
190 marker_probe_unregister("kernel_sched_wakeup_new",
193 marker_probe_unregister("kernel_sched_wakeup",
198 void tracing_start_sched_switch(void)
202 ref = atomic_inc_return(&sched_ref);
204 tracing_sched_register();
207 void tracing_stop_sched_switch(void)
211 ref = atomic_dec_and_test(&sched_ref);
213 tracing_sched_unregister();
216 static void start_sched_trace(struct trace_array *tr)
218 sched_switch_reset(tr);
219 atomic_inc(&trace_record_cmdline_enabled);
221 tracing_start_sched_switch();
224 static void stop_sched_trace(struct trace_array *tr)
226 tracing_stop_sched_switch();
227 atomic_dec(&trace_record_cmdline_enabled);
231 static void sched_switch_trace_init(struct trace_array *tr)
236 start_sched_trace(tr);
239 static void sched_switch_trace_reset(struct trace_array *tr)
242 stop_sched_trace(tr);
245 static void sched_switch_trace_ctrl_update(struct trace_array *tr)
247 /* When starting a new trace, reset the buffers */
249 start_sched_trace(tr);
251 stop_sched_trace(tr);
254 static struct tracer sched_switch_trace __read_mostly =
256 .name = "sched_switch",
257 .init = sched_switch_trace_init,
258 .reset = sched_switch_trace_reset,
259 .ctrl_update = sched_switch_trace_ctrl_update,
260 #ifdef CONFIG_FTRACE_SELFTEST
261 .selftest = trace_selftest_startup_sched_switch,
265 __init static int init_sched_switch_trace(void)
269 if (atomic_read(&sched_ref))
270 ret = tracing_sched_register();
272 pr_info("error registering scheduler trace\n");
275 return register_tracer(&sched_switch_trace);
277 device_initcall(init_sched_switch_trace);