Bug 1457662 - [mozdevice] Use separate file object to read shell output for callback; r=bc
authorGeoff Brown <gbrown@mozilla.com>
Mon, 30 Apr 2018 17:58:24 -0600
changeset 472494 1bfab79dc9a7feb92aea531de3b49efde3f20255
parent 472493 0247bf6522996220bf123f9d8443986fa71e554d
child 472495 d94eb310957112cb2cdc177371cfd6c61285e52d
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbc
bugs1457662
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1457662 - [mozdevice] Use separate file object to read shell output for callback; r=bc
testing/mozbase/mozdevice/mozdevice/adb.py
--- a/testing/mozbase/mozdevice/mozdevice/adb.py
+++ b/testing/mozbase/mozdevice/mozdevice/adb.py
@@ -20,17 +20,17 @@ from distutils import dir_util
 
 class ADBProcess(object):
     """ADBProcess encapsulates the data related to executing the adb process."""
 
     def __init__(self, args):
         #: command argument argument list.
         self.args = args
         #: Temporary file handle to be used for stdout.
-        self.stdout_file = tempfile.TemporaryFile(mode='w+b')
+        self.stdout_file = tempfile.NamedTemporaryFile(mode='w+b')
         #: boolean indicating if the command timed out.
         self.timedout = None
         #: exitcode of the process.
         self.exitcode = None
         #: subprocess Process object used to execute the command.
         self.proc = subprocess.Popen(args,
                                      stdout=self.stdout_file,
                                      stderr=subprocess.STDOUT)
@@ -1042,24 +1042,25 @@ class ADBDevice(ADBCommand):
             """
             Attempt to readline from filehandle. If readline does not return
             within timeout seconds, raise IOError('ReadLineTimeout').
             On Windows, required signal facilities are usually not available;
             as a result, the timeout is not respected and some reads may
             block on Windows.
             """
             if not hasattr(signal, 'SIGALRM'):
-                return filehandle.readline().rstrip()
+                return filehandle.readline()
             if timeout is None:
                 timeout = 5
+            line = ''
             default_alarm_handler = signal.getsignal(signal.SIGALRM)
             signal.signal(signal.SIGALRM, _timed_read_line_handler)
             signal.alarm(timeout)
             try:
-                line = filehandle.readline().rstrip()
+                line = filehandle.readline()
             finally:
                 signal.alarm(0)
                 signal.signal(signal.SIGALRM, default_alarm_handler)
             return line
 
         if root and not self._have_root_shell:
             # If root was requested and we do not already have a root
             # shell, then use the appropriate version of su to invoke
@@ -1091,44 +1092,49 @@ class ADBDevice(ADBCommand):
         args.extend(["wait-for-device", "shell", cmd])
         adb_process = ADBProcess(args)
 
         if timeout is None:
             timeout = self._timeout
 
         start_time = time.time()
         exitcode = adb_process.proc.poll()
-        if stdout_callback:
-            stdout_dup = os.fdopen(os.dup(adb_process.stdout_file.fileno()))
-            offset = 0
-        while ((time.time() - start_time) <= timeout) and exitcode is None:
-            if stdout_callback:
-                while True:
-                    try:
-                        stdout_dup.seek(offset, os.SEEK_SET)
-                        line = _timed_read_line(stdout_dup)
-                        offset = stdout_dup.tell()
-                        if line and len(line) > 0:
-                            stdout_callback(line)
-                        else:
-                            # no new output, so sleep and poll
-                            break
-                    except IOError:
-                        pass
-            time.sleep(self._polling_interval)
-            exitcode = adb_process.proc.poll()
+        if not stdout_callback:
+            while ((time.time() - start_time) <= timeout) and exitcode is None:
+                time.sleep(self._polling_interval)
+                exitcode = adb_process.proc.poll()
+        else:
+            stdout2 = open(adb_process.stdout_file.name, 'rb')
+            while ((time.time() - start_time) <= timeout) and exitcode is None:
+                try:
+                    line = _timed_read_line(stdout2)
+                    if line and len(line) > 0:
+                        stdout_callback(line.rstrip())
+                    else:
+                        # no new output, so sleep and poll
+                        time.sleep(self._polling_interval)
+                except IOError:
+                    pass
+                exitcode = adb_process.proc.poll()
         if exitcode is None:
             adb_process.proc.kill()
             adb_process.timedout = True
             adb_process.exitcode = adb_process.proc.poll()
         elif exitcode == 0:
             adb_process.exitcode = self._get_exitcode(adb_process.stdout_file)
         else:
             adb_process.exitcode = exitcode
 
+        if stdout_callback:
+            line = stdout2.readline()
+            while line:
+                stdout_callback(line.rstrip())
+                line = stdout2.readline()
+            stdout2.close()
+
         adb_process.stdout_file.seek(0, os.SEEK_SET)
 
         return adb_process
 
     def shell_bool(self, cmd, env=None, cwd=None, timeout=None, root=False):
         """Executes a shell command on the device returning True on success
         and False on failure.