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