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