xref: /llvm-project/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py (revision 0a21144614950ce063d8dac6394307bd3be604cd)
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    def test_missing_methods_scripted_register_context(self):
64        """Test that we only instanciate scripted processes if they implement
65        all the required abstract methods."""
66        self.build()
67
68        os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"] = "1"
69
70        def cleanup():
71            del os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"]
72
73        self.addTearDownHook(cleanup)
74
75        target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
76        self.assertTrue(target, VALID_TARGET)
77        log_file = self.getBuildArtifact("script.log")
78        self.runCmd("log enable lldb script -f " + log_file)
79        self.assertTrue(os.path.isfile(log_file))
80        script_path = os.path.join(
81            self.getSourceDir(), "missing_methods_scripted_process.py"
82        )
83        self.runCmd("command script import " + script_path)
84
85        launch_info = lldb.SBLaunchInfo(None)
86        launch_info.SetProcessPluginName("ScriptedProcess")
87        launch_info.SetScriptedProcessClassName(
88            "missing_methods_scripted_process.MissingMethodsScriptedProcess"
89        )
90        error = lldb.SBError()
91
92        process = target.Launch(launch_info, error)
93
94        self.assertFailure(error)
95
96        with open(log_file, "r") as f:
97            log = f.read()
98
99        self.assertIn(
100            "Abstract method MissingMethodsScriptedProcess.read_memory_at_address not implemented",
101            log,
102        )
103        self.assertIn(
104            "Abstract method MissingMethodsScriptedProcess.is_alive not implemented",
105            log,
106        )
107        self.assertIn(
108            "Abstract method MissingMethodsScriptedProcess.get_scripted_thread_plugin not implemented",
109            log,
110        )
111
112    @skipUnlessDarwin
113    def test_invalid_scripted_register_context(self):
114        """Test that we can launch an lldb scripted process with an invalid
115        Scripted Thread, with invalid register context."""
116        self.build()
117
118        os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"] = "1"
119
120        def cleanup():
121            del os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"]
122
123        self.addTearDownHook(cleanup)
124
125        self.runCmd("settings set target.load-script-from-symbol-file true")
126        self.move_blueprint_to_dsym("invalid_scripted_process.py")
127        target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
128        self.assertTrue(target, VALID_TARGET)
129        log_file = self.getBuildArtifact("thread.log")
130        self.runCmd("log enable lldb thread -f " + log_file)
131        self.assertTrue(os.path.isfile(log_file))
132
133        launch_info = lldb.SBLaunchInfo(None)
134        launch_info.SetProcessPluginName("ScriptedProcess")
135        launch_info.SetScriptedProcessClassName("a_out.InvalidScriptedProcess")
136        error = lldb.SBError()
137
138        process = target.Launch(launch_info, error)
139
140        self.assertSuccess(error)
141        self.assertTrue(process, PROCESS_IS_VALID)
142        self.assertEqual(process.GetProcessID(), 666)
143        self.assertEqual(process.GetNumThreads(), 0)
144
145        impl = process.GetScriptedImplementation()
146        self.assertTrue(impl)
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
154        addr = 0x500000000
155        buff = process.ReadMemory(addr, 4, error)
156        self.assertEqual(buff, None)
157        self.assertTrue(error.Fail())
158        self.assertEqual(error.GetCString(), "This is an invalid scripted process!")
159
160        with open(log_file, "r") as f:
161            log = f.read()
162
163        self.assertIn("Failed to get scripted thread registers data.", log)
164
165    @skipUnlessDarwin
166    def test_scripted_process_and_scripted_thread(self):
167        """Test that we can launch an lldb scripted process using the SBAPI,
168        check its process ID, read string from memory, check scripted thread
169        id, name stop reason and register context.
170        """
171        self.build()
172        target_0 = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
173        self.assertTrue(target_0, VALID_TARGET)
174
175        os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"] = "1"
176
177        def cleanup():
178            del os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"]
179
180        self.addTearDownHook(cleanup)
181
182        scripted_process_example_relpath = "dummy_scripted_process.py"
183        self.runCmd(
184            "command script import "
185            + os.path.join(self.getSourceDir(), scripted_process_example_relpath)
186        )
187
188        launch_info = lldb.SBLaunchInfo(None)
189        launch_info.SetProcessPluginName("ScriptedProcess")
190        launch_info.SetScriptedProcessClassName(
191            "dummy_scripted_process.DummyScriptedProcess"
192        )
193
194        error = lldb.SBError()
195        process_0 = target_0.Launch(launch_info, error)
196        self.assertTrue(process_0 and process_0.IsValid(), PROCESS_IS_VALID)
197        self.assertEqual(process_0.GetProcessID(), 42)
198        self.assertEqual(process_0.GetNumThreads(), 1)
199
200        py_impl = process_0.GetScriptedImplementation()
201        self.assertTrue(py_impl)
202        self.assertTrue(
203            isinstance(py_impl, dummy_scripted_process.DummyScriptedProcess)
204        )
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