brintos

brintos / llvm-project-archived public Read only

0
0
Text · 17.7 KiB · bb56062 Raw
456 lines · python
1"""Test stepping over and into inlined functions."""2 3import lldb4from lldbsuite.test.decorators import *5from lldbsuite.test.lldbtest import *6from lldbsuite.test import lldbutil7 8 9class TestInlineStepping(TestBase):10    @add_test_categories(["pyapi"])11    @skipIf(oslist=["windows"], archs=["aarch64"])  # Flaky on buildbot12    @expectedFailureAll(13        compiler="icc",14        bugnumber="# Not really a bug.  ICC combines two inlined functions.",15    )16    def test_with_python_api(self):17        """Test stepping over and into inlined functions."""18        self.build()19        self.inline_stepping()20 21    @add_test_categories(["pyapi"])22    def test_step_over_with_python_api(self):23        """Test stepping over and into inlined functions."""24        self.build()25        self.inline_stepping_step_over()26 27    @add_test_categories(["pyapi"])28    @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr32343")29    def test_step_in_template_with_python_api(self):30        """Test stepping in to templated functions."""31        self.build()32        self.step_in_template()33 34    @add_test_categories(["pyapi"])35    def test_virtual_inline_stepping(self):36        """Test stepping through a virtual inlined call stack"""37        self.build()38        self.virtual_inline_stepping()39 40    def setUp(self):41        # Call super's setUp().42        TestBase.setUp(self)43        # Find the line numbers that we will step to in main:44        self.main_source = "calling.cpp"45        self.source_lines = {}46        functions = [47            "caller_ref_1",48            "caller_ref_2",49            "inline_ref_1",50            "inline_ref_2",51            "called_by_inline_ref",52            "caller_trivial_1",53            "caller_trivial_2",54            "inline_trivial_1",55            "inline_trivial_2",56            "called_by_inline_trivial",57        ]58        for name in functions:59            self.source_lines[name] = line_number(60                self.main_source, "// In " + name + "."61            )62        self.main_source_spec = lldb.SBFileSpec(self.main_source)63 64    def do_step(self, step_type, destination_line_entry, test_stack_depth):65        expected_stack_depth = self.thread.GetNumFrames()66        if step_type == "into":67            expected_stack_depth += 168            self.thread.StepInto()69        elif step_type == "out":70            expected_stack_depth -= 171            self.thread.StepOut()72        elif step_type == "over":73            self.thread.StepOver()74        else:75            self.fail("Unrecognized step type: " + step_type)76 77        threads = lldbutil.get_stopped_threads(78            self.process, lldb.eStopReasonPlanComplete79        )80        if len(threads) != 1:81            destination_description = lldb.SBStream()82            destination_line_entry.GetDescription(destination_description)83            self.fail(84                "Failed to stop due to step "85                + step_type86                + " operation stepping to: "87                + destination_description.GetData()88            )89 90        self.thread = threads[0]91 92        stop_line_entry = self.thread.GetFrameAtIndex(0).GetLineEntry()93        self.assertTrue(stop_line_entry.IsValid(), "Stop line entry was not valid.")94 95        # Don't use the line entry equal operator because we don't care about96        # the column number.97        stop_at_right_place = (98            stop_line_entry.GetFileSpec() == destination_line_entry.GetFileSpec()99            and stop_line_entry.GetLine() == destination_line_entry.GetLine()100        )101        if not stop_at_right_place:102            destination_description = lldb.SBStream()103            destination_line_entry.GetDescription(destination_description)104 105            actual_description = lldb.SBStream()106            stop_line_entry.GetDescription(actual_description)107 108            self.fail(109                "Step "110                + step_type111                + " stopped at wrong place: expected: "112                + destination_description.GetData()113                + " got: "114                + actual_description.GetData()115                + "."116            )117 118        real_stack_depth = self.thread.GetNumFrames()119 120        if test_stack_depth and real_stack_depth != expected_stack_depth:121            destination_description = lldb.SBStream()122            destination_line_entry.GetDescription(destination_description)123            self.fail(124                "Step %s to %s got wrong number of frames, should be: %d was: %d."125                % (126                    step_type,127                    destination_description.GetData(),128                    expected_stack_depth,129                    real_stack_depth,130                )131            )132 133    def run_step_sequence(self, step_sequence):134        """This function takes a list of duples instructing how to run the program.  The first element in each duple is135        a source pattern for the target location, and the second is the operation that will take you from the current136        source location to the target location.  It will then run all the steps in the sequence.137        It will check that you arrived at the expected source location at each step, and that the stack depth changed138        correctly for the operation in the sequence."""139 140        target_line_entry = lldb.SBLineEntry()141        target_line_entry.SetFileSpec(self.main_source_spec)142 143        test_stack_depth = True144        # Work around for <rdar://problem/16363195>, the darwin unwinder seems flakey about whether it duplicates the first frame145        # or not, which makes counting stack depth unreliable.146        if self.platformIsDarwin():147            test_stack_depth = False148 149        for step_pattern in step_sequence:150            step_stop_line = line_number(self.main_source, step_pattern[0])151            target_line_entry.SetLine(step_stop_line)152            self.do_step(step_pattern[1], target_line_entry, test_stack_depth)153 154    def inline_stepping(self):155        """Use Python APIs to test stepping over and hitting breakpoints."""156        exe = self.getBuildArtifact("a.out")157 158        target = self.dbg.CreateTarget(exe)159        self.assertTrue(target, VALID_TARGET)160 161        break_1_in_main = target.BreakpointCreateBySourceRegex(162            "// Stop here and step over to set up stepping over.", self.main_source_spec163        )164        self.assertTrue(break_1_in_main, VALID_BREAKPOINT)165 166        # Now launch the process, and do not stop at entry point.167        self.process = target.LaunchSimple(168            None, None, self.get_process_working_directory()169        )170 171        self.assertTrue(self.process, PROCESS_IS_VALID)172 173        # The stop reason of the thread should be breakpoint.174        threads = lldbutil.get_threads_stopped_at_breakpoint(175            self.process, break_1_in_main176        )177 178        if len(threads) != 1:179            self.fail("Failed to stop at first breakpoint in main.")180 181        self.thread = threads[0]182 183        # Step over the inline_value = 0 line to get us to inline_trivial_1 called from main.  Doing it this way works184        # around a bug in lldb where the breakpoint on the containing line of an inlined function with no return value185        # gets set past the insertion line in the function.186        # Then test stepping over a simple inlined function.  Note, to test all the parts of the inlined stepping187        # the calls inline_stepping_1 and inline_stepping_2 should line up at the same address, that way we will test188        # the "virtual" stepping.189        # FIXME: Put in a check to see if that is true and warn if it is not.190 191        step_sequence = [192            ["// At inline_trivial_1 called from main.", "over"],193            ["// At first call of caller_trivial_1 in main.", "over"],194        ]195        self.run_step_sequence(step_sequence)196 197        # Now step from caller_ref_1 all the way into called_by_inline_trivial198 199        step_sequence = [200            ["// In caller_trivial_1.", "into"],201            ["// In caller_trivial_2.", "into"],202            ["// In inline_trivial_1.", "into"],203            ["// In inline_trivial_2.", "into"],204            ["// At caller_by_inline_trivial in inline_trivial_2.", "over"],205            ["// In called_by_inline_trivial.", "into"],206        ]207        self.run_step_sequence(step_sequence)208 209        # Now run to the inline_trivial_1 just before the immediate step into210        # inline_trivial_2:211 212        break_2_in_main = target.BreakpointCreateBySourceRegex(213            "// At second call of caller_trivial_1 in main.", self.main_source_spec214        )215        self.assertTrue(break_2_in_main, VALID_BREAKPOINT)216 217        threads = lldbutil.continue_to_breakpoint(self.process, break_2_in_main)218        self.assertEqual(219            len(threads),220            1,221            "Successfully ran to call site of second caller_trivial_1 call.",222        )223        self.thread = threads[0]224 225        step_sequence = [226            ["// In caller_trivial_1.", "into"],227            ["// In caller_trivial_2.", "into"],228            ["// In inline_trivial_1.", "into"],229        ]230        self.run_step_sequence(step_sequence)231 232        # Then call some trivial function, and make sure we end up back where233        # we were in the inlined call stack:234 235        frame = self.thread.GetFrameAtIndex(0)236        before_line_entry = frame.GetLineEntry()237        value = frame.EvaluateExpression("function_to_call()")238        after_line_entry = frame.GetLineEntry()239 240        self.assertEqual(241            before_line_entry.GetLine(),242            after_line_entry.GetLine(),243            "Line entry before and after function calls are the same.",244        )245 246        # Now make sure stepping OVER in the middle of the stack works, and247        # then check finish from the inlined frame:248 249        step_sequence = [250            ["// At increment in inline_trivial_1.", "over"],251            ["// At increment in caller_trivial_2.", "out"],252        ]253        self.run_step_sequence(step_sequence)254 255        # Now run to the place in main just before the first call to256        # caller_ref_1:257 258        break_3_in_main = target.BreakpointCreateBySourceRegex(259            "// At first call of caller_ref_1 in main.", self.main_source_spec260        )261        self.assertTrue(break_3_in_main, VALID_BREAKPOINT)262 263        threads = lldbutil.continue_to_breakpoint(self.process, break_3_in_main)264        self.assertEqual(265            len(threads), 1, "Successfully ran to call site of first caller_ref_1 call."266        )267        self.thread = threads[0]268 269        step_sequence = [270            ["// In caller_ref_1.", "into"],271            ["// In caller_ref_2.", "into"],272            ["// In inline_ref_1.", "into"],273            ["// In inline_ref_2.", "into"],274            ["// In called_by_inline_ref.", "into"],275            ["// In inline_ref_2.", "out"],276            ["// In inline_ref_1.", "out"],277            ["// At increment in inline_ref_1.", "over"],278            ["// In caller_ref_2.", "out"],279            ["// At increment in caller_ref_2.", "over"],280        ]281        self.run_step_sequence(step_sequence)282 283    def inline_stepping_step_over(self):284        """Use Python APIs to test stepping over and hitting breakpoints."""285        exe = self.getBuildArtifact("a.out")286 287        target = self.dbg.CreateTarget(exe)288        self.assertTrue(target, VALID_TARGET)289 290        break_1_in_main = target.BreakpointCreateBySourceRegex(291            "// At second call of caller_ref_1 in main.", self.main_source_spec292        )293        self.assertGreater(break_1_in_main.GetNumLocations(), 0, VALID_BREAKPOINT)294 295        # Now launch the process, and do not stop at entry point.296        self.process = target.LaunchSimple(297            None, None, self.get_process_working_directory()298        )299 300        self.assertTrue(self.process, PROCESS_IS_VALID)301 302        # The stop reason of the thread should be breakpoint.303        threads = lldbutil.get_threads_stopped_at_breakpoint(304            self.process, break_1_in_main305        )306 307        if len(threads) != 1:308            self.fail("Failed to stop at first breakpoint in main.")309 310        self.thread = threads[0]311 312        step_sequence = [313            ["// In caller_ref_1.", "into"],314            ["// In caller_ref_2.", "into"],315            ["// At increment in caller_ref_2.", "over"],316        ]317        self.run_step_sequence(step_sequence)318 319        # Now make sure that next to a virtual inlined call stack320        # gets the call stack depth correct.321        break_2_in_main = target.BreakpointCreateBySourceRegex(322            "// Call max_value specialized", self.main_source_spec323        )324        self.assertGreater(break_2_in_main.GetNumLocations(), 0, VALID_BREAKPOINT)325        threads = lldbutil.continue_to_breakpoint(self.process, break_2_in_main)326        self.assertEqual(len(threads), 1, "Hit our second breakpoint")327        self.assertEqual(threads[0].id, self.thread.id, "Stopped at right thread")328        self.thread.StepOver()329        frame_0 = self.thread.frames[0]330        line_entry = frame_0.line_entry331        self.assertEqual(332            line_entry.file.basename, self.main_source_spec.basename, "File matches"333        )334        target_line = line_number("calling.cpp", "// At caller_trivial_inline_1")335        self.assertEqual(line_entry.line, target_line, "Lines match as well.")336 337    def step_in_template(self):338        """Use Python APIs to test stepping in to templated functions."""339        exe = self.getBuildArtifact("a.out")340 341        target = self.dbg.CreateTarget(exe)342        self.assertTrue(target, VALID_TARGET)343 344        break_1_in_main = target.BreakpointCreateBySourceRegex(345            "// Call max_value template", self.main_source_spec346        )347        self.assertTrue(break_1_in_main, VALID_BREAKPOINT)348 349        break_2_in_main = target.BreakpointCreateBySourceRegex(350            "// Call max_value specialized", self.main_source_spec351        )352        self.assertTrue(break_2_in_main, VALID_BREAKPOINT)353 354        # Now launch the process, and do not stop at entry point.355        self.process = target.LaunchSimple(356            None, None, self.get_process_working_directory()357        )358        self.assertTrue(self.process, PROCESS_IS_VALID)359 360        # The stop reason of the thread should be breakpoint.361        threads = lldbutil.get_threads_stopped_at_breakpoint(362            self.process, break_1_in_main363        )364 365        if len(threads) != 1:366            self.fail("Failed to stop at first breakpoint in main.")367 368        self.thread = threads[0]369 370        step_sequence = [["// In max_value template", "into"]]371        self.run_step_sequence(step_sequence)372 373        threads = lldbutil.continue_to_breakpoint(self.process, break_2_in_main)374        self.assertEqual(375            len(threads),376            1,377            "Successfully ran to call site of second caller_trivial_1 call.",378        )379        self.thread = threads[0]380 381        step_sequence = [["// In max_value specialized", "into"]]382        self.run_step_sequence(step_sequence)383 384    def run_to_call_site_and_step(385        self, source_regex, func_name, start_pos, one_more_step_loc=None386    ):387        main_spec = lldb.SBFileSpec("calling.cpp")388        # Set the breakpoint by file and line, not sourced regex because389        # we want to make sure we can set breakpoints on call sites:390        call_site_line_num = line_number(self.main_source, source_regex)391        target, process, thread, bkpt = lldbutil.run_to_line_breakpoint(392            self, main_spec, call_site_line_num393        )394 395        # Make sure that the location is at the call site (run_to_line_breakpoint already asserted396        # that there's one location.):397        bkpt_loc = bkpt.location[0]398        strm = lldb.SBStream()399        result = bkpt_loc.GetDescription(strm, lldb.eDescriptionLevelFull)400 401        self.assertTrue(result, "Got a location description")402        desc = strm.GetData()403        self.assertIn(f"calling.cpp:{call_site_line_num}", desc, "Right line listed")404        # We don't get the function name right yet - so we omit it in printing.405        # Turn on this test when that is working.406        # self.assertIn(func_name, desc, "Right function listed")407 408        pc = thread.frame[0].pc409        for i in range(start_pos, 3):410            thread.StepInto()411            frame_0 = thread.frame[0]412 413            trivial_line_num = line_number(414                self.main_source, f"In caller_trivial_inline_{i}."415            )416            self.assertEqual(417                frame_0.line_entry.line,418                trivial_line_num,419                f"Stepped into the caller_trivial_inline_{i}",420            )421            if pc != frame_0.pc:422                # If we get here, we stepped to the expected line number, but423                # the compiler on this system has decided to insert an instruction424                # between the call site of an inlined function with no arguments,425                # returning void, and its immediate call to another void inlined function426                # with no arguments.  We aren't going to be testing virtual inline427                # stepping for this function...428                break429 430        if one_more_step_loc:431            thread.StepInto()432            frame_0 = thread.frame[0]433            self.assertEqual(434                frame_0.line_entry.line,435                line_number(self.main_source, one_more_step_loc),436                "Was able to step one more time",437            )438        process.Kill()439        target.Clear()440 441    def virtual_inline_stepping(self):442        """Use the Python API's to step through a virtual inlined stack"""443        self.run_to_call_site_and_step("At caller_trivial_inline_1", "main", 1)444        self.run_to_call_site_and_step(445            "In caller_trivial_inline_1", "caller_trivial_inline_1", 2446        )447        self.run_to_call_site_and_step(448            "In caller_trivial_inline_2", "caller_trivial_inline_2", 3449        )450        self.run_to_call_site_and_step(451            "In caller_trivial_inline_3",452            "caller_trivial_inline_3",453            4,454            "After caller_trivial_inline_3",455        )456