diff options
| -rw-r--r-- | meta/conf/machine/qemuarm.conf | 2 | ||||
| -rw-r--r-- | meta/conf/machine/qemuarm64.conf | 2 | ||||
| -rw-r--r-- | meta/conf/machine/qemumips.conf | 2 | ||||
| -rw-r--r-- | meta/conf/machine/qemumips64.conf | 2 | ||||
| -rw-r--r-- | meta/conf/machine/qemuppc.conf | 2 | ||||
| -rw-r--r-- | meta/conf/machine/qemux86-64.conf | 2 | ||||
| -rw-r--r-- | meta/conf/machine/qemux86.conf | 2 | ||||
| -rw-r--r-- | meta/lib/oeqa/utils/qemurunner.py | 128 |
8 files changed, 133 insertions, 9 deletions
diff --git a/meta/conf/machine/qemuarm.conf b/meta/conf/machine/qemuarm.conf index d07084bcf3..cdad03fc4d 100644 --- a/meta/conf/machine/qemuarm.conf +++ b/meta/conf/machine/qemuarm.conf | |||
| @@ -8,5 +8,5 @@ require conf/machine/include/tune-arm926ejs.inc | |||
| 8 | 8 | ||
| 9 | KERNEL_IMAGETYPE = "zImage" | 9 | KERNEL_IMAGETYPE = "zImage" |
| 10 | 10 | ||
| 11 | SERIAL_CONSOLE = "115200 ttyAMA0" | 11 | SERIAL_CONSOLES = "115200;ttyAMA0 115200;ttyAMA1" |
| 12 | 12 | ||
diff --git a/meta/conf/machine/qemuarm64.conf b/meta/conf/machine/qemuarm64.conf index 20bcfbac99..7bbdad74a3 100644 --- a/meta/conf/machine/qemuarm64.conf +++ b/meta/conf/machine/qemuarm64.conf | |||
| @@ -9,4 +9,4 @@ MACHINE_FEATURES = "" | |||
| 9 | 9 | ||
| 10 | KERNEL_IMAGETYPE = "Image" | 10 | KERNEL_IMAGETYPE = "Image" |
| 11 | 11 | ||
| 12 | SERIAL_CONSOLE = "38400 ttyAMA0" | 12 | SERIAL_CONSOLES = "38400;ttyAMA0 38400;ttyAMA1" |
diff --git a/meta/conf/machine/qemumips.conf b/meta/conf/machine/qemumips.conf index d9d2421616..fbf813740b 100644 --- a/meta/conf/machine/qemumips.conf +++ b/meta/conf/machine/qemumips.conf | |||
| @@ -8,6 +8,6 @@ require conf/machine/include/tune-mips32r2.inc | |||
| 8 | KERNEL_IMAGETYPE = "vmlinux" | 8 | KERNEL_IMAGETYPE = "vmlinux" |
| 9 | KERNEL_ALT_IMAGETYPE = "vmlinux.bin" | 9 | KERNEL_ALT_IMAGETYPE = "vmlinux.bin" |
| 10 | 10 | ||
| 11 | SERIAL_CONSOLE = "115200 ttyS0" | 11 | SERIAL_CONSOLES = "115200;ttyS0 115200;ttyS1" |
| 12 | 12 | ||
| 13 | MACHINE_EXTRA_RRECOMMENDS = " kernel-modules" | 13 | MACHINE_EXTRA_RRECOMMENDS = " kernel-modules" |
diff --git a/meta/conf/machine/qemumips64.conf b/meta/conf/machine/qemumips64.conf index b2c7998a66..8c3f1fe283 100644 --- a/meta/conf/machine/qemumips64.conf +++ b/meta/conf/machine/qemumips64.conf | |||
| @@ -8,6 +8,6 @@ require conf/machine/include/tune-mips64.inc | |||
| 8 | KERNEL_IMAGETYPE = "vmlinux" | 8 | KERNEL_IMAGETYPE = "vmlinux" |
| 9 | KERNEL_ALT_IMAGETYPE = "vmlinux.bin" | 9 | KERNEL_ALT_IMAGETYPE = "vmlinux.bin" |
| 10 | 10 | ||
| 11 | SERIAL_CONSOLE = "115200 ttyS0" | 11 | SERIAL_CONSOLES = "115200;ttyS0 115200;ttyS1" |
| 12 | 12 | ||
| 13 | MACHINE_EXTRA_RRECOMMENDS = " kernel-modules" | 13 | MACHINE_EXTRA_RRECOMMENDS = " kernel-modules" |
diff --git a/meta/conf/machine/qemuppc.conf b/meta/conf/machine/qemuppc.conf index 93145a1e2f..85cbbf798d 100644 --- a/meta/conf/machine/qemuppc.conf +++ b/meta/conf/machine/qemuppc.conf | |||
| @@ -7,5 +7,5 @@ require conf/machine/include/tune-ppc7400.inc | |||
| 7 | 7 | ||
| 8 | KERNEL_IMAGETYPE = "vmlinux" | 8 | KERNEL_IMAGETYPE = "vmlinux" |
| 9 | 9 | ||
| 10 | SERIAL_CONSOLE = "115200 ttyS0" | 10 | SERIAL_CONSOLES = "115200;ttyS0 115200;ttyS1" |
| 11 | 11 | ||
diff --git a/meta/conf/machine/qemux86-64.conf b/meta/conf/machine/qemux86-64.conf index 837f9f4ab0..a4fd43ce1a 100644 --- a/meta/conf/machine/qemux86-64.conf +++ b/meta/conf/machine/qemux86-64.conf | |||
| @@ -13,7 +13,7 @@ require conf/machine/include/tune-core2.inc | |||
| 13 | 13 | ||
| 14 | KERNEL_IMAGETYPE = "bzImage" | 14 | KERNEL_IMAGETYPE = "bzImage" |
| 15 | 15 | ||
| 16 | SERIAL_CONSOLE = "115200 ttyS0" | 16 | SERIAL_CONSOLES = "115200;ttyS0 115200;ttyS1" |
| 17 | 17 | ||
| 18 | XSERVER = "xserver-xorg \ | 18 | XSERVER = "xserver-xorg \ |
| 19 | ${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'mesa-driver-swrast', '', d)} \ | 19 | ${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'mesa-driver-swrast', '', d)} \ |
diff --git a/meta/conf/machine/qemux86.conf b/meta/conf/machine/qemux86.conf index 3562276932..96cea66b49 100644 --- a/meta/conf/machine/qemux86.conf +++ b/meta/conf/machine/qemux86.conf | |||
| @@ -12,7 +12,7 @@ require conf/machine/include/tune-i586.inc | |||
| 12 | 12 | ||
| 13 | KERNEL_IMAGETYPE = "bzImage" | 13 | KERNEL_IMAGETYPE = "bzImage" |
| 14 | 14 | ||
| 15 | SERIAL_CONSOLE = "115200 ttyS0" | 15 | SERIAL_CONSOLES = "115200;ttyS0 115200;ttyS1" |
| 16 | 16 | ||
| 17 | XSERVER = "xserver-xorg \ | 17 | XSERVER = "xserver-xorg \ |
| 18 | ${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'mesa-driver-swrast', '', d)} \ | 18 | ${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'mesa-driver-swrast', '', d)} \ |
diff --git a/meta/lib/oeqa/utils/qemurunner.py b/meta/lib/oeqa/utils/qemurunner.py index c8d689900d..e976fd0819 100644 --- a/meta/lib/oeqa/utils/qemurunner.py +++ b/meta/lib/oeqa/utils/qemurunner.py | |||
| @@ -13,6 +13,7 @@ import re | |||
| 13 | import socket | 13 | import socket |
| 14 | import select | 14 | import select |
| 15 | import errno | 15 | import errno |
| 16 | import threading | ||
| 16 | 17 | ||
| 17 | import logging | 18 | import logging |
| 18 | logger = logging.getLogger("BitBake.QemuRunner") | 19 | logger = logging.getLogger("BitBake.QemuRunner") |
| @@ -38,6 +39,7 @@ class QemuRunner: | |||
| 38 | self.logfile = logfile | 39 | self.logfile = logfile |
| 39 | self.boottime = boottime | 40 | self.boottime = boottime |
| 40 | self.logged = False | 41 | self.logged = False |
| 42 | self.thread = None | ||
| 41 | 43 | ||
| 42 | self.runqemutime = 60 | 44 | self.runqemutime = 60 |
| 43 | 45 | ||
| @@ -81,6 +83,7 @@ class QemuRunner: | |||
| 81 | os.environ["DEPLOY_DIR_IMAGE"] = self.deploy_dir_image | 83 | os.environ["DEPLOY_DIR_IMAGE"] = self.deploy_dir_image |
| 82 | 84 | ||
| 83 | try: | 85 | try: |
| 86 | threadsock, threadport = self.create_socket() | ||
| 84 | self.server_socket, self.serverport = self.create_socket() | 87 | self.server_socket, self.serverport = self.create_socket() |
| 85 | except socket.error, msg: | 88 | except socket.error, msg: |
| 86 | logger.error("Failed to create listening socket: %s" % msg[1]) | 89 | logger.error("Failed to create listening socket: %s" % msg[1]) |
| @@ -89,7 +92,7 @@ class QemuRunner: | |||
| 89 | # Set this flag so that Qemu doesn't do any grabs as SDL grabs interact | 92 | # Set this flag so that Qemu doesn't do any grabs as SDL grabs interact |
| 90 | # badly with screensavers. | 93 | # badly with screensavers. |
| 91 | os.environ["QEMU_DONT_GRAB"] = "1" | 94 | os.environ["QEMU_DONT_GRAB"] = "1" |
| 92 | self.qemuparams = 'bootparams="console=tty1 console=ttyS0,115200n8" qemuparams="-serial tcp:127.0.0.1:%s"' % self.serverport | 95 | self.qemuparams = 'bootparams="console=tty1 console=ttyS0,115200n8" qemuparams="-serial tcp:127.0.0.1:{} -serial tcp:127.0.0.1:{}"'.format(threadport, self.serverport) |
| 93 | if qemuparams: | 96 | if qemuparams: |
| 94 | self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"' | 97 | self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"' |
| 95 | 98 | ||
| @@ -138,6 +141,18 @@ class QemuRunner: | |||
| 138 | return False | 141 | return False |
| 139 | logger.info("Target IP: %s" % self.ip) | 142 | logger.info("Target IP: %s" % self.ip) |
| 140 | logger.info("Server IP: %s" % self.server_ip) | 143 | logger.info("Server IP: %s" % self.server_ip) |
| 144 | |||
| 145 | logger.info("Starting logging thread") | ||
| 146 | self.thread = LoggingThread(self.log, threadsock, logger) | ||
| 147 | self.thread.start() | ||
| 148 | if not self.thread.connection_established.wait(self.boottime): | ||
| 149 | logger.error("Didn't receive a console connection from qemu. " | ||
| 150 | "Here is the qemu command line used:\n%s\nand " | ||
| 151 | "output from runqemu:\n%s" % (cmdline, | ||
| 152 | getOutput(output))) | ||
| 153 | self.stop_thread() | ||
| 154 | return False | ||
| 155 | |||
| 141 | logger.info("Waiting at most %d seconds for login banner" % self.boottime) | 156 | logger.info("Waiting at most %d seconds for login banner" % self.boottime) |
| 142 | endtime = time.time() + self.boottime | 157 | endtime = time.time() + self.boottime |
| 143 | socklist = [self.server_socket] | 158 | socklist = [self.server_socket] |
| @@ -157,7 +172,6 @@ class QemuRunner: | |||
| 157 | else: | 172 | else: |
| 158 | data = sock.recv(1024) | 173 | data = sock.recv(1024) |
| 159 | if data: | 174 | if data: |
| 160 | self.log(data) | ||
| 161 | bootlog += data | 175 | bootlog += data |
| 162 | if re.search(".* login:", bootlog): | 176 | if re.search(".* login:", bootlog): |
| 163 | self.server_socket = qemusock | 177 | self.server_socket = qemusock |
| @@ -214,6 +228,12 @@ class QemuRunner: | |||
| 214 | self.server_socket = None | 228 | self.server_socket = None |
| 215 | self.qemupid = None | 229 | self.qemupid = None |
| 216 | self.ip = None | 230 | self.ip = None |
| 231 | self.stop_thread() | ||
| 232 | |||
| 233 | def stop_thread(self): | ||
| 234 | if self.thread and self.thread.is_alive(): | ||
| 235 | self.thread.stop() | ||
| 236 | self.thread.join() | ||
| 217 | 237 | ||
| 218 | def restart(self, qemuparams = None): | 238 | def restart(self, qemuparams = None): |
| 219 | logger.info("Restarting qemu process") | 239 | logger.info("Restarting qemu process") |
| @@ -312,3 +332,107 @@ class QemuRunner: | |||
| 312 | if (status_cmd == "0"): | 332 | if (status_cmd == "0"): |
| 313 | status = 1 | 333 | status = 1 |
| 314 | return (status, str(data)) | 334 | return (status, str(data)) |
| 335 | |||
| 336 | # This class is for reading data from a socket and passing it to logfunc | ||
| 337 | # to be processed. It's completely event driven and has a straightforward | ||
| 338 | # event loop. The mechanism for stopping the thread is a simple pipe which | ||
| 339 | # will wake up the poll and allow for tearing everything down. | ||
| 340 | class LoggingThread(threading.Thread): | ||
| 341 | def __init__(self, logfunc, sock, logger): | ||
| 342 | self.connection_established = threading.Event() | ||
| 343 | self.serversock = sock | ||
| 344 | self.logfunc = logfunc | ||
| 345 | self.logger = logger | ||
| 346 | self.readsock = None | ||
| 347 | self.running = False | ||
| 348 | |||
| 349 | self.errorevents = select.POLLERR | select.POLLHUP | select.POLLNVAL | ||
| 350 | self.readevents = select.POLLIN | select.POLLPRI | ||
| 351 | |||
| 352 | threading.Thread.__init__(self, target=self.threadtarget) | ||
| 353 | |||
| 354 | def threadtarget(self): | ||
| 355 | try: | ||
| 356 | self.eventloop() | ||
| 357 | finally: | ||
| 358 | self.teardown() | ||
| 359 | |||
| 360 | def run(self): | ||
| 361 | self.logger.info("Starting logging thread") | ||
| 362 | self.readpipe, self.writepipe = os.pipe() | ||
| 363 | threading.Thread.run(self) | ||
| 364 | |||
| 365 | def stop(self): | ||
| 366 | self.logger.info("Stopping logging thread") | ||
| 367 | if self.running: | ||
| 368 | os.write(self.writepipe, "stop") | ||
| 369 | |||
| 370 | def teardown(self): | ||
| 371 | self.close_socket(self.serversock) | ||
| 372 | |||
| 373 | if self.readsock is not None: | ||
| 374 | self.close_socket(self.readsock) | ||
| 375 | |||
| 376 | self.close_ignore_error(self.readpipe) | ||
| 377 | self.close_ignore_error(self.writepipe) | ||
| 378 | self.running = False | ||
| 379 | |||
| 380 | def eventloop(self): | ||
| 381 | poll = select.poll() | ||
| 382 | eventmask = self.errorevents | self.readevents | ||
| 383 | poll.register(self.serversock.fileno()) | ||
| 384 | poll.register(self.readpipe, eventmask) | ||
| 385 | |||
| 386 | breakout = False | ||
| 387 | self.running = True | ||
| 388 | self.logger.info("Starting thread event loop") | ||
| 389 | while not breakout: | ||
| 390 | events = poll.poll() | ||
| 391 | for event in events: | ||
| 392 | # An error occurred, bail out | ||
| 393 | if event[1] & self.errorevents: | ||
| 394 | raise Exception(self.stringify_event(event[1])) | ||
| 395 | |||
| 396 | # Event to stop the thread | ||
| 397 | if self.readpipe == event[0]: | ||
| 398 | self.logger.info("Stop event received") | ||
| 399 | breakout = True | ||
| 400 | break | ||
| 401 | |||
| 402 | # A connection request was received | ||
| 403 | elif self.serversock.fileno() == event[0]: | ||
| 404 | self.logger.info("Connection request received") | ||
| 405 | self.readsock, _ = self.serversock.accept() | ||
| 406 | poll.unregister(self.serversock.fileno()) | ||
| 407 | poll.register(self.readsock.fileno()) | ||
| 408 | |||
| 409 | self.logger.info("Setting connection established event") | ||
| 410 | self.connection_established.set() | ||
| 411 | |||
| 412 | # Actual data to be logged | ||
| 413 | elif self.readsock.fileno() == event[0]: | ||
| 414 | data = self.readsock.recv(1024) | ||
| 415 | if not data: | ||
| 416 | raise Exception("No data on read ready socket") | ||
| 417 | |||
| 418 | self.logfunc(data) | ||
| 419 | |||
| 420 | def stringify_event(self, event): | ||
| 421 | val = '' | ||
| 422 | if select.POLLERR == event: | ||
| 423 | val = 'POLLER' | ||
| 424 | elif select.POLLHUP == event: | ||
| 425 | val = 'POLLHUP' | ||
| 426 | elif select.POLLNVAL == event: | ||
| 427 | val = 'POLLNVAL' | ||
| 428 | return val | ||
| 429 | |||
| 430 | def close_socket(self, sock): | ||
| 431 | sock.shutdown(socket.SHUT_RDWR) | ||
| 432 | sock.close() | ||
| 433 | |||
| 434 | def close_ignore_error(self, fd): | ||
| 435 | try: | ||
| 436 | os.close(fd) | ||
| 437 | except OSError: | ||
| 438 | pass | ||
