xref: /llvm-project/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py (revision 9c2468821ec51defd09c246fea4a47886fff8c01)
1"""
2Test python scripted process in lldb
3"""
4
5import os, shutil
6
7import lldb
8from lldbsuite.test.decorators import *
9from lldbsuite.test.lldbtest import *
10from lldbsuite.test import lldbutil
11from lldbsuite.test import lldbtest
12
13import dummy_scripted_process
14
15
16class ScriptedProcesTestCase(TestBase):
17    NO_DEBUG_INFO_TESTCASE = True
18
19    @skipUnlessDarwin
20    def test_python_plugin_package(self):
21        """Test that the lldb python module has a `plugins.scripted_process`
22        package."""
23        self.expect(
24            "script import lldb.plugins",
25            substrs=["ModuleNotFoundError"],
26            matching=False,
27        )
28
29        self.expect("script dir(lldb.plugins)", substrs=["scripted_process"])
30
31        self.expect(
32            "script import lldb.plugins.scripted_process",
33            substrs=["ModuleNotFoundError"],
34            matching=False,
35        )
36
37        self.expect(
38            "script dir(lldb.plugins.scripted_process)", substrs=["ScriptedProcess"]
39        )
40
41        self.expect(
42            "script from lldb.plugins.scripted_process import ScriptedProcess",
43            substrs=["ImportError"],
44            matching=False,
45        )
46
47        self.expect("script dir(ScriptedProcess)", substrs=["launch"])
48
49    def move_blueprint_to_dsym(self, blueprint_name):
50        blueprint_origin_path = os.path.join(self.getSourceDir(), blueprint_name)
51        dsym_bundle = self.getBuildArtifact("a.out.dSYM")
52        blueprint_destination_path = os.path.join(
53            dsym_bundle, "Contents", "Resources", "Python"
54        )
55        if not os.path.exists(blueprint_destination_path):
56            os.mkdir(blueprint_destination_path)
57
58        blueprint_destination_path = os.path.join(
59            blueprint_destination_path, "a_out.py"
60        )
61        shutil.copy(blueprint_origin_path, blueprint_destination_path)
62
63    # No dylib on Windows.
64    @skipIfWindows
65    def test_missing_methods_scripted_register_context(self):
66        """Test that we only instanciate scripted processes if they implement
67        all the required abstract methods."""
68        self.build()
69
70        os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"] = "1"
71
72        def cleanup():
73            del os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"]
74
75        self.addTearDownHook(cleanup)
76
77        target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
78        self.assertTrue(target, VALID_TARGET)
79        log_file = self.getBuildArtifact("script.log")
80        self.runCmd("log enable lldb script -f " + log_file)
81        self.assertTrue(os.path.isfile(log_file))
82        script_path = os.path.join(
83            self.getSourceDir(), "missing_methods_scripted_process.py"
84        )
85        self.runCmd("command script import " + script_path)
86
87        launch_info = lldb.SBLaunchInfo(None)
88        launch_info.SetProcessPluginName("ScriptedProcess")
89        launch_info.SetScriptedProcessClassName(
90            "missing_methods_scripted_process.MissingMethodsScriptedProcess"
91        )
92        error = lldb.SBError()
93
94        process = target.Launch(launch_info, error)
95
96        self.assertFailure(error)
97
98        with open(log_file, "r") as f:
99            log = f.read()
100
101        self.assertIn(
102            "Abstract method MissingMethodsScriptedProcess.read_memory_at_address not implemented",
103            log,
104        )
105        self.assertIn(
106            "Abstract method MissingMethodsScriptedProcess.is_alive not implemented",
107            log,
108        )
109        self.assertIn(
110            "Abstract method MissingMethodsScriptedProcess.get_scripted_thread_plugin not implemented",
111            log,
112        )
113
114    @skipUnlessDarwin
115    def test_invalid_scripted_register_context(self):
116        """Test that we can launch an lldb scripted process with an invalid
117        Scripted Thread, with invalid register context."""
118        self.build()
119
120        os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"] = "1"
121
122        def cleanup():
123            del os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"]
124
125        self.addTearDownHook(cleanup)
126
127        self.runCmd("settings set target.load-script-from-symbol-file true")
128        self.move_blueprint_to_dsym("invalid_scripted_process.py")
129        target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
130        self.assertTrue(target, VALID_TARGET)
131        log_file = self.getBuildArtifact("thread.log")
132        self.runCmd("log enable lldb thread -f " + log_file)
133        self.assertTrue(os.path.isfile(log_file))
134
135        launch_info = lldb.SBLaunchInfo(None)
136        launch_info.SetProcessPluginName("ScriptedProcess")
137        launch_info.SetScriptedProcessClassName("a_out.InvalidScriptedProcess")
138        error = lldb.SBError()
139
140        process = target.Launch(launch_info, error)
141
142        self.assertSuccess(error)
143        self.assertTrue(process, PROCESS_IS_VALID)
144        self.assertEqual(process.GetProcessID(), 666)
145        self.assertEqual(process.GetNumThreads(), 0)
146
147        impl = process.GetScriptedImplementation()
148        self.assertTrue(impl)
149        impl = process.GetScriptedImplementation()
150        self.assertTrue(impl)
151        impl = process.GetScriptedImplementation()
152        self.assertTrue(impl)
153        impl = process.GetScriptedImplementation()
154        self.assertTrue(impl)
155
156        addr = 0x500000000
157        buff = process.ReadMemory(addr, 4, error)
158        self.assertEqual(buff, None)
159        self.assertTrue(error.Fail())
160        self.assertEqual(error.GetCString(), "This is an invalid scripted process!")
161
162        with open(log_file, "r") as f:
163            log = f.read()
164
165        self.assertIn("Failed to get scripted thread registers data.", log)
166
167    @skipUnlessDarwin
168    def test_scripted_process_and_scripted_thread(self):
169        """Test that we can launch an lldb scripted process using the SBAPI,
170        check its process ID, read string from memory, check scripted thread
171        id, name stop reason and register context.
172        """
173        self.build()
174        target_0 = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
175        self.assertTrue(target_0, VALID_TARGET)
176
177        os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"] = "1"
178
179        def cleanup():
180            del os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"]
181
182        self.addTearDownHook(cleanup)
183
184        scripted_process_example_relpath = "dummy_scripted_process.py"
185        self.runCmd(
186            "command script import "
187            + os.path.join(self.getSourceDir(), scripted_process_example_relpath)
188        )
189
190        launch_info = lldb.SBLaunchInfo(None)
191        launch_info.SetProcessPluginName("ScriptedProcess")
192        launch_info.SetScriptedProcessClassName(
193            "dummy_scripted_process.DummyScriptedProcess"
194        )
195
196        error = lldb.SBError()
197        process_0 = target_0.Launch(launch_info, error)
198        self.assertTrue(process_0 and process_0.IsValid(), PROCESS_IS_VALID)
199        self.assertEqual(process_0.GetProcessID(), 42)
200        self.assertEqual(process_0.GetNumThreads(), 1)
201
202        py_impl = process_0.GetScriptedImplementation()
203        self.assertTrue(py_impl)
204        self.assertIsInstance(py_impl, dummy_scripted_process.DummyScriptedProcess)
205        self.assertFalse(hasattr(py_impl, "my_super_secret_member"))
206        py_impl.my_super_secret_member = 42
207        self.assertTrue(hasattr(py_impl, "my_super_secret_member"))
208        self.assertEqual(py_impl.my_super_secret_method(), 42)
209
210        # Try reading from target #0 process ...
211        addr = 0x500000000
212        message = "Hello, target 0"
213        buff = process_0.ReadCStringFromMemory(addr, len(message) + 1, error)
214        self.assertSuccess(error)
215        self.assertEqual(buff, message)
216
217        target_1 = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
218        self.assertTrue(target_1, VALID_TARGET)
219
220        # We still need to specify a PID when attaching even for scripted processes
221        attach_info = lldb.SBAttachInfo(42)
222        attach_info.SetProcessPluginName("ScriptedProcess")
223        attach_info.SetScriptedProcessClassName(
224            "dummy_scripted_process.DummyScriptedProcess"
225        )
226
227        error = lldb.SBError()
228        process_1 = target_1.Attach(attach_info, error)
229        self.assertTrue(process_1 and process_1.IsValid(), PROCESS_IS_VALID)
230        self.assertEqual(process_1.GetProcessID(), 42)
231        self.assertEqual(process_1.GetNumThreads(), 1)
232
233        # ... then try reading from target #1 process ...
234        message = "Hello, target 1"
235        buff = process_1.ReadCStringFromMemory(addr, len(message) + 1, error)
236        self.assertSuccess(error)
237        self.assertEqual(buff, message)
238
239        # ... now, reading again from target #0 process to make sure the call
240        # gets dispatched to the right target.
241        message = "Hello, target 0"
242        buff = process_0.ReadCStringFromMemory(addr, len(message) + 1, error)
243        self.assertSuccess(error)
244        self.assertEqual(buff, message)
245
246        # Let's write some memory.
247        message = "Hello, world!"
248        bytes_written = process_0.WriteMemoryAsCString(addr, message, error)
249        self.assertSuccess(error)
250        self.assertEqual(bytes_written, len(message) + 1)
251
252        # ... and check if that memory was saved properly.
253        buff = process_0.ReadCStringFromMemory(addr, len(message) + 1, error)
254        self.assertSuccess(error)
255        self.assertEqual(buff, message)
256
257        thread = process_0.GetSelectedThread()
258        self.assertTrue(thread, "Invalid thread.")
259        self.assertEqual(thread.GetThreadID(), 0x19)
260        self.assertEqual(thread.GetName(), "DummyScriptedThread.thread-1")
261        self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonTrace)
262
263        self.assertGreater(thread.GetNumFrames(), 0)
264
265        frame = thread.GetFrameAtIndex(0)
266        GPRs = None
267        register_set = frame.registers  # Returns an SBValueList.
268        for regs in register_set:
269            if "general purpose" in regs.name.lower():
270                GPRs = regs
271                break
272
273        self.assertTrue(GPRs, "Invalid General Purpose Registers Set")
274        self.assertGreater(GPRs.GetNumChildren(), 0)
275        for idx, reg in enumerate(GPRs, start=1):
276            if idx > 21:
277                break
278            self.assertEqual(idx, int(reg.value, 16))
279
280        self.assertTrue(frame.IsArtificial(), "Frame is not artificial")
281        pc = frame.GetPCAddress().GetLoadAddress(target_0)
282        self.assertEqual(pc, 0x0100001B00)
283