1""" 2Test the 'memory region' command. 3""" 4 5import lldb 6from lldbsuite.test.decorators import * 7from lldbsuite.test.lldbtest import * 8from lldbsuite.test import lldbutil 9from lldbsuite.test.gdbclientutils import MockGDBServerResponder 10from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase 11 12 13class MemoryCommandRegion(TestBase): 14 NO_DEBUG_INFO_TESTCASE = True 15 16 def setUp(self): 17 TestBase.setUp(self) 18 # Find the line number to break for main.c. 19 self.line = line_number( 20 "main.cpp", "// Run here before printing memory regions" 21 ) 22 23 def test_help(self): 24 """Test that help shows you must have one of address or --all, not both.""" 25 self.expect( 26 "help memory region", 27 substrs=["memory region <address-expression>", "memory region -a"], 28 ) 29 30 def setup_program(self): 31 self.build() 32 33 # Set breakpoint in main and run 34 self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) 35 lldbutil.run_break_set_by_file_and_line( 36 self, "main.cpp", self.line, num_expected_locations=-1, loc_exact=True 37 ) 38 39 self.runCmd("run", RUN_SUCCEEDED) 40 41 # This test and the next build a large result string in such a way that 42 # when run under ASAN the test always times out. Most of the time is in the asan 43 # checker under PyUnicode_Append. 44 # This seems to be a worst-case scenario for ASAN performance. 45 @skipIfAsan 46 def test_command(self): 47 self.setup_program() 48 49 interp = self.dbg.GetCommandInterpreter() 50 result = lldb.SBCommandReturnObject() 51 52 # Test that the first 'memory region' command prints the usage. 53 interp.HandleCommand("memory region", result) 54 self.assertFalse(result.Succeeded()) 55 self.assertEqual( 56 result.GetError(), 57 "error: 'memory region' takes one argument or \"--all\" option:\n" 58 "Usage: memory region <address-expression> (or --all)\n", 59 ) 60 61 # We allow --all or an address argument, not both 62 interp.HandleCommand("memory region --all 0", result) 63 self.assertFalse(result.Succeeded()) 64 self.assertRegex( 65 result.GetError(), 66 'The "--all" option cannot be used when an address argument is given', 67 ) 68 69 # Test that when the address fails to parse, we show an error and do not continue 70 interp.HandleCommand("memory region not_an_address", result) 71 self.assertFalse(result.Succeeded()) 72 self.assertEqual( 73 result.GetError(), 74 'error: invalid address argument "not_an_address": address expression "not_an_address" evaluation failed\n', 75 ) 76 77 # Accumulate the results to compare with the --all output 78 all_regions = "" 79 80 # Now let's print the memory region starting at 0 which should always work. 81 interp.HandleCommand("memory region 0x0", result) 82 self.assertTrue(result.Succeeded()) 83 self.assertRegex(result.GetOutput(), "\\[0x0+-") 84 all_regions += result.GetOutput() 85 86 # Keep printing memory regions until we printed all of them. 87 while True: 88 interp.HandleCommand("memory region", result) 89 if not result.Succeeded(): 90 break 91 all_regions += result.GetOutput() 92 93 # Now that we reached the end, 'memory region' should again print the usage. 94 interp.HandleCommand("memory region", result) 95 self.assertFalse(result.Succeeded()) 96 self.assertRegex( 97 result.GetError(), 98 "Usage: memory region <address\-expression> \(or \-\-all\)", 99 ) 100 101 # --all should match what repeating the command gives you 102 interp.HandleCommand("memory region --all", result) 103 self.assertTrue(result.Succeeded()) 104 self.assertEqual(result.GetOutput(), all_regions) 105 106 @skipIfAsan 107 def test_no_overlapping_regions(self): 108 # In the past on Windows we were recording AllocationBase as the base address 109 # of the current region, not BaseAddress. So if a range of pages was split 110 # into regions you would see several regions with the same base address. 111 # This checks that this no longer happens (and it shouldn't happen on any 112 # other OS either). 113 self.setup_program() 114 115 regions = self.process().GetMemoryRegions() 116 num_regions = regions.GetSize() 117 118 if num_regions: 119 region = lldb.SBMemoryRegionInfo() 120 regions.GetMemoryRegionAtIndex(0, region) 121 previous_base = region.GetRegionBase() 122 previous_end = region.GetRegionEnd() 123 124 for idx in range(1, regions.GetSize()): 125 regions.GetMemoryRegionAtIndex(idx, region) 126 127 # Check that it does not overlap the previous region. 128 # This could happen if we got the base addresses or size wrong. 129 # Also catches the base addresses being the same. 130 region_base = region.GetRegionBase() 131 region_end = region.GetRegionEnd() 132 133 self.assertFalse( 134 (region_base < previous_end) and (previous_base < region_end), 135 "Unexpected overlapping memory region found.", 136 ) 137 138 previous_base = region_base 139 previous_end = region_end 140 141 142class MemoryCommandRegionAll(GDBRemoteTestBase): 143 NO_DEBUG_INFO_TESTCASE = True 144 145 def test_all_error(self): 146 # The --all option should keep looping until the end of the memory range. 147 # If there is an error it should be reported as if you were just asking 148 # for one region. In this case the error is the remote not supporting 149 # qMemoryRegionInfo. 150 # (a region being unmapped is not an error, we just get a result 151 # describing an unmapped range) 152 class MyResponder(MockGDBServerResponder): 153 def qMemoryRegionInfo(self, addr): 154 # Empty string means unsupported. 155 return "" 156 157 self.server.responder = MyResponder() 158 target = self.dbg.CreateTarget("") 159 if self.TraceOn(): 160 self.runCmd("log enable gdb-remote packets") 161 self.addTearDownHook(lambda: self.runCmd("log disable gdb-remote packets")) 162 163 process = self.connect(target) 164 lldbutil.expect_state_changes( 165 self, self.dbg.GetListener(), process, [lldb.eStateStopped] 166 ) 167 168 interp = self.dbg.GetCommandInterpreter() 169 result = lldb.SBCommandReturnObject() 170 interp.HandleCommand("memory region --all ", result) 171 self.assertFalse(result.Succeeded()) 172 self.assertEqual( 173 result.GetError(), "error: qMemoryRegionInfo is not supported\n" 174 ) 175 176 @skipIfAsan 177 def test_all_no_abi_plugin(self): 178 # There are two conditions for breaking the all loop. Either we get to 179 # LLDB_INVALID_ADDRESS, or the ABI plugin tells us we have got beyond 180 # the mappable range. If we don't have an ABI plugin, the option should still 181 # work and only check the first condition. 182 183 class MyResponder(MockGDBServerResponder): 184 def qMemoryRegionInfo(self, addr): 185 if addr == 0: 186 return "start:0;size:100000000;" 187 # Goes until the end of memory. 188 if addr == 0x100000000: 189 return "start:100000000;size:fffffffeffffffff;" 190 191 self.server.responder = MyResponder() 192 target = self.dbg.CreateTarget("") 193 if self.TraceOn(): 194 self.runCmd("log enable gdb-remote packets") 195 self.addTearDownHook(lambda: self.runCmd("log disable gdb-remote packets")) 196 197 process = self.connect(target) 198 lldbutil.expect_state_changes( 199 self, self.dbg.GetListener(), process, [lldb.eStateStopped] 200 ) 201 202 interp = self.dbg.GetCommandInterpreter() 203 result = lldb.SBCommandReturnObject() 204 interp.HandleCommand("memory region --all ", result) 205 self.assertTrue(result.Succeeded()) 206 self.assertEqual( 207 result.GetOutput(), 208 "[0x0000000000000000-0x0000000100000000) ---\n" 209 "[0x0000000100000000-0xffffffffffffffff) ---\n", 210 ) 211