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 | ||