3 # The MIT License (MIT)
5 # Copyright (C) 2015 - Julien Desfossez <jdesfossez@efficios.com>
6 # 2015 - Antoine Busque <abusque@efficios.com>
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:
15 # The above copyright notice and this permission notice shall be included in
16 # all copies or substantial portions of the Software.
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
27 from linuxautomaton
import sp
, sv
, common
28 from babeltrace
import CTFScope
31 class IoStateProvider(sp
.StateProvider
):
32 def __init__(self
, state
):
34 'syscall_entry': self
._process
_syscall
_entry
,
35 'syscall_exit': self
._process
_syscall
_exit
,
36 'syscall_entry_connect': self
._process
_connect
,
37 'writeback_pages_written': self
._process
_writeback
_pages
_written
,
38 'mm_vmscan_wakeup_kswapd': self
._process
_mm
_vmscan
_wakeup
_kswapd
,
39 'mm_page_free': self
._process
_mm
_page
_free
43 self
._register
_cbs
(cbs
)
45 def process_event(self
, ev
):
46 self
._process
_event
_cb
(ev
)
48 def _process_syscall_entry(self
, event
):
49 # Only handle IO Syscalls
50 name
= common
.get_syscall_name(event
)
51 if name
not in sv
.SyscallConsts
.IO_SYSCALLS
:
54 cpu_id
= event
['cpu_id']
55 if cpu_id
not in self
._state
.cpus
:
58 cpu
= self
._state
.cpus
[cpu_id
]
59 if cpu
.current_tid
is None:
62 proc
= self
._state
.tids
[cpu
.current_tid
]
64 # check if we can fix the pid from a context
65 self
._fix
_context
_pid
(event
, proc
)
67 if name
in sv
.SyscallConsts
.OPEN_SYSCALLS
:
68 self
._track
_open
(event
, name
, proc
)
69 elif name
in sv
.SyscallConsts
.CLOSE_SYSCALLS
:
70 self
._track
_close
(event
, name
, proc
)
71 elif name
in sv
.SyscallConsts
.READ_SYSCALLS
or \
72 name
in sv
.SyscallConsts
.WRITE_SYSCALLS
:
73 self
._track
_read
_write
(event
, name
, proc
)
74 elif name
in sv
.SyscallConsts
.SYNC_SYSCALLS
:
75 self
._track
_sync
(event
, name
, proc
)
77 def _process_syscall_exit(self
, event
):
78 cpu_id
= event
['cpu_id']
79 if cpu_id
not in self
._state
.cpus
:
82 cpu
= self
._state
.cpus
[cpu_id
]
83 if cpu
.current_tid
is None:
86 proc
= self
._state
.tids
[cpu
.current_tid
]
87 current_syscall
= proc
.current_syscall
88 if current_syscall
is None:
91 name
= current_syscall
.name
92 if name
not in sv
.SyscallConsts
.IO_SYSCALLS
:
95 self
._track
_io
_rq
_exit
(event
, proc
)
97 proc
.current_syscall
= None
99 def _process_connect(self
, event
):
100 cpu_id
= event
['cpu_id']
101 if cpu_id
not in self
._state
.cpus
:
104 cpu
= self
._state
.cpus
[cpu_id
]
105 if cpu
.current_tid
is None:
108 proc
= self
._state
.tids
[cpu
.current_tid
]
109 parent_proc
= self
._get
_parent
_proc
(proc
)
111 # FIXME: handle on syscall_exit_connect only when succesful
112 if 'family' in event
and event
['family'] == socket
.AF_INET
:
114 if fd
in parent_proc
.fds
:
115 parent_proc
.fds
[fd
].filename
= (
116 '%s:%d' % (common
.get_v4_addr_str(event
['v4addr']),
119 def _process_writeback_pages_written(self
, event
):
120 for cpu
in self
._state
.cpus
.values():
121 if cpu
.current_tid
is None:
124 current_syscall
= self
._state
.tids
[cpu
.current_tid
].current_syscall
125 if current_syscall
is None:
128 if current_syscall
.io_rq
:
129 current_syscall
.io_rq
.pages_written
+= event
['pages']
131 def _process_mm_vmscan_wakeup_kswapd(self
, event
):
132 cpu_id
= event
['cpu_id']
133 if cpu_id
not in self
._state
.cpus
:
136 cpu
= self
._state
.cpus
[cpu_id
]
137 if cpu
.current_tid
is None:
140 current_syscall
= self
._state
.tids
[cpu
.current_tid
].current_syscall
141 if current_syscall
is None:
144 if current_syscall
.io_rq
:
145 current_syscall
.io_rq
.woke_kswapd
= True
147 def _process_mm_page_free(self
, event
):
148 for cpu
in self
._state
.cpus
.values():
149 if cpu
.current_tid
is None:
152 proc
= self
._state
.tids
[cpu
.current_tid
]
154 # if the current process is kswapd0, we need to
155 # attribute the page freed to the process that
157 if proc
.comm
== 'kswapd0' and proc
.prev_tid
> 0:
158 proc
= self
._state
.tids
[proc
.prev_tid
]
160 current_syscall
= proc
.current_syscall
161 if current_syscall
is None:
164 if current_syscall
.io_rq
and current_syscall
.io_rq
.woke_kswapd
:
165 current_syscall
.io_rq
.pages_freed
+= 1
167 def _track_open(self
, event
, name
, proc
):
168 current_syscall
= proc
.current_syscall
169 if name
in sv
.SyscallConsts
.DISK_OPEN_SYSCALLS
:
170 current_syscall
.io_rq
= sv
.OpenIORequest
.new_from_disk_open(
172 elif name
in ['accept', 'accept4']:
173 current_syscall
.io_rq
= sv
.OpenIORequest
.new_from_accept(
175 elif name
== 'socket':
176 current_syscall
.io_rq
= sv
.OpenIORequest
.new_from_socket(
178 elif name
in sv
.SyscallConsts
.DUP_OPEN_SYSCALLS
:
179 self
._track
_dup
(event
, name
, proc
)
181 def _track_dup(self
, event
, name
, proc
):
182 current_syscall
= proc
.current_syscall
184 # If the process that triggered the io_rq is a thread,
185 # its FDs are that of the parent process
186 parent_proc
= self
._get
_parent
_proc
(proc
)
187 fds
= parent_proc
.fds
190 oldfd
= event
['fildes']
191 elif name
in ['dup2', 'dup3']:
192 oldfd
= event
['oldfd']
193 newfd
= event
['newfd']
195 self
._close
_fd
(parent_proc
, newfd
, event
.timestamp
)
196 elif name
== 'fcntl':
197 # Only handle if cmd == F_DUPFD (0)
198 if event
['cmd'] != 0:
205 old_file
= fds
[oldfd
]
207 current_syscall
.io_rq
= sv
.OpenIORequest
.new_from_old_fd(
208 event
, proc
.tid
, old_file
)
211 cloexec
= event
['flags'] & common
.O_CLOEXEC
== common
.O_CLOEXEC
212 current_syscall
.io_rq
.cloexec
= cloexec
214 def _track_close(self
, event
, name
, proc
):
215 proc
.current_syscall
.io_rq
= sv
.CloseIORequest(
216 event
.timestamp
, proc
.tid
, event
['fd'])
218 def _track_read_write(self
, event
, name
, proc
):
219 current_syscall
= proc
.current_syscall
222 current_syscall
.io_rq
= sv
.ReadWriteIORequest
.new_from_splice(
225 elif name
== 'sendfile64':
226 current_syscall
.io_rq
= sv
.ReadWriteIORequest
.new_from_sendfile64(
230 if name
in ['writev', 'pwritev', 'readv', 'preadv']:
232 elif name
== 'recvfrom':
234 elif name
== 'sendto':
236 elif name
in ['recvmsg', 'sendmsg']:
241 current_syscall
.io_rq
= sv
.ReadWriteIORequest
.new_from_fd_event(
242 event
, proc
.tid
, size_key
)
244 def _track_sync(self
, event
, name
, proc
):
245 current_syscall
= proc
.current_syscall
248 current_syscall
.io_rq
= sv
.SyncIORequest
.new_from_sync(
250 elif name
in ['fsync', 'fdatasync']:
251 current_syscall
.io_rq
= sv
.SyncIORequest
.new_from_fsync(
253 elif name
== 'sync_file_range':
254 current_syscall
.io_rq
= sv
.SyncIORequest
.new_from_sync_file_range(
257 def _track_io_rq_exit(self
, event
, proc
):
259 io_rq
= proc
.current_syscall
.io_rq
260 # io_rq can be None in the case of fcntl when cmd is not
261 # F_DUPFD, in which case we disregard the syscall as it did
266 io_rq
.update_from_exit(event
)
269 self
._create
_fd
(proc
, io_rq
)
271 parent_proc
= self
._get
_parent
_proc
(proc
)
272 self
._state
.send_notification_cb('io_rq_exit',
275 parent_proc
=parent_proc
)
277 if isinstance(io_rq
, sv
.CloseIORequest
) and ret
== 0:
278 self
._close
_fd
(proc
, io_rq
.fd
, io_rq
.end_ts
)
280 def _create_fd(self
, proc
, io_rq
):
281 parent_proc
= self
._get
_parent
_proc
(proc
)
283 if io_rq
.fd
is not None and io_rq
.fd
not in parent_proc
.fds
:
284 if isinstance(io_rq
, sv
.OpenIORequest
):
285 parent_proc
.fds
[io_rq
.fd
] = sv
.FD
.new_from_open_rq(io_rq
)
287 parent_proc
.fds
[io_rq
.fd
] = sv
.FD(io_rq
.fd
)
289 self
._state
.send_notification_cb('create_fd',
291 parent_proc
=parent_proc
,
292 timestamp
=io_rq
.end_ts
)
293 elif isinstance(io_rq
, sv
.ReadWriteIORequest
):
294 if io_rq
.fd_in
is not None and io_rq
.fd_in
not in parent_proc
.fds
:
295 parent_proc
.fds
[io_rq
.fd_in
] = sv
.FD(io_rq
.fd_in
)
296 self
._state
.send_notification_cb('create_fd',
298 parent_proc
=parent_proc
,
299 timestamp
=io_rq
.end_ts
)
301 if io_rq
.fd_out
is not None and \
302 io_rq
.fd_out
not in parent_proc
.fds
:
303 parent_proc
.fds
[io_rq
.fd_out
] = sv
.FD(io_rq
.fd_out
)
304 self
._state
.send_notification_cb('create_fd',
306 parent_proc
=parent_proc
,
307 timestamp
=io_rq
.end_ts
)
309 def _close_fd(self
, proc
, fd
, timestamp
):
310 parent_proc
= self
._get
_parent
_proc
(proc
)
311 self
._state
.send_notification_cb('close_fd',
313 parent_proc
=parent_proc
,
315 del parent_proc
.fds
[fd
]
317 def _get_parent_proc(self
, proc
):
318 if proc
.pid
is not None and proc
.tid
!= proc
.pid
:
319 parent_proc
= self
._state
.tids
[proc
.pid
]
325 def _fix_context_pid(self
, event
, proc
):
326 for context
in event
.field_list_with_scope(
327 CTFScope
.STREAM_EVENT_CONTEXT
):
330 # make sure the 'pid' field is not also in the event
331 # payload, otherwise we might clash
332 for context
in event
.field_list_with_scope(
333 CTFScope
.EVENT_FIELDS
):
338 proc
.pid
= event
['pid']
339 if event
['pid'] != proc
.tid
:
340 proc
.pid
= event
['pid']
341 parent_proc
= sv
.Process(proc
.pid
, proc
.pid
, proc
.comm
)
342 self
._state
.tids
[parent_proc
.pid
] = parent_proc
This page took 0.074305 seconds and 5 git commands to generate.