Skip to content

Conversation

DavidSpickett
Copy link
Collaborator

@DavidSpickett DavidSpickett commented Aug 29, 2025

This started as me being annoyed that I got loads of this when inspecting memory regions on Mac:
Modified memory (dirty) page list provided, 0 entries.

So I thought I should test the existing behaviour, which led me to refactor the existing test to run the same checks on all regions.

In the process I realised that the output is not wrong. There is a difference between knowing that no pages are dirty and not knowing anything about dirty pages. We print that there are 0 entries so the user knows that difference.

The test case now checks "memory region" output as well as API use. There were also some checks only run on certain regions, like page size, which now run for all of them.

This started as me being annoyed that I got loads of this when
inspecting memory regions on Mac:
Modified memory (dirty) page list provided, 0 entries.

So I thought I should test the existing behaviour, which lead
me to refactor the existing test to run the same checks on all
regions.

In the process I realised that the output is not wrong. There is
a difference between knowing that no pages are dirty and not knowing
anything about dirty pages. So saying 0 are currently dirty is in
fact correct.

So the test now checks "memory region" output as well as API use.
There were also some checks only run on certain regions, like page
size, which now run for all of them.
@DavidSpickett DavidSpickett changed the title [lldb][test] Refactor TestMemoryRegionDirtyPages.py [lldb][test] Refactor and expand TestMemoryRegionDirtyPages.py Aug 29, 2025
@llvmbot llvmbot added the lldb label Aug 29, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 29, 2025

@llvm/pr-subscribers-lldb

Author: David Spickett (DavidSpickett)

Changes

This started as me being annoyed that I got loads of this when inspecting memory regions on Mac:
Modified memory (dirty) page list provided, 0 entries.

So I thought I should test the existing behaviour, which lead me to refactor the existing test to run the same checks on all regions.

In the process I realised that the output is not wrong. There is a difference between knowing that no pages are dirty and not knowing anything about dirty pages. We print that there are 0 entries so the user knows that difference.

The test case now checks "memory region" output as well as API use. There were also some checks only run on certain regions, like page size, which now run for all of them.


Full diff: https://github.com/llvm/llvm-project/pull/156035.diff

1 Files Affected:

  • (modified) lldb/test/API/functionalities/gdb_remote_client/TestMemoryRegionDirtyPages.py (+80-38)
diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestMemoryRegionDirtyPages.py b/lldb/test/API/functionalities/gdb_remote_client/TestMemoryRegionDirtyPages.py
index 9d7e0c0f7af6c..695faf896ef5d 100644
--- a/lldb/test/API/functionalities/gdb_remote_client/TestMemoryRegionDirtyPages.py
+++ b/lldb/test/API/functionalities/gdb_remote_client/TestMemoryRegionDirtyPages.py
@@ -5,60 +5,102 @@
 from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase
 
 
+class TestRegion(object):
+    def __init__(self, start_addr, size, dirty_pages):
+        self.start_addr = start_addr
+        self.size = size
+        self.dirty_pages = dirty_pages
+
+    def as_packet(self):
+        dirty_pages = ""
+        if self.dirty_pages is not None:
+            dirty_pages = (
+                "dirty-pages:"
+                + ",".join([format(a, "x") for a in self.dirty_pages])
+                + ";"
+            )
+        return f"start:{self.start_addr:x};size:{self.size};permissions:r;{dirty_pages}"
+
+    def expected_command_output(self):
+        if self.dirty_pages is None:
+            return [
+                "Modified memory (dirty) page list provided",
+                "Dirty pages:",
+            ], False
+
+        expected = [
+            f"Modified memory (dirty) page list provided, {len(self.dirty_pages)} entries."
+        ]
+        if self.dirty_pages:
+            expected.append(
+                "Dirty pages: "
+                + ", ".join([format(a, "#x") for a in self.dirty_pages])
+                + "."
+            )
+        return expected, True
+
+
 class TestMemoryRegionDirtyPages(GDBRemoteTestBase):
     @skipIfXmlSupportMissing
     def test(self):
+        test_regions = [
+            # A memory region where we don't know anything about dirty pages
+            TestRegion(0, 0x100000000, None),
+            # A memory region with dirty page information -- and zero dirty pages
+            TestRegion(0x100000000, 4000, []),
+            # A memory region with one dirty page
+            TestRegion(0x100004000, 4000, [0x100004000]),
+            # A memory region with multple dirty pages
+            TestRegion(
+                0x1000A2000,
+                5000,
+                [0x1000A2000, 0x1000A3000, 0x1000A4000, 0x1000A5000, 0x1000A6000],
+            ),
+        ]
+
         class MyResponder(MockGDBServerResponder):
             def qHostInfo(self):
                 return "ptrsize:8;endian:little;vm-page-size:4096;"
 
             def qMemoryRegionInfo(self, addr):
-                if addr == 0:
-                    return "start:0;size:100000000;"
-                if addr == 0x100000000:
-                    return "start:100000000;size:4000;permissions:rx;dirty-pages:;"
-                if addr == 0x100004000:
-                    return (
-                        "start:100004000;size:4000;permissions:r;dirty-pages:100004000;"
-                    )
-                if addr == 0x1000A2000:
-                    return "start:1000a2000;size:5000;permissions:r;dirty-pages:1000a2000,1000a3000,1000a4000,1000a5000,1000a6000;"
+                for region in test_regions:
+                    if region.start_addr == addr:
+                        return region.as_packet()
 
         self.server.responder = MyResponder()
         target = self.dbg.CreateTarget("")
         if self.TraceOn():
             self.runCmd("log enable gdb-remote packets")
             self.addTearDownHook(lambda: self.runCmd("log disable gdb-remote packets"))
+
         process = self.connect(target)
+        lldbutil.expect_state_changes(
+            self, self.dbg.GetListener(), process, [lldb.eStateStopped]
+        )
 
-        # A memory region where we don't know anything about dirty pages
-        region = lldb.SBMemoryRegionInfo()
-        err = process.GetMemoryRegionInfo(0, region)
-        self.assertSuccess(err)
-        self.assertFalse(region.HasDirtyMemoryPageList())
-        self.assertEqual(region.GetNumDirtyPages(), 0)
-        region.Clear()
+        for test_region in test_regions:
+            region = lldb.SBMemoryRegionInfo()
+            err = process.GetMemoryRegionInfo(test_region.start_addr, region)
+            self.assertSuccess(err)
+            self.assertEqual(region.GetPageSize(), 4096)
 
-        # A memory region with dirty page information -- and zero dirty pages
-        err = process.GetMemoryRegionInfo(0x100000000, region)
-        self.assertSuccess(err)
-        self.assertTrue(region.HasDirtyMemoryPageList())
-        self.assertEqual(region.GetNumDirtyPages(), 0)
-        self.assertEqual(region.GetPageSize(), 4096)
-        region.Clear()
+            if test_region.dirty_pages is None:
+                self.assertFalse(region.HasDirtyMemoryPageList())
+                self.assertEqual(0, region.GetNumDirtyPages())
+            else:
+                self.assertTrue(region.HasDirtyMemoryPageList())
+                self.assertEqual(
+                    len(test_region.dirty_pages), region.GetNumDirtyPages()
+                )
 
-        # A memory region with one dirty page
-        err = process.GetMemoryRegionInfo(0x100004000, region)
-        self.assertSuccess(err)
-        self.assertTrue(region.HasDirtyMemoryPageList())
-        self.assertEqual(region.GetNumDirtyPages(), 1)
-        self.assertEqual(region.GetDirtyPageAddressAtIndex(0), 0x100004000)
-        region.Clear()
+                for i, expected_dirty_page in enumerate(test_region.dirty_pages):
+                    self.assertEqual(
+                        expected_dirty_page, region.GetDirtyPageAddressAtIndex(i)
+                    )
 
-        # A memory region with multple dirty pages
-        err = process.GetMemoryRegionInfo(0x1000A2000, region)
-        self.assertSuccess(err)
-        self.assertTrue(region.HasDirtyMemoryPageList())
-        self.assertEqual(region.GetNumDirtyPages(), 5)
-        self.assertEqual(region.GetDirtyPageAddressAtIndex(4), 0x1000A6000)
-        region.Clear()
+            substrs, matching = test_region.expected_command_output()
+            self.expect(
+                f"memory region 0x{test_region.start_addr:x}",
+                substrs=substrs,
+                matching=matching,
+            )

@DavidSpickett DavidSpickett merged commit bafcc3e into llvm:main Sep 25, 2025
11 checks passed
@DavidSpickett DavidSpickett deleted the lldb-modified branch September 25, 2025 09:58
@llvm-ci
Copy link
Collaborator

llvm-ci commented Sep 25, 2025

LLVM Buildbot has detected a new failure on builder cross-project-tests-sie-ubuntu-dwarf5 running on doug-worker-1b while building lldb at step 6 "test-build-unified-tree-check-cross-project".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/163/builds/27102

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-cross-project) failure: test (failure)
******************** TEST 'cross-project-tests :: debuginfo-tests/dexter/feature_tests/commands/control/dex_step_function.cpp' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
clang++ -O0 -glldb -std=gnu++11 /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/dex_step_function.cpp -o /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/build/projects/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/Output/dex_step_function.cpp.tmp # RUN: at line 7
+ clang++ -O0 -glldb -std=gnu++11 /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/dex_step_function.cpp -o /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/build/projects/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/Output/dex_step_function.cpp.tmp
"/usr/bin/python3.10" "/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/cross-project-tests/debuginfo-tests/dexter/dexter.py" test --fail-lt 1.0 -w -v --debugger lldb-dap --lldb-executable "/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/build/bin/lldb-dap" --dap-message-log=-e -v --binary /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/build/projects/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/Output/dex_step_function.cpp.tmp -- /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/dex_step_function.cpp 2>&1 | /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/build/bin/FileCheck /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/dex_step_function.cpp # RUN: at line 8
+ /usr/bin/python3.10 /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/cross-project-tests/debuginfo-tests/dexter/dexter.py test --fail-lt 1.0 -w -v --debugger lldb-dap --lldb-executable /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/build/bin/lldb-dap --dap-message-log=-e -v --binary /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/build/projects/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/Output/dex_step_function.cpp.tmp -- /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/dex_step_function.cpp
+ /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/build/bin/FileCheck /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/dex_step_function.cpp
�[1m/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/dex_step_function.cpp:34:15: �[0m�[0;1;31merror: �[0m�[1mCHECK-NEXT: expected string not found in input
�[0m// CHECK-NEXT:. [0, "a(int)", "{{.*}}dex_step_function.cpp", 22, 3, "StopReason.BREAKPOINT", "StepKind.FUNC", []]
�[0;1;32m              ^
�[0m�[1m<stdin>:1527:12: �[0m�[0;1;30mnote: �[0m�[1mscanning from here
�[0m## BEGIN ##
�[0;1;32m           ^
�[0m�[1m<stdin>:1531:157: �[0m�[0;1;30mnote: �[0m�[1mpossible intended match here
�[0m. . . [3, "c(int)", "/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/dex_step_function.cpp", 12, 3, "StopReason.BREAKPOINT", "StepKind.FUNC", []]
�[0;1;32m                                                                                                                                                            ^
�[0m
Input file: <stdin>
Check file: /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/dex_step_function.cpp

-dump-input=help explains the following input dump.

Input was:
<<<<<<
�[1m�[0m�[0;1;30m           1: �[0m�[1m�[0;1;46mnote: Opening DAP server: /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/build/bin/lldb-dap �[0m
�[0;1;30m           2: �[0m�[1m�[0;1;46m-> { �[0m
�[0;1;30m           3: �[0m�[1m�[0;1;46m "type": "request", �[0m
�[0;1;30m           4: �[0m�[1m�[0;1;46m "command": "initialize", �[0m
�[0;1;30m           5: �[0m�[1m�[0;1;46m "arguments": { �[0m
�[0;1;30m           6: �[0m�[1m�[0;1;46m "clientID": "dexter", �[0m
�[0;1;30m           7: �[0m�[1m�[0;1;46m "adapterID": "lldb-dap", �[0m
�[0;1;30m           8: �[0m�[1m�[0;1;46m "pathFormat": "path", �[0m
�[0;1;30m           9: �[0m�[1m�[0;1;46m "linesStartAt1": true, �[0m
�[0;1;30m          10: �[0m�[1m�[0;1;46m "columnsStartAt1": true, �[0m
�[0;1;30m          11: �[0m�[1m�[0;1;46m "supportsVariableType": true, �[0m
�[0;1;30m          12: �[0m�[1m�[0;1;46m "supportsVariablePaging": true, �[0m
�[0;1;30m          13: �[0m�[1m�[0;1;46m "supportsRunInTerminalRequest": false �[0m
�[0;1;30m          14: �[0m�[1m�[0;1;46m }, �[0m
�[0;1;30m          15: �[0m�[1m�[0;1;46m "seq": 1 �[0m
�[0;1;30m          16: �[0m�[1m�[0;1;46m} �[0m
�[0;1;30m          17: �[0m�[1m�[0;1;46m<- { �[0m
�[0;1;30m          18: �[0m�[1m�[0;1;46m "body": { �[0m
�[0;1;30m          19: �[0m�[1m�[0;1;46m "$__lldb_version": "lldb version 22.0.0git (https://github.com/llvm/llvm-project.git revision bafcc3e3b3353718b47e77618f0bb5fad7d1aa15)\n clang revision bafcc3e3b3353718b47e77618f0bb5fad7d1aa15\n llvm revision bafcc3e3b3353718b47e77618f0bb5fad7d1aa15", �[0m
�[0;1;30m          20: �[0m�[1m�[0;1;46m "completionTriggerCharacters": [ �[0m
�[0;1;30m          21: �[0m�[1m�[0;1;46m ".", �[0m
�[0;1;30m          22: �[0m�[1m�[0;1;46m " ", �[0m
�[0;1;30m          23: �[0m�[1m�[0;1;46m "\t" �[0m
...

ckoparkar added a commit to ckoparkar/llvm-project that referenced this pull request Sep 25, 2025
* main: (502 commits)
  GlobalISel: Adjust insert point when expanding G_[SU]DIVREM  (llvm#160683)
  [LV] Add coverage for fixing-up scalar resume values (llvm#160492)
  AMDGPU: Convert wave_any test to use update_mc_test_checks
  [LV] Add partial reduction tests multiplying extend with constants.
  Revert "[MLIR] Implement remark emitting policies in MLIR" (llvm#160681)
  [NFC][InstSimplify] Refactor fminmax-folds.ll test (llvm#160504)
  [LoongArch] Pre-commit tests for [x]vldi instructions with special constant splats (llvm#159228)
  [BOLT] Fix dwarf5-dwoid-no-dwoname.s (llvm#160676)
  [lldb][test] Refactor and expand TestMemoryRegionDirtyPages.py (llvm#156035)
  [gn build] Port 833d5f0
  AMDGPU: Ensure both wavesize features are not set (llvm#159234)
  [LoopInterchange] Bail out when finding a dependency with all `*` elements (llvm#149049)
  [libc++] Avoid constructing additional objects when using map::at (llvm#157866)
  [lldb][test] Make hex prefix optional in DWARF union types test
  [X86] Add missing prefixes to trunc-sat tests (llvm#160662)
  [AMDGPU] Fix vector legalization for bf16 valu ops (llvm#158439)
  [LoongArch][NFC] Pre-commit tests for `[x]vadda.{b/h/w/d}`
  [mlir][tosa] Relax constraint on matmul verifier requiring equal operand types (llvm#155799)
  [clang][Sema] Accept gnu format attributes (llvm#160255)
  [LoongArch][NFC] Add tests for element extraction from binary add operation (llvm#159725)
  ...
YixingZhang007 pushed a commit to YixingZhang007/llvm-project that referenced this pull request Sep 27, 2025
…156035)

This started as me being annoyed that I got loads of this when
inspecting memory regions on Mac:
Modified memory (dirty) page list provided, 0 entries.

So I thought I should test the existing behaviour, which led me to
refactor the existing test to run the same checks on all regions.

In the process I realised that the output is not wrong. There is a
difference between knowing that no pages are dirty and not knowing
anything about dirty pages. We print that there are 0 entries so the
user knows that difference.

The test case now checks "memory region" output as well as API use.
There were also some checks only run on certain regions, like page size,
which now run for all of them.
mahesh-attarde pushed a commit to mahesh-attarde/llvm-project that referenced this pull request Oct 3, 2025
…156035)

This started as me being annoyed that I got loads of this when
inspecting memory regions on Mac:
Modified memory (dirty) page list provided, 0 entries.

So I thought I should test the existing behaviour, which led me to
refactor the existing test to run the same checks on all regions.

In the process I realised that the output is not wrong. There is a
difference between knowing that no pages are dirty and not knowing
anything about dirty pages. We print that there are 0 entries so the
user knows that difference.

The test case now checks "memory region" output as well as API use.
There were also some checks only run on certain regions, like page size,
which now run for all of them.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants