1from __future__ import print_function 2import lldb 3import unittest 4import os 5import json 6import stat 7import sys 8from textwrap import dedent 9import lldbsuite.test.lldbutil 10from lldbsuite.test.lldbtest import * 11from lldbsuite.test.decorators import * 12from lldbsuite.test.gdbclientutils import * 13 14 15@skipIfRemote 16@skipIfWindows 17class TestQemuLaunch(TestBase): 18 NO_DEBUG_INFO_TESTCASE = True 19 20 def set_emulator_setting(self, name, value): 21 self.runCmd("settings set -- platform.plugin.qemu-user.%s %s" % 22 (name, value)) 23 24 def setUp(self): 25 super().setUp() 26 emulator = self.getBuildArtifact("qemu.py") 27 with os.fdopen(os.open(emulator, os.O_WRONLY|os.O_CREAT, stat.S_IRWXU), 28 "w") as e: 29 30 e.write(dedent("""\ 31 #! {exec!s} 32 33 import runpy 34 import sys 35 36 sys.path = {path!r} 37 runpy.run_path({source!r}, run_name='__main__') 38 """).format(exec=sys.executable, path=sys.path, 39 source=self.getSourcePath("qemu.py"))) 40 41 self.set_emulator_setting("architecture", self.getArchitecture()) 42 self.set_emulator_setting("emulator-path", emulator) 43 44 def _create_target(self): 45 self.build() 46 exe = self.getBuildArtifact() 47 48 # Create a target using our platform 49 error = lldb.SBError() 50 target = self.dbg.CreateTarget(exe, '', 'qemu-user', False, error) 51 self.assertSuccess(error) 52 self.assertEqual(target.GetPlatform().GetName(), "qemu-user") 53 return target 54 55 def _run_and_get_state(self, target=None, info=None): 56 if target is None: 57 target = self._create_target() 58 59 if info is None: 60 info = target.GetLaunchInfo() 61 62 # "Launch" the process. Our fake qemu implementation will pretend it 63 # immediately exited. 64 info.SetArguments(["dump:" + self.getBuildArtifact("state.log")], True) 65 error = lldb.SBError() 66 process = target.Launch(info, error) 67 self.assertSuccess(error) 68 self.assertIsNotNone(process) 69 self.assertState(process.GetState(), lldb.eStateExited) 70 self.assertEqual(process.GetExitStatus(), 0x47) 71 72 # Verify the qemu invocation parameters. 73 with open(self.getBuildArtifact("state.log")) as s: 74 return json.load(s) 75 76 def test_basic_launch(self): 77 state = self._run_and_get_state() 78 79 self.assertEqual(state["program"], self.getBuildArtifact()) 80 self.assertEqual(state["args"], 81 ["dump:" + self.getBuildArtifact("state.log")]) 82 83 def test_stdio_pty(self): 84 target = self._create_target() 85 86 info = target.GetLaunchInfo() 87 info.SetArguments([ 88 "stdin:stdin", 89 "stdout:STDOUT CONTENT\n", 90 "stderr:STDERR CONTENT\n", 91 "dump:" + self.getBuildArtifact("state.log"), 92 ], False) 93 94 listener = lldb.SBListener("test_stdio") 95 info.SetListener(listener) 96 97 self.dbg.SetAsync(True) 98 error = lldb.SBError() 99 process = target.Launch(info, error) 100 self.assertSuccess(error) 101 lldbutil.expect_state_changes(self, listener, process, 102 [lldb.eStateRunning]) 103 104 process.PutSTDIN("STDIN CONTENT\n") 105 106 lldbutil.expect_state_changes(self, listener, process, 107 [lldb.eStateExited]) 108 109 # Echoed stdin, stdout and stderr. With a pty we cannot split standard 110 # output and error. 111 self.assertEqual(process.GetSTDOUT(1000), 112 "STDIN CONTENT\r\nSTDOUT CONTENT\r\nSTDERR CONTENT\r\n") 113 with open(self.getBuildArtifact("state.log")) as s: 114 state = json.load(s) 115 self.assertEqual(state["stdin"], "STDIN CONTENT\n") 116 117 def test_stdio_redirect(self): 118 self.build() 119 exe = self.getBuildArtifact() 120 121 # Create a target using our platform 122 error = lldb.SBError() 123 target = self.dbg.CreateTarget(exe, '', 'qemu-user', False, error) 124 self.assertSuccess(error) 125 126 info = lldb.SBLaunchInfo([ 127 "stdin:stdin", 128 "stdout:STDOUT CONTENT", 129 "stderr:STDERR CONTENT", 130 "dump:" + self.getBuildArtifact("state.log"), 131 ]) 132 133 info.AddOpenFileAction(0, self.getBuildArtifact("stdin.txt"), 134 True, False) 135 info.AddOpenFileAction(1, self.getBuildArtifact("stdout.txt"), 136 False, True) 137 info.AddOpenFileAction(2, self.getBuildArtifact("stderr.txt"), 138 False, True) 139 140 with open(self.getBuildArtifact("stdin.txt"), "w") as f: 141 f.write("STDIN CONTENT") 142 143 process = target.Launch(info, error) 144 self.assertSuccess(error) 145 self.assertState(process.GetState(), lldb.eStateExited) 146 147 with open(self.getBuildArtifact("stdout.txt")) as f: 148 self.assertEqual(f.read(), "STDOUT CONTENT") 149 with open(self.getBuildArtifact("stderr.txt")) as f: 150 self.assertEqual(f.read(), "STDERR CONTENT") 151 with open(self.getBuildArtifact("state.log")) as s: 152 state = json.load(s) 153 self.assertEqual(state["stdin"], "STDIN CONTENT") 154 155 def test_find_in_PATH(self): 156 emulator = self.getBuildArtifact("qemu-" + self.getArchitecture()) 157 os.rename(self.getBuildArtifact("qemu.py"), emulator) 158 self.set_emulator_setting("emulator-path", "''") 159 160 original_path = os.environ["PATH"] 161 os.environ["PATH"] = (self.getBuildDir() + 162 self.platformContext.shlib_path_separator + original_path) 163 def cleanup(): 164 os.environ["PATH"] = original_path 165 166 self.addTearDownHook(cleanup) 167 state = self._run_and_get_state() 168 169 self.assertEqual(state["program"], self.getBuildArtifact()) 170 self.assertEqual(state["args"], 171 ["dump:" + self.getBuildArtifact("state.log")]) 172 173 def test_bad_emulator_path(self): 174 self.set_emulator_setting("emulator-path", 175 self.getBuildArtifact("nonexistent.file")) 176 177 target = self._create_target() 178 info = lldb.SBLaunchInfo([]) 179 error = lldb.SBError() 180 target.Launch(info, error) 181 self.assertTrue(error.Fail()) 182 self.assertIn("doesn't exist", error.GetCString()) 183 184 def test_extra_args(self): 185 self.set_emulator_setting("emulator-args", "-fake-arg fake-value") 186 state = self._run_and_get_state() 187 188 self.assertEqual(state["fake-arg"], "fake-value") 189 190 def test_env_vars(self): 191 # First clear any global environment to have a clean slate for this test 192 self.runCmd("settings clear target.env-vars") 193 self.runCmd("settings clear target.unset-env-vars") 194 195 def var(i): 196 return "LLDB_TEST_QEMU_VAR%d" % i 197 198 # Set some variables in the host environment. 199 for i in range(4): 200 os.environ[var(i)]="from host" 201 def cleanup(): 202 for i in range(4): 203 del os.environ[var(i)] 204 self.addTearDownHook(cleanup) 205 206 # Set some emulator-only variables. 207 self.set_emulator_setting("emulator-env-vars", 208 "%s='emulator only'"%var(4)) 209 210 # And through the platform setting. 211 self.set_emulator_setting("target-env-vars", 212 "%s='from platform' %s='from platform'" % (var(1), var(2))) 213 214 target = self._create_target() 215 info = target.GetLaunchInfo() 216 env = info.GetEnvironment() 217 218 # Platform settings should trump host values. Emulator-only variables 219 # should not be visible. 220 self.assertEqual(env.Get(var(0)), "from host") 221 self.assertEqual(env.Get(var(1)), "from platform") 222 self.assertEqual(env.Get(var(2)), "from platform") 223 self.assertEqual(env.Get(var(3)), "from host") 224 self.assertIsNone(env.Get(var(4))) 225 226 # Finally, make some launch_info specific changes. 227 env.Set(var(2), "from target", True) 228 env.Unset(var(3)) 229 info.SetEnvironment(env, False) 230 231 # Now check everything. Launch info changes should trump everything, but 232 # only for the target environment -- the emulator should still get the 233 # host values. 234 state = self._run_and_get_state(target, info) 235 for i in range(4): 236 self.assertEqual(state["environ"][var(i)], "from host") 237 self.assertEqual(state["environ"][var(4)], "emulator only") 238 self.assertEqual(state["environ"]["QEMU_SET_ENV"], 239 "%s=from platform,%s=from target" % (var(1), var(2))) 240 self.assertEqual(state["environ"]["QEMU_UNSET_ENV"], 241 "%s,%s,QEMU_SET_ENV,QEMU_UNSET_ENV" % (var(3), var(4))) 242 243 def test_arg0(self): 244 target = self._create_target() 245 self.runCmd("settings set target.arg0 ARG0") 246 state = self._run_and_get_state(target) 247 248 self.assertEqual(state["program"], self.getBuildArtifact()) 249 self.assertEqual(state["0"], "ARG0") 250 251 def test_sysroot(self): 252 sysroot = self.getBuildArtifact("sysroot") 253 self.runCmd("platform select qemu-user --sysroot %s" % sysroot) 254 state = self._run_and_get_state() 255 self.assertEqual(state["environ"]["QEMU_LD_PREFIX"], sysroot) 256 self.assertIn("QEMU_LD_PREFIX", 257 state["environ"]["QEMU_UNSET_ENV"].split(",")) 258