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.assertIsInstance(py_impl, dummy_scripted_process.DummyScriptedProcess) 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