Commit | Line | Data |
---|---|---|
a0b1f42c | 1 | #!/usr/bin/env python3 |
9d16b343 MJ |
2 | # |
3 | # Copyright (C) 2016 Julien Desfossez <jdesfossez@efficios.com> | |
4 | # | |
5 | # SPDX-License-Identifier: GPL-2.0-only | |
6 | # | |
a0b1f42c | 7 | |
4c4634e3 FD |
8 | import argparse |
9 | import pprint | |
a0b1f42c JD |
10 | import sys |
11 | import time | |
d40c2620 | 12 | import json |
4c4634e3 FD |
13 | |
14 | from collections import defaultdict | |
a0b1f42c | 15 | |
a0b1f42c | 16 | try: |
53f8be7a | 17 | import bt2 |
a0b1f42c JD |
18 | except ImportError: |
19 | # quick fix for debian-based distros | |
20 | sys.path.append("/usr/local/lib/python%d.%d/site-packages" % | |
21 | (sys.version_info.major, sys.version_info.minor)) | |
53f8be7a | 22 | import bt2 |
a0b1f42c | 23 | |
53f8be7a | 24 | NSEC_PER_SEC = 1000000000 |
a0b1f42c JD |
25 | |
26 | class TraceParser: | |
53f8be7a FD |
27 | def __init__(self, trace_msg_iter, pid): |
28 | self.trace = trace_msg_iter | |
a0b1f42c | 29 | self.pid = pid |
4c4634e3 FD |
30 | |
31 | # This dictionnary holds the results of each testcases of a test. | |
32 | # Its layout is the following: | |
33 | # self.expect={ | |
34 | # 'event_name_1': {'check_1': 0, 'check_2: 1}, | |
35 | # 'event_name_2': {'check_1': 1} | |
36 | # } | |
37 | # Each test classes checks the payload of different events. Each of | |
38 | # those checks are stored in a event_name specific dictionnary in this | |
39 | # data structure. | |
40 | self.expect = defaultdict(lambda : defaultdict(int)) | |
41 | ||
42 | # This dictionnary holds the value recorded in the trace that are | |
43 | # tested. Its content is use to print the values that caused a test to | |
44 | # fail. | |
45 | self.recorded_values = {} | |
a0b1f42c JD |
46 | |
47 | def ns_to_hour_nsec(self, ns): | |
48 | d = time.localtime(ns/NSEC_PER_SEC) | |
49 | return "%02d:%02d:%02d.%09d" % (d.tm_hour, d.tm_min, d.tm_sec, | |
50 | ns % NSEC_PER_SEC) | |
51 | ||
52 | def parse(self): | |
53 | # iterate over all the events | |
53f8be7a FD |
54 | for msg in self.trace: |
55 | if type(msg) is not bt2._EventMessageConst: | |
a0b1f42c JD |
56 | continue |
57 | ||
53f8be7a FD |
58 | if self.pid is not None and msg.event["pid"] != self.pid: |
59 | continue | |
60 | ||
61 | method_name = "handle_%s" % msg.event.name.replace(":", "_").replace( | |
a0b1f42c JD |
62 | "+", "_") |
63 | # call the function to handle each event individually | |
64 | if hasattr(TraceParser, method_name): | |
65 | func = getattr(TraceParser, method_name) | |
53f8be7a | 66 | func(self, msg.event) |
a0b1f42c JD |
67 | |
68 | ret = 0 | |
4c4634e3 FD |
69 | # For each event of the test case, check all entries for failed |
70 | for event_name, event_results in self.expect.items(): | |
71 | for val in event_results.keys(): | |
72 | if self.expect[event_name][val] == 0: | |
73 | print("%s not validated" % val) | |
74 | print("Values of the local variables of this test:") | |
75 | # using pprint for pretty printing the dictionnary | |
76 | pprint.pprint(self.recorded_values[event_name]) | |
77 | ret = 1 | |
a0b1f42c JD |
78 | |
79 | return ret | |
80 | ||
81 | # epoll_ctl | |
82 | def handle_compat_syscall_entry_epoll_ctl(self, event): | |
83 | self.epoll_ctl_entry(event) | |
84 | ||
85 | def handle_compat_syscall_exit_epoll_ctl(self, event): | |
86 | self.epoll_ctl_exit(event) | |
87 | ||
88 | def handle_syscall_entry_epoll_ctl(self, event): | |
89 | self.epoll_ctl_entry(event) | |
90 | ||
91 | def handle_syscall_exit_epoll_ctl(self, event): | |
92 | self.epoll_ctl_exit(event) | |
93 | ||
94 | def epoll_ctl_entry(self, event): | |
95 | pass | |
96 | ||
97 | def epoll_ctl_exit(self, event): | |
98 | pass | |
99 | ||
100 | # epoll_wait + epoll_pwait | |
101 | def handle_compat_syscall_entry_epoll_wait(self, event): | |
102 | self.epoll_wait_entry(event) | |
103 | ||
104 | def handle_compat_syscall_exit_epoll_wait(self, event): | |
105 | self.epoll_wait_exit(event) | |
106 | ||
107 | def handle_syscall_entry_epoll_wait(self, event): | |
108 | self.epoll_wait_entry(event) | |
109 | ||
110 | def handle_syscall_exit_epoll_wait(self, event): | |
111 | self.epoll_wait_exit(event) | |
112 | ||
113 | def handle_compat_syscall_entry_epoll_pwait(self, event): | |
d40c2620 | 114 | self.epoll_pwait_entry(event) |
a0b1f42c JD |
115 | |
116 | def handle_compat_syscall_exit_epoll_pwait(self, event): | |
d40c2620 | 117 | self.epoll_pwait_exit(event) |
a0b1f42c JD |
118 | |
119 | def handle_syscall_entry_epoll_pwait(self, event): | |
d40c2620 | 120 | self.epoll_pwait_entry(event) |
a0b1f42c JD |
121 | |
122 | def handle_syscall_exit_epoll_pwait(self, event): | |
d40c2620 | 123 | self.epoll_pwait_exit(event) |
a0b1f42c JD |
124 | |
125 | def epoll_wait_entry(self, event): | |
126 | pass | |
127 | ||
128 | def epoll_wait_exit(self, event): | |
129 | pass | |
130 | ||
d40c2620 JG |
131 | def epoll_pwait_entry(self, event): |
132 | self.epoll_wait_entry(event) | |
133 | ||
134 | def epoll_pwait_exit(self, event): | |
135 | self.epoll_wait_exit(event) | |
136 | ||
a0b1f42c JD |
137 | ## poll + ppoll |
138 | def handle_compat_syscall_entry_poll(self, event): | |
139 | self.poll_entry(event) | |
140 | ||
141 | def handle_compat_syscall_exit_poll(self, event): | |
142 | self.poll_exit(event) | |
143 | ||
144 | def handle_syscall_entry_poll(self, event): | |
145 | self.poll_entry(event) | |
146 | ||
147 | def handle_syscall_exit_poll(self, event): | |
148 | self.poll_exit(event) | |
149 | ||
150 | def handle_compat_syscall_entry_ppoll(self, event): | |
151 | self.poll_entry(event) | |
152 | ||
153 | def handle_compat_syscall_exit_ppoll(self, event): | |
154 | self.poll_exit(event) | |
155 | ||
156 | def handle_syscall_entry_ppoll(self, event): | |
157 | self.poll_entry(event) | |
158 | ||
159 | def handle_syscall_exit_ppoll(self, event): | |
160 | self.poll_exit(event) | |
161 | ||
162 | def poll_entry(self, event): | |
163 | pass | |
164 | ||
165 | def poll_exit(self, event): | |
166 | pass | |
167 | ||
168 | # epoll_create | |
169 | def handle_compat_syscall_entry_epoll_create1(self, event): | |
170 | self.epoll_create_entry(event) | |
171 | ||
172 | def handle_compat_syscall_exit_epoll_create1(self, event): | |
173 | self.epoll_create_exit(event) | |
174 | ||
175 | def handle_compat_syscall_entry_epoll_create(self, event): | |
176 | self.epoll_create_entry(event) | |
177 | ||
178 | def handle_compat_syscall_exit_epoll_create(self, event): | |
179 | self.epoll_create_exit(event) | |
180 | ||
181 | def handle_syscall_entry_epoll_create1(self, event): | |
182 | self.epoll_create_entry(event) | |
183 | ||
184 | def handle_syscall_exit_epoll_create1(self, event): | |
185 | self.epoll_create_exit(event) | |
186 | ||
187 | def handle_syscall_entry_epoll_create(self, event): | |
188 | self.epoll_create_entry(event) | |
189 | ||
190 | def handle_syscall_exit_epoll_create(self, event): | |
191 | self.epoll_create_exit(event) | |
192 | ||
193 | def epoll_create_entry(self, event): | |
194 | pass | |
195 | ||
196 | def epoll_create_exit(self, event): | |
197 | pass | |
198 | ||
199 | # select + pselect6 | |
200 | def handle_syscall_entry_pselect6(self, event): | |
201 | self.select_entry(event) | |
202 | ||
203 | def handle_syscall_exit_pselect6(self, event): | |
204 | self.select_exit(event) | |
205 | ||
206 | def handle_compat_syscall_entry_pselect6(self, event): | |
207 | self.select_entry(event) | |
208 | ||
209 | def handle_compat_syscall_exit_pselect6(self, event): | |
210 | self.select_exit(event) | |
211 | ||
212 | def handle_syscall_entry_select(self, event): | |
213 | self.select_entry(event) | |
214 | ||
215 | def handle_syscall_exit_select(self, event): | |
216 | self.select_exit(event) | |
217 | ||
218 | def handle_compat_syscall_entry_select(self, event): | |
219 | self.select_entry(event) | |
220 | ||
221 | def handle_compat_syscall_exit_select(self, event): | |
222 | self.select_exit(event) | |
223 | ||
224 | def select_entry(self, event): | |
225 | pass | |
226 | ||
227 | def select_exit(self, event): | |
228 | pass | |
229 | ||
230 | ||
2a2ac572 | 231 | class WorkingCases(TraceParser): |
d40c2620 JG |
232 | def __init__(self, trace, validation_args): |
233 | super().__init__(trace, validation_args['pid']) | |
234 | ||
235 | # Values expected in the trace | |
236 | self.epoll_wait_fd = validation_args['epoll_wait_fd'] | |
237 | self.epoll_pwait_fd = validation_args['epoll_pwait_fd'] | |
238 | ||
4c4634e3 FD |
239 | self.expect["select_entry"]["select_in_fd0"] = 0 |
240 | self.expect["select_entry"]["select_in_fd1023"] = 0 | |
241 | self.expect["select_exit"]["select_out_fd0"] = 0 | |
242 | self.expect["select_exit"]["select_out_fd1023"] = 0 | |
243 | self.expect["poll_entry"]["poll_in_nfds1"] = 0 | |
244 | self.expect["poll_exit"]["poll_out_nfds1"] = 0 | |
245 | self.expect["epoll_ctl_entry"]["epoll_ctl_in_add"] = 0 | |
246 | self.expect["epoll_ctl_exit"]["epoll_ctl_out_ok"] = 0 | |
247 | self.expect["epoll_wait_entry"]["epoll_wait_in_ok"] = 0 | |
248 | self.expect["epoll_wait_exit"]["epoll_wait_out_fd0"] = 0 | |
d40c2620 JG |
249 | self.expect["epoll_pwait_entry"]["epoll_pwait_in_ok"] = 0 |
250 | self.expect["epoll_pwait_exit"]["epoll_pwait_out_fd0"] = 0 | |
a0b1f42c JD |
251 | |
252 | def select_entry(self, event): | |
a0b1f42c JD |
253 | n = event["n"] |
254 | overflow = event["overflow"] | |
4c4634e3 | 255 | readfd_0 = event["readfds"][0] |
a0b1f42c JD |
256 | |
257 | # check that the FD 0 is actually set in the readfds | |
4c4634e3 FD |
258 | if n == 1 and readfd_0 == 1: |
259 | self.expect["select_entry"]["select_in_fd0"] = 1 | |
a0b1f42c | 260 | if n == 1023: |
4c4634e3 FD |
261 | readfd_127 = event["readfds"][127] |
262 | writefd_127 = event["writefds"][127] | |
263 | exceptfd_127 = event["exceptfds"][127] | |
264 | ||
a0b1f42c | 265 | # check that the FD 1023 is actually set in the readfds |
4c4634e3 FD |
266 | if readfd_127 == 0x40 and writefd_127 == 0 and \ |
267 | exceptfd_127 == 0 and overflow == 0: | |
268 | self.expect["select_entry"]["select_in_fd1023"] = 1 | |
269 | ||
270 | # Save values of local variables to print in case of test failure | |
271 | self.recorded_values["select_entry"] = locals() | |
a0b1f42c JD |
272 | |
273 | def select_exit(self, event): | |
a0b1f42c | 274 | ret = event["ret"] |
a0b1f42c | 275 | tvp = event["tvp"] |
4c4634e3 | 276 | overflow = event["overflow"] |
a0b1f42c | 277 | _readfds_length = event["_readfds_length"] |
a0b1f42c JD |
278 | |
279 | if ret == 1: | |
280 | # check that the FD 0 is actually set in the readfds | |
4c4634e3 FD |
281 | readfd_0 = event["readfds"][0] |
282 | ||
283 | if readfd_0 == 1: | |
284 | self.expect["select_exit"]["select_out_fd0"] = 1 | |
a0b1f42c | 285 | # check that the FD 1023 is actually set in the readfds |
4c4634e3 FD |
286 | if _readfds_length == 128: |
287 | readfd_127 = event["readfds"][127] | |
288 | writefd_127 = event["writefds"][127] | |
289 | exceptfd_127 = event["exceptfds"][127] | |
290 | if readfd_127 == 0x40 and writefd_127 == 0 and \ | |
291 | exceptfd_127 == 0 and tvp == 0: | |
292 | self.expect["select_exit"]["select_out_fd1023"] = 1 | |
293 | ||
294 | # Save values of local variables to print in case of test failure | |
295 | self.recorded_values["select_exit"] = locals() | |
a0b1f42c JD |
296 | |
297 | def poll_entry(self, event): | |
a0b1f42c JD |
298 | nfds = event["nfds"] |
299 | fds_length = event["fds_length"] | |
300 | overflow = event["overflow"] | |
a0b1f42c JD |
301 | |
302 | # check that only one FD is set, that it has the POLLIN flag and that | |
303 | # the raw value matches the events bit field. | |
4c4634e3 FD |
304 | if nfds == 1 and fds_length == 1: |
305 | fd_0 = event["fds"][0] | |
306 | if fd_0["raw_events"] == 0x3 and fd_0["events"]["POLLIN"] == 1 and \ | |
307 | fd_0["events"]["padding"] == 0: | |
308 | self.expect["poll_entry"]["poll_in_nfds1"] = 1 | |
309 | ||
310 | # Save values of local variables to print in case of test failure | |
311 | self.recorded_values["poll_entry"] = locals() | |
a0b1f42c JD |
312 | |
313 | def poll_exit(self, event): | |
a0b1f42c | 314 | ret = event["ret"] |
a0b1f42c | 315 | fds_length = event["fds_length"] |
a0b1f42c JD |
316 | |
317 | # check that only one FD is set, that it has the POLLIN flag and that | |
318 | # the raw value matches the events bit field. | |
4c4634e3 FD |
319 | if ret == 1 and fds_length == 1: |
320 | fd_0 = event["fds"][0] | |
321 | if fd_0["raw_events"] == 0x1 and fd_0["events"]["POLLIN"] == 1 and \ | |
322 | fd_0["events"]["padding"] == 0: | |
323 | self.expect["poll_exit"]["poll_out_nfds1"] = 1 | |
324 | ||
325 | # Save values of local variables to print in case of test failure | |
326 | self.recorded_values["poll_exit"] = locals() | |
a0b1f42c JD |
327 | |
328 | def epoll_ctl_entry(self, event): | |
a0b1f42c JD |
329 | epfd = event["epfd"] |
330 | op_enum = event["op_enum"] | |
331 | fd = event["fd"] | |
332 | _event = event["event"] | |
333 | ||
334 | # check that we have FD 0 waiting for EPOLLIN|EPOLLPRI and that | |
335 | # data.fd = 0 | |
d40c2620 | 336 | if (epfd == self.epoll_wait_fd or epfd == self.epoll_pwait_fd) and 'EPOLL_CTL_ADD' in op_enum.labels and fd == 0 and \ |
a0b1f42c JD |
337 | _event["data_union"]["fd"] == 0 and \ |
338 | _event["events"]["EPOLLIN"] == 1 and \ | |
339 | _event["events"]["EPOLLPRI"] == 1: | |
4c4634e3 FD |
340 | self.expect["epoll_ctl_entry"]["epoll_ctl_in_add"] = 1 |
341 | ||
342 | # Save values of local variables to print in case of test failure | |
343 | self.recorded_values["epoll_ctl_entry"] = locals() | |
a0b1f42c JD |
344 | |
345 | def epoll_ctl_exit(self, event): | |
a0b1f42c JD |
346 | ret = event["ret"] |
347 | ||
348 | if ret == 0: | |
4c4634e3 FD |
349 | self.expect["epoll_ctl_exit"]["epoll_ctl_out_ok"] = 1 |
350 | ||
351 | # Save values of local variables to print in case of test failure | |
352 | self.recorded_values["epoll_ctl_exit"] = locals() | |
a0b1f42c JD |
353 | |
354 | def epoll_wait_entry(self, event): | |
a0b1f42c JD |
355 | epfd = event["epfd"] |
356 | maxevents = event["maxevents"] | |
357 | timeout = event["timeout"] | |
358 | ||
d40c2620 | 359 | if epfd == self.epoll_wait_fd and maxevents == 1 and timeout == -1: |
4c4634e3 FD |
360 | self.expect["epoll_wait_entry"]["epoll_wait_in_ok"] = 1 |
361 | ||
362 | # Save values of local variables to print in case of test failure | |
363 | self.recorded_values["epoll_wait_entry"] = locals() | |
a0b1f42c JD |
364 | |
365 | def epoll_wait_exit(self, event): | |
a0b1f42c JD |
366 | ret = event["ret"] |
367 | fds_length = event["fds_length"] | |
368 | overflow = event["overflow"] | |
a0b1f42c JD |
369 | |
370 | # check that FD 0 returned with EPOLLIN and the right data.fd | |
4c4634e3 FD |
371 | if ret == 1 and fds_length == 1: |
372 | fd_0 = event["fds"][0] | |
373 | if overflow == 0 and fd_0["data_union"]["fd"] == 0 and \ | |
374 | fd_0["events"]["EPOLLIN"] == 1: | |
375 | self.expect["epoll_wait_exit"]["epoll_wait_out_fd0"] = 1 | |
376 | ||
377 | # Save values of local variables to print in case of test failure | |
378 | self.recorded_values["epoll_wait_exit"] = locals() | |
a0b1f42c | 379 | |
d40c2620 JG |
380 | def epoll_pwait_entry(self, event): |
381 | epfd = event["epfd"] | |
382 | maxevents = event["maxevents"] | |
383 | timeout = event["timeout"] | |
384 | ||
385 | if epfd == self.epoll_pwait_fd and maxevents == 1 and timeout == -1: | |
386 | self.expect["epoll_pwait_entry"]["epoll_pwait_in_ok"] = 1 | |
387 | ||
388 | # Save values of local variables to print in case of test failure | |
389 | self.recorded_values["epoll_pwait_entry"] = locals() | |
390 | ||
391 | def epoll_pwait_exit(self, event): | |
392 | ret = event["ret"] | |
393 | fds_length = event["fds_length"] | |
394 | overflow = event["overflow"] | |
395 | ||
396 | # check that FD 0 returned with EPOLLIN and the right data.fd | |
397 | if ret == 1 and fds_length == 1: | |
398 | fd_0 = event["fds"][0] | |
399 | if overflow == 0 and fd_0["data_union"]["fd"] == 0 and \ | |
400 | fd_0["events"]["EPOLLIN"] == 1: | |
401 | self.expect["epoll_pwait_exit"]["epoll_pwait_out_fd0"] = 1 | |
402 | ||
403 | # Save values of local variables to print in case of test failure | |
404 | self.recorded_values["epoll_pwait_exit"] = locals() | |
a0b1f42c | 405 | |
2a2ac572 | 406 | class WorkingCasesTimeout(TraceParser): |
d40c2620 JG |
407 | def __init__(self, trace, validation_args): |
408 | super().__init__(trace, validation_args['pid']) | |
4c4634e3 FD |
409 | self.expect["select_entry"]["select_timeout_in_fd0"] = 0 |
410 | self.expect["select_entry"]["select_timeout_in_fd1023"] = 0 | |
411 | self.expect["select_exit"]["select_timeout_out"] = 0 | |
412 | self.expect["poll_entry"]["poll_timeout_in"] = 0 | |
413 | self.expect["poll_exit"]["poll_timeout_out"] = 0 | |
414 | self.expect["epoll_ctl_entry"]["epoll_ctl_timeout_in_add"] = 0 | |
415 | self.expect["epoll_ctl_exit"]["epoll_ctl_timeout_out_ok"] = 0 | |
416 | self.expect["epoll_wait_entry"]["epoll_wait_timeout_in"] = 0 | |
417 | self.expect["epoll_wait_exit"]["epoll_wait_timeout_out"] = 0 | |
a0b1f42c JD |
418 | |
419 | def select_entry(self, event): | |
a0b1f42c | 420 | n = event["n"] |
a0b1f42c | 421 | tvp = event["tvp"] |
a0b1f42c JD |
422 | |
423 | if n == 1 and tvp != 0: | |
4c4634e3 | 424 | self.expect["select_entry"]["select_timeout_in_fd0"] = 1 |
a0b1f42c | 425 | if n == 1023: |
4c4634e3 FD |
426 | readfd_127 = event["readfds"][127] |
427 | writefd_127 = event["writefds"][127] | |
428 | exceptfd_127 = event["exceptfds"][127] | |
429 | ||
430 | if readfd_127 == 0x40 and writefd_127 == 0 and \ | |
431 | exceptfd_127 == 0 and tvp != 0: | |
432 | self.expect["select_entry"]["select_timeout_in_fd1023"] = 1 | |
433 | ||
434 | # Save values of local variables to print in case of test failure | |
435 | self.recorded_values["select_entry"] = locals() | |
a0b1f42c JD |
436 | |
437 | def select_exit(self, event): | |
a0b1f42c | 438 | ret = event["ret"] |
a0b1f42c | 439 | tvp = event["tvp"] |
a0b1f42c JD |
440 | |
441 | if ret == 0 and tvp != 0: | |
4c4634e3 FD |
442 | self.expect["select_exit"]["select_timeout_out"] = 1 |
443 | ||
444 | # Save values of local variables to print in case of test failure | |
445 | self.recorded_values["select_exit"] = locals() | |
a0b1f42c JD |
446 | |
447 | def poll_entry(self, event): | |
a0b1f42c JD |
448 | nfds = event["nfds"] |
449 | fds_length = event["fds_length"] | |
a0b1f42c JD |
450 | |
451 | # check that we wait on FD 0 for POLLIN and that the raw_events | |
452 | # field matches the value of POLLIN | |
4c4634e3 FD |
453 | if nfds == 1 and fds_length == 1: |
454 | fd_0 = event["fds"][0] | |
455 | if fd_0["raw_events"] == 0x3 and \ | |
456 | fd_0["events"]["POLLIN"] == 1 and \ | |
457 | fd_0["events"]["padding"] == 0: | |
458 | self.expect["poll_entry"]["poll_timeout_in"] = 1 | |
459 | ||
460 | # Save values of local variables to print in case of test failure | |
461 | self.recorded_values["poll_entry"] = locals() | |
a0b1f42c JD |
462 | |
463 | def poll_exit(self, event): | |
a0b1f42c JD |
464 | ret = event["ret"] |
465 | nfds = event["nfds"] | |
466 | fds_length = event["fds_length"] | |
a0b1f42c JD |
467 | |
468 | if ret == 0 and nfds == 1 and fds_length == 0: | |
4c4634e3 FD |
469 | self.expect["poll_exit"]["poll_timeout_out"] = 1 |
470 | ||
471 | # Save values of local variables to print in case of test failure | |
472 | self.recorded_values["poll_exit"] = locals() | |
a0b1f42c JD |
473 | |
474 | def epoll_ctl_entry(self, event): | |
a0b1f42c | 475 | op_enum = event["op_enum"] |
a0b1f42c JD |
476 | _event = event["event"] |
477 | ||
478 | # make sure we see a EPOLLIN|EPOLLPRI | |
53f8be7a | 479 | if 'EPOLL_CTL_ADD' in op_enum.labels and \ |
a0b1f42c JD |
480 | _event["events"]["EPOLLIN"] == 1 and \ |
481 | _event["events"]["EPOLLPRI"] == 1: | |
4c4634e3 FD |
482 | self.expect["epoll_ctl_entry"]["epoll_ctl_timeout_in_add"] = 1 |
483 | ||
484 | # Save values of local variables to print in case of test failure | |
485 | self.recorded_values["epoll_ctl_entry"] = locals() | |
a0b1f42c JD |
486 | |
487 | def epoll_ctl_exit(self, event): | |
a0b1f42c JD |
488 | ret = event["ret"] |
489 | ||
490 | if ret == 0: | |
4c4634e3 FD |
491 | self.expect["epoll_ctl_exit"]["epoll_ctl_timeout_out_ok"] = 1 |
492 | ||
493 | # Save values of local variables to print in case of test failure | |
494 | self.recorded_values["epoll_ctl_exit"] = locals() | |
a0b1f42c JD |
495 | |
496 | def epoll_wait_entry(self, event): | |
a0b1f42c JD |
497 | maxevents = event["maxevents"] |
498 | timeout = event["timeout"] | |
499 | ||
500 | if maxevents == 1 and timeout == 1: | |
4c4634e3 FD |
501 | self.expect["epoll_wait_entry"]["epoll_wait_timeout_in"] = 1 |
502 | ||
503 | # Save values of local variables to print in case of test failure | |
504 | self.recorded_values["epoll_wait_entry"] = locals() | |
a0b1f42c JD |
505 | |
506 | def epoll_wait_exit(self, event): | |
a0b1f42c JD |
507 | ret = event["ret"] |
508 | fds_length = event["fds_length"] | |
509 | overflow = event["overflow"] | |
a0b1f42c JD |
510 | |
511 | if ret == 0 and fds_length == 0 and overflow == 0: | |
4c4634e3 FD |
512 | self.expect["epoll_wait_exit"]["epoll_wait_timeout_out"] = 1 |
513 | ||
514 | # Save values of local variables to print in case of test failure | |
515 | self.recorded_values["epoll_wait_exit"] = locals() | |
a0b1f42c JD |
516 | |
517 | ||
2a2ac572 | 518 | class PselectInvalidFd(TraceParser): |
d40c2620 JG |
519 | def __init__(self, trace, validation_args): |
520 | super().__init__(trace, validation_args['pid']) | |
4c4634e3 FD |
521 | self.expect["select_entry"]["select_invalid_fd_in"] = 0 |
522 | self.expect["select_exit"]["select_invalid_fd_out"] = 0 | |
a0b1f42c JD |
523 | |
524 | def select_entry(self, event): | |
a0b1f42c JD |
525 | n = event["n"] |
526 | overflow = event["overflow"] | |
a0b1f42c | 527 | |
8b3b99e2 | 528 | if n > 0 and overflow == 0: |
4c4634e3 FD |
529 | self.expect["select_entry"]["select_invalid_fd_in"] = 1 |
530 | ||
531 | # Save values of local variables to print in case of test failure | |
532 | self.recorded_values["select_entry"] = locals() | |
a0b1f42c JD |
533 | |
534 | def select_exit(self, event): | |
a0b1f42c JD |
535 | ret = event["ret"] |
536 | overflow = event["overflow"] | |
a0b1f42c | 537 | _readfds_length = event["_readfds_length"] |
a0b1f42c | 538 | |
8b3b99e2 | 539 | # make sure the event has a ret field equal to -EBADF |
a0b1f42c | 540 | if ret == -9 and overflow == 0 and _readfds_length == 0: |
4c4634e3 FD |
541 | self.expect["select_exit"]["select_invalid_fd_out"] = 1 |
542 | ||
543 | # Save values of local variables to print in case of test failure | |
544 | self.recorded_values["select_exit"] = locals() | |
a0b1f42c JD |
545 | |
546 | ||
2a2ac572 | 547 | class PpollBig(TraceParser): |
d40c2620 JG |
548 | def __init__(self, trace, validation_args): |
549 | super().__init__(trace, validation_args['pid']) | |
4c4634e3 FD |
550 | self.expect["poll_entry"]["big_poll_in"] = 0 |
551 | self.expect["poll_exit"]["big_poll_out"] = 0 | |
a0b1f42c JD |
552 | |
553 | def poll_entry(self, event): | |
a0b1f42c JD |
554 | nfds = event["nfds"] |
555 | fds_length = event["fds_length"] | |
556 | overflow = event["overflow"] | |
a0b1f42c JD |
557 | |
558 | # test of big list of FDs and the behaviour of the overflow | |
4c4634e3 FD |
559 | if nfds == 2047 and fds_length == 512 and overflow == 1: |
560 | fd_0 = event["fds"][0] | |
561 | fd_511 = event["fds"][511] | |
562 | if fd_0["raw_events"] == 0x3 and fd_0["events"]["POLLIN"] == 1 and \ | |
563 | fd_0["events"]["padding"] == 0 and \ | |
564 | fd_511["events"]["POLLIN"] == 1 and \ | |
565 | fd_511["events"]["POLLPRI"] == 1: | |
566 | self.expect["poll_entry"]["big_poll_in"] = 1 | |
567 | ||
568 | # Save values of local variables to print in case of test failure | |
569 | self.recorded_values["poll_entry"] = locals() | |
a0b1f42c JD |
570 | |
571 | def poll_exit(self, event): | |
a0b1f42c JD |
572 | ret = event["ret"] |
573 | nfds = event["nfds"] | |
574 | fds_length = event["fds_length"] | |
575 | overflow = event["overflow"] | |
a0b1f42c JD |
576 | |
577 | # test of big list of FDs and the behaviour of the overflow | |
4c4634e3 FD |
578 | if ret == 2047 and nfds == 2047 and fds_length == 512 and overflow == 1: |
579 | fd_0 = event["fds"][0] | |
580 | fd_511 = event["fds"][511] | |
581 | if fd_0["events"]["POLLIN"] == 1 and fd_511["events"]["POLLIN"] == 1: | |
582 | self.expect["poll_exit"]["big_poll_out"] = 1 | |
a0b1f42c | 583 | |
4c4634e3 FD |
584 | # Save values of local variables to print in case of test failure |
585 | self.recorded_values["poll_exit"] = locals() | |
a0b1f42c | 586 | |
2a2ac572 | 587 | class PpollFdsBufferOverflow(TraceParser): |
d40c2620 JG |
588 | def __init__(self, trace, validation_args): |
589 | super().__init__(trace, validation_args['pid']) | |
4c4634e3 FD |
590 | self.expect["poll_entry"]["poll_overflow_in"] = 0 |
591 | self.expect["poll_exit"]["poll_overflow_out"] = 0 | |
a0b1f42c JD |
592 | |
593 | def poll_entry(self, event): | |
a0b1f42c JD |
594 | nfds = event["nfds"] |
595 | fds_length = event["fds_length"] | |
596 | overflow = event["overflow"] | |
a0b1f42c JD |
597 | |
598 | # test that event in valid even though the target buffer is too small | |
599 | # and the program segfaults | |
4c4634e3 FD |
600 | if nfds == 100 and fds_length == 100 and overflow == 0: |
601 | fd_0 = event["fds"][0] | |
602 | if fd_0["events"]["POLLIN"] == 1: | |
603 | self.expect["poll_entry"]["poll_overflow_in"] = 1 | |
604 | ||
605 | # Save values of local variables to print in case of test failure | |
606 | self.recorded_values["poll_entry"] = locals() | |
a0b1f42c JD |
607 | |
608 | def poll_exit(self, event): | |
a0b1f42c | 609 | nfds = event["nfds"] |
a0b1f42c | 610 | overflow = event["overflow"] |
a0b1f42c JD |
611 | |
612 | # test that event in valid even though the target buffer is too small | |
613 | # and the program segfaults | |
614 | if nfds == 100 and overflow == 0: | |
4c4634e3 FD |
615 | self.expect["poll_exit"]["poll_overflow_out"] = 1 |
616 | ||
617 | # Save values of local variables to print in case of test failure | |
618 | self.recorded_values["poll_exit"] = locals() | |
a0b1f42c JD |
619 | |
620 | ||
2a2ac572 | 621 | class PselectInvalidPointer(TraceParser): |
d40c2620 JG |
622 | def __init__(self, trace, validation_args): |
623 | super().__init__(trace, validation_args['pid']) | |
4c4634e3 FD |
624 | self.expect["select_entry"]["pselect_invalid_in"] = 0 |
625 | self.expect["select_exit"]["pselect_invalid_out"] = 0 | |
a0b1f42c JD |
626 | |
627 | def select_entry(self, event): | |
a0b1f42c JD |
628 | n = event["n"] |
629 | overflow = event["overflow"] | |
a0b1f42c | 630 | _readfds_length = event["_readfds_length"] |
a0b1f42c JD |
631 | |
632 | # test that event in valid even though the target buffer pointer is | |
633 | # invalid and the program segfaults | |
634 | if n == 1 and overflow == 0 and _readfds_length == 0: | |
4c4634e3 FD |
635 | self.expect["select_entry"]["pselect_invalid_in"] = 1 |
636 | ||
637 | # Save values of local variables to print in case of test failure | |
638 | self.recorded_values["select_entry"] = locals() | |
a0b1f42c JD |
639 | |
640 | def select_exit(self, event): | |
a0b1f42c JD |
641 | ret = event["ret"] |
642 | overflow = event["overflow"] | |
a0b1f42c | 643 | _readfds_length = event["_readfds_length"] |
a0b1f42c JD |
644 | |
645 | # test that event in valid even though the target buffer pointer is | |
646 | # invalid and the program segfaults | |
647 | if ret == -14 and overflow == 0 and _readfds_length == 0: | |
4c4634e3 FD |
648 | self.expect["select_exit"]["pselect_invalid_out"] = 1 |
649 | ||
650 | # Save values of local variables to print in case of test failure | |
651 | self.recorded_values["select_exit"] = locals() | |
a0b1f42c JD |
652 | |
653 | ||
2a2ac572 | 654 | class PpollFdsULongMax(TraceParser): |
d40c2620 JG |
655 | def __init__(self, trace, validation_args): |
656 | super().__init__(trace, validation_args['pid']) | |
4c4634e3 FD |
657 | self.expect["poll_entry"]["poll_max_in"] = 0 |
658 | self.expect["poll_exit"]["poll_max_out"] = 0 | |
a0b1f42c JD |
659 | |
660 | def poll_entry(self, event): | |
a0b1f42c | 661 | nfds = event["nfds"] |
a0b1f42c | 662 | overflow = event["overflow"] |
a0b1f42c JD |
663 | |
664 | # check the proper working of INT_MAX maxevent value | |
665 | if nfds == 4294967295 and overflow == 1: | |
4c4634e3 FD |
666 | self.expect["poll_entry"]["poll_max_in"] = 1 |
667 | ||
668 | # Save values of local variables to print in case of test failure | |
669 | self.recorded_values["poll_entry"] = locals() | |
670 | ||
a0b1f42c JD |
671 | |
672 | def poll_exit(self, event): | |
a0b1f42c JD |
673 | ret = event["ret"] |
674 | nfds = event["nfds"] | |
a0b1f42c | 675 | overflow = event["overflow"] |
a0b1f42c JD |
676 | |
677 | # check the proper working of UINT_MAX maxevent value | |
678 | if ret == -22 and nfds == 4294967295 and overflow == 0: | |
4c4634e3 FD |
679 | self.expect["poll_exit"]["poll_max_out"] = 1 |
680 | ||
681 | # Save values of local variables to print in case of test failure | |
682 | self.recorded_values["poll_exit"] = locals() | |
a0b1f42c JD |
683 | |
684 | ||
2a2ac572 | 685 | class EpollPwaitInvalidPointer(TraceParser): |
d40c2620 JG |
686 | def __init__(self, trace, validation_args): |
687 | super().__init__(trace, validation_args['pid']) | |
688 | ||
689 | # Values expected in the trace | |
690 | self.epoll_fd = validation_args['epollfd'] | |
691 | ||
4c4634e3 FD |
692 | self.expect["epoll_wait_entry"]["epoll_wait_invalid_in"] = 0 |
693 | self.expect["epoll_wait_exit"]["epoll_wait_invalid_out"] = 0 | |
a0b1f42c JD |
694 | |
695 | def epoll_wait_entry(self, event): | |
a0b1f42c JD |
696 | epfd = event["epfd"] |
697 | maxevents = event["maxevents"] | |
698 | timeout = event["timeout"] | |
699 | ||
700 | # test that event in valid even though the target buffer pointer is | |
701 | # invalid and the program segfaults | |
d40c2620 | 702 | if epfd == self.epoll_fd and maxevents == 1 and timeout == -1: |
4c4634e3 FD |
703 | self.expect["epoll_wait_entry"]["epoll_wait_invalid_in"] = 1 |
704 | ||
705 | # Save values of local variables to print in case of test failure | |
706 | self.recorded_values["epoll_wait_entry"] = locals() | |
a0b1f42c JD |
707 | |
708 | def epoll_wait_exit(self, event): | |
a0b1f42c JD |
709 | ret = event["ret"] |
710 | fds_length = event["fds_length"] | |
711 | overflow = event["overflow"] | |
a0b1f42c JD |
712 | |
713 | # test that event in valid even though the target buffer pointer is | |
714 | # invalid and the program segfaults | |
715 | if ret == -14 and fds_length == 0 and overflow == 0: | |
4c4634e3 FD |
716 | self.expect["epoll_wait_exit"]["epoll_wait_invalid_out"] = 1 |
717 | ||
718 | # Save values of local variables to print in case of test failure | |
719 | self.recorded_values["epoll_wait_exit"] = locals() | |
a0b1f42c JD |
720 | |
721 | ||
2a2ac572 | 722 | class EpollPwaitIntMax(TraceParser): |
d40c2620 JG |
723 | def __init__(self, trace, validation_args): |
724 | super().__init__(trace, validation_args['pid']) | |
725 | ||
726 | # Values expected in the trace | |
727 | self.epoll_fd = validation_args['epollfd'] | |
728 | ||
4c4634e3 FD |
729 | self.expect["epoll_wait_entry"]["epoll_wait_max_in"] = 0 |
730 | self.expect["epoll_wait_exit"]["epoll_wait_max_out"] = 0 | |
a0b1f42c JD |
731 | |
732 | def epoll_wait_entry(self, event): | |
a0b1f42c JD |
733 | epfd = event["epfd"] |
734 | maxevents = event["maxevents"] | |
735 | timeout = event["timeout"] | |
736 | ||
737 | # check the proper working of INT_MAX maxevent value | |
d40c2620 | 738 | if epfd == self.epoll_fd and maxevents == 2147483647 and timeout == -1: |
4c4634e3 FD |
739 | self.expect["epoll_wait_entry"]["epoll_wait_max_in"] = 1 |
740 | ||
741 | # Save values of local variables to print in case of test failure | |
742 | self.recorded_values["epoll_wait_entry"] = locals() | |
a0b1f42c JD |
743 | |
744 | def epoll_wait_exit(self, event): | |
a0b1f42c JD |
745 | ret = event["ret"] |
746 | fds_length = event["fds_length"] | |
747 | overflow = event["overflow"] | |
a0b1f42c JD |
748 | |
749 | # check the proper working of INT_MAX maxevent value | |
750 | if ret == -22 and fds_length == 0 and overflow == 0: | |
4c4634e3 FD |
751 | self.expect["epoll_wait_exit"]["epoll_wait_max_out"] = 1 |
752 | ||
753 | # Save values of local variables to print in case of test failure | |
754 | self.recorded_values["epoll_wait_exit"] = locals() | |
a0b1f42c JD |
755 | |
756 | ||
757 | if __name__ == "__main__": | |
758 | parser = argparse.ArgumentParser(description='Trace parser') | |
759 | parser.add_argument('path', metavar="<path/to/trace>", help='Trace path') | |
2a2ac572 | 760 | parser.add_argument('-t', '--test', type=str, help='Test to validate') |
d40c2620 | 761 | parser.add_argument('-o', '--validation-file', type=str, help='Validation file path') |
a0b1f42c JD |
762 | args = parser.parse_args() |
763 | ||
764 | if not args.test: | |
d40c2620 | 765 | print("Need to pass a test to validate (--test/-t)") |
a0b1f42c JD |
766 | sys.exit(1) |
767 | ||
d40c2620 JG |
768 | if not args.validation_file: |
769 | print("Need to pass the test validation file (--validation-file/-o)") | |
a0b1f42c JD |
770 | sys.exit(1) |
771 | ||
53f8be7a | 772 | traces = bt2.TraceCollectionMessageIterator(args.path) |
a0b1f42c | 773 | |
d40c2620 JG |
774 | with open(args.validation_file) as f: |
775 | try: | |
776 | test_validation_args = json.load(f) | |
777 | except Exception as e: | |
778 | print('Failed to parse validation file: ' + str(e)) | |
779 | sys.exit(1) | |
780 | ||
a0b1f42c JD |
781 | t = None |
782 | ||
2a2ac572 JG |
783 | if args.test == "working_cases": |
784 | t = WorkingCases(traces, test_validation_args) | |
785 | elif args.test == "working_cases_timeout": | |
786 | t = WorkingCasesTimeout(traces, test_validation_args) | |
787 | elif args.test == "pselect_invalid_fd": | |
788 | t = PselectInvalidFd(traces, test_validation_args) | |
789 | elif args.test == "ppoll_big": | |
790 | t = PpollBig(traces, test_validation_args) | |
791 | elif args.test == "ppoll_fds_buffer_overflow": | |
792 | t = PpollFdsBufferOverflow(traces, test_validation_args) | |
793 | elif args.test == "pselect_invalid_pointer": | |
794 | t = PselectInvalidPointer(traces, test_validation_args) | |
795 | elif args.test == "ppoll_fds_ulong_max": | |
796 | t = PpollFdsULongMax(traces, test_validation_args) | |
797 | elif args.test == "epoll_pwait_invalid_pointer": | |
798 | t = EpollPwaitInvalidPointer(traces, test_validation_args) | |
799 | elif args.test == "epoll_pwait_int_max": | |
800 | t = EpollPwaitIntMax(traces, test_validation_args) | |
801 | elif args.test == "ppoll_concurrent_write": | |
a0b1f42c JD |
802 | # stress test, nothing reliable to check |
803 | ret = 0 | |
2a2ac572 | 804 | elif args.test == "epoll_pwait_concurrent_munmap": |
a0b1f42c JD |
805 | # stress test, nothing reliable to check |
806 | ret = 0 | |
807 | else: | |
808 | print("Invalid test case") | |
809 | sys.exit(1) | |
810 | ||
811 | if t is not None: | |
812 | ret = t.parse() | |
813 | ||
a0b1f42c | 814 | sys.exit(ret) |