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/events/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 24 void 25 tracing_sched_switch_trace(struct trace_array *tr, 26 struct task_struct *prev, 27 struct task_struct *next, 28 unsigned long flags, int pc) 29 { 30 struct ftrace_event_call *call = &event_context_switch; 31 struct ring_buffer *buffer = tr->buffer; 32 struct ring_buffer_event *event; 33 struct ctx_switch_entry *entry; 34 35 event = trace_buffer_lock_reserve(buffer, TRACE_CTX, 36 sizeof(*entry), flags, pc); 37 if (!event) 38 return; 39 entry = ring_buffer_event_data(event); 40 entry->prev_pid = prev->pid; 41 entry->prev_prio = prev->prio; 42 entry->prev_state = prev->state; 43 entry->next_pid = next->pid; 44 entry->next_prio = next->prio; 45 entry->next_state = next->state; 46 entry->next_cpu = task_cpu(next); 47 48 if (!filter_check_discard(call, entry, buffer, event)) 49 trace_buffer_unlock_commit(buffer, event, flags, pc); 50 } 51 52 static void 53 probe_sched_switch(void *ignore, struct task_struct *prev, struct task_struct *next) 54 { 55 struct trace_array_cpu *data; 56 unsigned long flags; 57 int cpu; 58 int pc; 59 60 if (unlikely(!sched_ref)) 61 return; 62 63 tracing_record_cmdline(prev); 64 tracing_record_cmdline(next); 65 66 if (!tracer_enabled || sched_stopped) 67 return; 68 69 pc = preempt_count(); 70 local_irq_save(flags); 71 cpu = raw_smp_processor_id(); 72 data = ctx_trace->data[cpu]; 73 74 if (likely(!atomic_read(&data->disabled))) 75 tracing_sched_switch_trace(ctx_trace, prev, next, flags, pc); 76 77 local_irq_restore(flags); 78 } 79 80 void 81 tracing_sched_wakeup_trace(struct trace_array *tr, 82 struct task_struct *wakee, 83 struct task_struct *curr, 84 unsigned long flags, int pc) 85 { 86 struct ftrace_event_call *call = &event_wakeup; 87 struct ring_buffer_event *event; 88 struct ctx_switch_entry *entry; 89 struct ring_buffer *buffer = tr->buffer; 90 91 event = trace_buffer_lock_reserve(buffer, TRACE_WAKE, 92 sizeof(*entry), flags, pc); 93 if (!event) 94 return; 95 entry = ring_buffer_event_data(event); 96 entry->prev_pid = curr->pid; 97 entry->prev_prio = curr->prio; 98 entry->prev_state = curr->state; 99 entry->next_pid = wakee->pid; 100 entry->next_prio = wakee->prio; 101 entry->next_state = wakee->state; 102 entry->next_cpu = task_cpu(wakee); 103 104 if (!filter_check_discard(call, entry, buffer, event)) 105 ring_buffer_unlock_commit(buffer, event); 106 ftrace_trace_stack(tr->buffer, flags, 6, pc); 107 ftrace_trace_userstack(tr->buffer, flags, pc); 108 } 109 110 static void 111 probe_sched_wakeup(void *ignore, struct task_struct *wakee, int success) 112 { 113 struct trace_array_cpu *data; 114 unsigned long flags; 115 int cpu, pc; 116 117 if (unlikely(!sched_ref)) 118 return; 119 120 tracing_record_cmdline(current); 121 122 if (!tracer_enabled || sched_stopped) 123 return; 124 125 pc = preempt_count(); 126 local_irq_save(flags); 127 cpu = raw_smp_processor_id(); 128 data = ctx_trace->data[cpu]; 129 130 if (likely(!atomic_read(&data->disabled))) 131 tracing_sched_wakeup_trace(ctx_trace, wakee, current, 132 flags, pc); 133 134 local_irq_restore(flags); 135 } 136 137 static int tracing_sched_register(void) 138 { 139 int ret; 140 141 ret = register_trace_sched_wakeup(probe_sched_wakeup, NULL); 142 if (ret) { 143 pr_info("wakeup trace: Couldn't activate tracepoint" 144 " probe to kernel_sched_wakeup\n"); 145 return ret; 146 } 147 148 ret = register_trace_sched_wakeup_new(probe_sched_wakeup, NULL); 149 if (ret) { 150 pr_info("wakeup trace: Couldn't activate tracepoint" 151 " probe to kernel_sched_wakeup_new\n"); 152 goto fail_deprobe; 153 } 154 155 ret = register_trace_sched_switch(probe_sched_switch, NULL); 156 if (ret) { 157 pr_info("sched trace: Couldn't activate tracepoint" 158 " probe to kernel_sched_switch\n"); 159 goto fail_deprobe_wake_new; 160 } 161 162 return ret; 163 fail_deprobe_wake_new: 164 unregister_trace_sched_wakeup_new(probe_sched_wakeup, NULL); 165 fail_deprobe: 166 unregister_trace_sched_wakeup(probe_sched_wakeup, NULL); 167 return ret; 168 } 169 170 static void tracing_sched_unregister(void) 171 { 172 unregister_trace_sched_switch(probe_sched_switch, NULL); 173 unregister_trace_sched_wakeup_new(probe_sched_wakeup, NULL); 174 unregister_trace_sched_wakeup(probe_sched_wakeup, NULL); 175 } 176 177 static void tracing_start_sched_switch(void) 178 { 179 mutex_lock(&sched_register_mutex); 180 if (!(sched_ref++)) 181 tracing_sched_register(); 182 mutex_unlock(&sched_register_mutex); 183 } 184 185 static void tracing_stop_sched_switch(void) 186 { 187 mutex_lock(&sched_register_mutex); 188 if (!(--sched_ref)) 189 tracing_sched_unregister(); 190 mutex_unlock(&sched_register_mutex); 191 } 192 193 void tracing_start_cmdline_record(void) 194 { 195 tracing_start_sched_switch(); 196 } 197 198 void tracing_stop_cmdline_record(void) 199 { 200 tracing_stop_sched_switch(); 201 } 202 203 /** 204 * tracing_start_sched_switch_record - start tracing context switches 205 * 206 * Turns on context switch tracing for a tracer. 207 */ 208 void tracing_start_sched_switch_record(void) 209 { 210 if (unlikely(!ctx_trace)) { 211 WARN_ON(1); 212 return; 213 } 214 215 tracing_start_sched_switch(); 216 217 mutex_lock(&sched_register_mutex); 218 tracer_enabled++; 219 mutex_unlock(&sched_register_mutex); 220 } 221 222 /** 223 * tracing_stop_sched_switch_record - start tracing context switches 224 * 225 * Turns off context switch tracing for a tracer. 226 */ 227 void tracing_stop_sched_switch_record(void) 228 { 229 mutex_lock(&sched_register_mutex); 230 tracer_enabled--; 231 WARN_ON(tracer_enabled < 0); 232 mutex_unlock(&sched_register_mutex); 233 234 tracing_stop_sched_switch(); 235 } 236 237 /** 238 * tracing_sched_switch_assign_trace - assign a trace array for ctx switch 239 * @tr: trace array pointer to assign 240 * 241 * Some tracers might want to record the context switches in their 242 * trace. This function lets those tracers assign the trace array 243 * to use. 244 */ 245 void tracing_sched_switch_assign_trace(struct trace_array *tr) 246 { 247 ctx_trace = tr; 248 } 249 250