diff options
| -rw-r--r-- | meta/lib/oeqa/utils/qemurunner.py | 54 |
1 files changed, 32 insertions, 22 deletions
diff --git a/meta/lib/oeqa/utils/qemurunner.py b/meta/lib/oeqa/utils/qemurunner.py index ee36707800..cdd0db5877 100644 --- a/meta/lib/oeqa/utils/qemurunner.py +++ b/meta/lib/oeqa/utils/qemurunner.py | |||
| @@ -29,6 +29,15 @@ control_chars = [chr(x) for x in control_range | |||
| 29 | if chr(x) not in string.printable] | 29 | if chr(x) not in string.printable] |
| 30 | re_control_char = re.compile('[%s]' % re.escape("".join(control_chars))) | 30 | re_control_char = re.compile('[%s]' % re.escape("".join(control_chars))) |
| 31 | 31 | ||
| 32 | def getOutput(o): | ||
| 33 | import fcntl | ||
| 34 | fl = fcntl.fcntl(o, fcntl.F_GETFL) | ||
| 35 | fcntl.fcntl(o, fcntl.F_SETFL, fl | os.O_NONBLOCK) | ||
| 36 | try: | ||
| 37 | return os.read(o.fileno(), 1000000).decode("utf-8") | ||
| 38 | except BlockingIOError: | ||
| 39 | return "" | ||
| 40 | |||
| 32 | class QemuRunner: | 41 | class QemuRunner: |
| 33 | 42 | ||
| 34 | def __init__(self, machine, rootfs, display, tmpdir, deploy_dir_image, logfile, boottime, dump_dir, use_kvm, logger, use_slirp=False, | 43 | def __init__(self, machine, rootfs, display, tmpdir, deploy_dir_image, logfile, boottime, dump_dir, use_kvm, logger, use_slirp=False, |
| @@ -55,6 +64,7 @@ class QemuRunner: | |||
| 55 | self.boottime = boottime | 64 | self.boottime = boottime |
| 56 | self.logged = False | 65 | self.logged = False |
| 57 | self.thread = None | 66 | self.thread = None |
| 67 | self.threadsock = None | ||
| 58 | self.use_kvm = use_kvm | 68 | self.use_kvm = use_kvm |
| 59 | self.use_ovmf = use_ovmf | 69 | self.use_ovmf = use_ovmf |
| 60 | self.use_slirp = use_slirp | 70 | self.use_slirp = use_slirp |
| @@ -119,21 +129,11 @@ class QemuRunner: | |||
| 119 | f.write(msg) | 129 | f.write(msg) |
| 120 | self.msg += self.decode_qemulog(msg) | 130 | self.msg += self.decode_qemulog(msg) |
| 121 | 131 | ||
| 122 | def getOutput(self, o): | ||
| 123 | import fcntl | ||
| 124 | fl = fcntl.fcntl(o, fcntl.F_GETFL) | ||
| 125 | fcntl.fcntl(o, fcntl.F_SETFL, fl | os.O_NONBLOCK) | ||
| 126 | try: | ||
| 127 | return os.read(o.fileno(), 1000000).decode("utf-8") | ||
| 128 | except BlockingIOError: | ||
| 129 | return "" | ||
| 130 | |||
| 131 | |||
| 132 | def handleSIGCHLD(self, signum, frame): | 132 | def handleSIGCHLD(self, signum, frame): |
| 133 | if self.runqemu and self.runqemu.poll(): | 133 | if self.runqemu and self.runqemu.poll(): |
| 134 | if self.runqemu.returncode: | 134 | if self.runqemu.returncode: |
| 135 | self.logger.error('runqemu exited with code %d' % self.runqemu.returncode) | 135 | self.logger.error('runqemu exited with code %d' % self.runqemu.returncode) |
| 136 | self.logger.error('Output from runqemu:\n%s' % self.getOutput(self.runqemu.stdout)) | 136 | self.logger.error('Output from runqemu:\n%s' % getOutput(self.runqemu.stdout)) |
| 137 | self.stop() | 137 | self.stop() |
| 138 | 138 | ||
| 139 | def start(self, qemuparams = None, get_ip = True, extra_bootparams = None, runqemuparams='', launch_cmd=None, discard_writes=True): | 139 | def start(self, qemuparams = None, get_ip = True, extra_bootparams = None, runqemuparams='', launch_cmd=None, discard_writes=True): |
| @@ -282,7 +282,7 @@ class QemuRunner: | |||
| 282 | if self.runqemu.returncode: | 282 | if self.runqemu.returncode: |
| 283 | # No point waiting any longer | 283 | # No point waiting any longer |
| 284 | self.logger.warning('runqemu exited with code %d' % self.runqemu.returncode) | 284 | self.logger.warning('runqemu exited with code %d' % self.runqemu.returncode) |
| 285 | self.logger.warning("Output from runqemu:\n%s" % self.getOutput(output)) | 285 | self.logger.warning("Output from runqemu:\n%s" % getOutput(output)) |
| 286 | self.stop() | 286 | self.stop() |
| 287 | return False | 287 | return False |
| 288 | time.sleep(0.5) | 288 | time.sleep(0.5) |
| @@ -309,7 +309,7 @@ class QemuRunner: | |||
| 309 | ps = subprocess.Popen(['ps', 'axww', '-o', 'pid,ppid,pri,ni,command '], stdout=subprocess.PIPE).communicate()[0] | 309 | ps = subprocess.Popen(['ps', 'axww', '-o', 'pid,ppid,pri,ni,command '], stdout=subprocess.PIPE).communicate()[0] |
| 310 | processes = ps.decode("utf-8") | 310 | processes = ps.decode("utf-8") |
| 311 | self.logger.debug("Running processes:\n%s" % processes) | 311 | self.logger.debug("Running processes:\n%s" % processes) |
| 312 | op = self.getOutput(output) | 312 | op = getOutput(output) |
| 313 | self.stop() | 313 | self.stop() |
| 314 | if op: | 314 | if op: |
| 315 | self.logger.error("Output from runqemu:\n%s" % op) | 315 | self.logger.error("Output from runqemu:\n%s" % op) |
| @@ -387,7 +387,7 @@ class QemuRunner: | |||
| 387 | time.time() - connect_time)) | 387 | time.time() - connect_time)) |
| 388 | 388 | ||
| 389 | # We are alive: qemu is running | 389 | # We are alive: qemu is running |
| 390 | out = self.getOutput(output) | 390 | out = getOutput(output) |
| 391 | netconf = False # network configuration is not required by default | 391 | netconf = False # network configuration is not required by default |
| 392 | self.logger.debug("qemu started in %.2f seconds - qemu procces pid is %s (%s)" % | 392 | self.logger.debug("qemu started in %.2f seconds - qemu procces pid is %s (%s)" % |
| 393 | (time.time() - (endtime - self.runqemutime), | 393 | (time.time() - (endtime - self.runqemutime), |
| @@ -430,9 +430,10 @@ class QemuRunner: | |||
| 430 | self.logger.debug("Target IP: %s" % self.ip) | 430 | self.logger.debug("Target IP: %s" % self.ip) |
| 431 | self.logger.debug("Server IP: %s" % self.server_ip) | 431 | self.logger.debug("Server IP: %s" % self.server_ip) |
| 432 | 432 | ||
| 433 | self.thread = LoggingThread(self.log, self.threadsock, self.logger, self.runqemu.stdout) | ||
| 434 | self.thread.start() | ||
| 435 | |||
| 433 | if self.serial_ports >= 2: | 436 | if self.serial_ports >= 2: |
| 434 | self.thread = LoggingThread(self.log, self.threadsock, self.logger) | ||
| 435 | self.thread.start() | ||
| 436 | if not self.thread.connection_established.wait(self.boottime): | 437 | if not self.thread.connection_established.wait(self.boottime): |
| 437 | self.logger.error("Didn't receive a console connection from qemu. " | 438 | self.logger.error("Didn't receive a console connection from qemu. " |
| 438 | "Here is the qemu command line used:\n%s\nand " | 439 | "Here is the qemu command line used:\n%s\nand " |
| @@ -444,7 +445,7 @@ class QemuRunner: | |||
| 444 | self.logger.debug("Waiting at most %d seconds for login banner (%s)" % | 445 | self.logger.debug("Waiting at most %d seconds for login banner (%s)" % |
| 445 | (self.boottime, time.strftime("%D %H:%M:%S"))) | 446 | (self.boottime, time.strftime("%D %H:%M:%S"))) |
| 446 | endtime = time.time() + self.boottime | 447 | endtime = time.time() + self.boottime |
| 447 | filelist = [self.server_socket, self.runqemu.stdout] | 448 | filelist = [self.server_socket] |
| 448 | reachedlogin = False | 449 | reachedlogin = False |
| 449 | stopread = False | 450 | stopread = False |
| 450 | qemusock = None | 451 | qemusock = None |
| @@ -564,7 +565,7 @@ class QemuRunner: | |||
| 564 | self.logger.debug("Sending SIGKILL to runqemu") | 565 | self.logger.debug("Sending SIGKILL to runqemu") |
| 565 | os.killpg(os.getpgid(self.runqemu.pid), signal.SIGKILL) | 566 | os.killpg(os.getpgid(self.runqemu.pid), signal.SIGKILL) |
| 566 | if not self.runqemu.stdout.closed: | 567 | if not self.runqemu.stdout.closed: |
| 567 | self.logger.info("Output from runqemu:\n%s" % self.getOutput(self.runqemu.stdout)) | 568 | self.logger.info("Output from runqemu:\n%s" % getOutput(self.runqemu.stdout)) |
| 568 | self.runqemu.stdin.close() | 569 | self.runqemu.stdin.close() |
| 569 | self.runqemu.stdout.close() | 570 | self.runqemu.stdout.close() |
| 570 | self.runqemu_exited = True | 571 | self.runqemu_exited = True |
| @@ -700,9 +701,11 @@ class QemuRunner: | |||
| 700 | # event loop. The mechanism for stopping the thread is a simple pipe which | 701 | # event loop. The mechanism for stopping the thread is a simple pipe which |
| 701 | # will wake up the poll and allow for tearing everything down. | 702 | # will wake up the poll and allow for tearing everything down. |
| 702 | class LoggingThread(threading.Thread): | 703 | class LoggingThread(threading.Thread): |
| 703 | def __init__(self, logfunc, sock, logger): | 704 | def __init__(self, logfunc, sock, logger, qemuoutput): |
| 704 | self.connection_established = threading.Event() | 705 | self.connection_established = threading.Event() |
| 706 | |||
| 705 | self.serversock = sock | 707 | self.serversock = sock |
| 708 | self.qemuoutput = qemuoutput | ||
| 706 | self.logfunc = logfunc | 709 | self.logfunc = logfunc |
| 707 | self.logger = logger | 710 | self.logger = logger |
| 708 | self.readsock = None | 711 | self.readsock = None |
| @@ -732,7 +735,8 @@ class LoggingThread(threading.Thread): | |||
| 732 | 735 | ||
| 733 | def teardown(self): | 736 | def teardown(self): |
| 734 | self.logger.debug("Tearing down logging thread") | 737 | self.logger.debug("Tearing down logging thread") |
| 735 | self.close_socket(self.serversock) | 738 | if self.serversock: |
| 739 | self.close_socket(self.serversock) | ||
| 736 | 740 | ||
| 737 | if self.readsock is not None: | 741 | if self.readsock is not None: |
| 738 | self.close_socket(self.readsock) | 742 | self.close_socket(self.readsock) |
| @@ -747,7 +751,9 @@ class LoggingThread(threading.Thread): | |||
| 747 | def eventloop(self): | 751 | def eventloop(self): |
| 748 | poll = select.poll() | 752 | poll = select.poll() |
| 749 | event_read_mask = self.errorevents | self.readevents | 753 | event_read_mask = self.errorevents | self.readevents |
| 750 | poll.register(self.serversock.fileno()) | 754 | if self.serversock: |
| 755 | poll.register(self.serversock.fileno()) | ||
| 756 | poll.register(self.qemuoutput.fileno()) | ||
| 751 | poll.register(self.readpipe, event_read_mask) | 757 | poll.register(self.readpipe, event_read_mask) |
| 752 | 758 | ||
| 753 | breakout = False | 759 | breakout = False |
| @@ -767,7 +773,7 @@ class LoggingThread(threading.Thread): | |||
| 767 | break | 773 | break |
| 768 | 774 | ||
| 769 | # A connection request was received | 775 | # A connection request was received |
| 770 | elif self.serversock.fileno() == event[0]: | 776 | elif self.serversock and self.serversock.fileno() == event[0]: |
| 771 | self.logger.debug("Connection request received") | 777 | self.logger.debug("Connection request received") |
| 772 | self.readsock, _ = self.serversock.accept() | 778 | self.readsock, _ = self.serversock.accept() |
| 773 | self.readsock.setblocking(0) | 779 | self.readsock.setblocking(0) |
| @@ -781,6 +787,10 @@ class LoggingThread(threading.Thread): | |||
| 781 | elif self.readsock.fileno() == event[0]: | 787 | elif self.readsock.fileno() == event[0]: |
| 782 | data = self.recv(1024) | 788 | data = self.recv(1024) |
| 783 | self.logfunc(data) | 789 | self.logfunc(data) |
| 790 | elif self.qemuoutput.fileno() == event[0]: | ||
| 791 | data = self.qemuoutput.read() | ||
| 792 | self.logger.debug("Data received on qemu stdout %s" % data) | ||
| 793 | self.logfunc(data, ".stdout") | ||
| 784 | 794 | ||
| 785 | # Since the socket is non-blocking make sure to honor EAGAIN | 795 | # Since the socket is non-blocking make sure to honor EAGAIN |
| 786 | # and EWOULDBLOCK. | 796 | # and EWOULDBLOCK. |
