4e5b076de71130a12047db997dfdac760c5cad83
[deliverable/lttng-analyses.git] / lttnganalyses / linuxautomaton / sched.py
1 # The MIT License (MIT)
2 #
3 # Copyright (C) 2015 - Julien Desfossez <jdesfossez@efficios.com>
4 # 2015 - Antoine Busque <abusque@efficios.com>
5 #
6 # Permission is hereby granted, free of charge, to any person obtaining a copy
7 # of this software and associated documentation files (the "Software"), to deal
8 # in the Software without restriction, including without limitation the rights
9 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 # copies of the Software, and to permit persons to whom the Software is
11 # furnished to do so, subject to the following conditions:
12 #
13 # The above copyright notice and this permission notice shall be included in
14 # all copies or substantial portions of the Software.
15 #
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 # SOFTWARE.
23
24 from . import sp, sv
25 from ..common import version_utils
26
27
28 class SchedStateProvider(sp.StateProvider):
29 # The priority offset for sched_wak* events was fixed in
30 # lttng-modules 2.7.1 upwards
31 PRIO_OFFSET_FIX_VERSION = version_utils.Version(2,7,1)
32
33 def __init__(self, state):
34 cbs = {
35 'sched_switch': self._process_sched_switch,
36 'sched_migrate_task': self._process_sched_migrate_task,
37 'sched_wakeup': self._process_sched_wakeup,
38 'sched_wakeup_new': self._process_sched_wakeup,
39 'sched_waking': self._process_sched_wakeup,
40 'sched_process_fork': self._process_sched_process_fork,
41 'sched_process_exec': self._process_sched_process_exec,
42 'sched_pi_setprio': self._process_sched_pi_setprio,
43 }
44
45 super().__init__(state, cbs)
46
47 def _sched_switch_per_cpu(self, cpu_id, next_tid):
48 if cpu_id not in self._state.cpus:
49 self._state.cpus[cpu_id] = sv.CPU(cpu_id)
50
51 cpu = self._state.cpus[cpu_id]
52 # exclude swapper process
53 if next_tid == 0:
54 cpu.current_tid = None
55 else:
56 cpu.current_tid = next_tid
57
58 def _create_proc(self, tid):
59 if tid not in self._state.tids:
60 if tid == 0:
61 # special case for the swapper
62 self._state.tids[tid] = sv.Process(tid=tid, pid=0)
63 else:
64 self._state.tids[tid] = sv.Process(tid=tid)
65
66 def _sched_switch_per_tid(self, next_tid, next_comm, prev_tid):
67 # Instantiate processes if new
68 self._create_proc(prev_tid)
69 self._create_proc(next_tid)
70
71 next_proc = self._state.tids[next_tid]
72 next_proc.comm = next_comm
73 next_proc.prev_tid = prev_tid
74
75 def _check_prio_changed(self, timestamp, tid, prio):
76 # Ignore swapper
77 if tid == 0:
78 return
79
80 proc = self._state.tids[tid]
81
82 if proc.prio != prio:
83 proc.prio = prio
84 self._state.send_notification_cb(
85 'prio_changed', timestamp=timestamp, tid=tid, prio=prio)
86
87 def _process_sched_switch(self, event):
88 timestamp = event.timestamp
89 cpu_id = event['cpu_id']
90 next_tid = event['next_tid']
91 next_comm = event['next_comm']
92 next_prio = event['next_prio']
93 prev_tid = event['prev_tid']
94 prev_prio = event['prev_prio']
95
96 self._sched_switch_per_cpu(cpu_id, next_tid)
97 self._sched_switch_per_tid(next_tid, next_comm, prev_tid)
98 self._check_prio_changed(timestamp, prev_tid, prev_prio)
99 self._check_prio_changed(timestamp, next_tid, next_prio)
100
101 wakee_proc = self._state.tids[next_tid]
102 waker_proc = None
103 if wakee_proc.last_waker is not None:
104 waker_proc = self._state.tids[wakee_proc.last_waker]
105
106 cb_data = {
107 'timestamp': timestamp,
108 'cpu_id': cpu_id,
109 'prev_tid': prev_tid,
110 'next_tid': next_tid,
111 'next_comm': next_comm,
112 'wakee_proc': wakee_proc,
113 'waker_proc': waker_proc,
114 }
115
116 self._state.send_notification_cb('sched_switch_per_cpu', **cb_data)
117 self._state.send_notification_cb('sched_switch_per_tid', **cb_data)
118
119 wakee_proc.last_wakeup = None
120 wakee_proc.last_waker = None
121
122 def _process_sched_migrate_task(self, event):
123 tid = event['tid']
124 prio = event['prio']
125
126 if tid not in self._state.tids:
127 proc = sv.Process()
128 proc.tid = tid
129 proc.comm = event['comm']
130 self._state.tids[tid] = proc
131 else:
132 proc = self._state.tids[tid]
133
134 self._check_prio_changed(event.timestamp, tid, prio)
135 self._state.send_notification_cb(
136 'sched_migrate_task', proc=proc, cpu_id=event['cpu_id'])
137
138 def _process_sched_wakeup(self, event):
139 target_cpu = event['target_cpu']
140 current_cpu = event['cpu_id']
141 prio = event['prio']
142 tid = event['tid']
143
144 if self._state.tracer_version < self.PRIO_OFFSET_FIX_VERSION:
145 prio -= 100
146
147 if target_cpu not in self._state.cpus:
148 self._state.cpus[target_cpu] = sv.CPU(target_cpu)
149
150 if current_cpu not in self._state.cpus:
151 self._state.cpus[current_cpu] = sv.CPU(current_cpu)
152
153 # If the TID is already executing on a CPU, ignore this wakeup
154 for cpu_id in self._state.cpus:
155 cpu = self._state.cpus[cpu_id]
156 if cpu.current_tid == tid:
157 return
158
159 if tid not in self._state.tids:
160 proc = sv.Process()
161 proc.tid = tid
162 self._state.tids[tid] = proc
163
164 self._check_prio_changed(event.timestamp, tid, prio)
165
166 # A process can be woken up multiple times, only record
167 # the first one
168 if self._state.tids[tid].last_wakeup is None:
169 self._state.tids[tid].last_wakeup = event.timestamp
170 if self._state.cpus[current_cpu].current_tid is not None:
171 self._state.tids[tid].last_waker = \
172 self._state.cpus[current_cpu].current_tid
173
174 def _process_sched_process_fork(self, event):
175 child_tid = event['child_tid']
176 child_pid = event['child_pid']
177 child_comm = event['child_comm']
178 parent_pid = event['parent_pid']
179 parent_tid = event['parent_pid']
180 parent_comm = event['parent_comm']
181
182 if parent_tid not in self._state.tids:
183 self._state.tids[parent_tid] = sv.Process(
184 parent_tid, parent_pid, parent_comm)
185 else:
186 self._state.tids[parent_tid].pid = parent_pid
187 self._state.tids[parent_tid].comm = parent_comm
188
189 parent_proc = self._state.tids[parent_pid]
190 child_proc = sv.Process(child_tid, child_pid, child_comm)
191
192 for fd in parent_proc.fds:
193 old_fd = parent_proc.fds[fd]
194 child_proc.fds[fd] = sv.FD.new_from_fd(old_fd)
195 # Note: the parent_proc key in the notification function
196 # refers to the parent of the FD, which in this case is
197 # the child_proc created by the fork
198 self._state.send_notification_cb(
199 'create_fd', fd=fd, parent_proc=child_proc,
200 timestamp=event.timestamp, cpu_id=event['cpu_id'])
201
202 self._state.tids[child_tid] = child_proc
203
204 def _process_sched_process_exec(self, event):
205 tid = event['tid']
206
207 if tid not in self._state.tids:
208 proc = sv.Process()
209 proc.tid = tid
210 self._state.tids[tid] = proc
211 else:
212 proc = self._state.tids[tid]
213
214 # Use LTTng procname context if available
215 if 'procname' in event:
216 proc.comm = event['procname']
217
218 toremove = []
219 for fd in proc.fds:
220 if proc.fds[fd].cloexec:
221 toremove.append(fd)
222 for fd in toremove:
223 self._state.send_notification_cb(
224 'close_fd', fd=fd, parent_proc=proc,
225 timestamp=event.timestamp, cpu_id=event['cpu_id'])
226 del proc.fds[fd]
227
228 def _process_sched_pi_setprio(self, event):
229 timestamp = event.timestamp
230 newprio = event['newprio']
231 tid = event['tid']
232
233 self._check_prio_changed(timestamp, tid, newprio)
This page took 0.034823 seconds and 4 git commands to generate.