1"""
2Test that stepping works even when the OS Plugin doesn't report
3all threads at every stop.
4"""
5
6from __future__ import print_function
7
8
9import os
10import lldb
11from lldbsuite.test.decorators import *
12from lldbsuite.test.lldbtest import *
13import lldbsuite.test.lldbutil as lldbutil
14
15
16class TestOSPluginStepping(TestBase):
17    NO_DEBUG_INFO_TESTCASE = True
18
19    @skipIfWindows
20    @skipIf(oslist=["freebsd"], bugnumber="llvm.org/pr48352")
21    def test_python_os_plugin(self):
22        """Test that stepping works when the OS Plugin doesn't report all
23           threads at every stop"""
24        self.build()
25        self.main_file = lldb.SBFileSpec('main.cpp')
26        self.run_python_os_step_missing_thread(False)
27
28    @skipIfWindows
29    @skipIf(oslist=["freebsd"], bugnumber="llvm.org/pr48352")
30    def test_python_os_plugin_prune(self):
31        """Test that pruning the unreported PlanStacks works"""
32        self.build()
33        self.main_file = lldb.SBFileSpec('main.cpp')
34        self.run_python_os_step_missing_thread(True)
35
36    def get_os_thread(self):
37        return self.process.GetThreadByID(0x111111111)
38
39    def is_os_thread(self, thread):
40        id = thread.GetID()
41        return id == 0x111111111
42
43    def run_python_os_step_missing_thread(self, do_prune):
44        """Test that the Python operating system plugin works correctly"""
45
46        # Our OS plugin does NOT report all threads:
47        result = self.dbg.HandleCommand("settings set process.experimental.os-plugin-reports-all-threads false")
48
49        python_os_plugin_path = os.path.join(self.getSourceDir(),
50                                             "operating_system.py")
51        (target, self.process, thread, thread_bkpt) = lldbutil.run_to_source_breakpoint(
52            self, "first stop in thread - do a step out", self.main_file)
53
54        main_bkpt = target.BreakpointCreateBySourceRegex('Stop here and do not make a memory thread for thread_1',
55                                                         self.main_file)
56        self.assertEqual(main_bkpt.GetNumLocations(), 1, "Main breakpoint has one location")
57
58        # There should not be an os thread before we load the plugin:
59        self.assertFalse(self.get_os_thread().IsValid(), "No OS thread before loading plugin")
60
61        # Now load the python OS plug-in which should update the thread list and we should have
62        # an OS plug-in thread overlaying thread_1 with id 0x111111111
63        command = "settings set target.process.python-os-plugin-path '%s'" % python_os_plugin_path
64        self.dbg.HandleCommand(command)
65
66        # Verify our OS plug-in threads showed up
67        os_thread = self.get_os_thread()
68        self.assertTrue(
69            os_thread.IsValid(),
70            "Make sure we added the thread 0x111111111 after we load the python OS plug-in")
71
72        # Now we are going to step-out.  This should get interrupted by main_bkpt.  We've
73        # set up the OS plugin so at this stop, we have lost the OS thread 0x111111111.
74        # Make sure both of these are true:
75        os_thread.StepOut()
76
77        stopped_threads = lldbutil.get_threads_stopped_at_breakpoint(self.process, main_bkpt)
78        self.assertEqual(len(stopped_threads), 1, "Stopped at main_bkpt")
79        thread = self.process.GetThreadByID(0x111111111)
80        self.assertFalse(thread.IsValid(), "No thread 0x111111111 on second stop.")
81
82        # Make sure we still have the thread plans for this thread:
83        # First, don't show unreported threads, that should fail:
84        command = "thread plan list -t 0x111111111"
85        result = lldb.SBCommandReturnObject()
86        interp = self.dbg.GetCommandInterpreter()
87        interp.HandleCommand(command, result)
88        self.assertFalse(result.Succeeded(), "We found no plans for the unreported thread.")
89        # Now do it again but with the -u flag:
90        command	= "thread plan list -u -t 0x111111111"
91        result = lldb.SBCommandReturnObject()
92        interp.HandleCommand(command, result)
93        self.assertTrue(result.Succeeded(), "We found plans for the unreported thread.")
94
95        if do_prune:
96            # Prune the thread plan and continue, and we will run to exit.
97            interp.HandleCommand("thread plan prune 0x111111111", result)
98            self.assertTrue(result.Succeeded(), "Found the plan for 0x111111111 and pruned it")
99
100            # List again, make sure it doesn't work:
101            command	= "thread plan list -u -t 0x111111111"
102            interp.HandleCommand(command, result)
103            self.assertFalse(result.Succeeded(), "We still found plans for the unreported thread.")
104
105            self.process.Continue()
106            self.assertState(self.process.GetState(), lldb.eStateExited, "We exited.")
107        else:
108            # Now we are going to continue, and when we hit the step-out breakpoint, we will
109            # put the OS plugin thread back, lldb will recover its ThreadPlanStack, and
110            # we will stop with a "step-out" reason.
111            self.process.Continue()
112            os_thread = self.get_os_thread()
113            self.assertTrue(os_thread.IsValid(), "The OS thread is back after continue")
114            self.assertIn("step out", os_thread.GetStopDescription(100), "Completed step out plan")
115