c18d6ded4632f5815da36a1e3747b1073e251c21
1 # The MIT License (MIT)
3 # Copyright (C) 2015 - Julien Desfossez <jdesfossez@efficios.com>
4 # 2015 - Antoine Busque <abusque@efficios.com>
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:
13 # The above copyright notice and this permission notice shall be included in
14 # all copies or substantial portions of the Software.
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
33 def __init__(self
, tid
=None, pid
=None, comm
='', prio
=None):
40 self
.current_syscall
= None
41 # the process scheduled before this one
43 self
.last_wakeup
= None
44 self
.last_waker
= None
48 def __init__(self
, cpu_id
):
50 self
.current_tid
= None
51 self
.current_hard_irq
= None
52 # softirqs use a dict because multiple ones can be raised before
53 # handling. They are indexed by vec, and each entry is a list,
54 # ordered chronologically
55 self
.current_softirqs
= {}
58 class MemoryManagement():
64 def __init__(self
, name
, begin_ts
):
66 self
.begin_ts
= begin_ts
70 # Only applicable to I/O syscalls
73 def process_exit(self
, event
):
74 self
.end_ts
= event
.timestamp
76 self
.ret
= event
['ret']
78 print("[warning] syscall %s does not have a return code, that "
79 "should not happen" % event
.name
)
81 self
.duration
= self
.end_ts
- self
.begin_ts
84 def new_from_entry(cls
, event
):
85 name
= common
.get_syscall_name(event
)
86 return cls(name
, event
.timestamp
)
91 # pending block IO Requests, indexed by sector
92 self
.pending_requests
= {}
99 # not 100% sure they are network FDs (assumed when net_dev_xmit is
100 # called during a write syscall and the type in unknown).
104 def get_fd_type(name
, family
):
105 if name
in SyscallConsts
.NET_OPEN_SYSCALLS
:
106 if family
in SyscallConsts
.INET_FAMILIES
:
108 if family
in SyscallConsts
.DISK_FAMILIES
:
111 if name
in SyscallConsts
.DISK_OPEN_SYSCALLS
:
114 return FDType
.unknown
118 def __init__(self
, fd
, filename
='unknown', fd_type
=FDType
.unknown
,
119 cloexec
=False, family
=None):
121 self
.filename
= filename
122 self
.fd_type
= fd_type
123 self
.cloexec
= cloexec
127 def new_from_fd(cls
, fd
):
128 return cls(fd
.fd
, fd
.filename
, fd
.fd_type
, fd
.cloexec
, fd
.family
)
131 def new_from_open_rq(cls
, io_rq
):
132 return cls(io_rq
.fd
, io_rq
.filename
, io_rq
.fd_type
, io_rq
.cloexec
,
137 def __init__(self
, id, cpu_id
, begin_ts
=None):
140 self
.begin_ts
= begin_ts
145 if not self
.end_ts
or not self
.begin_ts
:
148 return self
.end_ts
- self
.begin_ts
152 def __init__(self
, id, cpu_id
, begin_ts
):
153 super().__init
__(id, cpu_id
, begin_ts
)
157 def new_from_irq_handler_entry(cls
, event
):
159 cpu_id
= event
['cpu_id']
160 begin_ts
= event
.timestamp
161 return cls(id, cpu_id
, begin_ts
)
165 def __init__(self
, id, cpu_id
, raise_ts
=None, begin_ts
=None):
166 super().__init
__(id, cpu_id
, begin_ts
)
167 self
.raise_ts
= raise_ts
170 def new_from_softirq_raise(cls
, event
):
172 cpu_id
= event
['cpu_id']
173 raise_ts
= event
.timestamp
174 return cls(id, cpu_id
, raise_ts
)
177 def new_from_softirq_entry(cls
, event
):
179 cpu_id
= event
['cpu_id']
180 begin_ts
= event
.timestamp
181 return cls(id, cpu_id
, begin_ts
=begin_ts
)
191 # Operation used for requests that both read and write,
192 # e.g. splice and sendfile
195 def __init__(self
, begin_ts
, size
, tid
, operation
):
196 self
.begin_ts
= begin_ts
199 # request size in bytes
201 self
.operation
= operation
202 # tid of process that triggered the rq
204 # Error number if request failed
208 def is_equivalent_operation(left_op
, right_op
):
209 """Predicate used to compare equivalence of IO_OPERATION.
211 This method is employed because OP_READ_WRITE behaves like a
212 set containing both OP_READ and OP_WRITE and is therefore
213 equivalent to these operations as well as itself
215 if left_op
== IORequest
.OP_READ_WRITE
:
216 return right_op
in [IORequest
.OP_READ
, IORequest
.OP_WRITE
,
217 IORequest
.OP_READ_WRITE
]
218 if left_op
== IORequest
.OP_READ
:
219 return right_op
in [IORequest
.OP_READ
, IORequest
.OP_READ_WRITE
]
220 if left_op
== IORequest
.OP_WRITE
:
221 return right_op
in [IORequest
.OP_WRITE
, IORequest
.OP_READ_WRITE
]
223 return left_op
== right_op
226 class SyscallIORequest(IORequest
):
227 def __init__(self
, begin_ts
, size
, tid
, operation
, syscall_name
):
228 super().__init
__(begin_ts
, None, tid
, operation
)
230 self
.syscall_name
= syscall_name
231 # Number of pages alloc'd/freed/written to disk during the rq
232 self
.pages_allocated
= 0
234 self
.pages_written
= 0
235 # Whether kswapd was forced to wakeup during the rq
236 self
.woke_kswapd
= False
238 def update_from_exit(self
, event
):
239 self
.end_ts
= event
.timestamp
240 self
.duration
= self
.end_ts
- self
.begin_ts
242 self
.errno
= -event
['ret']
245 class OpenIORequest(SyscallIORequest
):
246 def __init__(self
, begin_ts
, tid
, syscall_name
, filename
,
248 super().__init
__(begin_ts
, None, tid
, IORequest
.OP_OPEN
, syscall_name
)
249 # FD set on syscall exit
251 self
.filename
= filename
252 self
.fd_type
= fd_type
256 def update_from_exit(self
, event
):
257 super().update_from_exit(event
)
258 if event
['ret'] >= 0:
259 self
.fd
= event
['ret']
262 def new_from_disk_open(cls
, event
, tid
):
263 begin_ts
= event
.timestamp
264 name
= common
.get_syscall_name(event
)
265 filename
= event
['filename']
267 req
= cls(begin_ts
, tid
, name
, filename
, FDType
.disk
)
268 req
.cloexec
= event
['flags'] & common
.O_CLOEXEC
== common
.O_CLOEXEC
273 def new_from_accept(cls
, event
, tid
):
274 # Handle both accept and accept4
275 begin_ts
= event
.timestamp
276 name
= common
.get_syscall_name(event
)
277 req
= cls(begin_ts
, tid
, name
, 'socket', FDType
.net
)
279 if 'family' in event
:
280 req
.family
= event
['family']
281 # Set filename to ip:port if INET socket
282 if req
.family
== socket
.AF_INET
:
283 req
.filename
= '%s:%d' % (common
.get_v4_addr_str(
284 event
['v4addr']), event
['sport'])
289 def new_from_socket(cls
, event
, tid
):
290 begin_ts
= event
.timestamp
291 req
= cls(begin_ts
, tid
, 'socket', 'socket', FDType
.net
)
293 if 'family' in event
:
294 req
.family
= event
['family']
299 def new_from_old_fd(cls
, event
, tid
, old_fd
):
300 begin_ts
= event
.timestamp
301 name
= common
.get_syscall_name(event
)
304 fd_type
= FDType
.unknown
306 filename
= old_fd
.filename
307 fd_type
= old_fd
.fd_type
309 return cls(begin_ts
, tid
, name
, filename
, fd_type
)
312 class CloseIORequest(SyscallIORequest
):
313 def __init__(self
, begin_ts
, tid
, fd
):
314 super().__init
__(begin_ts
, None, tid
, IORequest
.OP_CLOSE
, 'close')
318 class ReadWriteIORequest(SyscallIORequest
):
319 def __init__(self
, begin_ts
, size
, tid
, operation
, syscall_name
):
320 super().__init
__(begin_ts
, size
, tid
, operation
, syscall_name
)
321 # The size returned on syscall exit, in bytes. May differ from
322 # the size initially requested
323 self
.returned_size
= None
324 # Unused if fd is set
328 def update_from_exit(self
, event
):
329 super().update_from_exit(event
)
332 self
.returned_size
= ret
333 # Set the size to the returned one if none was set at
334 # entry, as with recvmsg or sendmsg
335 if self
.size
is None:
339 def new_from_splice(cls
, event
, tid
):
340 begin_ts
= event
.timestamp
343 req
= cls(begin_ts
, size
, tid
, IORequest
.OP_READ_WRITE
, 'splice')
344 req
.fd_in
= event
['fd_in']
345 req
.fd_out
= event
['fd_out']
350 def new_from_sendfile64(cls
, event
, tid
):
351 begin_ts
= event
.timestamp
352 size
= event
['count']
354 req
= cls(begin_ts
, size
, tid
, IORequest
.OP_READ_WRITE
, 'sendfile64')
355 req
.fd_in
= event
['in_fd']
356 req
.fd_out
= event
['out_fd']
361 def new_from_fd_event(cls
, event
, tid
, size_key
):
362 begin_ts
= event
.timestamp
363 # Some events, like recvmsg or sendmsg, only have size info on return
364 if size_key
is not None:
365 size
= event
[size_key
]
369 syscall_name
= common
.get_syscall_name(event
)
370 if syscall_name
in SyscallConsts
.READ_SYSCALLS
:
371 operation
= IORequest
.OP_READ
373 operation
= IORequest
.OP_WRITE
375 req
= cls(begin_ts
, size
, tid
, operation
, syscall_name
)
381 class SyncIORequest(SyscallIORequest
):
382 def __init__(self
, begin_ts
, size
, tid
, syscall_name
):
383 super().__init
__(begin_ts
, size
, tid
, IORequest
.OP_SYNC
, syscall_name
)
386 def new_from_sync(cls
, event
, tid
):
387 begin_ts
= event
.timestamp
390 return cls(begin_ts
, size
, tid
, 'sync')
393 def new_from_fsync(cls
, event
, tid
):
394 # Also handle fdatasync
395 begin_ts
= event
.timestamp
397 syscall_name
= common
.get_syscall_name(event
)
399 req
= cls(begin_ts
, size
, tid
, syscall_name
)
405 def new_from_sync_file_range(cls
, event
, tid
):
406 begin_ts
= event
.timestamp
407 size
= event
['nbytes']
409 req
= cls(begin_ts
, size
, tid
, 'sync_file_range')
415 class BlockIORequest(IORequest
):
416 # Logical sector size in bytes, according to the kernel
419 def __init__(self
, begin_ts
, tid
, operation
, dev
, sector
, nr_sector
):
420 size
= nr_sector
* BlockIORequest
.SECTOR_SIZE
421 super().__init
__(begin_ts
, size
, tid
, operation
)
424 self
.nr_sector
= nr_sector
426 def update_from_rq_complete(self
, event
):
427 self
.end_ts
= event
.timestamp
428 self
.duration
= self
.end_ts
- self
.begin_ts
431 def new_from_rq_issue(cls
, event
):
432 begin_ts
= event
.timestamp
434 sector
= event
['sector']
435 nr_sector
= event
['nr_sector']
437 # An even rwbs indicates read operation, odd indicates write
438 if event
['rwbs'] % 2 == 0:
439 operation
= IORequest
.OP_READ
441 operation
= IORequest
.OP_WRITE
443 return cls(begin_ts
, tid
, operation
, dev
, sector
, nr_sector
)
446 class BlockRemapRequest():
447 def __init__(self
, dev
, sector
, old_dev
, old_sector
):
450 self
.old_dev
= old_dev
451 self
.old_sector
= old_sector
454 class SyscallConsts():
455 # TODO: decouple socket/family logic from this class
456 INET_FAMILIES
= [socket
.AF_INET
, socket
.AF_INET6
]
457 DISK_FAMILIES
= [socket
.AF_UNIX
]
458 # list nof syscalls that open a FD on disk (in the exit_syscall event)
459 DISK_OPEN_SYSCALLS
= ['open', 'openat']
460 # list of syscalls that open a FD on the network
461 # (in the exit_syscall event)
462 NET_OPEN_SYSCALLS
= ['socket']
463 # list of syscalls that can duplicate a FD
464 DUP_OPEN_SYSCALLS
= ['fcntl', 'dup', 'dup2', 'dup3']
465 SYNC_SYSCALLS
= ['sync', 'sync_file_range', 'fsync', 'fdatasync']
466 # merge the 3 open lists
467 OPEN_SYSCALLS
= DISK_OPEN_SYSCALLS
+ NET_OPEN_SYSCALLS
+ DUP_OPEN_SYSCALLS
468 # list of syscalls that close a FD (in the 'fd =' field)
469 CLOSE_SYSCALLS
= ['close']
470 # list of syscall that read on a FD, value in the exit_syscall following
471 READ_SYSCALLS
= ['read', 'recvmsg', 'recvfrom', 'readv', 'pread',
473 # list of syscall that write on a FD, value in the exit_syscall following
474 WRITE_SYSCALLS
= ['write', 'sendmsg', 'sendto', 'writev', 'pwrite',
475 'pwrite64', 'pwritev']
476 # list of syscalls that both read and write on two FDs
477 READ_WRITE_SYSCALLS
= ['splice', 'sendfile64']
478 # All I/O related syscalls
479 IO_SYSCALLS
= OPEN_SYSCALLS
+ CLOSE_SYSCALLS
+ READ_SYSCALLS
+ \
480 WRITE_SYSCALLS
+ SYNC_SYSCALLS
+ READ_WRITE_SYSCALLS
This page took 0.040153 seconds and 4 git commands to generate.