xref: /llvm-project/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py (revision 2238dcc39358353cac21df75c3c3286ab20b8f53)
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    @skipUnlessDarwin
64    def test_invalid_scripted_register_context(self):
65        """Test that we can launch an lldb scripted process with an invalid
66        Scripted Thread, with invalid register context."""
67        self.build()
68
69        os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"] = "1"
70
71        def cleanup():
72            del os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"]
73
74        self.addTearDownHook(cleanup)
75
76        self.runCmd("settings set target.load-script-from-symbol-file true")
77        self.move_blueprint_to_dsym("invalid_scripted_process.py")
78        target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
79        self.assertTrue(target, VALID_TARGET)
80        log_file = self.getBuildArtifact("thread.log")
81        self.runCmd("log enable lldb thread -f " + log_file)
82        self.assertTrue(os.path.isfile(log_file))
83
84        launch_info = lldb.SBLaunchInfo(None)
85        launch_info.SetProcessPluginName("ScriptedProcess")
86        launch_info.SetScriptedProcessClassName("a_out.InvalidScriptedProcess")
87        error = lldb.SBError()
88
89        process = target.Launch(launch_info, error)
90
91        self.assertSuccess(error)
92        self.assertTrue(process, PROCESS_IS_VALID)
93        self.assertEqual(process.GetProcessID(), 666)
94        self.assertEqual(process.GetNumThreads(), 0)
95
96        impl = process.GetScriptedImplementation()
97        self.assertTrue(impl)
98        impl = process.GetScriptedImplementation()
99        self.assertTrue(impl)
100        impl = process.GetScriptedImplementation()
101        self.assertTrue(impl)
102        impl = process.GetScriptedImplementation()
103        self.assertTrue(impl)
104
105        addr = 0x500000000
106        buff = process.ReadMemory(addr, 4, error)
107        self.assertEqual(buff, None)
108        self.assertTrue(error.Fail())
109        self.assertEqual(error.GetCString(), "This is an invalid scripted process!")
110
111        with open(log_file, "r") as f:
112            log = f.read()
113
114        self.assertIn("Failed to get scripted thread registers data.", log)
115
116    @skipUnlessDarwin
117    def test_scripted_process_and_scripted_thread(self):
118        """Test that we can launch an lldb scripted process using the SBAPI,
119        check its process ID, read string from memory, check scripted thread
120        id, name stop reason and register context.
121        """
122        self.build()
123        target_0 = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
124        self.assertTrue(target_0, VALID_TARGET)
125
126        os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"] = "1"
127
128        def cleanup():
129            del os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"]
130
131        self.addTearDownHook(cleanup)
132
133        scripted_process_example_relpath = "dummy_scripted_process.py"
134        self.runCmd(
135            "command script import "
136            + os.path.join(self.getSourceDir(), scripted_process_example_relpath)
137        )
138
139        launch_info = lldb.SBLaunchInfo(None)
140        launch_info.SetProcessPluginName("ScriptedProcess")
141        launch_info.SetScriptedProcessClassName(
142            "dummy_scripted_process.DummyScriptedProcess"
143        )
144
145        error = lldb.SBError()
146        process_0 = target_0.Launch(launch_info, error)
147        self.assertTrue(process_0 and process_0.IsValid(), PROCESS_IS_VALID)
148        self.assertEqual(process_0.GetProcessID(), 42)
149        self.assertEqual(process_0.GetNumThreads(), 1)
150
151        py_impl = process_0.GetScriptedImplementation()
152        self.assertTrue(py_impl)
153        self.assertTrue(
154            isinstance(py_impl, dummy_scripted_process.DummyScriptedProcess)
155        )
156        self.assertFalse(hasattr(py_impl, "my_super_secret_member"))
157        py_impl.my_super_secret_member = 42
158        self.assertTrue(hasattr(py_impl, "my_super_secret_member"))
159        self.assertEqual(py_impl.my_super_secret_method(), 42)
160
161        # Try reading from target #0 process ...
162        addr = 0x500000000
163        message = "Hello, target 0"
164        buff = process_0.ReadCStringFromMemory(addr, len(message) + 1, error)
165        self.assertSuccess(error)
166        self.assertEqual(buff, message)
167
168        target_1 = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
169        self.assertTrue(target_1, VALID_TARGET)
170
171        # We still need to specify a PID when attaching even for scripted processes
172        attach_info = lldb.SBAttachInfo(42)
173        attach_info.SetProcessPluginName("ScriptedProcess")
174        attach_info.SetScriptedProcessClassName(
175            "dummy_scripted_process.DummyScriptedProcess"
176        )
177
178        error = lldb.SBError()
179        process_1 = target_1.Attach(attach_info, error)
180        self.assertTrue(process_1 and process_1.IsValid(), PROCESS_IS_VALID)
181        self.assertEqual(process_1.GetProcessID(), 42)
182        self.assertEqual(process_1.GetNumThreads(), 1)
183
184        # ... then try reading from target #1 process ...
185        message = "Hello, target 1"
186        buff = process_1.ReadCStringFromMemory(addr, len(message) + 1, error)
187        self.assertSuccess(error)
188        self.assertEqual(buff, message)
189
190        # ... now, reading again from target #0 process to make sure the call
191        # gets dispatched to the right target.
192        message = "Hello, target 0"
193        buff = process_0.ReadCStringFromMemory(addr, len(message) + 1, error)
194        self.assertSuccess(error)
195        self.assertEqual(buff, message)
196
197        # Let's write some memory.
198        message = "Hello, world!"
199        bytes_written = process_0.WriteMemoryAsCString(addr, message, error)
200        self.assertSuccess(error)
201        self.assertEqual(bytes_written, len(message) + 1)
202
203        # ... and check if that memory was saved properly.
204        buff = process_0.ReadCStringFromMemory(addr, len(message) + 1, error)
205        self.assertSuccess(error)
206        self.assertEqual(buff, message)
207
208        thread = process_0.GetSelectedThread()
209        self.assertTrue(thread, "Invalid thread.")
210        self.assertEqual(thread.GetThreadID(), 0x19)
211        self.assertEqual(thread.GetName(), "DummyScriptedThread.thread-1")
212        self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonTrace)
213
214        self.assertGreater(thread.GetNumFrames(), 0)
215
216        frame = thread.GetFrameAtIndex(0)
217        GPRs = None
218        register_set = frame.registers  # Returns an SBValueList.
219        for regs in register_set:
220            if "general purpose" in regs.name.lower():
221                GPRs = regs
222                break
223
224        self.assertTrue(GPRs, "Invalid General Purpose Registers Set")
225        self.assertGreater(GPRs.GetNumChildren(), 0)
226        for idx, reg in enumerate(GPRs, start=1):
227            if idx > 21:
228                break
229            self.assertEqual(idx, int(reg.value, 16))
230
231        self.assertTrue(frame.IsArtificial(), "Frame is not artificial")
232        pc = frame.GetPCAddress().GetLoadAddress(target_0)
233        self.assertEqual(pc, 0x0100001B00)
234