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
29 def __init__(self
, tid
=None, pid
=None, comm
='', prio
=None):
36 self
.current_syscall
= None
37 # the process scheduled before this one
39 self
.last_wakeup
= None
40 self
.last_waker
= None
44 def __init__(self
, cpu_id
):
46 self
.current_tid
= None
47 self
.current_hard_irq
= None
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
= {}
54 class MemoryManagement():
60 def __init__(self
, name
, begin_ts
):
62 self
.begin_ts
= begin_ts
66 # Only applicable to I/O syscalls
69 def process_exit(self
, event
):
70 self
.end_ts
= event
.timestamp
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
77 self
.ret
= event
.get('ret')
78 self
.duration
= self
.end_ts
- self
.begin_ts
81 def new_from_entry(cls
, event
):
82 name
= common
.get_syscall_name(event
)
83 return cls(name
, event
.timestamp
)
88 # pending block IO Requests, indexed by sector
89 self
.pending_requests
= {}
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).
101 def get_fd_type(name
, family
):
102 if name
in SyscallConsts
.NET_OPEN_SYSCALLS
:
103 if family
in SyscallConsts
.INET_FAMILIES
:
105 if family
in SyscallConsts
.DISK_FAMILIES
:
108 if name
in SyscallConsts
.DISK_OPEN_SYSCALLS
:
111 return FDType
.unknown
115 def __init__(self
, fd
, filename
='unknown', fd_type
=FDType
.unknown
,
116 cloexec
=False, family
=None):
118 self
.filename
= filename
119 self
.fd_type
= fd_type
120 self
.cloexec
= cloexec
124 def new_from_fd(cls
, fd
):
125 return cls(fd
.fd
, fd
.filename
, fd
.fd_type
, fd
.cloexec
, fd
.family
)
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
,
134 def __init__(self
, id, cpu_id
, begin_ts
=None):
137 self
.begin_ts
= begin_ts
142 if not self
.end_ts
or not self
.begin_ts
:
145 return self
.end_ts
- self
.begin_ts
149 def __init__(self
, id, cpu_id
, begin_ts
):
150 super().__init
__(id, cpu_id
, begin_ts
)
154 def new_from_irq_handler_entry(cls
, event
):
156 cpu_id
= event
['cpu_id']
157 begin_ts
= event
.timestamp
158 return cls(id, cpu_id
, begin_ts
)
162 def __init__(self
, id, cpu_id
, raise_ts
=None, begin_ts
=None):
163 super().__init
__(id, cpu_id
, begin_ts
)
164 self
.raise_ts
= raise_ts
167 def new_from_softirq_raise(cls
, event
):
169 cpu_id
= event
['cpu_id']
170 raise_ts
= event
.timestamp
171 return cls(id, cpu_id
, raise_ts
)
174 def new_from_softirq_entry(cls
, event
):
176 cpu_id
= event
['cpu_id']
177 begin_ts
= event
.timestamp
178 return cls(id, cpu_id
, begin_ts
=begin_ts
)
188 # Operation used for requests that both read and write,
189 # e.g. splice and sendfile
192 def __init__(self
, begin_ts
, size
, tid
, operation
):
193 self
.begin_ts
= begin_ts
196 # request size in bytes
198 self
.operation
= operation
199 # tid of process that triggered the rq
201 # Error number if request failed
205 def is_equivalent_operation(left_op
, right_op
):
206 """Predicate used to compare equivalence of IO_OPERATION.
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
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
]
220 return left_op
== right_op
223 class SyscallIORequest(IORequest
):
224 def __init__(self
, begin_ts
, size
, tid
, operation
, syscall_name
):
225 super().__init
__(begin_ts
, None, tid
, operation
)
227 self
.syscall_name
= syscall_name
228 # Number of pages alloc'd/freed/written to disk during the rq
229 self
.pages_allocated
= 0
231 self
.pages_written
= 0
232 # Whether kswapd was forced to wakeup during the rq
233 self
.woke_kswapd
= False
235 def update_from_exit(self
, event
):
236 self
.end_ts
= event
.timestamp
237 self
.duration
= self
.end_ts
- self
.begin_ts
239 self
.errno
= -event
['ret']
242 class OpenIORequest(SyscallIORequest
):
243 def __init__(self
, begin_ts
, tid
, syscall_name
, filename
,
245 super().__init
__(begin_ts
, None, tid
, IORequest
.OP_OPEN
, syscall_name
)
246 # FD set on syscall exit
248 self
.filename
= filename
249 self
.fd_type
= fd_type
253 def update_from_exit(self
, event
):
254 super().update_from_exit(event
)
255 if event
['ret'] >= 0:
256 self
.fd
= event
['ret']
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']
264 req
= cls(begin_ts
, tid
, name
, filename
, FDType
.disk
)
265 req
.cloexec
= event
['flags'] & common
.O_CLOEXEC
== common
.O_CLOEXEC
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
)
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'])
286 def new_from_socket(cls
, event
, tid
):
287 begin_ts
= event
.timestamp
288 req
= cls(begin_ts
, tid
, 'socket', 'socket', FDType
.net
)
290 if 'family' in event
:
291 req
.family
= event
['family']
296 def new_from_old_fd(cls
, event
, tid
, old_fd
):
297 begin_ts
= event
.timestamp
298 name
= common
.get_syscall_name(event
)
301 fd_type
= FDType
.unknown
303 filename
= old_fd
.filename
304 fd_type
= old_fd
.fd_type
306 return cls(begin_ts
, tid
, name
, filename
, fd_type
)
309 class CloseIORequest(SyscallIORequest
):
310 def __init__(self
, begin_ts
, tid
, fd
):
311 super().__init
__(begin_ts
, None, tid
, IORequest
.OP_CLOSE
, 'close')
315 class ReadWriteIORequest(SyscallIORequest
):
316 def __init__(self
, begin_ts
, size
, tid
, operation
, syscall_name
):
317 super().__init
__(begin_ts
, size
, tid
, operation
, syscall_name
)
318 # The size returned on syscall exit, in bytes. May differ from
319 # the size initially requested
320 self
.returned_size
= None
321 # Unused if fd is set
325 def update_from_exit(self
, event
):
326 super().update_from_exit(event
)
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:
336 def new_from_splice(cls
, event
, tid
):
337 begin_ts
= event
.timestamp
340 req
= cls(begin_ts
, size
, tid
, IORequest
.OP_READ_WRITE
, 'splice')
341 req
.fd_in
= event
['fd_in']
342 req
.fd_out
= event
['fd_out']
347 def new_from_sendfile64(cls
, event
, tid
):
348 begin_ts
= event
.timestamp
349 size
= event
['count']
351 req
= cls(begin_ts
, size
, tid
, IORequest
.OP_READ_WRITE
, 'sendfile64')
352 req
.fd_in
= event
['in_fd']
353 req
.fd_out
= event
['out_fd']
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
]
366 syscall_name
= common
.get_syscall_name(event
)
367 if syscall_name
in SyscallConsts
.READ_SYSCALLS
:
368 operation
= IORequest
.OP_READ
370 operation
= IORequest
.OP_WRITE
372 req
= cls(begin_ts
, size
, tid
, operation
, syscall_name
)
378 class SyncIORequest(SyscallIORequest
):
379 def __init__(self
, begin_ts
, size
, tid
, syscall_name
):
380 super().__init
__(begin_ts
, size
, tid
, IORequest
.OP_SYNC
, syscall_name
)
383 def new_from_sync(cls
, event
, tid
):
384 begin_ts
= event
.timestamp
387 return cls(begin_ts
, size
, tid
, 'sync')
390 def new_from_fsync(cls
, event
, tid
):
391 # Also handle fdatasync
392 begin_ts
= event
.timestamp
394 syscall_name
= common
.get_syscall_name(event
)
396 req
= cls(begin_ts
, size
, tid
, syscall_name
)
402 def new_from_sync_file_range(cls
, event
, tid
):
403 begin_ts
= event
.timestamp
404 size
= event
['nbytes']
406 req
= cls(begin_ts
, size
, tid
, 'sync_file_range')
412 class BlockIORequest(IORequest
):
413 # Logical sector size in bytes, according to the kernel
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
)
421 self
.nr_sector
= nr_sector
423 def update_from_rq_complete(self
, event
):
424 self
.end_ts
= event
.timestamp
425 self
.duration
= self
.end_ts
- self
.begin_ts
428 def new_from_rq_issue(cls
, event
):
429 begin_ts
= event
.timestamp
431 sector
= event
['sector']
432 nr_sector
= event
['nr_sector']
434 # An even rwbs indicates read operation, odd indicates write
435 if event
['rwbs'] % 2 == 0:
436 operation
= IORequest
.OP_READ
438 operation
= IORequest
.OP_WRITE
440 return cls(begin_ts
, tid
, operation
, dev
, sector
, nr_sector
)
443 class BlockRemapRequest():
444 def __init__(self
, dev
, sector
, old_dev
, old_sector
):
447 self
.old_dev
= old_dev
448 self
.old_sector
= old_sector
451 class 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)
456 DISK_OPEN_SYSCALLS
= ['open', 'openat']
457 # list of syscalls that open a FD on the network
458 # (in the exit_syscall event)
459 NET_OPEN_SYSCALLS
= ['socket']
460 # list of syscalls that can duplicate a FD
461 DUP_OPEN_SYSCALLS
= ['fcntl', 'dup', 'dup2', 'dup3']
462 SYNC_SYSCALLS
= ['sync', 'sync_file_range', 'fsync', 'fdatasync']
463 # merge the 3 open lists
464 OPEN_SYSCALLS
= DISK_OPEN_SYSCALLS
+ NET_OPEN_SYSCALLS
+ DUP_OPEN_SYSCALLS
465 # list of syscalls that close a FD (in the 'fd =' field)
466 CLOSE_SYSCALLS
= ['close']
467 # list of syscall that read on a FD, value in the exit_syscall following
468 READ_SYSCALLS
= ['read', 'recvmsg', 'recvfrom', 'readv', 'pread',
470 # list of syscall that write on a FD, value in the exit_syscall following
471 WRITE_SYSCALLS
= ['write', 'sendmsg', 'sendto', 'writev', 'pwrite',
472 'pwrite64', 'pwritev']
473 # list of syscalls that both read and write on two FDs
474 READ_WRITE_SYSCALLS
= ['splice', 'sendfile64']
475 # All I/O related syscalls
476 IO_SYSCALLS
= OPEN_SYSCALLS
+ CLOSE_SYSCALLS
+ READ_SYSCALLS
+ \
477 WRITE_SYSCALLS
+ SYNC_SYSCALLS
+ READ_WRITE_SYSCALLS
This page took 0.042209 seconds and 5 git commands to generate.