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 self.runCmd( 191 "target stop-hook add -k first -v 1 -k second -v 2 -P dummy_scripted_process.DummyStopHook" 192 ) 193 194 launch_info = lldb.SBLaunchInfo(None) 195 launch_info.SetProcessPluginName("ScriptedProcess") 196 launch_info.SetScriptedProcessClassName( 197 "dummy_scripted_process.DummyScriptedProcess" 198 ) 199 200 error = lldb.SBError() 201 process_0 = target_0.Launch(launch_info, error) 202 self.assertTrue(process_0 and process_0.IsValid(), PROCESS_IS_VALID) 203 self.assertEqual(process_0.GetProcessID(), 42) 204 self.assertEqual(process_0.GetNumThreads(), 1) 205 206 py_impl = process_0.GetScriptedImplementation() 207 self.assertTrue(py_impl) 208 self.assertIsInstance(py_impl, dummy_scripted_process.DummyScriptedProcess) 209 self.assertFalse(hasattr(py_impl, "my_super_secret_member")) 210 py_impl.my_super_secret_member = 42 211 self.assertTrue(hasattr(py_impl, "my_super_secret_member")) 212 self.assertEqual(py_impl.my_super_secret_method(), 42) 213 214 self.assertTrue(hasattr(py_impl, "handled_stop")) 215 self.assertTrue(py_impl.handled_stop) 216 217 # Try reading from target #0 process ... 218 addr = 0x500000000 219 message = "Hello, target 0" 220 buff = process_0.ReadCStringFromMemory(addr, len(message) + 1, error) 221 self.assertSuccess(error) 222 self.assertEqual(buff, message) 223 224 target_1 = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) 225 self.assertTrue(target_1, VALID_TARGET) 226 227 # We still need to specify a PID when attaching even for scripted processes 228 attach_info = lldb.SBAttachInfo(42) 229 attach_info.SetProcessPluginName("ScriptedProcess") 230 attach_info.SetScriptedProcessClassName( 231 "dummy_scripted_process.DummyScriptedProcess" 232 ) 233 234 error = lldb.SBError() 235 process_1 = target_1.Attach(attach_info, error) 236 self.assertTrue(process_1 and process_1.IsValid(), PROCESS_IS_VALID) 237 self.assertEqual(process_1.GetProcessID(), 42) 238 self.assertEqual(process_1.GetNumThreads(), 1) 239 240 # ... then try reading from target #1 process ... 241 message = "Hello, target 1" 242 buff = process_1.ReadCStringFromMemory(addr, len(message) + 1, error) 243 self.assertSuccess(error) 244 self.assertEqual(buff, message) 245 246 # ... now, reading again from target #0 process to make sure the call 247 # gets dispatched to the right target. 248 message = "Hello, target 0" 249 buff = process_0.ReadCStringFromMemory(addr, len(message) + 1, error) 250 self.assertSuccess(error) 251 self.assertEqual(buff, message) 252 253 # Let's write some memory. 254 message = "Hello, world!" 255 bytes_written = process_0.WriteMemoryAsCString(addr, message, error) 256 self.assertSuccess(error) 257 self.assertEqual(bytes_written, len(message) + 1) 258 259 # ... and check if that memory was saved properly. 260 buff = process_0.ReadCStringFromMemory(addr, len(message) + 1, error) 261 self.assertSuccess(error) 262 self.assertEqual(buff, message) 263 264 thread = process_0.GetSelectedThread() 265 self.assertTrue(thread, "Invalid thread.") 266 self.assertEqual(thread.GetThreadID(), 0x19) 267 self.assertEqual(thread.GetName(), "DummyScriptedThread.thread-1") 268 self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonTrace) 269 270 self.assertGreater(thread.GetNumFrames(), 0) 271 272 frame = thread.GetFrameAtIndex(0) 273 GPRs = None 274 register_set = frame.registers # Returns an SBValueList. 275 for regs in register_set: 276 if "general purpose" in regs.name.lower(): 277 GPRs = regs 278 break 279 280 self.assertTrue(GPRs, "Invalid General Purpose Registers Set") 281 self.assertGreater(GPRs.GetNumChildren(), 0) 282 for idx, reg in enumerate(GPRs, start=1): 283 if idx > 21: 284 break 285 self.assertEqual(idx, int(reg.value, 16)) 286 287 self.assertTrue(frame.IsArtificial(), "Frame is not artificial") 288 pc = frame.GetPCAddress().GetLoadAddress(target_0) 289 self.assertEqual(pc, 0x0100001B00) 290