-
Notifications
You must be signed in to change notification settings - Fork 224
Stream tool output through BenchExec for truncation prevention #1170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 8 commits
6526b5d
3d02db1
ed72686
a6fde54
8e1044b
dbdd8de
6c953c1
8a34a3e
65d4b7c
7b3c030
eab95e4
07d7036
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -773,12 +773,16 @@ def child(): | |
| grandchild_proc = subprocess.Popen( | ||
| args, | ||
| stdin=stdin, | ||
| stdout=stdout, | ||
| stderr=stderr, | ||
| stdout=None if stdout is None else subprocess.PIPE, | ||
| stderr=None if stderr is None else subprocess.PIPE, | ||
| env=env, | ||
| close_fds=False, | ||
| preexec_fn=grandchild, | ||
| ) | ||
| if stdout is not None or stderr is not None: | ||
| self._stream_output_with_selector( | ||
| grandchild_proc, stdout, stderr | ||
| ) | ||
|
Comment on lines
+782
to
+785
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we call self._stream_output_with_selector() here and the tool does not send an EOF but is instead terminated by a signal, the procedure gets blocked in this function (more precisely, in selector.select() within self._stream_output_with_selector()), and runexec does not terminate. (See this pytest error case.) To handle this properly, we need to process stdout and stderr while also checking for walltime limits and signals. In the implementation, we need to integrate self._stream_output_with_selector() with wait_for_child_and_forward_signals(). However, as far as I can tell, there are two possible solutions—neither of which seems ideal:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you make select wait for termination of a process? I think that might be possible and should solve the problem.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’m trying to address the pytest error case. According to the selectors documentation, the latest code should detect when the events list is empty. An empty list indicates that "this method can return before any file object becomes ready or the timeout has elapsed if the current process receives a signal." However, in the pytest error case, when the timeout limit is exceeded, a SIGKILL is sent to the grandchild process. Despite this, selector.select() does not return an empty list in response, which contradicts the expected behavior. I don’t yet understand the reason for this. As far as I can tell from the documentation, there seems to be no other way within BenchExec to detect process termination using only the selectors module.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the documentation talks about the case if the select()ing process receives a signal. But this is not our case. I would assume that the pipe is closed if the only listening process terminates, and that this causes select() to return. Can you check if there are any other processes that still have the same pipe open, for example BenchExec itself? BenchExec should only have the read ends of the pipe open, and the benchmarked process only the write ends. Another way could be to add a file descriptor that signals if the process ends. Maybe we can do this with a pidfd. Or the part of the code that calls wait could close the pipes, such that select() returns?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry for the late reply.
Thanks for pointing that out — I misunderstood. I've now updated the code to retry select() when it returns an empty list.
Which pipe do you mean? I assume you're referring to
I’m now using
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, I was referring to
This is nice! I general I think that PID fds are great and eventually BenchExec will use them anyway. However, they are available only since Linux 5.1, and this is still somewhat new. I would prefer if we do not introduce a hard dependency. So if we find a solution without PID fs, it would be nicer. I am pretty sure there is such a way, because what we need seems like a highly common problem for subprocess spawning and many tools have needed this for a long time even before the introduction of PID fs.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Alright, I’ll check which process is opening which file descriptors. |
||
| except (OSError, RuntimeError) as e: | ||
| logging.critical("Cannot start process: %s", e) | ||
| return CHILD_OSERROR | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’ve currently set the read size to 4096 bytes. However, we may need to evaluate whether this is optimal in terms of performance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it is fine. I wouldn't know a better number either.