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