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