labath updated this revision to Diff 390722.
labath added a comment.
fix typo
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D114796/new/
https://reviews.llvm.org/D114796
Files:
lldb/source/Host/common/ProcessLaunchInfo.cpp
lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.cpp
lldb/source/Target/Target.cpp
lldb/test/API/qemu/TestQemuLaunch.py
lldb/test/API/qemu/qemu.py
Index: lldb/test/API/qemu/qemu.py
===================================================================
--- lldb/test/API/qemu/qemu.py
+++ lldb/test/API/qemu/qemu.py
@@ -1,36 +1,63 @@
-from textwrap import dedent
import argparse
import socket
import json
+import sys
import use_lldb_suite
from lldbsuite.test.gdbclientutils import *
+_description = """\
+Implements a fake qemu for testing purposes. The executable program
+is not actually run. Instead a very basic mock process is presented
+to lldb. This allows us to check the invocation parameters.
+
+The behavior of the emulated "process" is controlled via its command line
+arguments, which should take the form of key:value pairs. Currently supported
+actions are:
+- dump: Dump the state of the emulator as a json dictionary. <value> specifies
+ the target filename.
+- stdout: Write <value> to program stdout.
+- stderr: Write <value> to program stderr.
+- stdin: Read a line from stdin and store it in the emulator state. <value>
+ specifies the dictionary key.
+"""
+
class MyResponder(MockGDBServerResponder):
+ def __init__(self, state):
+ super().__init__()
+ self._state = state
+
def cont(self):
+ for a in self._state["args"]:
+ action, data = a.split(":", 1)
+ if action == "dump":
+ with open(data, "w") as f:
+ json.dump(self._state, f)
+ elif action == "stdout":
+ sys.stdout.write(data)
+ elif action == "stderr":
+ sys.stderr.write(data)
+ elif action == "stdin":
+ self._state[data] = sys.stdin.readline()
+ else:
+ print("Unknown action: %r\n" % a)
+ return "X01"
return "W47"
class FakeEmulator(MockGDBServer):
- def __init__(self, addr):
+ def __init__(self, addr, state):
super().__init__(UnixServerSocket(addr))
- self.responder = MyResponder()
+ self.responder = MyResponder(state)
def main():
- parser = argparse.ArgumentParser(description=dedent("""\
- Implements a fake qemu for testing purposes. The executable program
- is not actually run. Instead a very basic mock process is presented
- to lldb. The emulated program must accept at least one argument.
- This should be a path where the emulator will dump its state. This
- allows us to check the invocation parameters.
- """))
+ parser = argparse.ArgumentParser(description=_description,
+ formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('-g', metavar="unix-socket", required=True)
parser.add_argument('program', help="The program to 'emulate'.")
- parser.add_argument('state_file', help="Where to dump the emulator state.")
- parsed, rest = parser.parse_known_args()
- with open(parsed.state_file, "w") as f:
- json.dump({"program":parsed.program, "rest":rest}, f)
+ parser.add_argument("args", nargs=argparse.REMAINDER)
+ args = parser.parse_args()
- emulator = FakeEmulator(parsed.g)
+ emulator = FakeEmulator(args.g, vars(args))
emulator.run()
if __name__ == "__main__":
Index: lldb/test/API/qemu/TestQemuLaunch.py
===================================================================
--- lldb/test/API/qemu/TestQemuLaunch.py
+++ lldb/test/API/qemu/TestQemuLaunch.py
@@ -6,6 +6,7 @@
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 *
@@ -46,7 +47,7 @@
self.build()
exe = self.getBuildArtifact()
- # Create a target using out platform
+ # Create a target using our platform
error = lldb.SBError()
target = self.dbg.CreateTarget(exe, '', 'qemu-user', False, error)
self.assertSuccess(error)
@@ -55,7 +56,7 @@
# "Launch" the process. Our fake qemu implementation will pretend it
# immediately exited.
process = target.LaunchSimple(
- [self.getBuildArtifact("state.log"), "arg2", "arg3"], None, None)
+ ["dump:" + self.getBuildArtifact("state.log")], None, None)
self.assertIsNotNone(process)
self.assertEqual(process.GetState(), lldb.eStateExited)
self.assertEqual(process.GetExitStatus(), 0x47)
@@ -64,7 +65,84 @@
with open(self.getBuildArtifact("state.log")) as s:
state = json.load(s)
self.assertEqual(state["program"], self.getBuildArtifact())
- self.assertEqual(state["rest"], ["arg2", "arg3"])
+ self.assertEqual(state["args"],
+ ["dump:" + self.getBuildArtifact("state.log")])
+
+ def test_stdio_pty(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\n",
+ "stderr:STDERR CONTENT\n",
+ "dump:" + self.getBuildArtifact("state.log"),
+ ])
+
+ listener = lldb.SBListener("test_stdio")
+ info.SetListener(listener)
+
+ self.dbg.SetAsync(True)
+ 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.assertEqual(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_bad_emulator_path(self):
self.set_emulator_setting("emulator-path",
Index: lldb/source/Target/Target.cpp
===================================================================
--- lldb/source/Target/Target.cpp
+++ lldb/source/Target/Target.cpp
@@ -3321,8 +3321,7 @@
err_file_spec);
}
- if (default_to_use_pty &&
- (!in_file_spec || !out_file_spec || !err_file_spec)) {
+ if (default_to_use_pty) {
llvm::Error Err = info.SetUpPtyRedirection();
LLDB_LOG_ERROR(log, std::move(Err), "SetUpPtyRedirection failed: {0}");
}
Index: lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.cpp
===================================================================
--- lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.cpp
+++ lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.cpp
@@ -126,6 +126,11 @@
launch_info.SetMonitorProcessCallback(ProcessLaunchInfo::NoOpMonitorCallback,
false);
+ // This is automatically done for host platform in
+ // Target::FinalizeFileActions, but we're not a host platform.
+ llvm::Error Err = launch_info.SetUpPtyRedirection();
+ LLDB_LOG_ERROR(log, std::move(Err), "SetUpPtyRedirection failed: {0}");
+
error = Host::LaunchProcess(launch_info);
if (error.Fail())
return nullptr;
@@ -134,6 +139,7 @@
launch_info.GetListener(),
process_gdb_remote::ProcessGDBRemote::GetPluginNameStatic(), nullptr,
true);
+
ListenerSP listener_sp =
Listener::MakeListener("lldb.platform_qemu_user.debugprocess");
launch_info.SetHijackListener(listener_sp);
@@ -143,6 +149,11 @@
if (error.Fail())
return nullptr;
+ if (launch_info.GetPTY().GetPrimaryFileDescriptor() !=
+ PseudoTerminal::invalid_fd)
+ process_sp->SetSTDIOFileDescriptor(
+ launch_info.GetPTY().ReleasePrimaryFileDescriptor());
+
process_sp->WaitForProcessToStop(llvm::None, nullptr, false, listener_sp);
return process_sp;
}
Index: lldb/source/Host/common/ProcessLaunchInfo.cpp
===================================================================
--- lldb/source/Host/common/ProcessLaunchInfo.cpp
+++ lldb/source/Host/common/ProcessLaunchInfo.cpp
@@ -212,6 +212,14 @@
llvm::Error ProcessLaunchInfo::SetUpPtyRedirection() {
Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS);
+
+ bool stdin_free = GetFileActionForFD(STDIN_FILENO) == nullptr;
+ bool stdout_free = GetFileActionForFD(STDOUT_FILENO) == nullptr;
+ bool stderr_free = GetFileActionForFD(STDERR_FILENO) == nullptr;
+ bool any_free = stdin_free || stdout_free || stderr_free;
+ if (!any_free)
+ return llvm::Error::success();
+
LLDB_LOG(log, "Generating a pty to use for stdin/out/err");
int open_flags = O_RDWR | O_NOCTTY;
@@ -226,19 +234,13 @@
const FileSpec secondary_file_spec(m_pty->GetSecondaryName());
- // Only use the secondary tty if we don't have anything specified for
- // input and don't have an action for stdin
- if (GetFileActionForFD(STDIN_FILENO) == nullptr)
+ if (stdin_free)
AppendOpenFileAction(STDIN_FILENO, secondary_file_spec, true, false);
- // Only use the secondary tty if we don't have anything specified for
- // output and don't have an action for stdout
- if (GetFileActionForFD(STDOUT_FILENO) == nullptr)
+ if (stdout_free)
AppendOpenFileAction(STDOUT_FILENO, secondary_file_spec, false, true);
- // Only use the secondary tty if we don't have anything specified for
- // error and don't have an action for stderr
- if (GetFileActionForFD(STDERR_FILENO) == nullptr)
+ if (stderr_free)
AppendOpenFileAction(STDERR_FILENO, secondary_file_spec, false, true);
return llvm::Error::success();
}
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits