xref: /llvm-project/lldb/test/API/functionalities/thread_plan/TestThreadPlanCommands.py (revision 9c2468821ec51defd09c246fea4a47886fff8c01)
11893065dSJim Ingham"""
21893065dSJim InghamTest that thread plan listing, and deleting works.
31893065dSJim Ingham"""
41893065dSJim Ingham
51893065dSJim Ingham
61893065dSJim Inghamimport lldb
72d658c56SJim Inghamfrom lldbsuite.test.decorators import *
81893065dSJim Inghamimport lldbsuite.test.lldbutil as lldbutil
91893065dSJim Inghamfrom lldbsuite.test.lldbtest import *
101893065dSJim Ingham
111893065dSJim Ingham
121893065dSJim Inghamclass TestThreadPlanCommands(TestBase):
131893065dSJim Ingham    NO_DEBUG_INFO_TESTCASE = True
141893065dSJim Ingham
152d658c56SJim Ingham    @skipIfWindows
161893065dSJim Ingham    def test_thread_plan_actions(self):
171893065dSJim Ingham        self.build()
181893065dSJim Ingham        self.main_source_file = lldb.SBFileSpec("main.c")
191893065dSJim Ingham        self.thread_plan_test()
201893065dSJim Ingham
212238dcc3SJonas Devlieghere    def check_list_output(
222238dcc3SJonas Devlieghere        self, command, active_plans=[], completed_plans=[], discarded_plans=[]
232238dcc3SJonas Devlieghere    ):
241893065dSJim Ingham        # Check the "thread plan list" output against a list of active & completed and discarded plans.
251893065dSJim Ingham        # If all three check arrays are empty, that means the command is expected to fail.
261893065dSJim Ingham
271893065dSJim Ingham        interp = self.dbg.GetCommandInterpreter()
281893065dSJim Ingham        result = lldb.SBCommandReturnObject()
291893065dSJim Ingham
301893065dSJim Ingham        num_active = len(active_plans)
311893065dSJim Ingham        num_completed = len(completed_plans)
321893065dSJim Ingham        num_discarded = len(discarded_plans)
331893065dSJim Ingham
341893065dSJim Ingham        interp.HandleCommand(command, result)
351893065dSJim Ingham        print("Command: %s" % (command))
361893065dSJim Ingham        print(result.GetOutput())
371893065dSJim Ingham
381893065dSJim Ingham        if num_active == 0 and num_completed == 0 and num_discarded == 0:
392238dcc3SJonas Devlieghere            self.assertFalse(
402238dcc3SJonas Devlieghere                result.Succeeded(),
412238dcc3SJonas Devlieghere                "command: '%s' succeeded when it should have failed: '%s'"
422238dcc3SJonas Devlieghere                % (command, result.GetError()),
432238dcc3SJonas Devlieghere            )
441893065dSJim Ingham            return
451893065dSJim Ingham
462238dcc3SJonas Devlieghere        self.assertTrue(
472238dcc3SJonas Devlieghere            result.Succeeded(),
482238dcc3SJonas Devlieghere            "command: '%s' failed: '%s'" % (command, result.GetError()),
492238dcc3SJonas Devlieghere        )
501893065dSJim Ingham        result_arr = result.GetOutput().splitlines()
511893065dSJim Ingham        num_results = len(result_arr)
521893065dSJim Ingham
531893065dSJim Ingham        # Now iterate through the results array and pick out the results.
541893065dSJim Ingham        result_idx = 0
552238dcc3SJonas Devlieghere        self.assertIn("thread #", result_arr[result_idx], "Found thread header")
562238dcc3SJonas Devlieghere        result_idx += 1
572238dcc3SJonas Devlieghere        self.assertIn(
582238dcc3SJonas Devlieghere            "Active plan stack", result_arr[result_idx], "Found active header"
592238dcc3SJonas Devlieghere        )
602238dcc3SJonas Devlieghere        result_idx += 1
612238dcc3SJonas Devlieghere        self.assertIn(
622238dcc3SJonas Devlieghere            "Element 0: Base thread plan", result_arr[result_idx], "Found base plan"
632238dcc3SJonas Devlieghere        )
642238dcc3SJonas Devlieghere        result_idx += 1
651893065dSJim Ingham
661893065dSJim Ingham        for text in active_plans:
672238dcc3SJonas Devlieghere            self.assertIn(
682238dcc3SJonas Devlieghere                text, result_arr[result_idx], "Didn't find active plan: %s" % (text)
692238dcc3SJonas Devlieghere            )
702238dcc3SJonas Devlieghere            result_idx += 1
71aa4b37b2SJim Ingham
721893065dSJim Ingham        if len(completed_plans) > 0:
73aa4b37b2SJim Ingham            # First consume any remaining active plans:
74aa4b37b2SJim Ingham            while not "Completed plan stack:" in result_arr[result_idx]:
75aa4b37b2SJim Ingham                result_idx += 1
76aa4b37b2SJim Ingham                if result_idx == num_results:
772238dcc3SJonas Devlieghere                    self.fail(
782238dcc3SJonas Devlieghere                        "There should have been completed plans, but I never saw the completed stack header"
792238dcc3SJonas Devlieghere                    )
80aa4b37b2SJim Ingham            # We are at the Completed header, skip it:
81aa4b37b2SJim Ingham            result_idx += 1
821893065dSJim Ingham            for text in completed_plans:
832238dcc3SJonas Devlieghere                self.assertIn(
842238dcc3SJonas Devlieghere                    text,
852238dcc3SJonas Devlieghere                    result_arr[result_idx],
862238dcc3SJonas Devlieghere                    "Didn't find completed plan: %s" % (text),
872238dcc3SJonas Devlieghere                )
882238dcc3SJonas Devlieghere                result_idx += 1
891893065dSJim Ingham
901893065dSJim Ingham        if len(discarded_plans) > 0:
91aa4b37b2SJim Ingham            # First consume any remaining completed plans:
92aa4b37b2SJim Ingham            while not "Discarded plan stack:" in result_arr[result_idx]:
93aa4b37b2SJim Ingham                result_idx += 1
94aa4b37b2SJim Ingham                if result_idx == num_results:
952238dcc3SJonas Devlieghere                    self.fail(
962238dcc3SJonas Devlieghere                        "There should have been discarded plans, but I never saw the discarded stack header"
972238dcc3SJonas Devlieghere                    )
98aa4b37b2SJim Ingham
99aa4b37b2SJim Ingham            # We are at the Discarded header, skip it:
100aa4b37b2SJim Ingham            result_idx += 1
1011893065dSJim Ingham            for text in discarded_plans:
1022238dcc3SJonas Devlieghere                self.assertIn(
1032238dcc3SJonas Devlieghere                    text,
1042238dcc3SJonas Devlieghere                    result_arr[result_idx],
1052238dcc3SJonas Devlieghere                    "Didn't find discarded plan: %s" % (text),
1062238dcc3SJonas Devlieghere                )
1072238dcc3SJonas Devlieghere                result_idx += 1
1081893065dSJim Ingham
1091893065dSJim Ingham    def thread_plan_test(self):
1102238dcc3SJonas Devlieghere        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
1112238dcc3SJonas Devlieghere            self, "Set a breakpoint here", self.main_source_file
1122238dcc3SJonas Devlieghere        )
1131893065dSJim Ingham
114aa4b37b2SJim Ingham        # We need to have an internal plan so we can test listing one.
115aa4b37b2SJim Ingham        # The most consistent way to do that is to use a scripted thread plan
116aa4b37b2SJim Ingham        # that uses a sub-plan.  Source that in now.
117aa4b37b2SJim Ingham        source_path = os.path.join(self.getSourceDir(), "wrap_step_over.py")
118aa4b37b2SJim Ingham        self.runCmd("command script import '%s'" % (source_path))
119aa4b37b2SJim Ingham
120aa4b37b2SJim Ingham        # Now set a breakpoint that we will hit by running our scripted step.
1212238dcc3SJonas Devlieghere        call_me_bkpt = target.BreakpointCreateBySourceRegex(
1222238dcc3SJonas Devlieghere            "Set another here", self.main_source_file
1232238dcc3SJonas Devlieghere        )
124*9c246882SJordan Rupprecht        self.assertGreater(
125*9c246882SJordan Rupprecht            call_me_bkpt.GetNumLocations(), 0, "Set the breakpoint successfully"
1262238dcc3SJonas Devlieghere        )
127aa4b37b2SJim Ingham        thread.StepUsingScriptedThreadPlan("wrap_step_over.WrapStepOver")
1281893065dSJim Ingham        threads = lldbutil.get_threads_stopped_at_breakpoint(process, call_me_bkpt)
1291893065dSJim Ingham        self.assertEqual(len(threads), 1, "Hit my breakpoint while stepping over")
1301893065dSJim Ingham
1311893065dSJim Ingham        current_id = threads[0].GetIndexID()
1321893065dSJim Ingham        current_tid = threads[0].GetThreadID()
1331893065dSJim Ingham        # Run thread plan list without the -i flag:
1341893065dSJim Ingham        command = "thread plan list %d" % (current_id)
135aa4b37b2SJim Ingham        self.check_list_output(command, ["wrap_step_over.WrapStepOver"], [])
1361893065dSJim Ingham
1371893065dSJim Ingham        # Run thread plan list with the -i flag:
1381893065dSJim Ingham        command = "thread plan list -i %d" % (current_id)
139aa4b37b2SJim Ingham        self.check_list_output(command, ["WrapStepOver", "Stepping over line main.c"])
1401893065dSJim Ingham
1411893065dSJim Ingham        # Run thread plan list providing TID, output should be the same:
1421893065dSJim Ingham        command = "thread plan list -t %d" % (current_tid)
143aa4b37b2SJim Ingham        self.check_list_output(command, ["wrap_step_over.WrapStepOver"])
1441893065dSJim Ingham
1451893065dSJim Ingham        # Provide both index & tid, and make sure we only print once:
1461893065dSJim Ingham        command = "thread plan list -t %d %d" % (current_tid, current_id)
147aa4b37b2SJim Ingham        self.check_list_output(command, ["wrap_step_over.WrapStepOver"])
1481893065dSJim Ingham
1491893065dSJim Ingham        # Try a fake TID, and make sure that fails:
1501893065dSJim Ingham        fake_tid = 0
1511893065dSJim Ingham        for i in range(100, 10000, 100):
1521893065dSJim Ingham            fake_tid = current_tid + i
1531893065dSJim Ingham            thread = process.GetThreadByID(fake_tid)
1541893065dSJim Ingham            if not thread:
1551893065dSJim Ingham                break
1561893065dSJim Ingham
1571893065dSJim Ingham        command = "thread plan list -t %d" % (fake_tid)
1581893065dSJim Ingham        self.check_list_output(command)
1591893065dSJim Ingham
1601893065dSJim Ingham        # Now continue, and make sure we printed the completed plan:
1611893065dSJim Ingham        process.Continue()
1621893065dSJim Ingham        threads = lldbutil.get_stopped_threads(process, lldb.eStopReasonPlanComplete)
1631893065dSJim Ingham        self.assertEqual(len(threads), 1, "One thread completed a step")
1641893065dSJim Ingham
1651893065dSJim Ingham        # Run thread plan list - there aren't any private plans at this point:
1661893065dSJim Ingham        command = "thread plan list %d" % (current_id)
167aa4b37b2SJim Ingham        self.check_list_output(command, [], ["wrap_step_over.WrapStepOver"])
1681893065dSJim Ingham
1691893065dSJim Ingham        # Set another breakpoint that we can run to, to try deleting thread plans.
1702238dcc3SJonas Devlieghere        second_step_bkpt = target.BreakpointCreateBySourceRegex(
1712238dcc3SJonas Devlieghere            "Run here to step over again", self.main_source_file
1722238dcc3SJonas Devlieghere        )
173*9c246882SJordan Rupprecht        self.assertGreater(
174*9c246882SJordan Rupprecht            second_step_bkpt.GetNumLocations(), 0, "Set the breakpoint successfully"
1752238dcc3SJonas Devlieghere        )
1762238dcc3SJonas Devlieghere        final_bkpt = target.BreakpointCreateBySourceRegex(
1772238dcc3SJonas Devlieghere            "Make sure we get here on last continue", self.main_source_file
1782238dcc3SJonas Devlieghere        )
179*9c246882SJordan Rupprecht        self.assertGreater(
180*9c246882SJordan Rupprecht            final_bkpt.GetNumLocations(), 0, "Set the breakpoint successfully"
1812238dcc3SJonas Devlieghere        )
1821893065dSJim Ingham
1831893065dSJim Ingham        threads = lldbutil.continue_to_breakpoint(process, second_step_bkpt)
1841893065dSJim Ingham        self.assertEqual(len(threads), 1, "Hit the second step breakpoint")
1851893065dSJim Ingham
1861893065dSJim Ingham        threads[0].StepOver()
1871893065dSJim Ingham        threads = lldbutil.get_threads_stopped_at_breakpoint(process, call_me_bkpt)
1881893065dSJim Ingham
1891893065dSJim Ingham        result = lldb.SBCommandReturnObject()
1901893065dSJim Ingham        interp = self.dbg.GetCommandInterpreter()
1911893065dSJim Ingham        interp.HandleCommand("thread plan discard 1", result)
1922238dcc3SJonas Devlieghere        self.assertTrue(
1932238dcc3SJonas Devlieghere            result.Succeeded(), "Deleted the step over plan: %s" % (result.GetOutput())
1942238dcc3SJonas Devlieghere        )
1951893065dSJim Ingham
1961893065dSJim Ingham        # Make sure the plan gets listed in the discarded plans:
1971893065dSJim Ingham        command = "thread plan list %d" % (current_id)
1981893065dSJim Ingham        self.check_list_output(command, [], [], ["Stepping over line main.c:"])
1991893065dSJim Ingham
2001893065dSJim Ingham        process.Continue()
2011893065dSJim Ingham        threads = lldbutil.get_threads_stopped_at_breakpoint(process, final_bkpt)
2021893065dSJim Ingham        self.assertEqual(len(threads), 1, "Ran to final breakpoint")
2031893065dSJim Ingham        threads = lldbutil.get_stopped_threads(process, lldb.eStopReasonPlanComplete)
2041893065dSJim Ingham        self.assertEqual(len(threads), 0, "Did NOT complete the step over plan")
205