xref: /llvm-project/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py (revision 48c36a15a363e32757c51aa69de7adf88c143c37)
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    @skipIf(archs=no_match(['arm64', 'arm64e']))
146    @skipIfOutOfTreeDebugserver
147    def test_launch_scripted_process_stack_frames(self):
148        """Test that we can launch an lldb scripted process from the command
149        line, check its process ID and read string from memory."""
150        self.build()
151        target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
152        self.assertTrue(target, VALID_TARGET)
153
154        for module in target.modules:
155            if 'a.out' in module.GetFileSpec().GetFilename():
156                main_module = module
157                break
158
159        self.assertTrue(main_module, "Invalid main module.")
160        error = target.SetModuleLoadAddress(main_module, 0)
161        self.assertTrue(error.Success(), "Reloading main module at offset 0 failed.")
162
163        os.environ['SKIP_SCRIPTED_PROCESS_LAUNCH'] = '1'
164        def cleanup():
165          del os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"]
166        self.addTearDownHook(cleanup)
167
168        scripted_process_example_relpath = 'stack_core_scripted_process.py'
169        self.runCmd("command script import " + os.path.join(self.getSourceDir(),
170                                                            scripted_process_example_relpath))
171
172        corefile_process = None
173        with tempfile.NamedTemporaryFile() as file:
174            self.create_stack_skinny_corefile(file.name)
175            corefile_target = self.dbg.CreateTarget(None)
176            corefile_process = corefile_target.LoadCore(self.getBuildArtifact(file.name))
177        self.assertTrue(corefile_process, PROCESS_IS_VALID)
178
179        structured_data = lldb.SBStructuredData()
180        structured_data.SetFromJSON(json.dumps({
181            "backing_target_idx" : self.dbg.GetIndexOfTarget(corefile_process.GetTarget())
182        }))
183        launch_info = lldb.SBLaunchInfo(None)
184        launch_info.SetProcessPluginName("ScriptedProcess")
185        launch_info.SetScriptedProcessClassName("stack_core_scripted_process.StackCoreScriptedProcess")
186        launch_info.SetScriptedProcessDictionary(structured_data)
187
188        error = lldb.SBError()
189        process = target.Launch(launch_info, error)
190        self.assertTrue(error.Success(), error.GetCString())
191        self.assertTrue(process, PROCESS_IS_VALID)
192        self.assertEqual(process.GetProcessID(), 42)
193
194        self.assertEqual(process.GetNumThreads(), 3)
195        thread = process.GetThreadAtIndex(2)
196        self.assertTrue(thread, "Invalid thread.")
197        self.assertEqual(thread.GetName(), "StackCoreScriptedThread.thread-2")
198
199        self.assertEqual(thread.GetNumFrames(), 6)
200        frame = thread.GetSelectedFrame()
201        self.assertTrue(frame, "Invalid frame.")
202        self.assertIn("bar", frame.GetFunctionName())
203        self.assertEqual(int(frame.FindValue("i", lldb.eValueTypeVariableArgument).GetValue()), 42)
204        self.assertEqual(int(frame.FindValue("j", lldb.eValueTypeVariableLocal).GetValue()), 42 * 42)
205