Add support for SBProcess::PutSTDIN to remote processes

Processes running on a remote target can already send $O messages
to send stdout but there is no way to send stdin to a remote
inferior.

This allows processes using the API to pump stdin into a remote
inferior process.

It fixes a hang in TestProcessIO.py when running against a remote
target.

llvm-svn: 228419
This commit is contained in:
Vince Harron 2015-02-06 18:32:57 +00:00
parent 7206d7a5d2
commit e0be425a53
9 changed files with 91 additions and 0 deletions

View File

@ -3180,6 +3180,7 @@ protected:
lldb::IOHandlerSP m_process_input_reader;
Communication m_stdio_communication;
Mutex m_stdio_communication_mutex;
bool m_stdio_disable; /// Remember process launch setting
std::string m_stdout_data;
std::string m_stderr_data;
Mutex m_profile_data_comm_mutex;

View File

@ -1866,6 +1866,21 @@ GDBRemoteCommunicationClient::SendAttach
return -1;
}
int
GDBRemoteCommunicationClient::SendStdinNotification (const char* data, size_t data_len)
{
StreamString packet;
packet.PutCString("I");
packet.PutBytesAsRawHex8(data, data_len);
StringExtractorGDBRemote response;
if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success)
{
return 0;
}
return response.GetError();
}
const lldb_private::ArchSpec &
GDBRemoteCommunicationClient::GetHostArchitecture ()
{

View File

@ -174,6 +174,23 @@ public:
StringExtractorGDBRemote& response);
//------------------------------------------------------------------
/// Sends a GDB remote protocol 'I' packet that delivers stdin
/// data to the remote process.
///
/// @param[in] data
/// A pointer to stdin data.
///
/// @param[in] data_len
/// The number of bytes available at \a data.
///
/// @return
/// Zero if the attach was successful, or an error indicating
/// an error code.
//------------------------------------------------------------------
int
SendStdinNotification(const char* data, size_t data_len);
//------------------------------------------------------------------
/// Sets the path to use for stdin/out/err for a process
/// that will be launched with the 'A' packet.

View File

@ -370,6 +370,10 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec,
packet_result = Handle_H (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_I:
packet_result = Handle_I (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_m:
packet_result = Handle_m (packet);
break;
@ -3429,6 +3433,46 @@ GDBRemoteCommunicationServer::Handle_H (StringExtractorGDBRemote &packet)
return SendOKResponse();
}
GDBRemoteCommunicationServer::PacketResult
GDBRemoteCommunicationServer::Handle_I (StringExtractorGDBRemote &packet)
{
Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
// Ensure we're llgs.
if (!IsGdbServer())
return SendUnimplementedResponse("GDBRemoteCommunicationServer::Handle_I() unimplemented");
// Fail if we don't have a current process.
if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID))
{
if (log)
log->Printf ("GDBRemoteCommunicationServer::%s failed, no process available", __FUNCTION__);
return SendErrorResponse (0x15);
}
packet.SetFilePos (::strlen("I"));
char tmp[4096];
for (;;)
{
size_t read = packet.GetHexBytesAvail(tmp, sizeof(tmp));
if (read == 0)
{
break;
}
// write directly to stdin *this might block if stdin buffer is full*
// TODO: enqueue this block in circular buffer and send window size to remote host
ConnectionStatus status;
Error error;
m_stdio_communication.Write(tmp, read, status, &error);
if (error.Fail())
{
return SendErrorResponse (0x15);
}
}
return SendOKResponse();
}
GDBRemoteCommunicationServer::PacketResult
GDBRemoteCommunicationServer::Handle_interrupt (StringExtractorGDBRemote &packet)
{

View File

@ -414,6 +414,9 @@ protected:
PacketResult
Handle_H (StringExtractorGDBRemote &packet);
PacketResult
Handle_I (StringExtractorGDBRemote &packet);
PacketResult
Handle_interrupt (StringExtractorGDBRemote &packet);

View File

@ -942,6 +942,7 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info)
SetPrivateState (SetThreadStopInfo (m_last_stop_packet));
m_stdio_disable = disable_stdio;
if (!disable_stdio)
{
if (pty.GetMasterFileDescriptor() != lldb_utility::PseudoTerminal::invalid_fd)
@ -2477,6 +2478,10 @@ ProcessGDBRemote::PutSTDIN (const char *src, size_t src_len, Error &error)
ConnectionStatus status;
m_stdio_communication.Write(src, src_len, status, NULL);
}
else if (!m_stdio_disable)
{
m_gdb_comm.SendStdinNotification(src, src_len);
}
return 0;
}

View File

@ -720,6 +720,7 @@ Process::Process(Target &target, Listener &listener, const UnixSignalsSP &unix_s
m_process_input_reader (),
m_stdio_communication ("process.stdio"),
m_stdio_communication_mutex (Mutex::eMutexTypeRecursive),
m_stdio_disable(true),
m_stdout_data (),
m_stderr_data (),
m_profile_data_comm_mutex (Mutex::eMutexTypeRecursive),
@ -3911,6 +3912,7 @@ Process::Destroy ()
}
m_stdio_communication.StopReadThread();
m_stdio_communication.Disconnect();
m_stdio_disable = true;
if (m_process_input_reader)
{

View File

@ -265,6 +265,9 @@ StringExtractorGDBRemote::GetServerPacketType () const
case 'H':
return eServerPacketType_H;
case 'I':
return eServerPacketType_I;
case 'k':
if (packet_size == 1) return eServerPacketType_k;
break;

View File

@ -130,6 +130,7 @@ public:
eServerPacketType_g,
eServerPacketType_G,
eServerPacketType_H,
eServerPacketType_I, // stdin notification
eServerPacketType_k,
eServerPacketType_m,
eServerPacketType_M,