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