444 lines · python
1"""2Test Debugger APIs.3"""4 5import lldb6 7from lldbsuite.test.decorators import *8from lldbsuite.test.lldbtest import *9from lldbsuite.test import lldbutil10 11 12class DebuggerAPITestCase(TestBase):13 NO_DEBUG_INFO_TESTCASE = True14 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 if28 # 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 properties45 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 context71 # 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 + 51280 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 = None157 158 def foo(dbg_id):159 # Need nonlocal to modify closure variable.160 nonlocal destroy_dbg_id161 destroy_dbg_id = dbg_id162 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 called176 called += [('foo', dbg_id)]177 178 def bar(dbg_id):179 # Need nonlocal to modify closure variable.180 nonlocal called181 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 called200 called += [('foo', dbg_id)]201 202 def bar(dbg_id):203 # Need nonlocal to modify closure variable.204 nonlocal called205 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 successful213 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 = 32413220 called = []221 222 def foo(dbg_id):223 # Need nonlocal to modify closure variable.224 nonlocal called225 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 unsuccessful232 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 = None245 246 def foo(dbg_id):247 # Need nonlocal to modify closure variable.248 nonlocal events249 events.append(('foo called', dbg_id))250 251 def bar(dbg_id):252 # Need nonlocal to modify closure variable.253 nonlocal events254 events.append(('bar called', dbg_id))255 256 def add_foo(dbg_id):257 # Need nonlocal to modify closure variable.258 nonlocal events259 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 events265 events.append(('remove_bar called', dbg_id))266 events.append(('remove bar ret', self.dbg.RemoveDestroyCallback(bar_token)))267 268 # Setup269 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 # Destroy274 self.dbg.Destroy(self.dbg)275 276 self.assertEqual(events, [277 # Setup278 ('add_foo token', 0), # add_foo should be added279 ('bar token', 1), # bar should be added280 ('remove_bar token', 2), # remove_bar should be added281 # Destroy282 ('add_foo called', original_dbg_id), # add_foo should be called283 ('foo token', 3), # foo should be added284 ('bar called', original_dbg_id), # bar should be called285 ('remove_bar called', original_dbg_id), # remove_bar should be called286 ('remove bar ret', False), # remove_bar should fail, because it's already invoked and removed287 ('foo called', original_dbg_id), # foo should be called288 ])289 290 def test_version(self):291 instance_str = self.dbg.GetVersionString()292 class_str = lldb.SBDebugger.GetVersionString()293 property_str = lldb.SBDebugger.version294 295 self.assertEqual(instance_str, class_str)296 self.assertEqual(class_str, property_str)297 298 def test_find_target_with_unique_id(self):299 """Test SBDebugger.FindTargetByGloballyUniqueID() functionality."""300 301 # Test with invalid ID - should return invalid target302 invalid_target = self.dbg.FindTargetByGloballyUniqueID(999999)303 self.assertFalse(invalid_target.IsValid())304 305 # Test with ID 0 - should return invalid target306 zero_target = self.dbg.FindTargetByGloballyUniqueID(0)307 self.assertFalse(zero_target.IsValid())308 309 # Build a real executable and create target with it310 self.build()311 exe = self.getBuildArtifact("a.out")312 target = self.dbg.CreateTarget(exe)313 self.assertTrue(target.IsValid())314 315 # Find the target using its unique ID316 unique_id = target.GetGloballyUniqueID()317 self.assertNotEqual(unique_id, lldb.LLDB_INVALID_GLOBALLY_UNIQUE_TARGET_ID)318 found_target = self.dbg.FindTargetByGloballyUniqueID(unique_id)319 self.assertTrue(found_target.IsValid())320 self.assertEqual(321 self.dbg.GetIndexOfTarget(target), self.dbg.GetIndexOfTarget(found_target)322 )323 self.assertEqual(found_target.GetGloballyUniqueID(), unique_id)324 325 def test_target_unique_id_uniqueness(self):326 """Test that Target.GetGloballyUniqueID() returns unique values across multiple targets."""327 328 # Create multiple targets and verify they all have unique IDs329 self.build()330 exe = self.getBuildArtifact("a.out")331 targets = []332 unique_ids = set()333 334 for i in range(10):335 target = self.dbg.CreateTarget(exe)336 self.assertTrue(target.IsValid())337 338 unique_id = target.GetGloballyUniqueID()339 self.assertNotEqual(unique_id, 0)340 341 # Verify this ID hasn't been used before342 self.assertNotIn(343 unique_id, unique_ids, f"Duplicate unique ID found: {unique_id}"344 )345 346 unique_ids.add(unique_id)347 targets.append(target)348 349 # Verify all targets can still be found by their IDs350 for target in targets:351 unique_id = target.GetGloballyUniqueID()352 found = self.dbg.FindTargetByGloballyUniqueID(unique_id)353 self.assertTrue(found.IsValid())354 self.assertEqual(found.GetGloballyUniqueID(), unique_id)355 356 def test_target_unique_id_uniqueness_after_deletion(self):357 """Test finding targets have unique ID after target deletion."""358 # Create two targets359 self.build()360 exe = self.getBuildArtifact("a.out")361 target1 = self.dbg.CreateTarget(exe)362 target2 = self.dbg.CreateTarget(exe)363 self.assertTrue(target1.IsValid())364 self.assertTrue(target2.IsValid())365 366 unique_id1 = target1.GetGloballyUniqueID()367 unique_id2 = target2.GetGloballyUniqueID()368 self.assertNotEqual(unique_id1, 0)369 self.assertNotEqual(unique_id2, 0)370 self.assertNotEqual(unique_id1, unique_id2)371 372 # Verify we can find them initially373 found_target1 = self.dbg.FindTargetByGloballyUniqueID(unique_id1)374 found_target2 = self.dbg.FindTargetByGloballyUniqueID(unique_id2)375 self.assertTrue(found_target1.IsValid())376 self.assertTrue(found_target2.IsValid())377 target2_index = self.dbg.GetIndexOfTarget(target2)378 379 # Delete target 2380 deleted = self.dbg.DeleteTarget(target2)381 self.assertTrue(deleted)382 383 # Try to find the deleted target - should not be found384 not_found_target = self.dbg.FindTargetByGloballyUniqueID(unique_id2)385 self.assertFalse(not_found_target.IsValid())386 387 # Create a new target388 target3 = self.dbg.CreateTarget(exe)389 self.assertTrue(target3.IsValid())390 # Target list index of target3 should be the same as target2's391 # since it was deleted, but it should have a distinct unique ID392 target3_index = self.dbg.GetIndexOfTarget(target3)393 unique_id3 = target3.GetGloballyUniqueID()394 self.assertEqual(target3_index, target2_index)395 self.assertNotEqual(unique_id3, unique_id2)396 self.assertNotEqual(unique_id3, unique_id1)397 # Make sure we can find the new target398 found_target3 = self.dbg.FindTargetByGloballyUniqueID(399 target3.GetGloballyUniqueID()400 )401 self.assertTrue(found_target3.IsValid())402 403 def test_target_globally_unique_id_across_debuggers(self):404 """Test that target IDs are globally unique across multiple debuggers."""405 self.build()406 exe = self.getBuildArtifact("a.out")407 408 # Create two debuggers with targets each409 debugger1 = lldb.SBDebugger.Create()410 debugger2 = lldb.SBDebugger.Create()411 self.addTearDownHook(lambda: lldb.SBDebugger.Destroy(debugger1))412 self.addTearDownHook(lambda: lldb.SBDebugger.Destroy(debugger2))413 414 # Create 2 targets per debugger415 targets_d1 = [debugger1.CreateTarget(exe), debugger1.CreateTarget(exe)]416 targets_d2 = [debugger2.CreateTarget(exe), debugger2.CreateTarget(exe)]417 targets = targets_d1 + targets_d2418 419 # Get all IDs and verify they're unique420 ids = [target.GetGloballyUniqueID() for target in targets]421 self.assertEqual(422 len(set(ids)), len(ids), f"IDs should be globally unique: {ids}"423 )424 self.assertTrue(425 all(uid != lldb.LLDB_INVALID_GLOBALLY_UNIQUE_TARGET_ID for uid in ids),426 "All IDs should be valid",427 )428 429 # Verify targets can be found by their IDs in respective debuggers430 for debugger, target_pair in [431 (debugger1, targets[:2]),432 (debugger2, targets[2:]),433 ]:434 for target in target_pair:435 found = debugger.FindTargetByGloballyUniqueID(436 target.GetGloballyUniqueID()437 )438 self.assertTrue(439 found.IsValid(), "Target should be found by its unique ID"440 )441 self.assertEqual(442 found.GetGloballyUniqueID(), target.GetGloballyUniqueID()443 )444