xref: /llvm-project/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py (revision a6b5624372a652b18633704e7a5732e292f2f61b)
1"""
2Test python scripted process in lldb
3"""
4
5import os, json, tempfile
6
7import lldb
8from lldbsuite.test.decorators import *
9from lldbsuite.test.lldbtest import *
10from lldbsuite.test import lldbutil
11from lldbsuite.test import lldbtest
12
13class ScriptedProcesTestCase(TestBase):
14
15    mydir = TestBase.compute_mydir(__file__)
16
17    def setUp(self):
18        TestBase.setUp(self)
19
20    def tearDown(self):
21        TestBase.tearDown(self)
22
23    def test_python_plugin_package(self):
24        """Test that the lldb python module has a `plugins.scripted_process`
25        package."""
26        self.expect('script import lldb.plugins',
27                    substrs=["ModuleNotFoundError"], matching=False)
28
29        self.expect('script dir(lldb.plugins)',
30                    substrs=["scripted_process"])
31
32        self.expect('script import lldb.plugins.scripted_process',
33                    substrs=["ModuleNotFoundError"], matching=False)
34
35        self.expect('script dir(lldb.plugins.scripted_process)',
36                    substrs=["ScriptedProcess"])
37
38        self.expect('script from lldb.plugins.scripted_process import ScriptedProcess',
39                    substrs=["ImportError"], matching=False)
40
41        self.expect('script dir(ScriptedProcess)',
42                    substrs=["launch"])
43
44    @skipUnlessDarwin
45    def test_invalid_scripted_register_context(self):
46        """Test that we can launch an lldb scripted process with an invalid
47        Scripted Thread, with invalid register context."""
48        self.build()
49        target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
50        self.assertTrue(target, VALID_TARGET)
51        log_file = self.getBuildArtifact('thread.log')
52        self.runCmd("log enable lldb thread -f " + log_file)
53        self.assertTrue(os.path.isfile(log_file))
54
55        os.environ['SKIP_SCRIPTED_PROCESS_LAUNCH'] = '1'
56        def cleanup():
57          del os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"]
58        self.addTearDownHook(cleanup)
59
60        scripted_process_example_relpath = 'invalid_scripted_process.py'
61        self.runCmd("command script import " + os.path.join(self.getSourceDir(),
62                                                            scripted_process_example_relpath))
63
64        launch_info = lldb.SBLaunchInfo(None)
65        launch_info.SetProcessPluginName("ScriptedProcess")
66        launch_info.SetScriptedProcessClassName("invalid_scripted_process.InvalidScriptedProcess")
67        error = lldb.SBError()
68
69        process = target.Launch(launch_info, error)
70
71        self.assertTrue(error.Success(), error.GetCString())
72        self.assertTrue(process, PROCESS_IS_VALID)
73        self.assertEqual(process.GetProcessID(), 666)
74        self.assertEqual(process.GetNumThreads(), 0)
75
76        with open(log_file, 'r') as f:
77            log = f.read()
78
79        self.assertIn("Failed to get scripted thread registers data.", log)
80
81    @skipIf(archs=no_match(['x86_64', 'arm64', 'arm64e']))
82    def test_scripted_process_and_scripted_thread(self):
83        """Test that we can launch an lldb scripted process using the SBAPI,
84        check its process ID, read string from memory, check scripted thread
85        id, name stop reason and register context.
86        """
87        self.build()
88        target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
89        self.assertTrue(target, VALID_TARGET)
90
91        os.environ['SKIP_SCRIPTED_PROCESS_LAUNCH'] = '1'
92        def cleanup():
93          del os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"]
94        self.addTearDownHook(cleanup)
95
96        scripted_process_example_relpath = 'dummy_scripted_process.py'
97        self.runCmd("command script import " + os.path.join(self.getSourceDir(),
98                                                            scripted_process_example_relpath))
99
100        launch_info = lldb.SBLaunchInfo(None)
101        launch_info.SetProcessPluginName("ScriptedProcess")
102        launch_info.SetScriptedProcessClassName("dummy_scripted_process.DummyScriptedProcess")
103
104        error = lldb.SBError()
105        process = target.Launch(launch_info, error)
106        self.assertTrue(process and process.IsValid(), PROCESS_IS_VALID)
107        self.assertEqual(process.GetProcessID(), 42)
108
109        self.assertEqual(process.GetNumThreads(), 1)
110
111        thread = process.GetSelectedThread()
112        self.assertTrue(thread, "Invalid thread.")
113        self.assertEqual(thread.GetThreadID(), 0x19)
114        self.assertEqual(thread.GetName(), "DummyScriptedThread.thread-1")
115        self.assertEqual(thread.GetStopReason(), lldb.eStopReasonSignal)
116
117        self.assertGreater(thread.GetNumFrames(), 0)
118
119        frame = thread.GetFrameAtIndex(0)
120        GPRs = None
121        register_set = frame.registers # Returns an SBValueList.
122        for regs in register_set:
123            if 'general purpose' in regs.name.lower():
124                GPRs = regs
125                break
126
127        self.assertTrue(GPRs, "Invalid General Purpose Registers Set")
128        self.assertGreater(GPRs.GetNumChildren(), 0)
129        for idx, reg in enumerate(GPRs, start=1):
130            if idx > 21:
131                break
132            self.assertEqual(idx, int(reg.value, 16))
133
134    def create_stack_skinny_corefile(self, file):
135        self.build()
136        target, process, thread, _ = lldbutil.run_to_source_breakpoint(self, "// break here",
137                                                                       lldb.SBFileSpec("main.cpp"))
138        self.assertTrue(process.IsValid(), "Process is invalid.")
139        # FIXME: Use SBAPI to save the process corefile.
140        self.runCmd("process save-core -s stack  " + file)
141        self.assertTrue(os.path.exists(file), "No stack-only corefile found.")
142        self.assertTrue(self.dbg.DeleteTarget(target), "Couldn't delete target")
143
144    @skipUnlessDarwin
145    @skipIfOutOfTreeDebugserver
146    def test_launch_scripted_process_stack_frames(self):
147        """Test that we can launch an lldb scripted process from the command
148        line, check its process ID and read string from memory."""
149        self.build()
150        target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
151        self.assertTrue(target, VALID_TARGET)
152
153        for module in target.modules:
154            if 'a.out' in module.GetFileSpec().GetFilename():
155                main_module = module
156                break
157
158        self.assertTrue(main_module, "Invalid main module.")
159        error = target.SetModuleLoadAddress(main_module, 0)
160        self.assertTrue(error.Success(), "Reloading main module at offset 0 failed.")
161
162        os.environ['SKIP_SCRIPTED_PROCESS_LAUNCH'] = '1'
163        def cleanup():
164          del os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"]
165        self.addTearDownHook(cleanup)
166
167        scripted_process_example_relpath = 'stack_core_scripted_process.py'
168        self.runCmd("command script import " + os.path.join(self.getSourceDir(),
169                                                            scripted_process_example_relpath))
170
171        corefile_process = None
172        with tempfile.NamedTemporaryFile() as file:
173            self.create_stack_skinny_corefile(file.name)
174            corefile_target = self.dbg.CreateTarget(None)
175            corefile_process = corefile_target.LoadCore(self.getBuildArtifact(file.name))
176        self.assertTrue(corefile_process, PROCESS_IS_VALID)
177
178        structured_data = lldb.SBStructuredData()
179        structured_data.SetFromJSON(json.dumps({
180            "backing_target_idx" : self.dbg.GetIndexOfTarget(corefile_process.GetTarget())
181        }))
182        launch_info = lldb.SBLaunchInfo(None)
183        launch_info.SetProcessPluginName("ScriptedProcess")
184        launch_info.SetScriptedProcessClassName("stack_core_scripted_process.StackCoreScriptedProcess")
185        launch_info.SetScriptedProcessDictionary(structured_data)
186
187        error = lldb.SBError()
188        process = target.Launch(launch_info, error)
189        self.assertTrue(error.Success(), error.GetCString())
190        self.assertTrue(process, PROCESS_IS_VALID)
191        self.assertEqual(process.GetProcessID(), 42)
192
193        self.assertEqual(process.GetNumThreads(), 3)
194        thread = process.GetThreadAtIndex(2)
195        self.assertTrue(thread, "Invalid thread.")
196        self.assertEqual(thread.GetName(), "StackCoreScriptedThread.thread-2")
197
198        self.assertEqual(thread.GetNumFrames(), 6)
199        frame = thread.GetSelectedFrame()
200        self.assertTrue(frame, "Invalid frame.")
201        self.assertIn("bar", frame.GetFunctionName())
202        self.assertEqual(int(frame.FindValue("i", lldb.eValueTypeVariableArgument).GetValue()), 42)
203        self.assertEqual(int(frame.FindValue("j", lldb.eValueTypeVariableLocal).GetValue()), 42 * 42)
204