xref: /llvm-project/lldb/test/API/commands/command/script/TestCommandScript.py (revision 5784bf85bc5143266565586ece0113cd773a8616)
1"""
2Test lldb Python commands.
3"""
4
5
6import sys
7import lldb
8from lldbsuite.test.decorators import *
9from lldbsuite.test.lldbtest import *
10
11
12class CmdPythonTestCase(TestBase):
13    NO_DEBUG_INFO_TESTCASE = True
14
15    def test(self):
16        self.build()
17        self.pycmd_tests()
18
19    def pycmd_tests(self):
20        self.runCmd("command source py_import")
21
22        # Test that we did indeed add these commands as user commands:
23        interp = self.dbg.GetCommandInterpreter()
24        self.assertTrue(interp.UserCommandExists("foobar"), "foobar exists")
25        self.assertFalse(interp.CommandExists("foobar"), "It is not a builtin.")
26
27        # Test a bunch of different kinds of python callables with
28        # both 4 and 5 positional arguments.
29        self.expect("foobar", substrs=["All good"])
30        self.expect("foobar4", substrs=["All good"])
31        self.expect("vfoobar", substrs=["All good"])
32        self.expect("v5foobar", substrs=["All good"])
33        self.expect("sfoobar", substrs=["All good"])
34        self.expect("cfoobar", substrs=["All good"])
35        self.expect("ifoobar", substrs=["All good"])
36        self.expect("sfoobar4", substrs=["All good"])
37        self.expect("cfoobar4", substrs=["All good"])
38        self.expect("ifoobar4", substrs=["All good"])
39        self.expect("ofoobar", substrs=["All good"])
40        self.expect("ofoobar4", substrs=["All good"])
41
42        # Verify command that specifies eCommandRequiresTarget returns failure
43        # without a target.
44        self.expect("targetname", substrs=["a.out"], matching=False, error=True)
45
46        exe = self.getBuildArtifact("a.out")
47        self.expect("file " + exe, patterns=["Current executable set to .*a.out"])
48
49        self.expect("targetname", substrs=["a.out"], matching=True, error=False)
50
51        # This is the function to remove the custom commands in order to have a
52        # clean slate for the next test case.
53        def cleanup():
54            self.runCmd("command script delete welcome", check=False)
55            self.runCmd("command script delete targetname", check=False)
56            self.runCmd("command script delete longwait", check=False)
57            self.runCmd("command script delete mysto", check=False)
58            self.runCmd("command script delete tell_sync", check=False)
59            self.runCmd("command script delete tell_async", check=False)
60            self.runCmd("command script delete tell_curr", check=False)
61            self.runCmd("command script delete bug11569", check=False)
62            self.runCmd("command script delete takes_exe_ctx", check=False)
63            self.runCmd("command script delete decorated", check=False)
64
65        # Execute the cleanup function during test case tear down.
66        self.addTearDownHook(cleanup)
67
68        # Interact with debugger in synchronous mode
69        self.setAsync(False)
70
71        # We don't want to display the stdout if not in TraceOn() mode.
72        if not self.TraceOn():
73            self.HideStdout()
74
75        self.expect("welcome Enrico", substrs=["Hello Enrico, welcome to LLDB"])
76
77        self.expect(
78            "help welcome",
79            substrs=[
80                "Just a docstring for welcome_impl",
81                "A command that says hello to LLDB users",
82            ],
83        )
84
85        decorated_commands = ["decorated" + str(n) for n in range(1, 5)]
86        for name in decorated_commands:
87            self.expect(name, substrs=["hello from " + name])
88            self.expect(
89                "help " + name, substrs=["Python command defined by @lldb.command"]
90            )
91
92        self.expect(
93            "help",
94            substrs=["For more information run"] + decorated_commands + ["welcome"],
95        )
96
97        self.expect(
98            "help -a",
99            substrs=["For more information run"] + decorated_commands + ["welcome"],
100        )
101
102        self.expect("help -u", matching=False, substrs=["For more information run"])
103
104        self.runCmd("command script delete welcome")
105
106        self.expect(
107            "welcome Enrico",
108            matching=False,
109            error=True,
110            substrs=["Hello Enrico, welcome to LLDB"],
111        )
112
113        self.expect(
114            "targetname fail", error=True, substrs=["a test for error in command"]
115        )
116
117        self.expect(
118            "command script list", substrs=["targetname", "For more information run"]
119        )
120
121        self.expect(
122            "help targetname",
123            substrs=["Expects", "'raw'", "input", "help", "raw-input"],
124        )
125
126        self.expect("longwait", substrs=["Done; if you saw the delays I am doing OK"])
127
128        self.runCmd("break set -f main.cpp -l 48")
129        self.runCmd("run")
130        self.runCmd("mysto 3")
131        self.expect(
132            "frame variable array",
133            substrs=["[0] = 79630", "[1] = 388785018", "[2] = 0"],
134        )
135        self.runCmd("mysto 3")
136        self.expect(
137            "frame variable array",
138            substrs=["[0] = 79630", "[4] = 388785018", "[5] = 0"],
139        )
140
141        # we cannot use the stepover command to check for async execution mode since LLDB
142        # seems to get confused when events start to queue up
143        self.expect("tell_sync", substrs=["running sync"])
144        self.expect("tell_async", substrs=["running async"])
145        self.expect("tell_curr", substrs=["I am running sync"])
146
147        # check that the execution context is passed in to commands that ask for it
148        self.expect("takes_exe_ctx", substrs=["a.out"])
149
150        # Test that a python command can redefine itself
151        self.expect('command script add -f foobar welcome -h "just some help"')
152
153        self.runCmd("command script clear")
154
155        # Test that re-defining an existing command works
156        self.runCmd("command script add my_command --class welcome.WelcomeCommand")
157        self.expect("my_command Blah", substrs=["Hello Blah, welcome to LLDB"])
158
159        self.runCmd(
160            "command script add my_command -o --class welcome.TargetnameCommand"
161        )
162        self.expect("my_command", substrs=["a.out"])
163
164        # Test that without --overwrite we are not allowed to redefine the command.
165        self.expect(
166            "command script add my_command --class welcome.TargetnameCommand",
167            substrs=[
168                (
169                    'user command "my_command" already exists and force replace was'
170                    " not set by --overwrite or 'settings set"
171                    " interpreter.require-overwrite false'"
172                ),
173            ],
174            error=True,
175        )
176
177        self.runCmd("command script clear")
178
179        self.expect(
180            "command script list", matching=False, substrs=["targetname", "longwait"]
181        )
182
183        self.expect(
184            "command script add -f foobar frame",
185            error=True,
186            substrs=["cannot add command"],
187        )
188
189        # http://llvm.org/bugs/show_bug.cgi?id=11569
190        # LLDBSwigPythonCallCommand crashes when a command script returns an
191        # object
192        self.runCmd("command script add -f bug11569 bug11569")
193        # This should not crash.
194        self.runCmd("bug11569", check=False)
195
196        # Make sure that a reference to a non-existent class raises an error:
197        bad_class_name = "LLDBNoSuchModule.LLDBNoSuchClass"
198        self.expect(
199            "command script add wont-work --class {0}".format(bad_class_name),
200            error=True,
201            substrs=[bad_class_name],
202        )
203
204    def test_persistence(self):
205        """
206        Ensure that function arguments meaningfully persist (and do not crash!)
207        even after the function terminates.
208        """
209        self.runCmd("command script import persistence.py")
210        self.runCmd("command script add -f persistence.save_debugger save_debugger")
211        self.expect("save_debugger", substrs=[str(self.dbg)])
212
213        # After the command completes, the debugger object should still be
214        # valid.
215        self.expect("script str(persistence.debugger_copy)", substrs=[str(self.dbg)])
216        # The result object will be replaced by an empty result object (in the
217        # "Started" state).
218        self.expect("script str(persistence.result_copy)", substrs=["Started"])
219
220    def test_interactive(self):
221        """
222        Test that we can add multiple lines interactively.
223        """
224        interp = self.dbg.GetCommandInterpreter()
225        cmd_file = self.getSourcePath("cmd_file.lldb")
226        result = lldb.SBCommandReturnObject()
227        interp.HandleCommand(f"command source {cmd_file}", result)
228        self.assertCommandReturn(result, "Sourcing the command should cause no errors.")
229        self.assertTrue(interp.UserCommandExists("my_cmd"), "Command defined.")
230        interp.HandleCommand("my_cmd", result)
231        self.assertCommandReturn(result, "Running the command succeeds")
232        self.assertIn("My Command Result", result.GetOutput(), "Command was correct")
233