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