* [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.