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 def test_missing_methods_scripted_register_context(self): 64 """Test that we only instanciate scripted processes if they implement 65 all the required abstract methods.""" 66 self.build() 67 68 os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"] = "1" 69 70 def cleanup(): 71 del os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"] 72 73 self.addTearDownHook(cleanup) 74 75 target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) 76 self.assertTrue(target, VALID_TARGET) 77 log_file = self.getBuildArtifact("script.log") 78 self.runCmd("log enable lldb script -f " + log_file) 79 self.assertTrue(os.path.isfile(log_file)) 80 script_path = os.path.join( 81 self.getSourceDir(), "missing_methods_scripted_process.py" 82 ) 83 self.runCmd("command script import " + script_path) 84 85 launch_info = lldb.SBLaunchInfo(None) 86 launch_info.SetProcessPluginName("ScriptedProcess") 87 launch_info.SetScriptedProcessClassName( 88 "missing_methods_scripted_process.MissingMethodsScriptedProcess" 89 ) 90 error = lldb.SBError() 91 92 process = target.Launch(launch_info, error) 93 94 self.assertFailure(error) 95 96 with open(log_file, "r") as f: 97 log = f.read() 98 99 self.assertIn( 100 "Abstract method MissingMethodsScriptedProcess.read_memory_at_address not implemented", 101 log, 102 ) 103 self.assertIn( 104 "Abstract method MissingMethodsScriptedProcess.is_alive not implemented", 105 log, 106 ) 107 self.assertIn( 108 "Abstract method MissingMethodsScriptedProcess.get_scripted_thread_plugin not implemented", 109 log, 110 ) 111 112 @skipUnlessDarwin 113 def test_invalid_scripted_register_context(self): 114 """Test that we can launch an lldb scripted process with an invalid 115 Scripted Thread, with invalid register context.""" 116 self.build() 117 118 os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"] = "1" 119 120 def cleanup(): 121 del os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"] 122 123 self.addTearDownHook(cleanup) 124 125 self.runCmd("settings set target.load-script-from-symbol-file true") 126 self.move_blueprint_to_dsym("invalid_scripted_process.py") 127 target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) 128 self.assertTrue(target, VALID_TARGET) 129 log_file = self.getBuildArtifact("thread.log") 130 self.runCmd("log enable lldb thread -f " + log_file) 131 self.assertTrue(os.path.isfile(log_file)) 132 133 launch_info = lldb.SBLaunchInfo(None) 134 launch_info.SetProcessPluginName("ScriptedProcess") 135 launch_info.SetScriptedProcessClassName("a_out.InvalidScriptedProcess") 136 error = lldb.SBError() 137 138 process = target.Launch(launch_info, error) 139 140 self.assertSuccess(error) 141 self.assertTrue(process, PROCESS_IS_VALID) 142 self.assertEqual(process.GetProcessID(), 666) 143 self.assertEqual(process.GetNumThreads(), 0) 144 145 impl = process.GetScriptedImplementation() 146 self.assertTrue(impl) 147 impl = process.GetScriptedImplementation() 148 self.assertTrue(impl) 149 impl = process.GetScriptedImplementation() 150 self.assertTrue(impl) 151 impl = process.GetScriptedImplementation() 152 self.assertTrue(impl) 153 154 addr = 0x500000000 155 buff = process.ReadMemory(addr, 4, error) 156 self.assertEqual(buff, None) 157 self.assertTrue(error.Fail()) 158 self.assertEqual(error.GetCString(), "This is an invalid scripted process!") 159 160 with open(log_file, "r") as f: 161 log = f.read() 162 163 self.assertIn("Failed to get scripted thread registers data.", log) 164 165 @skipUnlessDarwin 166 def test_scripted_process_and_scripted_thread(self): 167 """Test that we can launch an lldb scripted process using the SBAPI, 168 check its process ID, read string from memory, check scripted thread 169 id, name stop reason and register context. 170 """ 171 self.build() 172 target_0 = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) 173 self.assertTrue(target_0, VALID_TARGET) 174 175 os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"] = "1" 176 177 def cleanup(): 178 del os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"] 179 180 self.addTearDownHook(cleanup) 181 182 scripted_process_example_relpath = "dummy_scripted_process.py" 183 self.runCmd( 184 "command script import " 185 + os.path.join(self.getSourceDir(), scripted_process_example_relpath) 186 ) 187 188 launch_info = lldb.SBLaunchInfo(None) 189 launch_info.SetProcessPluginName("ScriptedProcess") 190 launch_info.SetScriptedProcessClassName( 191 "dummy_scripted_process.DummyScriptedProcess" 192 ) 193 194 error = lldb.SBError() 195 process_0 = target_0.Launch(launch_info, error) 196 self.assertTrue(process_0 and process_0.IsValid(), PROCESS_IS_VALID) 197 self.assertEqual(process_0.GetProcessID(), 42) 198 self.assertEqual(process_0.GetNumThreads(), 1) 199 200 py_impl = process_0.GetScriptedImplementation() 201 self.assertTrue(py_impl) 202 self.assertTrue( 203 isinstance(py_impl, dummy_scripted_process.DummyScriptedProcess) 204 ) 205 self.assertFalse(hasattr(py_impl, "my_super_secret_member")) 206 py_impl.my_super_secret_member = 42 207 self.assertTrue(hasattr(py_impl, "my_super_secret_member")) 208 self.assertEqual(py_impl.my_super_secret_method(), 42) 209 210 # Try reading from target #0 process ... 211 addr = 0x500000000 212 message = "Hello, target 0" 213 buff = process_0.ReadCStringFromMemory(addr, len(message) + 1, error) 214 self.assertSuccess(error) 215 self.assertEqual(buff, message) 216 217 target_1 = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) 218 self.assertTrue(target_1, VALID_TARGET) 219 220 # We still need to specify a PID when attaching even for scripted processes 221 attach_info = lldb.SBAttachInfo(42) 222 attach_info.SetProcessPluginName("ScriptedProcess") 223 attach_info.SetScriptedProcessClassName( 224 "dummy_scripted_process.DummyScriptedProcess" 225 ) 226 227 error = lldb.SBError() 228 process_1 = target_1.Attach(attach_info, error) 229 self.assertTrue(process_1 and process_1.IsValid(), PROCESS_IS_VALID) 230 self.assertEqual(process_1.GetProcessID(), 42) 231 self.assertEqual(process_1.GetNumThreads(), 1) 232 233 # ... then try reading from target #1 process ... 234 message = "Hello, target 1" 235 buff = process_1.ReadCStringFromMemory(addr, len(message) + 1, error) 236 self.assertSuccess(error) 237 self.assertEqual(buff, message) 238 239 # ... now, reading again from target #0 process to make sure the call 240 # gets dispatched to the right target. 241 message = "Hello, target 0" 242 buff = process_0.ReadCStringFromMemory(addr, len(message) + 1, error) 243 self.assertSuccess(error) 244 self.assertEqual(buff, message) 245 246 # Let's write some memory. 247 message = "Hello, world!" 248 bytes_written = process_0.WriteMemoryAsCString(addr, message, error) 249 self.assertSuccess(error) 250 self.assertEqual(bytes_written, len(message) + 1) 251 252 # ... and check if that memory was saved properly. 253 buff = process_0.ReadCStringFromMemory(addr, len(message) + 1, error) 254 self.assertSuccess(error) 255 self.assertEqual(buff, message) 256 257 thread = process_0.GetSelectedThread() 258 self.assertTrue(thread, "Invalid thread.") 259 self.assertEqual(thread.GetThreadID(), 0x19) 260 self.assertEqual(thread.GetName(), "DummyScriptedThread.thread-1") 261 self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonTrace) 262 263 self.assertGreater(thread.GetNumFrames(), 0) 264 265 frame = thread.GetFrameAtIndex(0) 266 GPRs = None 267 register_set = frame.registers # Returns an SBValueList. 268 for regs in register_set: 269 if "general purpose" in regs.name.lower(): 270 GPRs = regs 271 break 272 273 self.assertTrue(GPRs, "Invalid General Purpose Registers Set") 274 self.assertGreater(GPRs.GetNumChildren(), 0) 275 for idx, reg in enumerate(GPRs, start=1): 276 if idx > 21: 277 break 278 self.assertEqual(idx, int(reg.value, 16)) 279 280 self.assertTrue(frame.IsArtificial(), "Frame is not artificial") 281 pc = frame.GetPCAddress().GetLoadAddress(target_0) 282 self.assertEqual(pc, 0x0100001B00) 283