xref: /llvm-project/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py (revision b9d4c94a603d3cc1f44ab7dd1d4f3ae9c80da98b)
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        addr = 0x500000000
154        message = "Hello, target 1"
155        buff = process_1.ReadCStringFromMemory(addr, len(message) + 1, error)
156        self.assertSuccess(error)
157        self.assertEqual(buff, message)
158
159        # ... now, reading again from target #0 process to make sure the call
160        # gets dispatched to the right target.
161        addr = 0x500000000
162        message = "Hello, target 0"
163        buff = process_0.ReadCStringFromMemory(addr, len(message) + 1, error)
164        self.assertSuccess(error)
165        self.assertEqual(buff, message)
166
167        thread = process_0.GetSelectedThread()
168        self.assertTrue(thread, "Invalid thread.")
169        self.assertEqual(thread.GetThreadID(), 0x19)
170        self.assertEqual(thread.GetName(), "DummyScriptedThread.thread-1")
171        self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonSignal)
172
173        self.assertGreater(thread.GetNumFrames(), 0)
174
175        frame = thread.GetFrameAtIndex(0)
176        GPRs = None
177        register_set = frame.registers # Returns an SBValueList.
178        for regs in register_set:
179            if 'general purpose' in regs.name.lower():
180                GPRs = regs
181                break
182
183        self.assertTrue(GPRs, "Invalid General Purpose Registers Set")
184        self.assertGreater(GPRs.GetNumChildren(), 0)
185        for idx, reg in enumerate(GPRs, start=1):
186            if idx > 21:
187                break
188            self.assertEqual(idx, int(reg.value, 16))
189
190        self.assertTrue(frame.IsArtificial(), "Frame is not artificial")
191        pc = frame.GetPCAddress().GetLoadAddress(target_0)
192        self.assertEqual(pc, 0x0100001b00)
193