From: Mark Brown <broonie@kernel.org>
To: Brendan Higgins <brendan.higgins@linux.dev>,
David Gow <david@davidgow.net>, Rae Moar <raemoar63@gmail.com>,
Shuah Khan <skhan@linuxfoundation.org>,
Daniel Latypov <dlatypov@google.com>,
Jonathan Corbet <corbet@lwn.net>,
Randy Dunlap <rdunlap@infradead.org>
Cc: linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com,
linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org,
Mark Brown <broonie@kernel.org>
Subject: [PATCH 4/7] kunit: tool: Add an outfile argument to run_kernel()
Date: Tue, 13 Oct 2026 16:43:23 +0100 [thread overview]
Message-ID: <20261013-kunit-parallel-v1-4-f4be28c895e5@kernel.org> (raw)
In-Reply-To: <20261013-kunit-parallel-v1-0-f4be28c895e5@kernel.org>
run_kernel() always tees the kernel's console output to test.log in the
build directory. If more than one kernel were to be run at a time they
would all write to the same file, so allow the caller to choose where
the log goes with a new optional outfile argument.
Also teach get_outfile_path() to generate per-kernel log names of the
form test.<name>.log when given a name, for callers that need to keep
the logs of several kernels apart. Characters that are awkward in file
names, such as the '*' in a filter glob, are replaced with '_'.
Add a unit test covering the new argument.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/kunit/kunit_kernel.py | 28 ++++++++++++++++++++++++----
tools/testing/kunit/kunit_tool_test.py | 17 +++++++++++++++++
2 files changed, 41 insertions(+), 4 deletions(-)
diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py
index 58557c47d85f..2641db9eddba 100644
--- a/tools/testing/kunit/kunit_kernel.py
+++ b/tools/testing/kunit/kunit_kernel.py
@@ -11,6 +11,7 @@ import importlib.util
import logging
import subprocess
import os
+import re
import shlex
import shutil
import signal
@@ -193,8 +194,19 @@ def get_parsed_kunitconfig(build_dir: str,
merged.merge_in_entries(partial)
return merged
-def get_outfile_path(build_dir: str) -> str:
- return os.path.join(build_dir, OUTFILE_PATH)
+def get_outfile_path(build_dir: str, name: Optional[str]=None) -> str:
+ """Returns the path of the kernel console log.
+
+ When several kernels are run each needs its own log, so `name` is
+ used to tell them apart, giving test.<name>.log. Characters that are
+ awkward in file names (anything other than letters, digits, '_', '-'
+ and '.') are replaced with '_'.
+ """
+ if name is None:
+ return os.path.join(build_dir, OUTFILE_PATH)
+ base, ext = os.path.splitext(OUTFILE_PATH)
+ name = re.sub(r'[^\w.-]', '_', name)
+ return os.path.join(build_dir, f'{base}.{name}{ext}')
def _default_qemu_config_path(arch: str) -> str:
config_path = os.path.join(QEMU_CONFIGS_DIR, arch + '.py')
@@ -352,7 +364,12 @@ class LinuxSourceTree:
return
subprocess.call(['stty', 'sane'])
- def run_kernel(self, args: Optional[List[str]]=None, build_dir: str='', filter_glob: str='', filter: str='', filter_action: Optional[str]=None, timeout: Optional[int]=None) -> Iterator[str]:
+ def run_kernel(self, args: Optional[List[str]]=None, build_dir: str='', filter_glob: str='', filter: str='', filter_action: Optional[str]=None, timeout: Optional[int]=None, outfile: Optional[str]=None) -> Iterator[str]:
+ """Boots the kernel and yields its console output line by line.
+
+ The output is also written to `outfile`, which defaults to
+ test.log in the build directory.
+ """
# Copy to avoid mutating the caller-supplied list. exec_tests() reuses
# the same args across repeated run_kernel() calls (e.g. --run_isolated),
# so appending to the original would accumulate stale flags on each call.
@@ -365,6 +382,9 @@ class LinuxSourceTree:
args.append('kunit.filter_action=' + filter_action)
args.append('kunit.enable=1')
+ if outfile is None:
+ outfile = get_outfile_path(build_dir)
+
self._process = self._ops.start(args, build_dir)
assert self._process is not None # tell mypy it's set
assert self._process.stdout is not None # tell mypy it's set
@@ -382,7 +402,7 @@ class LinuxSourceTree:
waiter = threading.Thread(target=_wait_proc)
waiter.start()
- output = open(get_outfile_path(build_dir), 'w')
+ output = open(outfile, 'w')
try:
# Tee the output to the file and to our caller in real time.
for line in self._process.stdout:
diff --git a/tools/testing/kunit/kunit_tool_test.py b/tools/testing/kunit/kunit_tool_test.py
index 17f926ef2820..4e8068dce918 100755
--- a/tools/testing/kunit/kunit_tool_test.py
+++ b/tools/testing/kunit/kunit_tool_test.py
@@ -522,6 +522,23 @@ class LinuxSourceTreeTest(unittest.TestCase):
with open(kunit_kernel.get_outfile_path(build_dir), 'rt') as outfile:
self.assertEqual(outfile.read(), 'hi\nbye\n', msg='Missing some output')
+ def test_run_kernel_outfile(self):
+ """Verify run_kernel() logs to the given outfile instead of test.log."""
+ def fake_start(unused_args, unused_build_dir):
+ return subprocess.Popen(['printf', 'hi\n'], text=True, stdout=subprocess.PIPE)
+
+ with tempfile.TemporaryDirectory('') as build_dir:
+ tree = kunit_kernel.LinuxSourceTree(build_dir, kunitconfig_paths=[os.devnull])
+ mock.patch.object(tree._ops, 'start', side_effect=fake_start).start()
+
+ # Awkward characters in the name are sanitised.
+ outfile = kunit_kernel.get_outfile_path(build_dir, 'my suite.test*')
+ self.assertEqual(outfile, os.path.join(build_dir, 'test.my_suite.test_.log'))
+ self.assertEqual(list(tree.run_kernel(build_dir=build_dir, outfile=outfile)), ['hi\n'])
+ with open(outfile, 'rt') as f:
+ self.assertEqual(f.read(), 'hi\n')
+ self.assertFalse(os.path.exists(kunit_kernel.get_outfile_path(build_dir)))
+
def test_run_kernel_args_not_mutated(self):
"""Verify run_kernel() copies args so callers can reuse them."""
start_calls = []
--
2.47.3
next prev parent reply other threads:[~2026-10-13 15:51 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-13 15:43 [PATCH 0/7] kunit: tool: Support parallel test execution Mark Brown
2026-10-13 15:43 ` [PATCH 1/7] kunit: tool: Preserve failures from kernels without test results Mark Brown
2026-10-13 15:43 ` [PATCH 2/7] kunit: tool: Combine reports from isolated kernels Mark Brown
2026-10-13 15:43 ` [PATCH 3/7] kunit: tool: Boot with a larger log buffer when listing tests Mark Brown
2026-10-13 15:43 ` Mark Brown [this message]
2026-10-13 15:43 ` [PATCH 5/7] kunit: tool: Allow several run_kernel() calls to be in flight at once Mark Brown
2026-10-13 15:43 ` [PATCH 6/7] kunit: tool: Serialise output written from multiple threads Mark Brown
2026-10-13 15:43 ` [PATCH 7/7] kunit: tool: Add --parallel to run suites in concurrent kernels Mark Brown
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261013-kunit-parallel-v1-4-f4be28c895e5@kernel.org \
--to=broonie@kernel.org \
--cc=brendan.higgins@linux.dev \
--cc=corbet@lwn.net \
--cc=david@davidgow.net \
--cc=dlatypov@google.com \
--cc=kunit-dev@googlegroups.com \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=raemoar63@gmail.com \
--cc=rdunlap@infradead.org \
--cc=skhan@linuxfoundation.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox