333 lines · python
1"""2Use lldb Python API to test dynamic values in C++3"""4 5 6import lldb7from lldbsuite.test.decorators import *8from lldbsuite.test.lldbtest import *9from lldbsuite.test import lldbutil10 11 12class DynamicValueTestCase(TestBase):13 def setUp(self):14 # Call super's setUp().15 TestBase.setUp(self)16 17 # Find the line number to break for main.c.18 19 self.do_something_line = line_number(20 "pass-to-base.cpp", "// Break here in doSomething."21 )22 self.main_first_call_line = line_number(23 "pass-to-base.cpp",24 "// Break here and get real addresses of myB and otherB.",25 )26 self.main_second_call_line = line_number(27 "pass-to-base.cpp", "// Break here and get real address of reallyA."28 )29 30 @add_test_categories(["pyapi"])31 @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24663")32 def test_get_dynamic_vals(self):33 """Test fetching C++ dynamic values from pointers & references."""34 self.build()35 exe = self.getBuildArtifact("a.out")36 37 # Create a target from the debugger.38 39 target = self.dbg.CreateTarget(exe)40 self.assertTrue(target, VALID_TARGET)41 42 # Set up our breakpoints:43 44 do_something_bpt = target.BreakpointCreateByLocation(45 "pass-to-base.cpp", self.do_something_line46 )47 self.assertTrue(do_something_bpt, VALID_BREAKPOINT)48 49 first_call_bpt = target.BreakpointCreateByLocation(50 "pass-to-base.cpp", self.main_first_call_line51 )52 self.assertTrue(first_call_bpt, VALID_BREAKPOINT)53 54 second_call_bpt = target.BreakpointCreateByLocation(55 "pass-to-base.cpp", self.main_second_call_line56 )57 self.assertTrue(second_call_bpt, VALID_BREAKPOINT)58 59 # Now launch the process, and do not stop at the entry point.60 process = target.LaunchSimple(None, None, self.get_process_working_directory())61 62 self.assertState(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED)63 64 threads = lldbutil.get_threads_stopped_at_breakpoint(process, first_call_bpt)65 self.assertEqual(len(threads), 1)66 thread = threads[0]67 68 frame = thread.GetFrameAtIndex(0)69 70 # Now find the dynamic addresses of myB and otherB so we can compare them71 # with the dynamic values we get in doSomething:72 73 use_dynamic = lldb.eDynamicCanRunTarget74 no_dynamic = lldb.eNoDynamicValues75 76 myB = frame.FindVariable("myB", no_dynamic)77 self.assertTrue(myB)78 myB_loc = int(myB.GetLocation(), 16)79 80 otherB = frame.FindVariable("otherB", no_dynamic)81 self.assertTrue(otherB)82 otherB_loc = int(otherB.GetLocation(), 16)83 84 # Okay now run to doSomething:85 86 threads = lldbutil.continue_to_breakpoint(process, do_something_bpt)87 self.assertEqual(len(threads), 1)88 thread = threads[0]89 90 frame = thread.GetFrameAtIndex(0)91 92 # Get "this" using FindVariable:93 94 this_static = frame.FindVariable("this", no_dynamic)95 this_dynamic = frame.FindVariable("this", use_dynamic)96 self.examine_value_object_of_this_ptr(this_static, this_dynamic, myB_loc)97 98 # Now make sure that the "GetDynamicValue" works:99 # This doesn't work currently because we can't get dynamic values from100 # ConstResult objects.101 fetched_dynamic_value = this_static.GetDynamicValue(use_dynamic)102 self.examine_value_object_of_this_ptr(103 this_static, fetched_dynamic_value, myB_loc104 )105 106 # And conversely that the GetDynamicValue() interface also works:107 fetched_static_value = this_dynamic.GetStaticValue()108 self.examine_value_object_of_this_ptr(109 fetched_static_value, this_dynamic, myB_loc110 )111 112 # Get "this" using FindValue, make sure that works too:113 this_static = frame.FindValue(114 "this", lldb.eValueTypeVariableArgument, no_dynamic115 )116 this_dynamic = frame.FindValue(117 "this", lldb.eValueTypeVariableArgument, use_dynamic118 )119 self.examine_value_object_of_this_ptr(this_static, this_dynamic, myB_loc)120 121 # Get "this" using the EvaluateExpression:122 this_static = frame.EvaluateExpression("this", False)123 this_dynamic = frame.EvaluateExpression("this", True)124 self.examine_value_object_of_this_ptr(this_static, this_dynamic, myB_loc)125 126 # The "frame var" code uses another path to get into children, so let's127 # make sure that works as well:128 129 self.expect(130 "frame var -d run-target --ptr-depth=2 --show-types anotherA.m_client_A",131 "frame var finds its way into a child member",132 patterns=[r"\(B \*\)"],133 )134 135 # Now make sure we also get it right for a reference as well:136 137 anotherA_static = frame.FindVariable("anotherA", False)138 self.assertTrue(anotherA_static)139 anotherA_static_addr = int(anotherA_static.GetValue(), 16)140 141 anotherA_dynamic = frame.FindVariable("anotherA", True)142 self.assertTrue(anotherA_dynamic)143 anotherA_dynamic_addr = int(anotherA_dynamic.GetValue(), 16)144 anotherA_dynamic_typename = anotherA_dynamic.GetTypeName()145 self.assertNotEqual(anotherA_dynamic_typename.find("B"), -1)146 147 self.assertLess(anotherA_dynamic_addr, anotherA_static_addr)148 149 anotherA_m_b_value_dynamic = anotherA_dynamic.GetChildMemberWithName(150 "m_b_value", True151 )152 self.assertTrue(anotherA_m_b_value_dynamic)153 anotherA_m_b_val = int(anotherA_m_b_value_dynamic.GetValue(), 10)154 self.assertEqual(anotherA_m_b_val, 300)155 156 anotherA_m_b_value_static = anotherA_static.GetChildMemberWithName(157 "m_b_value", True158 )159 self.assertFalse(anotherA_m_b_value_static)160 161 # Okay, now continue again, and when we hit the second breakpoint in162 # main163 164 threads = lldbutil.continue_to_breakpoint(process, second_call_bpt)165 self.assertEqual(len(threads), 1)166 thread = threads[0]167 168 frame = thread.GetFrameAtIndex(0)169 reallyA_value = frame.FindVariable("reallyA", False)170 self.assertTrue(reallyA_value)171 reallyA_loc = int(reallyA_value.GetLocation(), 16)172 173 # Continue to doSomething again, and make sure we get the right value for anotherA,174 # which this time around is just an "A".175 176 threads = lldbutil.continue_to_breakpoint(process, do_something_bpt)177 self.assertEqual(len(threads), 1)178 thread = threads[0]179 180 frame = thread.GetFrameAtIndex(0)181 anotherA_value = frame.FindVariable("anotherA", True)182 self.assertTrue(anotherA_value)183 anotherA_loc = int(anotherA_value.GetValue(), 16)184 self.assertEqual(anotherA_loc, reallyA_loc)185 self.assertEqual(anotherA_value.GetTypeName().find("B"), -1)186 187 # Finally do the same with a B in an anonymous namespace.188 threads = lldbutil.continue_to_breakpoint(process, do_something_bpt)189 self.assertEqual(len(threads), 1)190 thread = threads[0]191 192 frame = thread.GetFrameAtIndex(0)193 anotherA_value = frame.FindVariable("anotherA", use_dynamic)194 self.assertTrue(anotherA_value)195 self.assertIn("B", anotherA_value.GetTypeName())196 anon_b_value = anotherA_value.GetChildMemberWithName("m_anon_b_value")197 self.assertTrue(anon_b_value)198 self.assertEqual(anon_b_value.GetValueAsSigned(), 47)199 200 def examine_value_object_of_this_ptr(201 self, this_static, this_dynamic, dynamic_location202 ):203 # Get "this" as its static value204 self.assertTrue(this_static)205 this_static_loc = int(this_static.GetValue(), 16)206 207 # Get "this" as its dynamic value208 209 self.assertTrue(this_dynamic)210 this_dynamic_typename = this_dynamic.GetTypeName()211 self.assertNotEqual(this_dynamic_typename.find("B"), -1)212 this_dynamic_loc = int(this_dynamic.GetValue(), 16)213 214 # Make sure we got the right address for "this"215 216 self.assertEqual(this_dynamic_loc, dynamic_location)217 218 # And that the static address is greater than the dynamic one219 220 self.assertGreater(this_static_loc, this_dynamic_loc)221 222 # Now read m_b_value which is only in the dynamic value:223 224 use_dynamic = lldb.eDynamicCanRunTarget225 no_dynamic = lldb.eNoDynamicValues226 227 this_dynamic_m_b_value = this_dynamic.GetChildMemberWithName(228 "m_b_value", use_dynamic229 )230 self.assertTrue(this_dynamic_m_b_value)231 232 m_b_value = int(this_dynamic_m_b_value.GetValue(), 0)233 self.assertEqual(m_b_value, 10)234 235 # Make sure it is not in the static version236 237 this_static_m_b_value = this_static.GetChildMemberWithName(238 "m_b_value", no_dynamic239 )240 self.assertFalse(this_static_m_b_value)241 242 # Okay, now let's make sure that we can get the dynamic type of a child243 # element:244 245 contained_auto_ptr = this_dynamic.GetChildMemberWithName(246 "m_client_A", use_dynamic247 )248 self.assertTrue(contained_auto_ptr)249 contained_b = contained_auto_ptr.GetChildMemberWithName("_M_ptr", use_dynamic)250 if not contained_b:251 contained_b = contained_auto_ptr.GetChildMemberWithName(252 "__ptr_", use_dynamic253 )254 self.assertTrue(contained_b)255 256 contained_b_static = contained_auto_ptr.GetChildMemberWithName(257 "_M_ptr", no_dynamic258 )259 if not contained_b_static:260 contained_b_static = contained_auto_ptr.GetChildMemberWithName(261 "__ptr_", no_dynamic262 )263 self.assertTrue(contained_b_static)264 265 contained_b_addr = int(contained_b.GetValue(), 16)266 contained_b_static_addr = int(contained_b_static.GetValue(), 16)267 268 self.assertLess(contained_b_addr, contained_b_static_addr)269 270 @no_debug_info_test271 @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24663")272 def test_from_forward_decl(self):273 """Test fetching C++ dynamic values forward-declared types. It's274 imperative that this is a separate test so that we don't end up parsing275 a definition of A from somewhere else."""276 self.build()277 lldbutil.run_to_name_breakpoint(self, "take_A")278 self.expect(279 "frame var -d run-target --ptr-depth=1 --show-types a",280 substrs=["(B *) a", "m_b_value = 10"],281 )282 283 @no_debug_info_test284 @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24663")285 @expectedFailureAll(archs=["arm$"]) # Minidump saving not implemented286 def test_from_core_file(self):287 """Test fetching C++ dynamic values from core files. Specifically, test288 that we can determine the dynamic type of the value if the core file289 does not contain the type vtable."""290 self.build()291 lldbutil.run_to_name_breakpoint(self, "take_A")292 293 # Get the address of our object and its vtable294 a = self.frame().FindVariable("a")295 self.assertSuccess(a.GetError())296 vtable = a.GetVTable()297 self.assertSuccess(vtable.GetError())298 a = a.GetValueAsAddress()299 vtable = vtable.GetValueAsAddress()300 301 # Create a core file which will only contain the memory region302 # containing `a`. The object is on the stack, so this will automatically303 # include the stack of the main thread.304 core = self.getBuildArtifact("a.dmp")305 options = lldb.SBSaveCoreOptions()306 options.SetPluginName("minidump")307 options.SetStyle(lldb.eSaveCoreCustomOnly)308 options.SetOutputFile(lldb.SBFileSpec(core))309 region = lldb.SBMemoryRegionInfo()310 self.assertSuccess(self.process().GetMemoryRegionInfo(a, region))311 self.assertSuccess(options.AddMemoryRegionToSave(region))312 313 # Save the core file and load it.314 self.assertSuccess(self.process().SaveCore(options))315 self.process().Kill()316 error = lldb.SBError()317 self.target().LoadCore(core, error)318 self.assertSuccess(error)319 320 # Sanity check -- the process should be able to read the object but not321 # its vtable..322 self.process().ReadPointerFromMemory(a, error)323 self.assertSuccess(error)324 self.process().ReadPointerFromMemory(vtable, error)325 self.assertTrue(error.Fail())326 327 # .. but we should still be able to see the dynamic type by reading the328 # vtable from the executable file.329 self.expect(330 "frame var -d run-target --ptr-depth=1 --show-types a",331 substrs=["(B *) a", "m_b_value = 10"],332 )333