| """ | |
| Test completing types using information from other shared libraries. | |
| """ | |
| import os | |
| import lldb | |
| from lldbsuite.test.decorators import * | |
| from lldbsuite.test.lldbtest import * | |
| from lldbsuite.test import lldbutil | |
| @skipIfTargetDoesNotSupportSharedLibraries() | |
| class LimitDebugInfoTestCase(TestBase): | |
| SHARED_BUILD_TESTCASE = False | |
| def _check_type(self, target, name): | |
| exe = target.FindModule(lldb.SBFileSpec("a.out")) | |
| type_ = exe.FindFirstType(name) | |
| self.trace("type_: %s" % type_) | |
| self.assertTrue(type_) | |
| self.assertTrue(type_.IsTypeComplete()) | |
| base = type_.GetDirectBaseClassAtIndex(0).GetType() | |
| self.trace("base:%s" % base) | |
| self.assertTrue(base) | |
| self.assertEqual(base.GetNumberOfFields(), 0) | |
| self.assertFalse(base.IsTypeComplete()) | |
| def _check_debug_info_is_limited(self, target): | |
| # Without other shared libraries we should only see the member declared | |
| # in the derived class. This serves as a sanity check that we are truly | |
| # building with limited debug info. | |
| self._check_type(target, "InheritsFromOne") | |
| self._check_type(target, "InheritsFromTwo") | |
| # Check that the statistics show that we had incomplete debug info. | |
| stats = self.get_stats() | |
| # Find the a.out module info in the stats and verify it has the | |
| # "debugInfoHadIncompleteTypes" key value pair set to True | |
| exe_module_found = False | |
| for module in stats["modules"]: | |
| if module["path"].endswith("a.out"): | |
| self.assertTrue(module["debugInfoHadIncompleteTypes"]) | |
| exe_module_found = True | |
| break | |
| self.assertTrue(exe_module_found) | |
| # Verify that "totalModuleCountWithIncompleteTypes" at the top level | |
| # is greater than zero which shows we had incomplete debug info in a | |
| # module | |
| self.assertGreater(stats["totalModuleCountWithIncompleteTypes"], 0) | |
| def _check_incomplete_frame_variable_output(self): | |
| # Check that the display of the "frame variable" output identifies the | |
| # incomplete types. Currently the expression parser will find the real | |
| # definition for a type when running an expression for any forcefully | |
| # completed types, but "frame variable" won't. I hope to fix this with | |
| # a follow up patch, but if we don't find the actual definition we | |
| # should clearly show this to the user by showing which types were | |
| # incomplete. So this will test verifies the expected output for such | |
| # types. We also need to verify the standard "frame variable" output | |
| # which will inline all of the members on one line, versus the full | |
| # output from "frame variable --raw" and a few other options. | |
| # self.expect("frame variable two_as_member", error=True, | |
| # substrs=["no member named 'one' in 'InheritsFromOne'"]) | |
| command_expect_pairs = [ | |
| # Test standard "frame variable" output for types to make sure | |
| # "<incomplete type>" shows up where we expect it to | |
| [ | |
| "var two_as_member", | |
| [ | |
| "(TwoAsMember) ::two_as_member = (two = <incomplete type>, member = 47)" | |
| ], | |
| ], | |
| [ | |
| "var inherits_from_one", | |
| [ | |
| "(InheritsFromOne) ::inherits_from_one = (One = <incomplete type>, member = 47)" | |
| ], | |
| ], | |
| [ | |
| "var inherits_from_two", | |
| [ | |
| "(InheritsFromTwo) ::inherits_from_two = (Two = <incomplete type>, member = 47)" | |
| ], | |
| ], | |
| [ | |
| "var one_as_member", | |
| [ | |
| "(OneAsMember) ::one_as_member = (one = <incomplete type>, member = 47)" | |
| ], | |
| ], | |
| [ | |
| "var two_as_member", | |
| [ | |
| "(TwoAsMember) ::two_as_member = (two = <incomplete type>, member = 47)" | |
| ], | |
| ], | |
| [ | |
| "var array_of_one", | |
| [ | |
| "(array::One[3]) ::array_of_one = ([0] = <incomplete type>, [1] = <incomplete type>, [2] = <incomplete type>)" | |
| ], | |
| ], | |
| [ | |
| "var array_of_two", | |
| [ | |
| "(array::Two[3]) ::array_of_two = ([0] = <incomplete type>, [1] = <incomplete type>, [2] = <incomplete type>)" | |
| ], | |
| ], | |
| [ | |
| "var shadowed_one", | |
| [ | |
| "(ShadowedOne) ::shadowed_one = (func_shadow::One = <incomplete type>, member = 47)" | |
| ], | |
| ], | |
| # Now test "frame variable --show-types output" which has multi-line | |
| # output and should not always show classes that were forcefully | |
| # completed to the user to let them know they have a type that should | |
| # have been complete but wasn't. | |
| [ | |
| "var --show-types inherits_from_one", | |
| [ | |
| "(InheritsFromOne) ::inherits_from_one = {", | |
| " (One) One = <incomplete type> {}", | |
| " (int) member = 47", | |
| "}", | |
| ], | |
| ], | |
| [ | |
| "var --show-types inherits_from_two", | |
| [ | |
| "(InheritsFromTwo) ::inherits_from_two = {", | |
| " (Two) Two = <incomplete type> {}", | |
| " (int) member = 47", | |
| "}", | |
| ], | |
| ], | |
| [ | |
| "var --show-types one_as_member", | |
| [ | |
| "(OneAsMember) ::one_as_member = {", | |
| " (member::One) one = <incomplete type> {}", | |
| " (int) member = 47", | |
| "}", | |
| ], | |
| ], | |
| [ | |
| "var --show-types two_as_member", | |
| [ | |
| "(TwoAsMember) ::two_as_member = {", | |
| " (member::Two) two = <incomplete type> {}", | |
| " (int) member = 47", | |
| "}", | |
| ], | |
| ], | |
| [ | |
| "var --show-types array_of_one", | |
| [ | |
| "(array::One[3]) ::array_of_one = {", | |
| " (array::One) [0] = <incomplete type> {}", | |
| " (array::One) [1] = <incomplete type> {}", | |
| " (array::One) [2] = <incomplete type> {}", | |
| "}", | |
| ], | |
| ], | |
| [ | |
| "var --show-types array_of_two", | |
| [ | |
| "(array::Two[3]) ::array_of_two = {", | |
| " (array::Two) [0] = <incomplete type> {}", | |
| " (array::Two) [1] = <incomplete type> {}", | |
| " (array::Two) [2] = <incomplete type> {}", | |
| "}", | |
| ], | |
| ], | |
| [ | |
| "var --show-types shadowed_one", | |
| [ | |
| "(ShadowedOne) ::shadowed_one = {", | |
| " (func_shadow::One) func_shadow::One = <incomplete type> {}", | |
| " (int) member = 47", | |
| "}", | |
| ], | |
| ], | |
| ] | |
| for command, expect_items in command_expect_pairs: | |
| self.expect(command, substrs=expect_items) | |
| @skipIf(bugnumber="pr46284", debug_info="gmodules") | |
| @skipIfWindows # Clang emits type info even with -flimit-debug-info | |
| # Requires DW_CC_pass_by_* attributes from Clang 7 to correctly call | |
| # by-value functions. | |
| @skipIf(compiler="clang", compiler_version=["<", "7.0"]) | |
| def test_one_and_two_debug(self): | |
| self.build() | |
| target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) | |
| self._check_debug_info_is_limited(target) | |
| lldbutil.run_to_name_breakpoint(self, "main", extra_images=["one", "two"]) | |
| # But when other shared libraries are loaded, we should be able to see | |
| # all members. | |
| self.expect_expr("inherits_from_one.member", result_value="47") | |
| self.expect_expr("inherits_from_one.one", result_value="142") | |
| self.expect_expr("inherits_from_two.member", result_value="47") | |
| self.expect_expr("inherits_from_two.one", result_value="142") | |
| self.expect_expr("inherits_from_two.two", result_value="242") | |
| self.expect_expr("one_as_member.member", result_value="47") | |
| self.expect_expr("one_as_member.one.member", result_value="147") | |
| self.expect_expr("two_as_member.member", result_value="47") | |
| self.expect_expr("two_as_member.two.one.member", result_value="147") | |
| self.expect_expr("two_as_member.two.member", result_value="247") | |
| self.expect_expr("array_of_one[2].member", result_value="174") | |
| self.expect_expr("array_of_two[2].one[2].member", result_value="174") | |
| self.expect_expr("array_of_two[2].member", result_value="274") | |
| self.expect_expr("get_one().member", result_value="124") | |
| self.expect_expr("get_two().one().member", result_value="124") | |
| self.expect_expr("get_two().member", result_value="224") | |
| self.expect_expr("shadowed_one.member", result_value="47") | |
| self.expect_expr("shadowed_one.one", result_value="142") | |
| self._check_incomplete_frame_variable_output() | |
| @skipIf(bugnumber="pr46284", debug_info="gmodules") | |
| @skipIfWindows # Clang emits type info even with -flimit-debug-info | |
| # Requires DW_CC_pass_by_* attributes from Clang 7 to correctly call | |
| # by-value functions. | |
| @skipIf(compiler="clang", compiler_version=["<", "7.0"]) | |
| def test_two_debug(self): | |
| self.build(dictionary=dict(STRIP_ONE="1")) | |
| target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) | |
| self._check_debug_info_is_limited(target) | |
| lldbutil.run_to_name_breakpoint(self, "main", extra_images=["one", "two"]) | |
| # This time, we should only see the members from the second library. | |
| self.expect_expr("inherits_from_one.member", result_value="47") | |
| self.expect( | |
| "expr inherits_from_one.one", | |
| error=True, | |
| substrs=["no member named 'one' in 'InheritsFromOne'"], | |
| ) | |
| self.expect_expr("inherits_from_two.member", result_value="47") | |
| self.expect( | |
| "expr inherits_from_two.one", | |
| error=True, | |
| substrs=["no member named 'one' in 'InheritsFromTwo'"], | |
| ) | |
| self.expect_expr("inherits_from_two.two", result_value="242") | |
| self.expect_expr("one_as_member.member", result_value="47") | |
| self.expect( | |
| "expr one_as_member.one.member", | |
| error=True, | |
| substrs=["no member named 'member' in 'member::One'"], | |
| ) | |
| self.expect_expr("two_as_member.member", result_value="47") | |
| self.expect( | |
| "expr two_as_member.two.one.member", | |
| error=True, | |
| substrs=["no member named 'member' in 'member::One'"], | |
| ) | |
| self.expect_expr("two_as_member.two.member", result_value="247") | |
| self.expect( | |
| "expr array_of_one[2].member", | |
| error=True, | |
| substrs=["no member named 'member' in 'array::One'"], | |
| ) | |
| self.expect( | |
| "expr array_of_two[2].one[2].member", | |
| error=True, | |
| substrs=["no member named 'member' in 'array::One'"], | |
| ) | |
| self.expect_expr("array_of_two[2].member", result_value="274") | |
| self.expect( | |
| "expr get_one().member", | |
| error=True, | |
| substrs=["calling 'get_one' with incomplete return type 'result::One'"], | |
| ) | |
| self.expect( | |
| "expr get_two().one().member", | |
| error=True, | |
| substrs=["calling 'one' with incomplete return type 'result::One'"], | |
| ) | |
| self.expect_expr("get_two().member", result_value="224") | |
| self._check_incomplete_frame_variable_output() | |
| @skipIf(bugnumber="pr46284", debug_info="gmodules") | |
| @skipIfWindows # Clang emits type info even with -flimit-debug-info | |
| # Requires DW_CC_pass_by_* attributes from Clang 7 to correctly call | |
| # by-value functions. | |
| @skipIf(compiler="clang", compiler_version=["<", "7.0"]) | |
| def test_one_debug(self): | |
| self.build(dictionary=dict(STRIP_TWO="1")) | |
| target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) | |
| self._check_debug_info_is_limited(target) | |
| lldbutil.run_to_name_breakpoint(self, "main", extra_images=["one", "two"]) | |
| # In this case we should only see the members from the second library. | |
| # Note that we cannot see inherits_from_two.one because without debug | |
| # info for "Two", we cannot determine that it in fact inherits from | |
| # "One". | |
| self.expect_expr("inherits_from_one.member", result_value="47") | |
| self.expect_expr("inherits_from_one.one", result_value="142") | |
| self.expect_expr("inherits_from_two.member", result_value="47") | |
| self.expect( | |
| "expr inherits_from_two.one", | |
| error=True, | |
| substrs=["no member named 'one' in 'InheritsFromTwo'"], | |
| ) | |
| self.expect( | |
| "expr inherits_from_two.two", | |
| error=True, | |
| substrs=["no member named 'two' in 'InheritsFromTwo'"], | |
| ) | |
| self.expect_expr("one_as_member.member", result_value="47") | |
| self.expect_expr("one_as_member.one.member", result_value="147") | |
| self.expect_expr("two_as_member.member", result_value="47") | |
| self.expect( | |
| "expr two_as_member.two.one.member", | |
| error=True, | |
| substrs=["no member named 'one' in 'member::Two'"], | |
| ) | |
| self.expect( | |
| "expr two_as_member.two.member", | |
| error=True, | |
| substrs=["no member named 'member' in 'member::Two'"], | |
| ) | |
| self.expect_expr("array_of_one[2].member", result_value="174") | |
| self.expect( | |
| "expr array_of_two[2].one[2].member", | |
| error=True, | |
| substrs=["no member named 'one' in 'array::Two'"], | |
| ) | |
| self.expect( | |
| "expr array_of_two[2].member", | |
| error=True, | |
| substrs=["no member named 'member' in 'array::Two'"], | |
| ) | |
| self.expect_expr("get_one().member", result_value="124") | |
| self.expect( | |
| "expr get_two().one().member", | |
| error=True, | |
| substrs=["calling 'get_two' with incomplete return type 'result::Two'"], | |
| ) | |
| self.expect( | |
| "expr get_two().member", | |
| error=True, | |
| substrs=["calling 'get_two' with incomplete return type 'result::Two'"], | |
| ) | |
| self._check_incomplete_frame_variable_output() |