[Concept,15/19] test: py: Add --malloc-dump support to pytest

Message ID 20260314231618.338113-16-sjg@u-boot.org
State New
Headers
Series test: Fix pytest inter-test side effects |

Commit Message

Simon Glass March 14, 2026, 11:16 p.m. UTC
  From: Simon Glass <sjg@chromium.org>

Add a --malloc-dump option to the pytest framework that passes
--malloc_dump to the sandbox binary. The filename may contain '%d'
which is replaced with a sequence number that increments on each U-Boot
restart, so each instance produces a separate dump.

Override close() in ConsoleSandbox to send 'poweroff' before closing
the PTY when --malloc-dump is active, so that state_uninit() runs and
writes the dump file.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 doc/develop/malloc.rst     | 18 ++++++++++++++++++
 test/py/conftest.py        |  3 +++
 test/py/console_sandbox.py | 26 ++++++++++++++++++++++++++
 3 files changed, 47 insertions(+)
  

Patch

diff --git a/doc/develop/malloc.rst b/doc/develop/malloc.rst
index b5c84c4c3bd..7e46c05dfde 100644
--- a/doc/develop/malloc.rst
+++ b/doc/develop/malloc.rst
@@ -511,6 +511,24 @@  by checking ``malloc_get_info()`` before and after::
    allocations during the operation
 6. Fix the leak and verify the test passes
 
+**Dumping heap state on exit**
+
+When running sandbox, the ``--malloc_dump`` command-line option writes a heap
+dump to a file when U-Boot exits cleanly (via ``poweroff`` or ``reset``). This
+is useful for capturing heap state at the end of a test session::
+
+    ./u-boot -Tf -c "poweroff" --malloc_dump /tmp/heap.txt
+
+The pytest framework also supports this via ``--malloc-dump``::
+
+    test/py/test.py -B sandbox --malloc-dump /tmp/heap.txt -k test_source
+
+The filename may contain ``%d`` which is replaced with a sequence number
+that increments each time U-Boot restarts during the test session, so each
+instance produces a separate dump::
+
+    test/py/test.py -B sandbox --malloc-dump /tmp/heap%d.txt -k test_vboot
+
 API Reference
 -------------
 
diff --git a/test/py/conftest.py b/test/py/conftest.py
index f4c5e390a93..47a0d112e51 100644
--- a/test/py/conftest.py
+++ b/test/py/conftest.py
@@ -106,6 +106,8 @@  def pytest_addoption(parser):
                      help='Disable console timeout (useful for debugging)')
     parser.addoption('--no-full', default=False, action='store_true',
                      help='Skip flat-tree tests (run live-tree only)')
+    parser.addoption('--malloc-dump', default=None,
+                     help='Write malloc dump to file on exit')
 
 
 def run_build(config, source_dir, build_dir, board_type, log):
@@ -362,6 +364,7 @@  def pytest_configure(config):
     ubconfig.allow_exceptions = config.getoption('allow_exceptions')
     ubconfig.no_timeout = config.getoption('no_timeout')
     ubconfig.no_full = config.getoption('no_full')
+    ubconfig.malloc_dump = config.getoption('malloc_dump')
 
     env_vars = (
         'board_type',
diff --git a/test/py/console_sandbox.py b/test/py/console_sandbox.py
index 3bd109acef5..424e3ad2dd7 100644
--- a/test/py/console_sandbox.py
+++ b/test/py/console_sandbox.py
@@ -25,6 +25,7 @@  class ConsoleSandbox(ConsoleBase):
         super().__init__(log, config, max_fifo_fill=1024)
         self.sandbox_flags = []
         self.use_dtb = True
+        self.malloc_dump_seq = 0
 
     def get_spawn(self):
         """Connect to a fresh U-Boot instance.
@@ -57,6 +58,14 @@  class ConsoleSandbox(ConsoleBase):
         if self.config.no_full:
             cmd.append('-F')
 
+        if self.config.malloc_dump:
+            try:
+                fname = self.config.malloc_dump % self.malloc_dump_seq
+            except TypeError:
+                fname = self.config.malloc_dump
+            self.malloc_dump_seq += 1
+            cmd += ['--malloc_dump', fname]
+
         # Always disable the pager
         cmd.append('-P')
 
@@ -84,6 +93,23 @@  class ConsoleSandbox(ConsoleBase):
             self.sandbox_flags = []
             self.use_dtb = True
 
+    def close(self):
+        """Terminate the sandbox, using poweroff for a clean shutdown.
+
+        When --malloc-dump is active we need state_uninit() to run, so
+        send 'poweroff' instead of just closing the PTY.
+        """
+        if self.p and self.config.malloc_dump:
+            try:
+                self.p.send('poweroff\n')
+                for _ in range(50):
+                    if not self.p.isalive():
+                        break
+                    time.sleep(0.1)
+            except:
+                pass
+        super().close()
+
     def kill(self, sig):
         """Send a specific Unix signal to the sandbox process.