All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] layer: add telnet for testimage/runfvp
@ 2021-10-15 13:25 Ross Burton
  2021-10-15 13:25 ` [PATCH 2/2] runfvp: use asyncio instead of selector Ross Burton
  0 siblings, 1 reply; 2+ messages in thread
From: Ross Burton @ 2021-10-15 13:25 UTC (permalink / raw)
  To: meta-arm

Change-Id: Ic31a3bf8ad981c7380339499a377e3f9d676cb15
Signed-off-by: Ross Burton <ross.burton@arm.com>
---
 meta-arm/conf/layer.conf | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/meta-arm/conf/layer.conf b/meta-arm/conf/layer.conf
index 24d90913..69817f46 100644
--- a/meta-arm/conf/layer.conf
+++ b/meta-arm/conf/layer.conf
@@ -14,3 +14,6 @@ LAYERDEPENDS_meta-arm = " \
     arm-toolchain \
 "
 LAYERSERIES_COMPAT_meta-arm = "honister"
+
+# runfvp --console needs telnet, so pull this in for testimage.
+HOSTTOOLS_NONFATAL += "telnet"
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 2+ messages in thread

* [PATCH 2/2] runfvp: use asyncio instead of selector
  2021-10-15 13:25 [PATCH 1/2] layer: add telnet for testimage/runfvp Ross Burton
@ 2021-10-15 13:25 ` Ross Burton
  0 siblings, 0 replies; 2+ messages in thread
From: Ross Burton @ 2021-10-15 13:25 UTC (permalink / raw)
  To: meta-arm

Using asyncio makes the code easier to understand.  Also start to expose
a callback for when consoles are opened.

Signed-off-by: Ross Burton <ross.burton@arm.com>
---
 scripts/runfvp | 71 +++++++++++++++++++++++++++++++++-----------------
 1 file changed, 47 insertions(+), 24 deletions(-)

diff --git a/scripts/runfvp b/scripts/runfvp
index e9654268..a5a436e8 100755
--- a/scripts/runfvp
+++ b/scripts/runfvp
@@ -1,7 +1,9 @@
 #! /usr/bin/env python3
 
+import asyncio
 import json
 import os
+import re
 import sys
 import subprocess
 import pathlib
@@ -153,6 +155,34 @@ def parse_config(args, config):
 
     return cli
 
+async def start_fvp(cli, console_cb):
+    try:
+        fvp_process = await asyncio.create_subprocess_exec(*cli, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
+        async for line in fvp_process.stdout:
+            line = line.strip().decode("utf-8", errors="replace")
+            if console_cb:
+                logger.debug(f"FVP output: {line}")
+            else:
+                print(line)
+
+            # Look for serial connections opening
+            if console_cb:
+                m = re.match(fr"^(\S+): Listening for serial connection on port (\d+)$", line)
+                if m:
+                    terminal = m.group(1)
+                    port = int(m.group(2))
+                    logger.debug(f"Console for {terminal} started on port {port}")
+                    # When we can assume Py3.7+, this can be create_task
+                    asyncio.ensure_future(console_cb(terminal, port))
+    finally:
+        # If we get cancelled or throw an exception, kill the FVP
+        logger.debug(f"Killing FVP PID {fvp_process.pid}")
+        fvp_process.terminate()
+
+    if await fvp_process.wait() != 0:
+        logger.info(f"{cli[0]} quit with code {fvp_process.returncode}")
+
 def runfvp(cli_args):
     args, fvp_args = parse_args(cli_args)
     config_file = find_config(args)
@@ -166,31 +196,24 @@ def runfvp(cli_args):
         if not expected_terminal:
             logger.error("--console used but FVP_CONSOLE not set in machine configuration")
             sys.exit(1)
-
-        fvp_process = subprocess.Popen(cli, bufsize=1, universal_newlines=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
-        import selectors, re
-        selector = selectors.DefaultSelector()
-        selector.register(fvp_process.stdout, selectors.EVENT_READ)
-        output = ""
-        looking = True
-        while fvp_process.poll() is None:
-            # TODO some sort of timeout for 'input never appeared'
-            events = selector.select(timeout=10)
-            for key, mask in events:
-                line = key.fileobj.readline()
-                output += line
-                if looking:
-                    m = re.match(fr"^{expected_terminal}: Listening.+port ([0-9]+)$", line)
-                    if m:
-                        port = m.group(1)
-                        subprocess.run(["telnet", "localhost", port])
-                        looking = False
-        if fvp_process.returncode:
-            logger.error(f"{fvp_process.args[0]} quit with code {fvp_process.returncode}:")
-            logger.error(output)
     else:
-        sys.exit(subprocess.run(cli).returncode)
+        expected_terminal = None
+
+    async def console_started(name, port):
+        if name == expected_terminal:
+            telnet = await asyncio.create_subprocess_exec("telnet", "localhost", str(port), stdin=sys.stdin, stdout=sys.stdout)
+            await telnet.wait()
+            logger.debug(f"Telnet quit, cancelling tasks")
+            for t in asyncio.all_tasks():
+                logger.debug(f"Cancelling {t}")
+                t.cancel()
+
+    try:
+        # When we can assume Py3.7+, this can simply be asyncio.run()
+        loop = asyncio.get_event_loop()
+        loop.run_until_complete(asyncio.gather(start_fvp(cli, console_cb=console_started)))
+    except asyncio.CancelledError:
+        pass
 
 if __name__ == "__main__":
     try:
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2021-10-15 13:25 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-15 13:25 [PATCH 1/2] layer: add telnet for testimage/runfvp Ross Burton
2021-10-15 13:25 ` [PATCH 2/2] runfvp: use asyncio instead of selector Ross Burton

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.