1""" 2Test Debugger APIs. 3""" 4 5import lldb 6 7from lldbsuite.test.decorators import * 8from lldbsuite.test.lldbtest import * 9from lldbsuite.test import lldbutil 10 11 12class DebuggerAPITestCase(TestBase): 13 NO_DEBUG_INFO_TESTCASE = True 14 15 def test_debugger_api_boundary_condition(self): 16 """Exercise SBDebugger APIs with boundary conditions.""" 17 self.dbg.HandleCommand(None) 18 self.dbg.SetDefaultArchitecture(None) 19 self.dbg.GetScriptingLanguage(None) 20 self.dbg.CreateTarget(None) 21 self.dbg.CreateTarget(None, None, None, True, lldb.SBError()) 22 self.dbg.CreateTargetWithFileAndTargetTriple(None, None) 23 self.dbg.CreateTargetWithFileAndArch(None, None) 24 self.dbg.FindTargetWithFileAndArch(None, None) 25 self.dbg.SetInternalVariable(None, None, None) 26 self.dbg.GetInternalVariableValue(None, None) 27 # FIXME (filcab): We must first allow for the swig bindings to know if 28 # a Python callback is set. (Check python-typemaps.swig) 29 # self.dbg.SetLoggingCallback(None) 30 self.dbg.SetPrompt(None) 31 self.dbg.SetCurrentPlatform(None) 32 self.dbg.SetCurrentPlatformSDKRoot(None) 33 34 fresh_dbg = lldb.SBDebugger() 35 self.assertEqual(len(fresh_dbg), 0) 36 37 def test_debugger_delete_invalid_target(self): 38 """SBDebugger.DeleteTarget() should not crash LLDB given and invalid target.""" 39 target = lldb.SBTarget() 40 self.assertFalse(target.IsValid()) 41 self.dbg.DeleteTarget(target) 42 43 def test_debugger_internal_variables(self): 44 """Ensure that SBDebugger reachs the same instance of properties 45 regardless CommandInterpreter's context initialization""" 46 self.build() 47 exe = self.getBuildArtifact("a.out") 48 49 # Create a target by the debugger. 50 target = self.dbg.CreateTarget(exe) 51 self.assertTrue(target, VALID_TARGET) 52 53 property_name = "target.process.memory-cache-line-size" 54 55 def get_cache_line_size(): 56 value_list = lldb.SBStringList() 57 value_list = self.dbg.GetInternalVariableValue( 58 property_name, self.dbg.GetInstanceName() 59 ) 60 61 self.assertEqual(value_list.GetSize(), 1) 62 try: 63 return int(value_list.GetStringAtIndex(0)) 64 except ValueError as error: 65 self.fail("Value is not a number: " + error) 66 67 # Get global property value while there are no processes. 68 global_cache_line_size = get_cache_line_size() 69 70 # Run a process via SB interface. CommandInterpreter's execution context 71 # remains empty. 72 error = lldb.SBError() 73 launch_info = lldb.SBLaunchInfo(None) 74 launch_info.SetLaunchFlags(lldb.eLaunchFlagStopAtEntry) 75 process = target.Launch(launch_info, error) 76 self.assertTrue(process, PROCESS_IS_VALID) 77 78 # This should change the value of a process's local property. 79 new_cache_line_size = global_cache_line_size + 512 80 error = self.dbg.SetInternalVariable( 81 property_name, str(new_cache_line_size), self.dbg.GetInstanceName() 82 ) 83 self.assertSuccess(error, property_name + " value was changed successfully") 84 85 # Check that it was set actually. 86 self.assertEqual(get_cache_line_size(), new_cache_line_size) 87 88 # Run any command to initialize CommandInterpreter's execution context. 89 self.runCmd("target list") 90 91 # Test the local property again, is it set to new_cache_line_size? 92 self.assertEqual(get_cache_line_size(), new_cache_line_size) 93 94 @expectedFailureAll( 95 hostoslist=["windows"], 96 remote=True, 97 bugnumber="github.com/llvm/llvm-project/issues/92419", 98 ) 99 def test_CreateTarget_platform(self): 100 exe = self.getBuildArtifact("a.out") 101 self.yaml2obj("elf.yaml", exe) 102 error = lldb.SBError() 103 target1 = self.dbg.CreateTarget(exe, None, "remote-linux", False, error) 104 self.assertSuccess(error) 105 platform1 = target1.GetPlatform() 106 platform1.SetWorkingDirectory("/foo/bar") 107 108 # Reuse a platform if it matches the currently selected one... 109 target2 = self.dbg.CreateTarget(exe, None, "remote-linux", False, error) 110 self.assertSuccess(error) 111 platform2 = target2.GetPlatform() 112 self.assertTrue( 113 platform2.GetWorkingDirectory().endswith("bar"), 114 platform2.GetWorkingDirectory(), 115 ) 116 117 # ... but create a new one if it doesn't. 118 self.dbg.SetSelectedPlatform(lldb.SBPlatform("remote-windows")) 119 target3 = self.dbg.CreateTarget(exe, None, "remote-linux", False, error) 120 self.assertSuccess(error) 121 platform3 = target3.GetPlatform() 122 self.assertIsNone(platform3.GetWorkingDirectory()) 123 124 def test_CreateTarget_arch(self): 125 exe = self.getBuildArtifact("a.out") 126 if lldbplatformutil.getHostPlatform() == "linux": 127 self.yaml2obj("macho.yaml", exe) 128 arch = "x86_64-apple-macosx" 129 expected_platform = "remote-macosx" 130 else: 131 self.yaml2obj("elf.yaml", exe) 132 arch = "x86_64-pc-linux" 133 expected_platform = "remote-linux" 134 135 fbsd = lldb.SBPlatform("remote-freebsd") 136 self.dbg.SetSelectedPlatform(fbsd) 137 138 error = lldb.SBError() 139 target1 = self.dbg.CreateTarget(exe, arch, None, False, error) 140 self.assertSuccess(error) 141 platform1 = target1.GetPlatform() 142 self.assertEqual(platform1.GetName(), expected_platform) 143 platform1.SetWorkingDirectory("/foo/bar") 144 145 # Reuse a platform even if it is not currently selected. 146 self.dbg.SetSelectedPlatform(fbsd) 147 target2 = self.dbg.CreateTarget(exe, arch, None, False, error) 148 self.assertSuccess(error) 149 platform2 = target2.GetPlatform() 150 self.assertEqual(platform2.GetName(), expected_platform) 151 self.assertTrue( 152 platform2.GetWorkingDirectory().endswith("bar"), 153 platform2.GetWorkingDirectory(), 154 ) 155 156 def test_SetDestroyCallback(self): 157 destroy_dbg_id = None 158 159 def foo(dbg_id): 160 # Need nonlocal to modify closure variable. 161 nonlocal destroy_dbg_id 162 destroy_dbg_id = dbg_id 163 164 self.dbg.SetDestroyCallback(foo) 165 166 original_dbg_id = self.dbg.GetID() 167 self.dbg.Destroy(self.dbg) 168 self.assertEqual(destroy_dbg_id, original_dbg_id) 169 170 def test_AddDestroyCallback(self): 171 original_dbg_id = self.dbg.GetID() 172 called = [] 173 174 def foo(dbg_id): 175 # Need nonlocal to modify closure variable. 176 nonlocal called 177 called += [('foo', dbg_id)] 178 179 def bar(dbg_id): 180 # Need nonlocal to modify closure variable. 181 nonlocal called 182 called += [('bar', dbg_id)] 183 184 token_foo = self.dbg.AddDestroyCallback(foo) 185 token_bar = self.dbg.AddDestroyCallback(bar) 186 self.dbg.Destroy(self.dbg) 187 188 # Should call both `foo()` and `bar()`. 189 self.assertEqual(called, [ 190 ('foo', original_dbg_id), 191 ('bar', original_dbg_id), 192 ]) 193 194 def test_RemoveDestroyCallback(self): 195 original_dbg_id = self.dbg.GetID() 196 called = [] 197 198 def foo(dbg_id): 199 # Need nonlocal to modify closure variable. 200 nonlocal called 201 called += [('foo', dbg_id)] 202 203 def bar(dbg_id): 204 # Need nonlocal to modify closure variable. 205 nonlocal called 206 called += [('bar', dbg_id)] 207 208 token_foo = self.dbg.AddDestroyCallback(foo) 209 token_bar = self.dbg.AddDestroyCallback(bar) 210 ret = self.dbg.RemoveDestroyCallback(token_foo) 211 self.dbg.Destroy(self.dbg) 212 213 # `Remove` should be successful 214 self.assertTrue(ret) 215 # Should only call `bar()` 216 self.assertEqual(called, [('bar', original_dbg_id)]) 217 218 def test_RemoveDestroyCallback_invalid_token(self): 219 original_dbg_id = self.dbg.GetID() 220 magic_token_that_should_not_exist = 32413 221 called = [] 222 223 def foo(dbg_id): 224 # Need nonlocal to modify closure variable. 225 nonlocal called 226 called += [('foo', dbg_id)] 227 228 token_foo = self.dbg.AddDestroyCallback(foo) 229 ret = self.dbg.RemoveDestroyCallback(magic_token_that_should_not_exist) 230 self.dbg.Destroy(self.dbg) 231 232 # `Remove` should be unsuccessful 233 self.assertFalse(ret) 234 # Should call `foo()` 235 self.assertEqual(called, [('foo', original_dbg_id)]) 236 237 def test_HandleDestroyCallback(self): 238 """ 239 Validates: 240 1. AddDestroyCallback and RemoveDestroyCallback work during debugger destroy. 241 2. HandleDestroyCallback invokes all callbacks in FIFO order. 242 """ 243 original_dbg_id = self.dbg.GetID() 244 events = [] 245 bar_token = None 246 247 def foo(dbg_id): 248 # Need nonlocal to modify closure variable. 249 nonlocal events 250 events.append(('foo called', dbg_id)) 251 252 def bar(dbg_id): 253 # Need nonlocal to modify closure variable. 254 nonlocal events 255 events.append(('bar called', dbg_id)) 256 257 def add_foo(dbg_id): 258 # Need nonlocal to modify closure variable. 259 nonlocal events 260 events.append(('add_foo called', dbg_id)) 261 events.append(('foo token', self.dbg.AddDestroyCallback(foo))) 262 263 def remove_bar(dbg_id): 264 # Need nonlocal to modify closure variable. 265 nonlocal events 266 events.append(('remove_bar called', dbg_id)) 267 events.append(('remove bar ret', self.dbg.RemoveDestroyCallback(bar_token))) 268 269 # Setup 270 events.append(('add_foo token', self.dbg.AddDestroyCallback(add_foo))) 271 bar_token = self.dbg.AddDestroyCallback(bar) 272 events.append(('bar token', bar_token)) 273 events.append(('remove_bar token', self.dbg.AddDestroyCallback(remove_bar))) 274 # Destroy 275 self.dbg.Destroy(self.dbg) 276 277 self.assertEqual(events, [ 278 # Setup 279 ('add_foo token', 0), # add_foo should be added 280 ('bar token', 1), # bar should be added 281 ('remove_bar token', 2), # remove_bar should be added 282 # Destroy 283 ('add_foo called', original_dbg_id), # add_foo should be called 284 ('foo token', 3), # foo should be added 285 ('bar called', original_dbg_id), # bar should be called 286 ('remove_bar called', original_dbg_id), # remove_bar should be called 287 ('remove bar ret', False), # remove_bar should fail, because it's already invoked and removed 288 ('foo called', original_dbg_id), # foo should be called 289 ]) 290