|
5 | 5 | from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase
|
6 | 6 |
|
7 | 7 |
|
| 8 | +class TestRegion(object): |
| 9 | + def __init__(self, start_addr, size, dirty_pages): |
| 10 | + self.start_addr = start_addr |
| 11 | + self.size = size |
| 12 | + self.dirty_pages = dirty_pages |
| 13 | + |
| 14 | + def as_packet(self): |
| 15 | + dirty_pages = "" |
| 16 | + if self.dirty_pages is not None: |
| 17 | + dirty_pages = ( |
| 18 | + "dirty-pages:" |
| 19 | + + ",".join([format(a, "x") for a in self.dirty_pages]) |
| 20 | + + ";" |
| 21 | + ) |
| 22 | + return f"start:{self.start_addr:x};size:{self.size};permissions:r;{dirty_pages}" |
| 23 | + |
| 24 | + def expected_command_output(self): |
| 25 | + if self.dirty_pages is None: |
| 26 | + return [ |
| 27 | + "Modified memory (dirty) page list provided", |
| 28 | + "Dirty pages:", |
| 29 | + ], False |
| 30 | + |
| 31 | + expected = [ |
| 32 | + f"Modified memory (dirty) page list provided, {len(self.dirty_pages)} entries." |
| 33 | + ] |
| 34 | + if self.dirty_pages: |
| 35 | + expected.append( |
| 36 | + "Dirty pages: " |
| 37 | + + ", ".join([format(a, "#x") for a in self.dirty_pages]) |
| 38 | + + "." |
| 39 | + ) |
| 40 | + return expected, True |
| 41 | + |
| 42 | + |
8 | 43 | class TestMemoryRegionDirtyPages(GDBRemoteTestBase):
|
9 | 44 | @skipIfXmlSupportMissing
|
10 | 45 | def test(self):
|
| 46 | + test_regions = [ |
| 47 | + # A memory region where we don't know anything about dirty pages |
| 48 | + TestRegion(0, 0x100000000, None), |
| 49 | + # A memory region with dirty page information -- and zero dirty pages |
| 50 | + TestRegion(0x100000000, 4000, []), |
| 51 | + # A memory region with one dirty page |
| 52 | + TestRegion(0x100004000, 4000, [0x100004000]), |
| 53 | + # A memory region with multple dirty pages |
| 54 | + TestRegion( |
| 55 | + 0x1000A2000, |
| 56 | + 5000, |
| 57 | + [0x1000A2000, 0x1000A3000, 0x1000A4000, 0x1000A5000, 0x1000A6000], |
| 58 | + ), |
| 59 | + ] |
| 60 | + |
11 | 61 | class MyResponder(MockGDBServerResponder):
|
12 | 62 | def qHostInfo(self):
|
13 | 63 | return "ptrsize:8;endian:little;vm-page-size:4096;"
|
14 | 64 |
|
15 | 65 | def qMemoryRegionInfo(self, addr):
|
16 |
| - if addr == 0: |
17 |
| - return "start:0;size:100000000;" |
18 |
| - if addr == 0x100000000: |
19 |
| - return "start:100000000;size:4000;permissions:rx;dirty-pages:;" |
20 |
| - if addr == 0x100004000: |
21 |
| - return ( |
22 |
| - "start:100004000;size:4000;permissions:r;dirty-pages:100004000;" |
23 |
| - ) |
24 |
| - if addr == 0x1000A2000: |
25 |
| - return "start:1000a2000;size:5000;permissions:r;dirty-pages:1000a2000,1000a3000,1000a4000,1000a5000,1000a6000;" |
| 66 | + for region in test_regions: |
| 67 | + if region.start_addr == addr: |
| 68 | + return region.as_packet() |
26 | 69 |
|
27 | 70 | self.server.responder = MyResponder()
|
28 | 71 | target = self.dbg.CreateTarget("")
|
29 | 72 | if self.TraceOn():
|
30 | 73 | self.runCmd("log enable gdb-remote packets")
|
31 | 74 | self.addTearDownHook(lambda: self.runCmd("log disable gdb-remote packets"))
|
| 75 | + |
32 | 76 | process = self.connect(target)
|
| 77 | + lldbutil.expect_state_changes( |
| 78 | + self, self.dbg.GetListener(), process, [lldb.eStateStopped] |
| 79 | + ) |
33 | 80 |
|
34 |
| - # A memory region where we don't know anything about dirty pages |
35 |
| - region = lldb.SBMemoryRegionInfo() |
36 |
| - err = process.GetMemoryRegionInfo(0, region) |
37 |
| - self.assertSuccess(err) |
38 |
| - self.assertFalse(region.HasDirtyMemoryPageList()) |
39 |
| - self.assertEqual(region.GetNumDirtyPages(), 0) |
40 |
| - region.Clear() |
| 81 | + for test_region in test_regions: |
| 82 | + region = lldb.SBMemoryRegionInfo() |
| 83 | + err = process.GetMemoryRegionInfo(test_region.start_addr, region) |
| 84 | + self.assertSuccess(err) |
| 85 | + self.assertEqual(region.GetPageSize(), 4096) |
41 | 86 |
|
42 |
| - # A memory region with dirty page information -- and zero dirty pages |
43 |
| - err = process.GetMemoryRegionInfo(0x100000000, region) |
44 |
| - self.assertSuccess(err) |
45 |
| - self.assertTrue(region.HasDirtyMemoryPageList()) |
46 |
| - self.assertEqual(region.GetNumDirtyPages(), 0) |
47 |
| - self.assertEqual(region.GetPageSize(), 4096) |
48 |
| - region.Clear() |
| 87 | + if test_region.dirty_pages is None: |
| 88 | + self.assertFalse(region.HasDirtyMemoryPageList()) |
| 89 | + self.assertEqual(0, region.GetNumDirtyPages()) |
| 90 | + else: |
| 91 | + self.assertTrue(region.HasDirtyMemoryPageList()) |
| 92 | + self.assertEqual( |
| 93 | + len(test_region.dirty_pages), region.GetNumDirtyPages() |
| 94 | + ) |
49 | 95 |
|
50 |
| - # A memory region with one dirty page |
51 |
| - err = process.GetMemoryRegionInfo(0x100004000, region) |
52 |
| - self.assertSuccess(err) |
53 |
| - self.assertTrue(region.HasDirtyMemoryPageList()) |
54 |
| - self.assertEqual(region.GetNumDirtyPages(), 1) |
55 |
| - self.assertEqual(region.GetDirtyPageAddressAtIndex(0), 0x100004000) |
56 |
| - region.Clear() |
| 96 | + for i, expected_dirty_page in enumerate(test_region.dirty_pages): |
| 97 | + self.assertEqual( |
| 98 | + expected_dirty_page, region.GetDirtyPageAddressAtIndex(i) |
| 99 | + ) |
57 | 100 |
|
58 |
| - # A memory region with multple dirty pages |
59 |
| - err = process.GetMemoryRegionInfo(0x1000A2000, region) |
60 |
| - self.assertSuccess(err) |
61 |
| - self.assertTrue(region.HasDirtyMemoryPageList()) |
62 |
| - self.assertEqual(region.GetNumDirtyPages(), 5) |
63 |
| - self.assertEqual(region.GetDirtyPageAddressAtIndex(4), 0x1000A6000) |
64 |
| - region.Clear() |
| 101 | + substrs, matching = test_region.expected_command_output() |
| 102 | + self.expect( |
| 103 | + f"memory region 0x{test_region.start_addr:x}", |
| 104 | + substrs=substrs, |
| 105 | + matching=matching, |
| 106 | + ) |
0 commit comments