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