Remove unused StateVariable class
[deliverable/lttng-analyses.git] / lttnganalyses / linuxautomaton / sv.py
CommitLineData
4ed24f86
JD
1# The MIT License (MIT)
2#
a3fa57c0 3# Copyright (C) 2015 - Julien Desfossez <jdesfossez@efficios.com>
39fec005 4# 2015 - Antoine Busque <abusque@efficios.com>
4ed24f86
JD
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
bd3cd7c5 24import socket
56936af2 25from . import common
bd3cd7c5
JD
26
27
bd3cd7c5 28class Process():
f5ab2566 29 def __init__(self, tid=None, pid=None, comm='', prio=None):
3ce8423c
AB
30 self.tid = tid
31 self.pid = pid
32 self.comm = comm
f5ab2566 33 self.prio = prio
bd3cd7c5
JD
34 # indexed by fd
35 self.fds = {}
b5f11254 36 self.current_syscall = None
bd3cd7c5 37 # the process scheduled before this one
2b4a3c12 38 self.prev_tid = None
b9f05f8d
AB
39 self.last_wakeup = None
40 self.last_waker = None
bd3cd7c5
JD
41
42
43class CPU():
3c9bf3fa
AB
44 def __init__(self, cpu_id):
45 self.cpu_id = cpu_id
2b4a3c12 46 self.current_tid = None
f4522f09 47 self.current_hard_irq = None
26facb3a
AB
48 # softirqs use a dict because multiple ones can be raised before
49 # handling. They are indexed by vec, and each entry is a list,
50 # ordered chronologically
51 self.current_softirqs = {}
bd3cd7c5
JD
52
53
994541c3
AB
54class MemoryManagement():
55 def __init__(self):
56 self.page_count = 0
57
5ea5dc05 58
21167679 59class SyscallEvent():
3b1dda96
AB
60 def __init__(self, name, begin_ts):
61 self.name = name
62 self.begin_ts = begin_ts
63 self.end_ts = None
21167679
JD
64 self.ret = None
65 self.duration = None
a4a9fb7b
AB
66 # Only applicable to I/O syscalls
67 self.io_rq = None
bd3cd7c5 68
3b1dda96
AB
69 def process_exit(self, event):
70 self.end_ts = event.timestamp
ef1f7a6d
AB
71 # On certain architectures (notably arm32), lttng-modules
72 # versions prior to 2.8 would erroneously trace certain
73 # syscalls (e.g. mmap2) without their return value. In this
74 # case, get() will simply set self.ret to None. These syscalls
75 # with a None return value should simply be ignored down the
76 # line.
77 self.ret = event.get('ret')
3b1dda96
AB
78 self.duration = self.end_ts - self.begin_ts
79
80 @classmethod
81 def new_from_entry(cls, event):
5ea5dc05
AB
82 name = common.get_syscall_name(event)
83 return cls(name, event.timestamp)
3b1dda96 84
bd3cd7c5
JD
85
86class Disk():
87 def __init__(self):
3b1dda96 88 # pending block IO Requests, indexed by sector
bd3cd7c5 89 self.pending_requests = {}
bd3cd7c5
JD
90
91
bd3cd7c5
JD
92class FDType():
93 unknown = 0
94 disk = 1
95 net = 2
96 # not 100% sure they are network FDs (assumed when net_dev_xmit is
97 # called during a write syscall and the type in unknown).
98 maybe_net = 3
99
8f9a5260
AB
100 @staticmethod
101 def get_fd_type(name, family):
102 if name in SyscallConsts.NET_OPEN_SYSCALLS:
103 if family in SyscallConsts.INET_FAMILIES:
104 return FDType.net
105 if family in SyscallConsts.DISK_FAMILIES:
106 return FDType.disk
107
108 if name in SyscallConsts.DISK_OPEN_SYSCALLS:
109 return FDType.disk
110
111 return FDType.unknown
112
bd3cd7c5
JD
113
114class FD():
ccfc666b
AB
115 def __init__(self, fd, filename='unknown', fd_type=FDType.unknown,
116 cloexec=False, family=None):
117 self.fd = fd
118 self.filename = filename
ccfc666b
AB
119 self.fd_type = fd_type
120 self.cloexec = cloexec
121 self.family = family
bd3cd7c5 122
6f2c9e92
AB
123 @classmethod
124 def new_from_fd(cls, fd):
ccfc666b
AB
125 return cls(fd.fd, fd.filename, fd.fd_type, fd.cloexec, fd.family)
126
127 @classmethod
128 def new_from_open_rq(cls, io_rq):
129 return cls(io_rq.fd, io_rq.filename, io_rq.fd_type, io_rq.cloexec,
130 io_rq.family)
bd3cd7c5
JD
131
132
133class IRQ():
0297cb98 134 def __init__(self, id, cpu_id, begin_ts=None):
f4522f09 135 self.id = id
9b94fe47 136 self.cpu_id = cpu_id
0297cb98
AB
137 self.begin_ts = begin_ts
138 self.end_ts = None
bd3cd7c5 139
4a2c7d60
AB
140 @property
141 def duration(self):
142 if not self.end_ts or not self.begin_ts:
143 return None
144
145 return self.end_ts - self.begin_ts
146
f4522f09
AB
147
148class HardIRQ(IRQ):
0297cb98
AB
149 def __init__(self, id, cpu_id, begin_ts):
150 super().__init__(id, cpu_id, begin_ts)
f4522f09
AB
151 self.ret = None
152
153 @classmethod
154 def new_from_irq_handler_entry(cls, event):
3f52a605 155 id = event['irq']
9b94fe47 156 cpu_id = event['cpu_id']
0297cb98
AB
157 begin_ts = event.timestamp
158 return cls(id, cpu_id, begin_ts)
f4522f09
AB
159
160
161class SoftIRQ(IRQ):
0297cb98
AB
162 def __init__(self, id, cpu_id, raise_ts=None, begin_ts=None):
163 super().__init__(id, cpu_id, begin_ts)
f4522f09
AB
164 self.raise_ts = raise_ts
165
166 @classmethod
167 def new_from_softirq_raise(cls, event):
3f52a605 168 id = event['vec']
9b94fe47 169 cpu_id = event['cpu_id']
f4522f09 170 raise_ts = event.timestamp
9b94fe47 171 return cls(id, cpu_id, raise_ts)
f4522f09
AB
172
173 @classmethod
174 def new_from_softirq_entry(cls, event):
3f52a605 175 id = event['vec']
9b94fe47 176 cpu_id = event['cpu_id']
0297cb98
AB
177 begin_ts = event.timestamp
178 return cls(id, cpu_id, begin_ts=begin_ts)
ca95b1c3 179
bd3cd7c5
JD
180
181class IORequest():
bd3cd7c5
JD
182 # I/O operations
183 OP_OPEN = 1
184 OP_READ = 2
185 OP_WRITE = 3
186 OP_CLOSE = 4
187 OP_SYNC = 5
54106800
AB
188 # Operation used for requests that both read and write,
189 # e.g. splice and sendfile
190 OP_READ_WRITE = 6
bd3cd7c5 191
3b1dda96
AB
192 def __init__(self, begin_ts, size, tid, operation):
193 self.begin_ts = begin_ts
194 self.end_ts = None
bd3cd7c5 195 self.duration = None
3b1dda96
AB
196 # request size in bytes
197 self.size = size
198 self.operation = operation
199 # tid of process that triggered the rq
200 self.tid = tid
a4a9fb7b
AB
201 # Error number if request failed
202 self.errno = None
3b1dda96 203
5157d702
AB
204 @staticmethod
205 def is_equivalent_operation(left_op, right_op):
206 """Predicate used to compare equivalence of IO_OPERATION.
207
208 This method is employed because OP_READ_WRITE behaves like a
209 set containing both OP_READ and OP_WRITE and is therefore
210 equivalent to these operations as well as itself
211 """
212 if left_op == IORequest.OP_READ_WRITE:
213 return right_op in [IORequest.OP_READ, IORequest.OP_WRITE,
214 IORequest.OP_READ_WRITE]
215 if left_op == IORequest.OP_READ:
216 return right_op in [IORequest.OP_READ, IORequest.OP_READ_WRITE]
217 if left_op == IORequest.OP_WRITE:
218 return right_op in [IORequest.OP_WRITE, IORequest.OP_READ_WRITE]
219
220 return left_op == right_op
3b1dda96 221
320e15ef 222
3b1dda96 223class SyscallIORequest(IORequest):
a4a9fb7b
AB
224 def __init__(self, begin_ts, size, tid, operation, syscall_name):
225 super().__init__(begin_ts, None, tid, operation)
bd3cd7c5 226 self.fd = None
a4a9fb7b 227 self.syscall_name = syscall_name
3b1dda96
AB
228 # Number of pages alloc'd/freed/written to disk during the rq
229 self.pages_allocated = 0
230 self.pages_freed = 0
231 self.pages_written = 0
232 # Whether kswapd was forced to wakeup during the rq
bd3cd7c5 233 self.woke_kswapd = False
3b1dda96 234
a4a9fb7b
AB
235 def update_from_exit(self, event):
236 self.end_ts = event.timestamp
237 self.duration = self.end_ts - self.begin_ts
238 if event['ret'] < 0:
239 self.errno = -event['ret']
240
241
242class OpenIORequest(SyscallIORequest):
243 def __init__(self, begin_ts, tid, syscall_name, filename,
244 fd_type):
245 super().__init__(begin_ts, None, tid, IORequest.OP_OPEN, syscall_name)
246 # FD set on syscall exit
247 self.fd = None
248 self.filename = filename
249 self.fd_type = fd_type
250 self.family = None
251 self.cloexec = False
252
253 def update_from_exit(self, event):
254 super().update_from_exit(event)
255 if event['ret'] >= 0:
256 self.fd = event['ret']
257
258 @classmethod
259 def new_from_disk_open(cls, event, tid):
260 begin_ts = event.timestamp
261 name = common.get_syscall_name(event)
262 filename = event['filename']
263
264 req = cls(begin_ts, tid, name, filename, FDType.disk)
265 req.cloexec = event['flags'] & common.O_CLOEXEC == common.O_CLOEXEC
266
267 return req
268
269 @classmethod
270 def new_from_accept(cls, event, tid):
271 # Handle both accept and accept4
272 begin_ts = event.timestamp
273 name = common.get_syscall_name(event)
274 req = cls(begin_ts, tid, name, 'socket', FDType.net)
275
276 if 'family' in event:
277 req.family = event['family']
278 # Set filename to ip:port if INET socket
279 if req.family == socket.AF_INET:
280 req.filename = '%s:%d' % (common.get_v4_addr_str(
281 event['v4addr']), event['sport'])
282
283 return req
284
285 @classmethod
286 def new_from_socket(cls, event, tid):
287 begin_ts = event.timestamp
288 req = cls(begin_ts, tid, 'socket', 'socket', FDType.net)
289
290 if 'family' in event:
291 req.family = event['family']
292
293 return req
294
295 @classmethod
296 def new_from_old_fd(cls, event, tid, old_fd):
297 begin_ts = event.timestamp
298 name = common.get_syscall_name(event)
299 if old_fd is None:
300 filename = 'unknown'
301 fd_type = FDType.unknown
302 else:
303 filename = old_fd.filename
304 fd_type = old_fd.fd_type
305
306 return cls(begin_ts, tid, name, filename, fd_type)
307
308
309class CloseIORequest(SyscallIORequest):
310 def __init__(self, begin_ts, tid, fd):
311 super().__init__(begin_ts, None, tid, IORequest.OP_CLOSE, 'close')
312 self.fd = fd
313
314
315class ReadWriteIORequest(SyscallIORequest):
316 def __init__(self, begin_ts, size, tid, operation, syscall_name):
317 super().__init__(begin_ts, size, tid, operation, syscall_name)
1b645a61
AB
318 # The size returned on syscall exit, in bytes. May differ from
319 # the size initially requested
320 self.returned_size = None
a4a9fb7b
AB
321 # Unused if fd is set
322 self.fd_in = None
323 self.fd_out = None
324
325 def update_from_exit(self, event):
326 super().update_from_exit(event)
327 ret = event['ret']
328 if ret >= 0:
329 self.returned_size = ret
330 # Set the size to the returned one if none was set at
331 # entry, as with recvmsg or sendmsg
332 if self.size is None:
333 self.size = ret
334
335 @classmethod
336 def new_from_splice(cls, event, tid):
337 begin_ts = event.timestamp
338 size = event['len']
339
54106800 340 req = cls(begin_ts, size, tid, IORequest.OP_READ_WRITE, 'splice')
a4a9fb7b
AB
341 req.fd_in = event['fd_in']
342 req.fd_out = event['fd_out']
343
344 return req
345
346 @classmethod
347 def new_from_sendfile64(cls, event, tid):
348 begin_ts = event.timestamp
349 size = event['count']
350
54106800 351 req = cls(begin_ts, size, tid, IORequest.OP_READ_WRITE, 'sendfile64')
a4a9fb7b
AB
352 req.fd_in = event['in_fd']
353 req.fd_out = event['out_fd']
354
355 return req
356
357 @classmethod
358 def new_from_fd_event(cls, event, tid, size_key):
359 begin_ts = event.timestamp
360 # Some events, like recvmsg or sendmsg, only have size info on return
361 if size_key is not None:
362 size = event[size_key]
363 else:
364 size = None
365
366 syscall_name = common.get_syscall_name(event)
367 if syscall_name in SyscallConsts.READ_SYSCALLS:
368 operation = IORequest.OP_READ
369 else:
370 operation = IORequest.OP_WRITE
371
372 req = cls(begin_ts, size, tid, operation, syscall_name)
373 req.fd = event['fd']
374
375 return req
376
377
378class SyncIORequest(SyscallIORequest):
379 def __init__(self, begin_ts, size, tid, syscall_name):
380 super().__init__(begin_ts, size, tid, IORequest.OP_SYNC, syscall_name)
381
a4a9fb7b
AB
382 @classmethod
383 def new_from_sync(cls, event, tid):
384 begin_ts = event.timestamp
385 size = None
386
387 return cls(begin_ts, size, tid, 'sync')
388
389 @classmethod
390 def new_from_fsync(cls, event, tid):
391 # Also handle fdatasync
392 begin_ts = event.timestamp
393 size = None
394 syscall_name = common.get_syscall_name(event)
395
396 req = cls(begin_ts, size, tid, syscall_name)
397 req.fd = event['fd']
398
399 return req
400
401 @classmethod
402 def new_from_sync_file_range(cls, event, tid):
403 begin_ts = event.timestamp
404 size = event['nbytes']
405
406 req = cls(begin_ts, size, tid, 'sync_file_range')
407 req.fd = event['fd']
408
409 return req
410
3b1dda96
AB
411
412class BlockIORequest(IORequest):
413 # Logical sector size in bytes, according to the kernel
414 SECTOR_SIZE = 512
415
416 def __init__(self, begin_ts, tid, operation, dev, sector, nr_sector):
417 size = nr_sector * BlockIORequest.SECTOR_SIZE
418 super().__init__(begin_ts, size, tid, operation)
419 self.dev = dev
420 self.sector = sector
421 self.nr_sector = nr_sector
422
423 def update_from_rq_complete(self, event):
424 self.end_ts = event.timestamp
425 self.duration = self.end_ts - self.begin_ts
426
427 @classmethod
428 def new_from_rq_issue(cls, event):
429 begin_ts = event.timestamp
430 dev = event['dev']
431 sector = event['sector']
432 nr_sector = event['nr_sector']
433 tid = event['tid']
434 # An even rwbs indicates read operation, odd indicates write
435 if event['rwbs'] % 2 == 0:
436 operation = IORequest.OP_READ
437 else:
438 operation = IORequest.OP_WRITE
439
440 return cls(begin_ts, tid, operation, dev, sector, nr_sector)
441
442
443class BlockRemapRequest():
444 def __init__(self, dev, sector, old_dev, old_sector):
445 self.dev = dev
446 self.sector = sector
447 self.old_dev = old_dev
448 self.old_sector = old_sector
bd3cd7c5
JD
449
450
bd3cd7c5
JD
451class SyscallConsts():
452 # TODO: decouple socket/family logic from this class
453 INET_FAMILIES = [socket.AF_INET, socket.AF_INET6]
454 DISK_FAMILIES = [socket.AF_UNIX]
455 # list nof syscalls that open a FD on disk (in the exit_syscall event)
baa4cfe9 456 DISK_OPEN_SYSCALLS = ['open', 'openat']
bd3cd7c5
JD
457 # list of syscalls that open a FD on the network
458 # (in the exit_syscall event)
a31d2d56 459 NET_OPEN_SYSCALLS = ['socket']
bd3cd7c5 460 # list of syscalls that can duplicate a FD
9110f542 461 DUP_OPEN_SYSCALLS = ['fcntl', 'dup', 'dup2', 'dup3']
baa4cfe9 462 SYNC_SYSCALLS = ['sync', 'sync_file_range', 'fsync', 'fdatasync']
bd3cd7c5
JD
463 # merge the 3 open lists
464 OPEN_SYSCALLS = DISK_OPEN_SYSCALLS + NET_OPEN_SYSCALLS + DUP_OPEN_SYSCALLS
3f52a605 465 # list of syscalls that close a FD (in the 'fd =' field)
baa4cfe9 466 CLOSE_SYSCALLS = ['close']
bd3cd7c5 467 # list of syscall that read on a FD, value in the exit_syscall following
a692ae7b
AB
468 READ_SYSCALLS = ['read', 'recvmsg', 'recvfrom', 'readv', 'pread',
469 'pread64', 'preadv']
bd3cd7c5 470 # list of syscall that write on a FD, value in the exit_syscall following
a692ae7b
AB
471 WRITE_SYSCALLS = ['write', 'sendmsg', 'sendto', 'writev', 'pwrite',
472 'pwrite64', 'pwritev']
54106800
AB
473 # list of syscalls that both read and write on two FDs
474 READ_WRITE_SYSCALLS = ['splice', 'sendfile64']
21167679
JD
475 # All I/O related syscalls
476 IO_SYSCALLS = OPEN_SYSCALLS + CLOSE_SYSCALLS + READ_SYSCALLS + \
54106800 477 WRITE_SYSCALLS + SYNC_SYSCALLS + READ_WRITE_SYSCALLS
This page took 0.046768 seconds and 5 git commands to generate.