| import lldb |
| import unittest |
| import os |
| import json |
| import stat |
| import sys |
| from textwrap import dedent |
| import lldbsuite.test.lldbutil |
| from lldbsuite.test.lldbtest import * |
| from lldbsuite.test.decorators import * |
| from lldbsuite.test.gdbclientutils import * |
| |
| |
| @skipIfRemote |
| @skipIfWindows |
| class TestQemuLaunch(TestBase): |
| NO_DEBUG_INFO_TESTCASE = True |
| |
| def set_emulator_setting(self, name, value): |
| self.runCmd("settings set -- platform.plugin.qemu-user.%s %s" % (name, value)) |
| |
| def setUp(self): |
| super().setUp() |
| emulator = self.getBuildArtifact("qemu.py") |
| with os.fdopen( |
| os.open(emulator, os.O_WRONLY | os.O_CREAT, stat.S_IRWXU), "w" |
| ) as e: |
| e.write( |
| dedent( |
| """\ |
| #! {exec!s} |
| |
| import runpy |
| import sys |
| |
| sys.path = {path!r} |
| runpy.run_path({source!r}, run_name='__main__') |
| """ |
| ).format( |
| exec=sys.executable, |
| path=sys.path, |
| source=self.getSourcePath("qemu.py"), |
| ) |
| ) |
| |
| self.set_emulator_setting("architecture", self.getArchitecture()) |
| self.set_emulator_setting("emulator-path", emulator) |
| |
| def _create_target(self): |
| self.build() |
| exe = self.getBuildArtifact() |
| |
| # Create a target using our platform |
| error = lldb.SBError() |
| target = self.dbg.CreateTarget(exe, "", "qemu-user", False, error) |
| self.assertSuccess(error) |
| self.assertEqual(target.GetPlatform().GetName(), "qemu-user") |
| return target |
| |
| def _run_and_get_state(self, target=None, info=None): |
| if target is None: |
| target = self._create_target() |
| |
| if info is None: |
| info = target.GetLaunchInfo() |
| |
| # "Launch" the process. Our fake qemu implementation will pretend it |
| # immediately exited. |
| info.SetArguments(["dump:" + self.getBuildArtifact("state.log")], True) |
| error = lldb.SBError() |
| process = target.Launch(info, error) |
| self.assertSuccess(error) |
| self.assertIsNotNone(process) |
| self.assertState(process.GetState(), lldb.eStateExited) |
| self.assertEqual(process.GetExitStatus(), 0x47) |
| |
| # Verify the qemu invocation parameters. |
| with open(self.getBuildArtifact("state.log")) as s: |
| return json.load(s) |
| |
| def test_basic_launch(self): |
| state = self._run_and_get_state() |
| |
| self.assertEqual(state["program"], self.getBuildArtifact()) |
| self.assertEqual(state["args"], ["dump:" + self.getBuildArtifact("state.log")]) |
| |
| def test_stdio_pty(self): |
| target = self._create_target() |
| |
| info = target.GetLaunchInfo() |
| info.SetArguments( |
| [ |
| "stdin:stdin", |
| "stdout:STDOUT CONTENT\n", |
| "stderr:STDERR CONTENT\n", |
| "dump:" + self.getBuildArtifact("state.log"), |
| ], |
| False, |
| ) |
| |
| listener = lldb.SBListener("test_stdio") |
| info.SetListener(listener) |
| |
| self.dbg.SetAsync(True) |
| error = lldb.SBError() |
| process = target.Launch(info, error) |
| self.assertSuccess(error) |
| lldbutil.expect_state_changes(self, listener, process, [lldb.eStateRunning]) |
| |
| process.PutSTDIN("STDIN CONTENT\n") |
| |
| lldbutil.expect_state_changes(self, listener, process, [lldb.eStateExited]) |
| |
| # Echoed stdin, stdout and stderr. With a pty we cannot split standard |
| # output and error. |
| self.assertEqual( |
| process.GetSTDOUT(1000), |
| "STDIN CONTENT\r\nSTDOUT CONTENT\r\nSTDERR CONTENT\r\n", |
| ) |
| with open(self.getBuildArtifact("state.log")) as s: |
| state = json.load(s) |
| self.assertEqual(state["stdin"], "STDIN CONTENT\n") |
| |
| def test_stdio_redirect(self): |
| self.build() |
| exe = self.getBuildArtifact() |
| |
| # Create a target using our platform |
| error = lldb.SBError() |
| target = self.dbg.CreateTarget(exe, "", "qemu-user", False, error) |
| self.assertSuccess(error) |
| |
| info = lldb.SBLaunchInfo( |
| [ |
| "stdin:stdin", |
| "stdout:STDOUT CONTENT", |
| "stderr:STDERR CONTENT", |
| "dump:" + self.getBuildArtifact("state.log"), |
| ] |
| ) |
| |
| info.AddOpenFileAction(0, self.getBuildArtifact("stdin.txt"), True, False) |
| info.AddOpenFileAction(1, self.getBuildArtifact("stdout.txt"), False, True) |
| info.AddOpenFileAction(2, self.getBuildArtifact("stderr.txt"), False, True) |
| |
| with open(self.getBuildArtifact("stdin.txt"), "w") as f: |
| f.write("STDIN CONTENT") |
| |
| process = target.Launch(info, error) |
| self.assertSuccess(error) |
| self.assertState(process.GetState(), lldb.eStateExited) |
| |
| with open(self.getBuildArtifact("stdout.txt")) as f: |
| self.assertEqual(f.read(), "STDOUT CONTENT") |
| with open(self.getBuildArtifact("stderr.txt")) as f: |
| self.assertEqual(f.read(), "STDERR CONTENT") |
| with open(self.getBuildArtifact("state.log")) as s: |
| state = json.load(s) |
| self.assertEqual(state["stdin"], "STDIN CONTENT") |
| |
| def test_find_in_PATH(self): |
| emulator = self.getBuildArtifact("qemu-" + self.getArchitecture()) |
| os.rename(self.getBuildArtifact("qemu.py"), emulator) |
| self.set_emulator_setting("emulator-path", "''") |
| |
| original_path = os.environ["PATH"] |
| os.environ["PATH"] = ( |
| self.getBuildDir() |
| + self.platformContext.shlib_path_separator |
| + original_path |
| ) |
| |
| def cleanup(): |
| os.environ["PATH"] = original_path |
| |
| self.addTearDownHook(cleanup) |
| state = self._run_and_get_state() |
| |
| self.assertEqual(state["program"], self.getBuildArtifact()) |
| self.assertEqual(state["args"], ["dump:" + self.getBuildArtifact("state.log")]) |
| |
| def test_bad_emulator_path(self): |
| self.set_emulator_setting( |
| "emulator-path", self.getBuildArtifact("nonexistent.file") |
| ) |
| |
| target = self._create_target() |
| info = lldb.SBLaunchInfo([]) |
| error = lldb.SBError() |
| target.Launch(info, error) |
| self.assertTrue(error.Fail()) |
| self.assertIn("doesn't exist", error.GetCString()) |
| |
| def test_extra_args(self): |
| self.set_emulator_setting("emulator-args", "-fake-arg fake-value") |
| state = self._run_and_get_state() |
| |
| self.assertEqual(state["fake-arg"], "fake-value") |
| |
| def test_env_vars(self): |
| # First clear any global environment to have a clean slate for this test |
| self.runCmd("settings clear target.env-vars") |
| self.runCmd("settings clear target.unset-env-vars") |
| |
| def var(i): |
| return "LLDB_TEST_QEMU_VAR%d" % i |
| |
| # Set some variables in the host environment. |
| for i in range(4): |
| os.environ[var(i)] = "from host" |
| |
| def cleanup(): |
| for i in range(4): |
| del os.environ[var(i)] |
| |
| self.addTearDownHook(cleanup) |
| |
| # Set some emulator-only variables. |
| self.set_emulator_setting("emulator-env-vars", "%s='emulator only'" % var(4)) |
| |
| # And through the platform setting. |
| self.set_emulator_setting( |
| "target-env-vars", |
| "%s='from platform' %s='from platform'" % (var(1), var(2)), |
| ) |
| |
| target = self._create_target() |
| info = target.GetLaunchInfo() |
| env = info.GetEnvironment() |
| |
| # Platform settings should trump host values. Emulator-only variables |
| # should not be visible. |
| self.assertEqual(env.Get(var(0)), "from host") |
| self.assertEqual(env.Get(var(1)), "from platform") |
| self.assertEqual(env.Get(var(2)), "from platform") |
| self.assertEqual(env.Get(var(3)), "from host") |
| self.assertIsNone(env.Get(var(4))) |
| |
| # Finally, make some launch_info specific changes. |
| env.Set(var(2), "from target", True) |
| env.Unset(var(3)) |
| info.SetEnvironment(env, False) |
| |
| # Now check everything. Launch info changes should trump everything, but |
| # only for the target environment -- the emulator should still get the |
| # host values. |
| state = self._run_and_get_state(target, info) |
| for i in range(4): |
| self.assertEqual(state["environ"][var(i)], "from host") |
| self.assertEqual(state["environ"][var(4)], "emulator only") |
| self.assertEqual( |
| state["environ"]["QEMU_SET_ENV"], |
| "%s=from platform,%s=from target" % (var(1), var(2)), |
| ) |
| self.assertEqual( |
| state["environ"]["QEMU_UNSET_ENV"], |
| "%s,%s,QEMU_SET_ENV,QEMU_UNSET_ENV" % (var(3), var(4)), |
| ) |
| |
| def test_arg0(self): |
| target = self._create_target() |
| self.runCmd("settings set target.arg0 ARG0") |
| state = self._run_and_get_state(target) |
| |
| self.assertEqual(state["program"], self.getBuildArtifact()) |
| self.assertEqual(state["0"], "ARG0") |
| |
| def test_sysroot(self): |
| sysroot = self.getBuildArtifact("sysroot") |
| self.runCmd("platform select qemu-user --sysroot %s" % sysroot) |
| state = self._run_and_get_state() |
| self.assertEqual(state["environ"]["QEMU_LD_PREFIX"], sysroot) |
| self.assertIn("QEMU_LD_PREFIX", state["environ"]["QEMU_UNSET_ENV"].split(",")) |