From 377912719ef730c2865923ba891f59c99b2d8a1b Mon Sep 17 00:00:00 2001 From: Arkadiusz Bokowy Date: Sat, 12 Mar 2022 22:24:41 +0100 Subject: [PATCH] [chiptest] Fix UTF-8 decoding error in stdout/err (#16101) * [chiptest] Fix UTF-8 decoding error in stdout/err This commit also unifies PIPE type for Linux and Darwin platforms. From now, all platforms will use PTY-based PIPE for reading subprocess output in real time (without 4k block buffering). * Fix typo in comment. Co-authored-by: Boris Zbarsky --- scripts/tests/chiptest/runner.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/scripts/tests/chiptest/runner.py b/scripts/tests/chiptest/runner.py index 3a18274767a60f..f606dc6607fd34 100644 --- a/scripts/tests/chiptest/runner.py +++ b/scripts/tests/chiptest/runner.py @@ -18,11 +18,18 @@ import queue import re import subprocess -import sys import threading class LogPipe(threading.Thread): + """Create PTY-based PIPE for IPC. + + Python provides a built-in mechanism for creating comunication PIPEs for + subprocesses spawned with Popen(). However, created PIPEs will most likely + enable IO buffering in the spawned process. In order to trick such process + to flush its streams immediately, we are going to create a PIPE based on + pseudoterminal (PTY). + """ def __init__(self, level, capture_delegate=None, name=None): """ @@ -32,12 +39,8 @@ def __init__(self, level, capture_delegate=None, name=None): self.daemon = False self.level = level - if sys.platform == 'darwin': - self.fd_read, self.fd_write = pty.openpty() - else: - self.fd_read, self.fd_write = os.pipe() - - self.pipeReader = os.fdopen(self.fd_read) + self.fd_read, self.fd_write = pty.openpty() + self.reader = open(self.fd_read, encoding='utf-8', errors='ignore') self.captured_logs = [] self.capture_delegate = capture_delegate self.name = name @@ -63,13 +66,20 @@ def fileno(self): def run(self): """Run the thread, logging everything.""" - for line in iter(self.pipeReader.readline, ''): + while True: + try: + line = self.reader.readline() + # It seems that Darwin platform returns empty string in case + # when writing side of PTY is closed (Linux raises OSError). + if line == '': + break + except OSError: + break logging.log(self.level, line.strip('\n')) self.captured_logs.append(line) if self.capture_delegate: self.capture_delegate.Log(self.name, line) - - self.pipeReader.close() + self.reader.close() def close(self): """Close the write end of the pipe."""