135e8e302SSteven Rostedt /* 235e8e302SSteven Rostedt * trace context switch 335e8e302SSteven Rostedt * 435e8e302SSteven Rostedt * Copyright (C) 2007 Steven Rostedt <srostedt@redhat.com> 535e8e302SSteven Rostedt * 635e8e302SSteven Rostedt */ 735e8e302SSteven Rostedt #include <linux/module.h> 835e8e302SSteven Rostedt #include <linux/fs.h> 935e8e302SSteven Rostedt #include <linux/debugfs.h> 1035e8e302SSteven Rostedt #include <linux/kallsyms.h> 1135e8e302SSteven Rostedt #include <linux/uaccess.h> 1235e8e302SSteven Rostedt #include <linux/ftrace.h> 13ad8d75ffSSteven Rostedt #include <trace/events/sched.h> 1435e8e302SSteven Rostedt 1535e8e302SSteven Rostedt #include "trace.h" 1635e8e302SSteven Rostedt 1735e8e302SSteven Rostedt static struct trace_array *ctx_trace; 1835e8e302SSteven Rostedt static int __read_mostly tracer_enabled; 19efade6e7SFrederic Weisbecker static int sched_ref; 20efade6e7SFrederic Weisbecker static DEFINE_MUTEX(sched_register_mutex); 215fec6ddcSSteven Rostedt static int sched_stopped; 2235e8e302SSteven Rostedt 2382e04af4SFrederic Weisbecker 2482e04af4SFrederic Weisbecker void 2582e04af4SFrederic Weisbecker tracing_sched_switch_trace(struct trace_array *tr, 2682e04af4SFrederic Weisbecker struct task_struct *prev, 2782e04af4SFrederic Weisbecker struct task_struct *next, 2882e04af4SFrederic Weisbecker unsigned long flags, int pc) 2982e04af4SFrederic Weisbecker { 3082e04af4SFrederic Weisbecker struct ftrace_event_call *call = &event_context_switch; 3182e04af4SFrederic Weisbecker struct ring_buffer_event *event; 3282e04af4SFrederic Weisbecker struct ctx_switch_entry *entry; 3382e04af4SFrederic Weisbecker 3482e04af4SFrederic Weisbecker event = trace_buffer_lock_reserve(tr, TRACE_CTX, 3582e04af4SFrederic Weisbecker sizeof(*entry), flags, pc); 3682e04af4SFrederic Weisbecker if (!event) 3782e04af4SFrederic Weisbecker return; 3882e04af4SFrederic Weisbecker entry = ring_buffer_event_data(event); 3982e04af4SFrederic Weisbecker entry->prev_pid = prev->pid; 4082e04af4SFrederic Weisbecker entry->prev_prio = prev->prio; 4182e04af4SFrederic Weisbecker entry->prev_state = prev->state; 4282e04af4SFrederic Weisbecker entry->next_pid = next->pid; 4382e04af4SFrederic Weisbecker entry->next_prio = next->prio; 4482e04af4SFrederic Weisbecker entry->next_state = next->state; 4582e04af4SFrederic Weisbecker entry->next_cpu = task_cpu(next); 4682e04af4SFrederic Weisbecker 4782e04af4SFrederic Weisbecker if (!filter_check_discard(call, entry, tr->buffer, event)) 4882e04af4SFrederic Weisbecker trace_buffer_unlock_commit(tr, event, flags, pc); 4982e04af4SFrederic Weisbecker } 5082e04af4SFrederic Weisbecker 51e309b41dSIngo Molnar static void 52b07c3f19SMathieu Desnoyers probe_sched_switch(struct rq *__rq, struct task_struct *prev, 535b82a1b0SMathieu Desnoyers struct task_struct *next) 5435e8e302SSteven Rostedt { 5535e8e302SSteven Rostedt struct trace_array_cpu *data; 5635e8e302SSteven Rostedt unsigned long flags; 5735e8e302SSteven Rostedt int cpu; 5838697053SSteven Rostedt int pc; 5935e8e302SSteven Rostedt 60dcef788eSZhaolei if (unlikely(!sched_ref)) 61b07c3f19SMathieu Desnoyers return; 62b07c3f19SMathieu Desnoyers 6341bc8144SSteven Rostedt tracing_record_cmdline(prev); 6441bc8144SSteven Rostedt tracing_record_cmdline(next); 6541bc8144SSteven Rostedt 66dcef788eSZhaolei if (!tracer_enabled || sched_stopped) 6735e8e302SSteven Rostedt return; 6835e8e302SSteven Rostedt 6938697053SSteven Rostedt pc = preempt_count(); 7018cef379SSteven Rostedt local_irq_save(flags); 7135e8e302SSteven Rostedt cpu = raw_smp_processor_id(); 72b07c3f19SMathieu Desnoyers data = ctx_trace->data[cpu]; 7335e8e302SSteven Rostedt 743ea2e6d7SSteven Rostedt if (likely(!atomic_read(&data->disabled))) 757be42151SArnaldo Carvalho de Melo tracing_sched_switch_trace(ctx_trace, prev, next, flags, pc); 7635e8e302SSteven Rostedt 7718cef379SSteven Rostedt local_irq_restore(flags); 7835e8e302SSteven Rostedt } 7935e8e302SSteven Rostedt 8082e04af4SFrederic Weisbecker void 8182e04af4SFrederic Weisbecker tracing_sched_wakeup_trace(struct trace_array *tr, 8282e04af4SFrederic Weisbecker struct task_struct *wakee, 8382e04af4SFrederic Weisbecker struct task_struct *curr, 8482e04af4SFrederic Weisbecker unsigned long flags, int pc) 8582e04af4SFrederic Weisbecker { 8682e04af4SFrederic Weisbecker struct ftrace_event_call *call = &event_wakeup; 8782e04af4SFrederic Weisbecker struct ring_buffer_event *event; 8882e04af4SFrederic Weisbecker struct ctx_switch_entry *entry; 8982e04af4SFrederic Weisbecker 9082e04af4SFrederic Weisbecker event = trace_buffer_lock_reserve(tr, TRACE_WAKE, 9182e04af4SFrederic Weisbecker sizeof(*entry), flags, pc); 9282e04af4SFrederic Weisbecker if (!event) 9382e04af4SFrederic Weisbecker return; 9482e04af4SFrederic Weisbecker entry = ring_buffer_event_data(event); 9582e04af4SFrederic Weisbecker entry->prev_pid = curr->pid; 9682e04af4SFrederic Weisbecker entry->prev_prio = curr->prio; 9782e04af4SFrederic Weisbecker entry->prev_state = curr->state; 9882e04af4SFrederic Weisbecker entry->next_pid = wakee->pid; 9982e04af4SFrederic Weisbecker entry->next_prio = wakee->prio; 10082e04af4SFrederic Weisbecker entry->next_state = wakee->state; 10182e04af4SFrederic Weisbecker entry->next_cpu = task_cpu(wakee); 10282e04af4SFrederic Weisbecker 10382e04af4SFrederic Weisbecker if (!filter_check_discard(call, entry, tr->buffer, event)) 10482e04af4SFrederic Weisbecker ring_buffer_unlock_commit(tr->buffer, event); 10582e04af4SFrederic Weisbecker ftrace_trace_stack(tr, flags, 6, pc); 10682e04af4SFrederic Weisbecker ftrace_trace_userstack(tr, flags, pc); 10782e04af4SFrederic Weisbecker } 10882e04af4SFrederic Weisbecker 1095b82a1b0SMathieu Desnoyers static void 110468a15bbSPeter Zijlstra probe_sched_wakeup(struct rq *__rq, struct task_struct *wakee, int success) 1115b82a1b0SMathieu Desnoyers { 11257422797SIngo Molnar struct trace_array_cpu *data; 11357422797SIngo Molnar unsigned long flags; 11438697053SSteven Rostedt int cpu, pc; 11557422797SIngo Molnar 116dcef788eSZhaolei if (unlikely(!sched_ref)) 117dcef788eSZhaolei return; 118dcef788eSZhaolei 119dcef788eSZhaolei tracing_record_cmdline(current); 120dcef788eSZhaolei 121dcef788eSZhaolei if (!tracer_enabled || sched_stopped) 12257422797SIngo Molnar return; 12357422797SIngo Molnar 12438697053SSteven Rostedt pc = preempt_count(); 12557422797SIngo Molnar local_irq_save(flags); 12657422797SIngo Molnar cpu = raw_smp_processor_id(); 127b07c3f19SMathieu Desnoyers data = ctx_trace->data[cpu]; 12857422797SIngo Molnar 1293ea2e6d7SSteven Rostedt if (likely(!atomic_read(&data->disabled))) 1307be42151SArnaldo Carvalho de Melo tracing_sched_wakeup_trace(ctx_trace, wakee, current, 13138697053SSteven Rostedt flags, pc); 13257422797SIngo Molnar 13357422797SIngo Molnar local_irq_restore(flags); 13457422797SIngo Molnar } 13557422797SIngo Molnar 1365b82a1b0SMathieu Desnoyers static int tracing_sched_register(void) 1375b82a1b0SMathieu Desnoyers { 1385b82a1b0SMathieu Desnoyers int ret; 1395b82a1b0SMathieu Desnoyers 140b07c3f19SMathieu Desnoyers ret = register_trace_sched_wakeup(probe_sched_wakeup); 1415b82a1b0SMathieu Desnoyers if (ret) { 142b07c3f19SMathieu Desnoyers pr_info("wakeup trace: Couldn't activate tracepoint" 1435b82a1b0SMathieu Desnoyers " probe to kernel_sched_wakeup\n"); 1445b82a1b0SMathieu Desnoyers return ret; 1455b82a1b0SMathieu Desnoyers } 1465b82a1b0SMathieu Desnoyers 147b07c3f19SMathieu Desnoyers ret = register_trace_sched_wakeup_new(probe_sched_wakeup); 1485b82a1b0SMathieu Desnoyers if (ret) { 149b07c3f19SMathieu Desnoyers pr_info("wakeup trace: Couldn't activate tracepoint" 1505b82a1b0SMathieu Desnoyers " probe to kernel_sched_wakeup_new\n"); 1515b82a1b0SMathieu Desnoyers goto fail_deprobe; 1525b82a1b0SMathieu Desnoyers } 1535b82a1b0SMathieu Desnoyers 154b07c3f19SMathieu Desnoyers ret = register_trace_sched_switch(probe_sched_switch); 1555b82a1b0SMathieu Desnoyers if (ret) { 156b07c3f19SMathieu Desnoyers pr_info("sched trace: Couldn't activate tracepoint" 15773d8b8bcSWenji Huang " probe to kernel_sched_switch\n"); 1585b82a1b0SMathieu Desnoyers goto fail_deprobe_wake_new; 1595b82a1b0SMathieu Desnoyers } 1605b82a1b0SMathieu Desnoyers 1615b82a1b0SMathieu Desnoyers return ret; 1625b82a1b0SMathieu Desnoyers fail_deprobe_wake_new: 163b07c3f19SMathieu Desnoyers unregister_trace_sched_wakeup_new(probe_sched_wakeup); 1645b82a1b0SMathieu Desnoyers fail_deprobe: 165b07c3f19SMathieu Desnoyers unregister_trace_sched_wakeup(probe_sched_wakeup); 1665b82a1b0SMathieu Desnoyers return ret; 1675b82a1b0SMathieu Desnoyers } 1685b82a1b0SMathieu Desnoyers 1695b82a1b0SMathieu Desnoyers static void tracing_sched_unregister(void) 1705b82a1b0SMathieu Desnoyers { 171b07c3f19SMathieu Desnoyers unregister_trace_sched_switch(probe_sched_switch); 172b07c3f19SMathieu Desnoyers unregister_trace_sched_wakeup_new(probe_sched_wakeup); 173b07c3f19SMathieu Desnoyers unregister_trace_sched_wakeup(probe_sched_wakeup); 1745b82a1b0SMathieu Desnoyers } 1755b82a1b0SMathieu Desnoyers 176f2252935SIngo Molnar static void tracing_start_sched_switch(void) 1775b82a1b0SMathieu Desnoyers { 178efade6e7SFrederic Weisbecker mutex_lock(&sched_register_mutex); 179e168e051SSteven Rostedt if (!(sched_ref++)) 1805b82a1b0SMathieu Desnoyers tracing_sched_register(); 181efade6e7SFrederic Weisbecker mutex_unlock(&sched_register_mutex); 1825b82a1b0SMathieu Desnoyers } 1835b82a1b0SMathieu Desnoyers 184f2252935SIngo Molnar static void tracing_stop_sched_switch(void) 1855b82a1b0SMathieu Desnoyers { 186efade6e7SFrederic Weisbecker mutex_lock(&sched_register_mutex); 187e168e051SSteven Rostedt if (!(--sched_ref)) 1885b82a1b0SMathieu Desnoyers tracing_sched_unregister(); 189efade6e7SFrederic Weisbecker mutex_unlock(&sched_register_mutex); 1905b82a1b0SMathieu Desnoyers } 1915b82a1b0SMathieu Desnoyers 19241bc8144SSteven Rostedt void tracing_start_cmdline_record(void) 19341bc8144SSteven Rostedt { 19441bc8144SSteven Rostedt tracing_start_sched_switch(); 19541bc8144SSteven Rostedt } 19641bc8144SSteven Rostedt 19741bc8144SSteven Rostedt void tracing_stop_cmdline_record(void) 19841bc8144SSteven Rostedt { 19941bc8144SSteven Rostedt tracing_stop_sched_switch(); 20041bc8144SSteven Rostedt } 20141bc8144SSteven Rostedt 20275f5c47dSSteven Rostedt /** 203e168e051SSteven Rostedt * tracing_start_sched_switch_record - start tracing context switches 204e168e051SSteven Rostedt * 205e168e051SSteven Rostedt * Turns on context switch tracing for a tracer. 206e168e051SSteven Rostedt */ 207e168e051SSteven Rostedt void tracing_start_sched_switch_record(void) 208e168e051SSteven Rostedt { 209e168e051SSteven Rostedt if (unlikely(!ctx_trace)) { 210e168e051SSteven Rostedt WARN_ON(1); 211e168e051SSteven Rostedt return; 212e168e051SSteven Rostedt } 213e168e051SSteven Rostedt 214e168e051SSteven Rostedt tracing_start_sched_switch(); 215e168e051SSteven Rostedt 216e168e051SSteven Rostedt mutex_lock(&sched_register_mutex); 217e168e051SSteven Rostedt tracer_enabled++; 218e168e051SSteven Rostedt mutex_unlock(&sched_register_mutex); 219e168e051SSteven Rostedt } 220e168e051SSteven Rostedt 221e168e051SSteven Rostedt /** 222e168e051SSteven Rostedt * tracing_stop_sched_switch_record - start tracing context switches 223e168e051SSteven Rostedt * 224e168e051SSteven Rostedt * Turns off context switch tracing for a tracer. 225e168e051SSteven Rostedt */ 226e168e051SSteven Rostedt void tracing_stop_sched_switch_record(void) 227e168e051SSteven Rostedt { 228e168e051SSteven Rostedt mutex_lock(&sched_register_mutex); 229e168e051SSteven Rostedt tracer_enabled--; 230e168e051SSteven Rostedt WARN_ON(tracer_enabled < 0); 231e168e051SSteven Rostedt mutex_unlock(&sched_register_mutex); 232e168e051SSteven Rostedt 233e168e051SSteven Rostedt tracing_stop_sched_switch(); 234e168e051SSteven Rostedt } 235e168e051SSteven Rostedt 236e168e051SSteven Rostedt /** 237e168e051SSteven Rostedt * tracing_sched_switch_assign_trace - assign a trace array for ctx switch 23875f5c47dSSteven Rostedt * @tr: trace array pointer to assign 23975f5c47dSSteven Rostedt * 24075f5c47dSSteven Rostedt * Some tracers might want to record the context switches in their 24175f5c47dSSteven Rostedt * trace. This function lets those tracers assign the trace array 24275f5c47dSSteven Rostedt * to use. 24375f5c47dSSteven Rostedt */ 244e168e051SSteven Rostedt void tracing_sched_switch_assign_trace(struct trace_array *tr) 24575f5c47dSSteven Rostedt { 24675f5c47dSSteven Rostedt ctx_trace = tr; 24775f5c47dSSteven Rostedt } 24875f5c47dSSteven Rostedt 249e309b41dSIngo Molnar static void stop_sched_trace(struct trace_array *tr) 25035e8e302SSteven Rostedt { 251e168e051SSteven Rostedt tracing_stop_sched_switch_record(); 25235e8e302SSteven Rostedt } 25335e8e302SSteven Rostedt 2541c80025aSFrederic Weisbecker static int sched_switch_trace_init(struct trace_array *tr) 25535e8e302SSteven Rostedt { 25635e8e302SSteven Rostedt ctx_trace = tr; 2575fec6ddcSSteven Rostedt tracing_reset_online_cpus(tr); 258b6f11df2SArnaldo Carvalho de Melo tracing_start_sched_switch_record(); 2591c80025aSFrederic Weisbecker return 0; 26035e8e302SSteven Rostedt } 26135e8e302SSteven Rostedt 262e309b41dSIngo Molnar static void sched_switch_trace_reset(struct trace_array *tr) 26335e8e302SSteven Rostedt { 264c76f0694SSteven Rostedt if (sched_ref) 26535e8e302SSteven Rostedt stop_sched_trace(tr); 26635e8e302SSteven Rostedt } 26735e8e302SSteven Rostedt 2689036990dSSteven Rostedt static void sched_switch_trace_start(struct trace_array *tr) 2699036990dSSteven Rostedt { 2705fec6ddcSSteven Rostedt sched_stopped = 0; 2719036990dSSteven Rostedt } 2729036990dSSteven Rostedt 2739036990dSSteven Rostedt static void sched_switch_trace_stop(struct trace_array *tr) 2749036990dSSteven Rostedt { 2755fec6ddcSSteven Rostedt sched_stopped = 1; 2769036990dSSteven Rostedt } 2779036990dSSteven Rostedt 27875f5c47dSSteven Rostedt static struct tracer sched_switch_trace __read_mostly = 27935e8e302SSteven Rostedt { 28035e8e302SSteven Rostedt .name = "sched_switch", 28135e8e302SSteven Rostedt .init = sched_switch_trace_init, 28235e8e302SSteven Rostedt .reset = sched_switch_trace_reset, 2839036990dSSteven Rostedt .start = sched_switch_trace_start, 2849036990dSSteven Rostedt .stop = sched_switch_trace_stop, 2856eaaa5d5SFrederic Weisbecker .wait_pipe = poll_wait_pipe, 28660a11774SSteven Rostedt #ifdef CONFIG_FTRACE_SELFTEST 28760a11774SSteven Rostedt .selftest = trace_selftest_startup_sched_switch, 28860a11774SSteven Rostedt #endif 28935e8e302SSteven Rostedt }; 29035e8e302SSteven Rostedt 29135e8e302SSteven Rostedt __init static int init_sched_switch_trace(void) 29235e8e302SSteven Rostedt { 29335e8e302SSteven Rostedt return register_tracer(&sched_switch_trace); 29435e8e302SSteven Rostedt } 29535e8e302SSteven Rostedt device_initcall(init_sched_switch_trace); 296c71dd42dSIngo Molnar 297