xref: /llvm-project/lldb/test/API/python_api/debugger/TestDebuggerAPI.py (revision 59e2a6b08f3e40afea87da3838ba69e1e15b6672)
199451b44SJordan Rupprecht"""
299451b44SJordan RupprechtTest Debugger APIs.
399451b44SJordan Rupprecht"""
499451b44SJordan Rupprecht
599451b44SJordan Rupprechtimport lldb
699451b44SJordan Rupprecht
799451b44SJordan Rupprechtfrom lldbsuite.test.decorators import *
899451b44SJordan Rupprechtfrom lldbsuite.test.lldbtest import *
999451b44SJordan Rupprechtfrom lldbsuite.test import lldbutil
1099451b44SJordan Rupprecht
1199451b44SJordan Rupprecht
1299451b44SJordan Rupprechtclass DebuggerAPITestCase(TestBase):
1399451b44SJordan Rupprecht    NO_DEBUG_INFO_TESTCASE = True
1499451b44SJordan Rupprecht
1599451b44SJordan Rupprecht    def test_debugger_api_boundary_condition(self):
1699451b44SJordan Rupprecht        """Exercise SBDebugger APIs with boundary conditions."""
1799451b44SJordan Rupprecht        self.dbg.HandleCommand(None)
1899451b44SJordan Rupprecht        self.dbg.SetDefaultArchitecture(None)
1999451b44SJordan Rupprecht        self.dbg.GetScriptingLanguage(None)
2099451b44SJordan Rupprecht        self.dbg.CreateTarget(None)
2199451b44SJordan Rupprecht        self.dbg.CreateTarget(None, None, None, True, lldb.SBError())
2299451b44SJordan Rupprecht        self.dbg.CreateTargetWithFileAndTargetTriple(None, None)
2399451b44SJordan Rupprecht        self.dbg.CreateTargetWithFileAndArch(None, None)
2499451b44SJordan Rupprecht        self.dbg.FindTargetWithFileAndArch(None, None)
2599451b44SJordan Rupprecht        self.dbg.SetInternalVariable(None, None, None)
2699451b44SJordan Rupprecht        self.dbg.GetInternalVariableValue(None, None)
2799451b44SJordan Rupprecht        # FIXME (filcab): We must first allow for the swig bindings to know if
2899451b44SJordan Rupprecht        # a Python callback is set. (Check python-typemaps.swig)
2999451b44SJordan Rupprecht        # self.dbg.SetLoggingCallback(None)
3099451b44SJordan Rupprecht        self.dbg.SetPrompt(None)
3199451b44SJordan Rupprecht        self.dbg.SetCurrentPlatform(None)
3299451b44SJordan Rupprecht        self.dbg.SetCurrentPlatformSDKRoot(None)
3399451b44SJordan Rupprecht
3499451b44SJordan Rupprecht        fresh_dbg = lldb.SBDebugger()
3580fcecb1SJonas Devlieghere        self.assertEqual(len(fresh_dbg), 0)
3699451b44SJordan Rupprecht
3799451b44SJordan Rupprecht    def test_debugger_delete_invalid_target(self):
3899451b44SJordan Rupprecht        """SBDebugger.DeleteTarget() should not crash LLDB given and invalid target."""
3999451b44SJordan Rupprecht        target = lldb.SBTarget()
4099451b44SJordan Rupprecht        self.assertFalse(target.IsValid())
4199451b44SJordan Rupprecht        self.dbg.DeleteTarget(target)
4236de94cfSTatyana Krasnukha
4336de94cfSTatyana Krasnukha    def test_debugger_internal_variables(self):
4436de94cfSTatyana Krasnukha        """Ensure that SBDebugger reachs the same instance of properties
4536de94cfSTatyana Krasnukha        regardless CommandInterpreter's context initialization"""
4636de94cfSTatyana Krasnukha        self.build()
4736de94cfSTatyana Krasnukha        exe = self.getBuildArtifact("a.out")
4836de94cfSTatyana Krasnukha
4936de94cfSTatyana Krasnukha        # Create a target by the debugger.
5036de94cfSTatyana Krasnukha        target = self.dbg.CreateTarget(exe)
5136de94cfSTatyana Krasnukha        self.assertTrue(target, VALID_TARGET)
5236de94cfSTatyana Krasnukha
5336de94cfSTatyana Krasnukha        property_name = "target.process.memory-cache-line-size"
5436de94cfSTatyana Krasnukha
5536de94cfSTatyana Krasnukha        def get_cache_line_size():
5636de94cfSTatyana Krasnukha            value_list = lldb.SBStringList()
572238dcc3SJonas Devlieghere            value_list = self.dbg.GetInternalVariableValue(
582238dcc3SJonas Devlieghere                property_name, self.dbg.GetInstanceName()
592238dcc3SJonas Devlieghere            )
6036de94cfSTatyana Krasnukha
6136de94cfSTatyana Krasnukha            self.assertEqual(value_list.GetSize(), 1)
6236de94cfSTatyana Krasnukha            try:
6336de94cfSTatyana Krasnukha                return int(value_list.GetStringAtIndex(0))
6436de94cfSTatyana Krasnukha            except ValueError as error:
6536de94cfSTatyana Krasnukha                self.fail("Value is not a number: " + error)
6636de94cfSTatyana Krasnukha
6736de94cfSTatyana Krasnukha        # Get global property value while there are no processes.
6836de94cfSTatyana Krasnukha        global_cache_line_size = get_cache_line_size()
6936de94cfSTatyana Krasnukha
7036de94cfSTatyana Krasnukha        # Run a process via SB interface. CommandInterpreter's execution context
7136de94cfSTatyana Krasnukha        # remains empty.
7236de94cfSTatyana Krasnukha        error = lldb.SBError()
7336de94cfSTatyana Krasnukha        launch_info = lldb.SBLaunchInfo(None)
7436de94cfSTatyana Krasnukha        launch_info.SetLaunchFlags(lldb.eLaunchFlagStopAtEntry)
7536de94cfSTatyana Krasnukha        process = target.Launch(launch_info, error)
7636de94cfSTatyana Krasnukha        self.assertTrue(process, PROCESS_IS_VALID)
7736de94cfSTatyana Krasnukha
7836de94cfSTatyana Krasnukha        # This should change the value of a process's local property.
7936de94cfSTatyana Krasnukha        new_cache_line_size = global_cache_line_size + 512
802238dcc3SJonas Devlieghere        error = self.dbg.SetInternalVariable(
812238dcc3SJonas Devlieghere            property_name, str(new_cache_line_size), self.dbg.GetInstanceName()
822238dcc3SJonas Devlieghere        )
832238dcc3SJonas Devlieghere        self.assertSuccess(error, property_name + " value was changed successfully")
8436de94cfSTatyana Krasnukha
8536de94cfSTatyana Krasnukha        # Check that it was set actually.
8636de94cfSTatyana Krasnukha        self.assertEqual(get_cache_line_size(), new_cache_line_size)
8736de94cfSTatyana Krasnukha
8836de94cfSTatyana Krasnukha        # Run any command to initialize CommandInterpreter's execution context.
8936de94cfSTatyana Krasnukha        self.runCmd("target list")
9036de94cfSTatyana Krasnukha
9136de94cfSTatyana Krasnukha        # Test the local property again, is it set to new_cache_line_size?
9236de94cfSTatyana Krasnukha        self.assertEqual(get_cache_line_size(), new_cache_line_size)
93af921006SPavel Labath
94*77369a7fSDmitry Vasilyev    @expectedFailureAll(
95*77369a7fSDmitry Vasilyev        remote=True,
96*77369a7fSDmitry Vasilyev        bugnumber="github.com/llvm/llvm-project/issues/92419",
97*77369a7fSDmitry Vasilyev    )
98af921006SPavel Labath    def test_CreateTarget_platform(self):
99af921006SPavel Labath        exe = self.getBuildArtifact("a.out")
100af921006SPavel Labath        self.yaml2obj("elf.yaml", exe)
101af921006SPavel Labath        error = lldb.SBError()
1022238dcc3SJonas Devlieghere        target1 = self.dbg.CreateTarget(exe, None, "remote-linux", False, error)
103af921006SPavel Labath        self.assertSuccess(error)
104af921006SPavel Labath        platform1 = target1.GetPlatform()
105af921006SPavel Labath        platform1.SetWorkingDirectory("/foo/bar")
106af921006SPavel Labath
107af921006SPavel Labath        # Reuse a platform if it matches the currently selected one...
1082238dcc3SJonas Devlieghere        target2 = self.dbg.CreateTarget(exe, None, "remote-linux", False, error)
109af921006SPavel Labath        self.assertSuccess(error)
110af921006SPavel Labath        platform2 = target2.GetPlatform()
1112238dcc3SJonas Devlieghere        self.assertTrue(
1122238dcc3SJonas Devlieghere            platform2.GetWorkingDirectory().endswith("bar"),
1132238dcc3SJonas Devlieghere            platform2.GetWorkingDirectory(),
1142238dcc3SJonas Devlieghere        )
115af921006SPavel Labath
116af921006SPavel Labath        # ... but create a new one if it doesn't.
117af921006SPavel Labath        self.dbg.SetSelectedPlatform(lldb.SBPlatform("remote-windows"))
1182238dcc3SJonas Devlieghere        target3 = self.dbg.CreateTarget(exe, None, "remote-linux", False, error)
119af921006SPavel Labath        self.assertSuccess(error)
120af921006SPavel Labath        platform3 = target3.GetPlatform()
121af921006SPavel Labath        self.assertIsNone(platform3.GetWorkingDirectory())
122af921006SPavel Labath
123af921006SPavel Labath    def test_CreateTarget_arch(self):
124af921006SPavel Labath        exe = self.getBuildArtifact("a.out")
1252238dcc3SJonas Devlieghere        if lldbplatformutil.getHostPlatform() == "linux":
126af921006SPavel Labath            self.yaml2obj("macho.yaml", exe)
127af921006SPavel Labath            arch = "x86_64-apple-macosx"
1287d70b1a4SPavel Labath            expected_platform = "remote-macosx"
129af921006SPavel Labath        else:
130af921006SPavel Labath            self.yaml2obj("elf.yaml", exe)
131af921006SPavel Labath            arch = "x86_64-pc-linux"
1327d70b1a4SPavel Labath            expected_platform = "remote-linux"
133af921006SPavel Labath
134af921006SPavel Labath        fbsd = lldb.SBPlatform("remote-freebsd")
135af921006SPavel Labath        self.dbg.SetSelectedPlatform(fbsd)
136af921006SPavel Labath
137af921006SPavel Labath        error = lldb.SBError()
138af921006SPavel Labath        target1 = self.dbg.CreateTarget(exe, arch, None, False, error)
139af921006SPavel Labath        self.assertSuccess(error)
140af921006SPavel Labath        platform1 = target1.GetPlatform()
1417d70b1a4SPavel Labath        self.assertEqual(platform1.GetName(), expected_platform)
142af921006SPavel Labath        platform1.SetWorkingDirectory("/foo/bar")
143af921006SPavel Labath
144af921006SPavel Labath        # Reuse a platform even if it is not currently selected.
145af921006SPavel Labath        self.dbg.SetSelectedPlatform(fbsd)
146af921006SPavel Labath        target2 = self.dbg.CreateTarget(exe, arch, None, False, error)
147af921006SPavel Labath        self.assertSuccess(error)
148af921006SPavel Labath        platform2 = target2.GetPlatform()
1497d70b1a4SPavel Labath        self.assertEqual(platform2.GetName(), expected_platform)
1502238dcc3SJonas Devlieghere        self.assertTrue(
1512238dcc3SJonas Devlieghere            platform2.GetWorkingDirectory().endswith("bar"),
1522238dcc3SJonas Devlieghere            platform2.GetWorkingDirectory(),
1532238dcc3SJonas Devlieghere        )
154b461398fSJeffrey Tan
155b461398fSJeffrey Tan    def test_SetDestroyCallback(self):
156b461398fSJeffrey Tan        destroy_dbg_id = None
1572238dcc3SJonas Devlieghere
158b461398fSJeffrey Tan        def foo(dbg_id):
159b461398fSJeffrey Tan            # Need nonlocal to modify closure variable.
160b461398fSJeffrey Tan            nonlocal destroy_dbg_id
161b461398fSJeffrey Tan            destroy_dbg_id = dbg_id
162b461398fSJeffrey Tan
163b461398fSJeffrey Tan        self.dbg.SetDestroyCallback(foo)
164b461398fSJeffrey Tan
165b461398fSJeffrey Tan        original_dbg_id = self.dbg.GetID()
166b461398fSJeffrey Tan        self.dbg.Destroy(self.dbg)
167b461398fSJeffrey Tan        self.assertEqual(destroy_dbg_id, original_dbg_id)
1689f627750Sroyitaqi
1699f627750Sroyitaqi    def test_AddDestroyCallback(self):
1709f627750Sroyitaqi        original_dbg_id = self.dbg.GetID()
1719f627750Sroyitaqi        called = []
1729f627750Sroyitaqi
1739f627750Sroyitaqi        def foo(dbg_id):
1749f627750Sroyitaqi            # Need nonlocal to modify closure variable.
1759f627750Sroyitaqi            nonlocal called
1769f627750Sroyitaqi            called += [('foo', dbg_id)]
1779f627750Sroyitaqi
1789f627750Sroyitaqi        def bar(dbg_id):
1799f627750Sroyitaqi            # Need nonlocal to modify closure variable.
1809f627750Sroyitaqi            nonlocal called
1819f627750Sroyitaqi            called += [('bar', dbg_id)]
1829f627750Sroyitaqi
1839f627750Sroyitaqi        token_foo = self.dbg.AddDestroyCallback(foo)
1849f627750Sroyitaqi        token_bar = self.dbg.AddDestroyCallback(bar)
1859f627750Sroyitaqi        self.dbg.Destroy(self.dbg)
1869f627750Sroyitaqi
1879f627750Sroyitaqi        # Should call both `foo()` and `bar()`.
1889f627750Sroyitaqi        self.assertEqual(called, [
1899f627750Sroyitaqi            ('foo', original_dbg_id),
1909f627750Sroyitaqi            ('bar', original_dbg_id),
1919f627750Sroyitaqi        ])
1929f627750Sroyitaqi
1939f627750Sroyitaqi    def test_RemoveDestroyCallback(self):
1949f627750Sroyitaqi        original_dbg_id = self.dbg.GetID()
1959f627750Sroyitaqi        called = []
1969f627750Sroyitaqi
1979f627750Sroyitaqi        def foo(dbg_id):
1989f627750Sroyitaqi            # Need nonlocal to modify closure variable.
1999f627750Sroyitaqi            nonlocal called
2009f627750Sroyitaqi            called += [('foo', dbg_id)]
2019f627750Sroyitaqi
2029f627750Sroyitaqi        def bar(dbg_id):
2039f627750Sroyitaqi            # Need nonlocal to modify closure variable.
2049f627750Sroyitaqi            nonlocal called
2059f627750Sroyitaqi            called += [('bar', dbg_id)]
2069f627750Sroyitaqi
2079f627750Sroyitaqi        token_foo = self.dbg.AddDestroyCallback(foo)
2089f627750Sroyitaqi        token_bar = self.dbg.AddDestroyCallback(bar)
2099f627750Sroyitaqi        ret = self.dbg.RemoveDestroyCallback(token_foo)
2109f627750Sroyitaqi        self.dbg.Destroy(self.dbg)
2119f627750Sroyitaqi
2129f627750Sroyitaqi        # `Remove` should be successful
2139f627750Sroyitaqi        self.assertTrue(ret)
2149f627750Sroyitaqi        # Should only call `bar()`
2159f627750Sroyitaqi        self.assertEqual(called, [('bar', original_dbg_id)])
2169f627750Sroyitaqi
2179f627750Sroyitaqi    def test_RemoveDestroyCallback_invalid_token(self):
2189f627750Sroyitaqi        original_dbg_id = self.dbg.GetID()
2199f627750Sroyitaqi        magic_token_that_should_not_exist = 32413
2209f627750Sroyitaqi        called = []
2219f627750Sroyitaqi
2229f627750Sroyitaqi        def foo(dbg_id):
2239f627750Sroyitaqi            # Need nonlocal to modify closure variable.
2249f627750Sroyitaqi            nonlocal called
2259f627750Sroyitaqi            called += [('foo', dbg_id)]
2269f627750Sroyitaqi
2279f627750Sroyitaqi        token_foo = self.dbg.AddDestroyCallback(foo)
2289f627750Sroyitaqi        ret = self.dbg.RemoveDestroyCallback(magic_token_that_should_not_exist)
2299f627750Sroyitaqi        self.dbg.Destroy(self.dbg)
2309f627750Sroyitaqi
2319f627750Sroyitaqi        # `Remove` should be unsuccessful
2329f627750Sroyitaqi        self.assertFalse(ret)
2339f627750Sroyitaqi        # Should call `foo()`
2349f627750Sroyitaqi        self.assertEqual(called, [('foo', original_dbg_id)])
2359f627750Sroyitaqi
2369f627750Sroyitaqi    def test_HandleDestroyCallback(self):
2379f627750Sroyitaqi        """
2389f627750Sroyitaqi        Validates:
2399f627750Sroyitaqi        1. AddDestroyCallback and RemoveDestroyCallback work during debugger destroy.
2409f627750Sroyitaqi        2. HandleDestroyCallback invokes all callbacks in FIFO order.
2419f627750Sroyitaqi        """
2429f627750Sroyitaqi        original_dbg_id = self.dbg.GetID()
2439f627750Sroyitaqi        events = []
2449f627750Sroyitaqi        bar_token = None
2459f627750Sroyitaqi
2469f627750Sroyitaqi        def foo(dbg_id):
2479f627750Sroyitaqi            # Need nonlocal to modify closure variable.
2489f627750Sroyitaqi            nonlocal events
2499f627750Sroyitaqi            events.append(('foo called', dbg_id))
2509f627750Sroyitaqi
2519f627750Sroyitaqi        def bar(dbg_id):
2529f627750Sroyitaqi            # Need nonlocal to modify closure variable.
2539f627750Sroyitaqi            nonlocal events
2549f627750Sroyitaqi            events.append(('bar called', dbg_id))
2559f627750Sroyitaqi
2569f627750Sroyitaqi        def add_foo(dbg_id):
2579f627750Sroyitaqi            # Need nonlocal to modify closure variable.
2589f627750Sroyitaqi            nonlocal events
2599f627750Sroyitaqi            events.append(('add_foo called', dbg_id))
2609f627750Sroyitaqi            events.append(('foo token', self.dbg.AddDestroyCallback(foo)))
2619f627750Sroyitaqi
2629f627750Sroyitaqi        def remove_bar(dbg_id):
2639f627750Sroyitaqi            # Need nonlocal to modify closure variable.
2649f627750Sroyitaqi            nonlocal events
2659f627750Sroyitaqi            events.append(('remove_bar called', dbg_id))
2669f627750Sroyitaqi            events.append(('remove bar ret', self.dbg.RemoveDestroyCallback(bar_token)))
2679f627750Sroyitaqi
2689f627750Sroyitaqi        # Setup
2699f627750Sroyitaqi        events.append(('add_foo token', self.dbg.AddDestroyCallback(add_foo)))
2709f627750Sroyitaqi        bar_token = self.dbg.AddDestroyCallback(bar)
2719f627750Sroyitaqi        events.append(('bar token', bar_token))
2729f627750Sroyitaqi        events.append(('remove_bar token', self.dbg.AddDestroyCallback(remove_bar)))
2739f627750Sroyitaqi        # Destroy
2749f627750Sroyitaqi        self.dbg.Destroy(self.dbg)
2759f627750Sroyitaqi
2769f627750Sroyitaqi        self.assertEqual(events, [
2779f627750Sroyitaqi            # Setup
2789f627750Sroyitaqi            ('add_foo token', 0), # add_foo should be added
2799f627750Sroyitaqi            ('bar token', 1), # bar should be added
2809f627750Sroyitaqi            ('remove_bar token', 2), # remove_bar should be added
2819f627750Sroyitaqi            # Destroy
2829f627750Sroyitaqi            ('add_foo called', original_dbg_id), # add_foo should be called
2839f627750Sroyitaqi            ('foo token', 3), # foo should be added
2849f627750Sroyitaqi            ('bar called', original_dbg_id), # bar should be called
2859f627750Sroyitaqi            ('remove_bar called', original_dbg_id), # remove_bar should be called
2869f627750Sroyitaqi            ('remove bar ret', False), # remove_bar should fail, because it's already invoked and removed
2879f627750Sroyitaqi            ('foo called', original_dbg_id), # foo should be called
2889f627750Sroyitaqi        ])
289