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 remote=True, 96 bugnumber="github.com/llvm/llvm-project/issues/92419", 97 ) 98 def test_CreateTarget_platform(self): 99 exe = self.getBuildArtifact("a.out") 100 self.yaml2obj("elf.yaml", exe) 101 error = lldb.SBError() 102 target1 = self.dbg.CreateTarget(exe, None, "remote-linux", False, error) 103 self.assertSuccess(error) 104 platform1 = target1.GetPlatform() 105 platform1.SetWorkingDirectory("/foo/bar") 106 107 # Reuse a platform if it matches the currently selected one... 108 target2 = self.dbg.CreateTarget(exe, None, "remote-linux", False, error) 109 self.assertSuccess(error) 110 platform2 = target2.GetPlatform() 111 self.assertTrue( 112 platform2.GetWorkingDirectory().endswith("bar"), 113 platform2.GetWorkingDirectory(), 114 ) 115 116 # ... but create a new one if it doesn't. 117 self.dbg.SetSelectedPlatform(lldb.SBPlatform("remote-windows")) 118 target3 = self.dbg.CreateTarget(exe, None, "remote-linux", False, error) 119 self.assertSuccess(error) 120 platform3 = target3.GetPlatform() 121 self.assertIsNone(platform3.GetWorkingDirectory()) 122 123 def test_CreateTarget_arch(self): 124 exe = self.getBuildArtifact("a.out") 125 if lldbplatformutil.getHostPlatform() == "linux": 126 self.yaml2obj("macho.yaml", exe) 127 arch = "x86_64-apple-macosx" 128 expected_platform = "remote-macosx" 129 else: 130 self.yaml2obj("elf.yaml", exe) 131 arch = "x86_64-pc-linux" 132 expected_platform = "remote-linux" 133 134 fbsd = lldb.SBPlatform("remote-freebsd") 135 self.dbg.SetSelectedPlatform(fbsd) 136 137 error = lldb.SBError() 138 target1 = self.dbg.CreateTarget(exe, arch, None, False, error) 139 self.assertSuccess(error) 140 platform1 = target1.GetPlatform() 141 self.assertEqual(platform1.GetName(), expected_platform) 142 platform1.SetWorkingDirectory("/foo/bar") 143 144 # Reuse a platform even if it is not currently selected. 145 self.dbg.SetSelectedPlatform(fbsd) 146 target2 = self.dbg.CreateTarget(exe, arch, None, False, error) 147 self.assertSuccess(error) 148 platform2 = target2.GetPlatform() 149 self.assertEqual(platform2.GetName(), expected_platform) 150 self.assertTrue( 151 platform2.GetWorkingDirectory().endswith("bar"), 152 platform2.GetWorkingDirectory(), 153 ) 154 155 def test_SetDestroyCallback(self): 156 destroy_dbg_id = None 157 158 def foo(dbg_id): 159 # Need nonlocal to modify closure variable. 160 nonlocal destroy_dbg_id 161 destroy_dbg_id = dbg_id 162 163 self.dbg.SetDestroyCallback(foo) 164 165 original_dbg_id = self.dbg.GetID() 166 self.dbg.Destroy(self.dbg) 167 self.assertEqual(destroy_dbg_id, original_dbg_id) 168 169 def test_AddDestroyCallback(self): 170 original_dbg_id = self.dbg.GetID() 171 called = [] 172 173 def foo(dbg_id): 174 # Need nonlocal to modify closure variable. 175 nonlocal called 176 called += [('foo', dbg_id)] 177 178 def bar(dbg_id): 179 # Need nonlocal to modify closure variable. 180 nonlocal called 181 called += [('bar', dbg_id)] 182 183 token_foo = self.dbg.AddDestroyCallback(foo) 184 token_bar = self.dbg.AddDestroyCallback(bar) 185 self.dbg.Destroy(self.dbg) 186 187 # Should call both `foo()` and `bar()`. 188 self.assertEqual(called, [ 189 ('foo', original_dbg_id), 190 ('bar', original_dbg_id), 191 ]) 192 193 def test_RemoveDestroyCallback(self): 194 original_dbg_id = self.dbg.GetID() 195 called = [] 196 197 def foo(dbg_id): 198 # Need nonlocal to modify closure variable. 199 nonlocal called 200 called += [('foo', dbg_id)] 201 202 def bar(dbg_id): 203 # Need nonlocal to modify closure variable. 204 nonlocal called 205 called += [('bar', dbg_id)] 206 207 token_foo = self.dbg.AddDestroyCallback(foo) 208 token_bar = self.dbg.AddDestroyCallback(bar) 209 ret = self.dbg.RemoveDestroyCallback(token_foo) 210 self.dbg.Destroy(self.dbg) 211 212 # `Remove` should be successful 213 self.assertTrue(ret) 214 # Should only call `bar()` 215 self.assertEqual(called, [('bar', original_dbg_id)]) 216 217 def test_RemoveDestroyCallback_invalid_token(self): 218 original_dbg_id = self.dbg.GetID() 219 magic_token_that_should_not_exist = 32413 220 called = [] 221 222 def foo(dbg_id): 223 # Need nonlocal to modify closure variable. 224 nonlocal called 225 called += [('foo', dbg_id)] 226 227 token_foo = self.dbg.AddDestroyCallback(foo) 228 ret = self.dbg.RemoveDestroyCallback(magic_token_that_should_not_exist) 229 self.dbg.Destroy(self.dbg) 230 231 # `Remove` should be unsuccessful 232 self.assertFalse(ret) 233 # Should call `foo()` 234 self.assertEqual(called, [('foo', original_dbg_id)]) 235 236 def test_HandleDestroyCallback(self): 237 """ 238 Validates: 239 1. AddDestroyCallback and RemoveDestroyCallback work during debugger destroy. 240 2. HandleDestroyCallback invokes all callbacks in FIFO order. 241 """ 242 original_dbg_id = self.dbg.GetID() 243 events = [] 244 bar_token = None 245 246 def foo(dbg_id): 247 # Need nonlocal to modify closure variable. 248 nonlocal events 249 events.append(('foo called', dbg_id)) 250 251 def bar(dbg_id): 252 # Need nonlocal to modify closure variable. 253 nonlocal events 254 events.append(('bar called', dbg_id)) 255 256 def add_foo(dbg_id): 257 # Need nonlocal to modify closure variable. 258 nonlocal events 259 events.append(('add_foo called', dbg_id)) 260 events.append(('foo token', self.dbg.AddDestroyCallback(foo))) 261 262 def remove_bar(dbg_id): 263 # Need nonlocal to modify closure variable. 264 nonlocal events 265 events.append(('remove_bar called', dbg_id)) 266 events.append(('remove bar ret', self.dbg.RemoveDestroyCallback(bar_token))) 267 268 # Setup 269 events.append(('add_foo token', self.dbg.AddDestroyCallback(add_foo))) 270 bar_token = self.dbg.AddDestroyCallback(bar) 271 events.append(('bar token', bar_token)) 272 events.append(('remove_bar token', self.dbg.AddDestroyCallback(remove_bar))) 273 # Destroy 274 self.dbg.Destroy(self.dbg) 275 276 self.assertEqual(events, [ 277 # Setup 278 ('add_foo token', 0), # add_foo should be added 279 ('bar token', 1), # bar should be added 280 ('remove_bar token', 2), # remove_bar should be added 281 # Destroy 282 ('add_foo called', original_dbg_id), # add_foo should be called 283 ('foo token', 3), # foo should be added 284 ('bar called', original_dbg_id), # bar should be called 285 ('remove_bar called', original_dbg_id), # remove_bar should be called 286 ('remove bar ret', False), # remove_bar should fail, because it's already invoked and removed 287 ('foo called', original_dbg_id), # foo should be called 288 ]) 289