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