1 /**
2 * Copyright (C) ARM Limited 2010-2012. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
10 #include <trace/events/sched.h>
11 #include "gator.h"
13 #define SCHED_SWITCH 1
14 #define SCHED_PROCESS_EXIT 2
16 #define TASK_MAP_ENTRIES 1024 /* must be power of 2 */
17 #define TASK_MAX_COLLISIONS 2
19 static DEFINE_PER_CPU(uint64_t *, taskname_keys);
21 enum {
22 STATE_WAIT_ON_OTHER = 0,
23 STATE_CONTENTION,
24 STATE_WAIT_ON_IO,
25 STATE_WAIT_ON_MUTEX,
26 };
28 void emit_pid_name(struct task_struct* task)
29 {
30 bool found = false;
31 char taskcomm[TASK_COMM_LEN + 3];
32 unsigned long x, cpu = smp_processor_id();
33 uint64_t *keys = &(per_cpu(taskname_keys, cpu)[(task->pid & 0xFF) * TASK_MAX_COLLISIONS]);
34 uint64_t value;
36 value = gator_chksum_crc32(task->comm);
37 value = (value << 32) | (uint32_t)task->pid;
39 // determine if the thread name was emitted already
40 for (x = 0; x < TASK_MAX_COLLISIONS; x++) {
41 if (keys[x] == value) {
42 found = true;
43 break;
44 }
45 }
47 if (!found) {
48 // shift values, new value always in front
49 uint64_t oldv, newv = value;
50 for (x = 0; x < TASK_MAX_COLLISIONS; x++) {
51 oldv = keys[x];
52 keys[x] = newv;
53 newv = oldv;
54 }
56 // emit pid names, cannot use get_task_comm, as it's not exported on all kernel versions
57 if (strlcpy(taskcomm, task->comm, TASK_COMM_LEN) == TASK_COMM_LEN - 1) {
58 // append ellipses if task->comm has length of TASK_COMM_LEN - 1
59 strcat(taskcomm, "...");
60 }
62 marshal_pid_name(task->pid, taskcomm);
63 }
64 }
67 static void collect_counters(void)
68 {
69 int *buffer, len;
70 long long *buffer64;
71 struct gator_interface *gi;
73 if (marshal_event_header()) {
74 list_for_each_entry(gi, &gator_events, list) {
75 if (gi->read) {
76 len = gi->read(&buffer);
77 marshal_event(len, buffer);
78 } else if (gi->read64) {
79 len = gi->read64(&buffer64);
80 marshal_event64(len, buffer64);
81 }
82 }
83 }
84 }
86 static void probe_sched_write(int type, struct task_struct* task, struct task_struct* old_task)
87 {
88 int cookie = 0, state = 0;
89 int cpu = smp_processor_id();
90 int pid = task->pid;
91 int tgid = task->tgid;
93 if (type == SCHED_SWITCH) {
94 // do as much work as possible before disabling interrupts
95 cookie = get_exec_cookie(cpu, BACKTRACE_BUF, task);
96 emit_pid_name(task);
97 if (old_task->state == TASK_RUNNING) {
98 state = STATE_CONTENTION;
99 } else if (old_task->in_iowait) {
100 state = STATE_WAIT_ON_IO;
101 #ifdef CONFIG_DEBUG_MUTEXES
102 } else if (old_task->blocked_on) {
103 state = STATE_WAIT_ON_MUTEX;
104 #endif
105 } else {
106 state = STATE_WAIT_ON_OTHER;
107 }
109 collect_counters();
110 }
112 // marshal_sched_trace() disables interrupts as the free may trigger while switch is writing to the buffer; disabling preemption is not sufficient
113 // is disable interrupts necessary now that exit is used instead of free?
114 marshal_sched_trace(type, pid, tgid, cookie, state);
115 }
117 // special case used during a suspend of the system
118 static void trace_sched_insert_idle(void)
119 {
120 marshal_sched_trace(SCHED_SWITCH, 0, 0, 0, 0);
121 }
123 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
124 GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next))
125 #else
126 GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next))
127 #endif
128 {
129 probe_sched_write(SCHED_SWITCH, next, prev);
130 }
132 GATOR_DEFINE_PROBE(sched_process_exit, TP_PROTO(struct task_struct *p))
133 {
134 probe_sched_write(SCHED_PROCESS_EXIT, p, 0);
135 }
137 static int register_scheduler_tracepoints(void) {
138 // register tracepoints
139 if (GATOR_REGISTER_TRACE(sched_switch))
140 goto fail_sched_switch;
141 if (GATOR_REGISTER_TRACE(sched_process_exit))
142 goto fail_sched_process_exit;
143 pr_debug("gator: registered tracepoints\n");
145 return 0;
147 // unregister tracepoints on error
148 fail_sched_process_exit:
149 GATOR_UNREGISTER_TRACE(sched_switch);
150 fail_sched_switch:
151 pr_err("gator: tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
153 return -1;
154 }
156 int gator_trace_sched_start(void)
157 {
158 int cpu, size;
160 for_each_present_cpu(cpu) {
161 size = TASK_MAP_ENTRIES * TASK_MAX_COLLISIONS * sizeof(uint64_t);
162 per_cpu(taskname_keys, cpu) = (uint64_t*)kmalloc(size, GFP_KERNEL);
163 if (!per_cpu(taskname_keys, cpu))
164 return -1;
165 memset(per_cpu(taskname_keys, cpu), 0, size);
166 }
168 return register_scheduler_tracepoints();
169 }
171 void gator_trace_sched_offline(void)
172 {
173 trace_sched_insert_idle();
174 }
176 static void unregister_scheduler_tracepoints(void)
177 {
178 GATOR_UNREGISTER_TRACE(sched_switch);
179 GATOR_UNREGISTER_TRACE(sched_process_exit);
180 pr_debug("gator: unregistered tracepoints\n");
181 }
183 void gator_trace_sched_stop(void)
184 {
185 int cpu;
186 unregister_scheduler_tracepoints();
188 for_each_present_cpu(cpu) {
189 kfree(per_cpu(taskname_keys, cpu));
190 }
191 }