mirror of
https://github.com/Gericom/teak-llvm.git
synced 2025-06-29 08:19:01 -04:00

Fixed a case where multiple threads can be asking to send a packet to the GDB server and one of three things will happen: 1 - everything works 2 - one thread will fail to send the packet due to not being able to get the sequence mutex 3 - one thread will try and interrupt the other packet sending and fail and not send the packet Now the flow is a bit different. Prior to this fix we did: if (try_get_sequence_mutex()) { send_packet() return success; } else { if (async_ok) { interrupt() send_packet() resume() return success; } } return fail The issue is that the call to "try_get_sequence_mutex()" could fail if another thread was sending a packet and could cause us to just not send the packet and an error would be returned. What we really want is to try and get the sequence mutex, and if this succeeds, send the packet. Else check if we are running and if we are, do what we used to do. The big difference is when we aren't running, we wait for the sequence mutex so we don't drop packets. Pseudo code is: if (try_get_sequence_mutex()) { // Safe to send the packet right away send_packet() return success; } else { if (running) { // We are running, interrupt and send async packet if ok to do so, // else it is ok to fail if (async_ok) { interrupt() send_packet() resume() return success; } } else { // Not running, wait for the sequence mutex so we don't drop packets get_sequence_mutex() send_packet() return success; } } return fail llvm-svn: 157751
646 lines
22 KiB
C++
646 lines
22 KiB
C++
//===-- GDBRemoteCommunication.cpp ------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
#include "GDBRemoteCommunication.h"
|
|
|
|
// C Includes
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
|
|
// C++ Includes
|
|
// Other libraries and framework includes
|
|
#include "lldb/Core/Log.h"
|
|
#include "lldb/Core/StreamFile.h"
|
|
#include "lldb/Core/StreamString.h"
|
|
#include "lldb/Host/FileSpec.h"
|
|
#include "lldb/Host/Host.h"
|
|
#include "lldb/Host/TimeValue.h"
|
|
#include "lldb/Target/Process.h"
|
|
|
|
// Project includes
|
|
#include "ProcessGDBRemoteLog.h"
|
|
|
|
#define DEBUGSERVER_BASENAME "debugserver"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
GDBRemoteCommunication::History::History (uint32_t size) :
|
|
m_packets(),
|
|
m_curr_idx (0),
|
|
m_total_packet_count (0),
|
|
m_dumped_to_log (false)
|
|
{
|
|
m_packets.resize(size);
|
|
}
|
|
|
|
GDBRemoteCommunication::History::~History ()
|
|
{
|
|
}
|
|
|
|
void
|
|
GDBRemoteCommunication::History::AddPacket (char packet_char,
|
|
PacketType type,
|
|
uint32_t bytes_transmitted)
|
|
{
|
|
const size_t size = m_packets.size();
|
|
if (size > 0)
|
|
{
|
|
const uint32_t idx = GetNextIndex();
|
|
m_packets[idx].packet.assign (1, packet_char);
|
|
m_packets[idx].type = type;
|
|
m_packets[idx].bytes_transmitted = bytes_transmitted;
|
|
m_packets[idx].packet_idx = m_total_packet_count;
|
|
m_packets[idx].tid = Host::GetCurrentThreadID();
|
|
}
|
|
}
|
|
|
|
void
|
|
GDBRemoteCommunication::History::AddPacket (const std::string &src,
|
|
uint32_t src_len,
|
|
PacketType type,
|
|
uint32_t bytes_transmitted)
|
|
{
|
|
const size_t size = m_packets.size();
|
|
if (size > 0)
|
|
{
|
|
const uint32_t idx = GetNextIndex();
|
|
m_packets[idx].packet.assign (src, 0, src_len);
|
|
m_packets[idx].type = type;
|
|
m_packets[idx].bytes_transmitted = bytes_transmitted;
|
|
m_packets[idx].packet_idx = m_total_packet_count;
|
|
m_packets[idx].tid = Host::GetCurrentThreadID();
|
|
}
|
|
}
|
|
|
|
void
|
|
GDBRemoteCommunication::History::Dump (lldb_private::Stream &strm) const
|
|
{
|
|
const uint32_t size = GetNumPacketsInHistory ();
|
|
const uint32_t first_idx = GetFirstSavedPacketIndex ();
|
|
const uint32_t stop_idx = m_curr_idx + size;
|
|
for (uint32_t i = first_idx; i < stop_idx; ++i)
|
|
{
|
|
const uint32_t idx = NormalizeIndex (i);
|
|
const Entry &entry = m_packets[idx];
|
|
if (entry.type == ePacketTypeInvalid || entry.packet.empty())
|
|
break;
|
|
strm.Printf ("history[%u] tid=0x%4.4llx <%4u> %s packet: %s\n",
|
|
entry.packet_idx,
|
|
entry.tid,
|
|
entry.bytes_transmitted,
|
|
(entry.type == ePacketTypeSend) ? "send" : "read",
|
|
entry.packet.c_str());
|
|
}
|
|
}
|
|
|
|
void
|
|
GDBRemoteCommunication::History::Dump (lldb_private::Log *log) const
|
|
{
|
|
if (log && !m_dumped_to_log)
|
|
{
|
|
m_dumped_to_log = true;
|
|
const uint32_t size = GetNumPacketsInHistory ();
|
|
const uint32_t first_idx = GetFirstSavedPacketIndex ();
|
|
const uint32_t stop_idx = m_curr_idx + size;
|
|
for (uint32_t i = first_idx; i < stop_idx; ++i)
|
|
{
|
|
const uint32_t idx = NormalizeIndex (i);
|
|
const Entry &entry = m_packets[idx];
|
|
if (entry.type == ePacketTypeInvalid || entry.packet.empty())
|
|
break;
|
|
log->Printf ("history[%u] tid=0x%4.4llx <%4u> %s packet: %s",
|
|
entry.packet_idx,
|
|
entry.tid,
|
|
entry.bytes_transmitted,
|
|
(entry.type == ePacketTypeSend) ? "send" : "read",
|
|
entry.packet.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// GDBRemoteCommunication constructor
|
|
//----------------------------------------------------------------------
|
|
GDBRemoteCommunication::GDBRemoteCommunication(const char *comm_name,
|
|
const char *listener_name,
|
|
bool is_platform) :
|
|
Communication(comm_name),
|
|
m_packet_timeout (1),
|
|
m_sequence_mutex (Mutex::eMutexTypeRecursive),
|
|
m_public_is_running (false),
|
|
m_private_is_running (false),
|
|
m_history (512),
|
|
m_send_acks (true),
|
|
m_is_platform (is_platform)
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Destructor
|
|
//----------------------------------------------------------------------
|
|
GDBRemoteCommunication::~GDBRemoteCommunication()
|
|
{
|
|
if (IsConnected())
|
|
{
|
|
Disconnect();
|
|
}
|
|
}
|
|
|
|
char
|
|
GDBRemoteCommunication::CalculcateChecksum (const char *payload, size_t payload_length)
|
|
{
|
|
int checksum = 0;
|
|
|
|
// We only need to compute the checksum if we are sending acks
|
|
if (GetSendAcks ())
|
|
{
|
|
for (size_t i = 0; i < payload_length; ++i)
|
|
checksum += payload[i];
|
|
}
|
|
return checksum & 255;
|
|
}
|
|
|
|
size_t
|
|
GDBRemoteCommunication::SendAck ()
|
|
{
|
|
LogSP log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS));
|
|
ConnectionStatus status = eConnectionStatusSuccess;
|
|
char ch = '+';
|
|
const size_t bytes_written = Write (&ch, 1, status, NULL);
|
|
if (log)
|
|
log->Printf ("<%4zu> send packet: %c", bytes_written, ch);
|
|
m_history.AddPacket (ch, History::ePacketTypeSend, bytes_written);
|
|
return bytes_written;
|
|
}
|
|
|
|
size_t
|
|
GDBRemoteCommunication::SendNack ()
|
|
{
|
|
LogSP log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS));
|
|
ConnectionStatus status = eConnectionStatusSuccess;
|
|
char ch = '-';
|
|
const size_t bytes_written = Write (&ch, 1, status, NULL);
|
|
if (log)
|
|
log->Printf ("<%4zu> send packet: %c", bytes_written, ch);
|
|
m_history.AddPacket (ch, History::ePacketTypeSend, bytes_written);
|
|
return bytes_written;
|
|
}
|
|
|
|
size_t
|
|
GDBRemoteCommunication::SendPacket (const char *payload, size_t payload_length)
|
|
{
|
|
Mutex::Locker locker(m_sequence_mutex);
|
|
return SendPacketNoLock (payload, payload_length);
|
|
}
|
|
|
|
size_t
|
|
GDBRemoteCommunication::SendPacketNoLock (const char *payload, size_t payload_length)
|
|
{
|
|
if (IsConnected())
|
|
{
|
|
StreamString packet(0, 4, eByteOrderBig);
|
|
|
|
packet.PutChar('$');
|
|
packet.Write (payload, payload_length);
|
|
packet.PutChar('#');
|
|
packet.PutHex8(CalculcateChecksum (payload, payload_length));
|
|
|
|
LogSP log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS));
|
|
ConnectionStatus status = eConnectionStatusSuccess;
|
|
size_t bytes_written = Write (packet.GetData(), packet.GetSize(), status, NULL);
|
|
if (log)
|
|
{
|
|
// If logging was just enabled and we have history, then dump out what
|
|
// we have to the log so we get the historical context. The Dump() call that
|
|
// logs all of the packet will set a boolean so that we don't dump this more
|
|
// than once
|
|
if (!m_history.DidDumpToLog ())
|
|
m_history.Dump (log.get());
|
|
|
|
log->Printf ("<%4zu> send packet: %.*s", bytes_written, (int)packet.GetSize(), packet.GetData());
|
|
}
|
|
|
|
m_history.AddPacket (packet.GetString(), packet.GetSize(), History::ePacketTypeSend, bytes_written);
|
|
|
|
|
|
if (bytes_written == packet.GetSize())
|
|
{
|
|
if (GetSendAcks ())
|
|
{
|
|
if (GetAck () != '+')
|
|
{
|
|
printf("get ack failed...");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (log)
|
|
log->Printf ("error: failed to send packet: %.*s", (int)packet.GetSize(), packet.GetData());
|
|
}
|
|
return bytes_written;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
char
|
|
GDBRemoteCommunication::GetAck ()
|
|
{
|
|
StringExtractorGDBRemote packet;
|
|
if (WaitForPacketWithTimeoutMicroSecondsNoLock (packet, GetPacketTimeoutInMicroSeconds ()) == 1)
|
|
return packet.GetChar();
|
|
return 0;
|
|
}
|
|
|
|
bool
|
|
GDBRemoteCommunication::GetSequenceMutex (Mutex::Locker& locker)
|
|
{
|
|
if (IsRunning())
|
|
return locker.TryLock (m_sequence_mutex);
|
|
|
|
locker.Lock (m_sequence_mutex);
|
|
return true;
|
|
}
|
|
|
|
|
|
bool
|
|
GDBRemoteCommunication::WaitForNotRunningPrivate (const TimeValue *timeout_ptr)
|
|
{
|
|
return m_private_is_running.WaitForValueEqualTo (false, timeout_ptr, NULL);
|
|
}
|
|
|
|
size_t
|
|
GDBRemoteCommunication::WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtractorGDBRemote &packet, uint32_t timeout_usec)
|
|
{
|
|
uint8_t buffer[8192];
|
|
Error error;
|
|
|
|
LogSP log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS | GDBR_LOG_VERBOSE));
|
|
|
|
// Check for a packet from our cache first without trying any reading...
|
|
if (CheckForPacket (NULL, 0, packet))
|
|
return packet.GetStringRef().size();
|
|
|
|
bool timed_out = false;
|
|
while (IsConnected() && !timed_out)
|
|
{
|
|
lldb::ConnectionStatus status = eConnectionStatusNoConnection;
|
|
size_t bytes_read = Read (buffer, sizeof(buffer), timeout_usec, status, &error);
|
|
|
|
if (log)
|
|
log->Printf ("%s: Read (buffer, (sizeof(buffer), timeout_usec = 0x%x, status = %s, error = %s) => bytes_read = %zu",
|
|
__PRETTY_FUNCTION__,
|
|
timeout_usec,
|
|
Communication::ConnectionStatusAsCString (status),
|
|
error.AsCString(),
|
|
bytes_read);
|
|
|
|
if (bytes_read > 0)
|
|
{
|
|
if (CheckForPacket (buffer, bytes_read, packet))
|
|
return packet.GetStringRef().size();
|
|
}
|
|
else
|
|
{
|
|
switch (status)
|
|
{
|
|
case eConnectionStatusTimedOut:
|
|
timed_out = true;
|
|
break;
|
|
case eConnectionStatusSuccess:
|
|
//printf ("status = success but error = %s\n", error.AsCString("<invalid>"));
|
|
break;
|
|
|
|
case eConnectionStatusEndOfFile:
|
|
case eConnectionStatusNoConnection:
|
|
case eConnectionStatusLostConnection:
|
|
case eConnectionStatusError:
|
|
Disconnect();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
packet.Clear ();
|
|
return 0;
|
|
}
|
|
|
|
bool
|
|
GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, StringExtractorGDBRemote &packet)
|
|
{
|
|
// Put the packet data into the buffer in a thread safe fashion
|
|
Mutex::Locker locker(m_bytes_mutex);
|
|
|
|
LogSP log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS));
|
|
|
|
if (src && src_len > 0)
|
|
{
|
|
if (log && log->GetVerbose())
|
|
{
|
|
StreamString s;
|
|
log->Printf ("GDBRemoteCommunication::%s adding %u bytes: %.*s",
|
|
__FUNCTION__,
|
|
(uint32_t)src_len,
|
|
(uint32_t)src_len,
|
|
src);
|
|
}
|
|
m_bytes.append ((const char *)src, src_len);
|
|
}
|
|
|
|
// Parse up the packets into gdb remote packets
|
|
if (!m_bytes.empty())
|
|
{
|
|
// end_idx must be one past the last valid packet byte. Start
|
|
// it off with an invalid value that is the same as the current
|
|
// index.
|
|
size_t content_start = 0;
|
|
size_t content_length = 0;
|
|
size_t total_length = 0;
|
|
size_t checksum_idx = std::string::npos;
|
|
|
|
switch (m_bytes[0])
|
|
{
|
|
case '+': // Look for ack
|
|
case '-': // Look for cancel
|
|
case '\x03': // ^C to halt target
|
|
content_length = total_length = 1; // The command is one byte long...
|
|
break;
|
|
|
|
case '$':
|
|
// Look for a standard gdb packet?
|
|
{
|
|
size_t hash_pos = m_bytes.find('#');
|
|
if (hash_pos != std::string::npos)
|
|
{
|
|
if (hash_pos + 2 < m_bytes.size())
|
|
{
|
|
checksum_idx = hash_pos + 1;
|
|
// Skip the dollar sign
|
|
content_start = 1;
|
|
// Don't include the # in the content or the $ in the content length
|
|
content_length = hash_pos - 1;
|
|
|
|
total_length = hash_pos + 3; // Skip the # and the two hex checksum bytes
|
|
}
|
|
else
|
|
{
|
|
// Checksum bytes aren't all here yet
|
|
content_length = std::string::npos;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
// We have an unexpected byte and we need to flush all bad
|
|
// data that is in m_bytes, so we need to find the first
|
|
// byte that is a '+' (ACK), '-' (NACK), \x03 (CTRL+C interrupt),
|
|
// or '$' character (start of packet header) or of course,
|
|
// the end of the data in m_bytes...
|
|
const size_t bytes_len = m_bytes.size();
|
|
bool done = false;
|
|
uint32_t idx;
|
|
for (idx = 1; !done && idx < bytes_len; ++idx)
|
|
{
|
|
switch (m_bytes[idx])
|
|
{
|
|
case '+':
|
|
case '-':
|
|
case '\x03':
|
|
case '$':
|
|
done = true;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (log)
|
|
log->Printf ("GDBRemoteCommunication::%s tossing %u junk bytes: '%.*s'",
|
|
__FUNCTION__, idx, idx, m_bytes.c_str());
|
|
m_bytes.erase(0, idx);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (content_length == std::string::npos)
|
|
{
|
|
packet.Clear();
|
|
return false;
|
|
}
|
|
else if (total_length > 0)
|
|
{
|
|
|
|
// We have a valid packet...
|
|
assert (content_length <= m_bytes.size());
|
|
assert (total_length <= m_bytes.size());
|
|
assert (content_length <= total_length);
|
|
|
|
bool success = true;
|
|
std::string &packet_str = packet.GetStringRef();
|
|
|
|
|
|
if (log)
|
|
{
|
|
// If logging was just enabled and we have history, then dump out what
|
|
// we have to the log so we get the historical context. The Dump() call that
|
|
// logs all of the packet will set a boolean so that we don't dump this more
|
|
// than once
|
|
if (!m_history.DidDumpToLog ())
|
|
m_history.Dump (log.get());
|
|
|
|
log->Printf ("<%4zu> read packet: %.*s", total_length, (int)(total_length), m_bytes.c_str());
|
|
}
|
|
|
|
m_history.AddPacket (m_bytes.c_str(), total_length, History::ePacketTypeRecv, total_length);
|
|
|
|
packet_str.assign (m_bytes, content_start, content_length);
|
|
|
|
if (m_bytes[0] == '$')
|
|
{
|
|
assert (checksum_idx < m_bytes.size());
|
|
if (::isxdigit (m_bytes[checksum_idx+0]) ||
|
|
::isxdigit (m_bytes[checksum_idx+1]))
|
|
{
|
|
if (GetSendAcks ())
|
|
{
|
|
const char *packet_checksum_cstr = &m_bytes[checksum_idx];
|
|
char packet_checksum = strtol (packet_checksum_cstr, NULL, 16);
|
|
char actual_checksum = CalculcateChecksum (packet_str.c_str(), packet_str.size());
|
|
success = packet_checksum == actual_checksum;
|
|
if (!success)
|
|
{
|
|
if (log)
|
|
log->Printf ("error: checksum mismatch: %.*s expected 0x%2.2x, got 0x%2.2x",
|
|
(int)(total_length),
|
|
m_bytes.c_str(),
|
|
(uint8_t)packet_checksum,
|
|
(uint8_t)actual_checksum);
|
|
}
|
|
// Send the ack or nack if needed
|
|
if (!success)
|
|
SendNack();
|
|
else
|
|
SendAck();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
success = false;
|
|
if (log)
|
|
log->Printf ("error: invalid checksum in packet: '%s'\n", m_bytes.c_str());
|
|
}
|
|
}
|
|
|
|
m_bytes.erase(0, total_length);
|
|
packet.SetFilePos(0);
|
|
return success;
|
|
}
|
|
}
|
|
packet.Clear();
|
|
return false;
|
|
}
|
|
|
|
Error
|
|
GDBRemoteCommunication::StartDebugserverProcess (const char *debugserver_url,
|
|
const char *unix_socket_name, // For handshaking
|
|
lldb_private::ProcessLaunchInfo &launch_info)
|
|
{
|
|
Error error;
|
|
// If we locate debugserver, keep that located version around
|
|
static FileSpec g_debugserver_file_spec;
|
|
|
|
// This function will fill in the launch information for the debugserver
|
|
// instance that gets launched.
|
|
launch_info.Clear();
|
|
|
|
char debugserver_path[PATH_MAX];
|
|
FileSpec &debugserver_file_spec = launch_info.GetExecutableFile();
|
|
|
|
// Always check to see if we have an environment override for the path
|
|
// to the debugserver to use and use it if we do.
|
|
const char *env_debugserver_path = getenv("LLDB_DEBUGSERVER_PATH");
|
|
if (env_debugserver_path)
|
|
debugserver_file_spec.SetFile (env_debugserver_path, false);
|
|
else
|
|
debugserver_file_spec = g_debugserver_file_spec;
|
|
bool debugserver_exists = debugserver_file_spec.Exists();
|
|
if (!debugserver_exists)
|
|
{
|
|
// The debugserver binary is in the LLDB.framework/Resources
|
|
// directory.
|
|
if (Host::GetLLDBPath (ePathTypeSupportExecutableDir, debugserver_file_spec))
|
|
{
|
|
debugserver_file_spec.GetFilename().SetCString(DEBUGSERVER_BASENAME);
|
|
debugserver_exists = debugserver_file_spec.Exists();
|
|
if (debugserver_exists)
|
|
{
|
|
g_debugserver_file_spec = debugserver_file_spec;
|
|
}
|
|
else
|
|
{
|
|
g_debugserver_file_spec.Clear();
|
|
debugserver_file_spec.Clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (debugserver_exists)
|
|
{
|
|
debugserver_file_spec.GetPath (debugserver_path, sizeof(debugserver_path));
|
|
|
|
Args &debugserver_args = launch_info.GetArguments();
|
|
debugserver_args.Clear();
|
|
char arg_cstr[PATH_MAX];
|
|
|
|
// Start args with "debugserver /file/path -r --"
|
|
debugserver_args.AppendArgument(debugserver_path);
|
|
debugserver_args.AppendArgument(debugserver_url);
|
|
// use native registers, not the GDB registers
|
|
debugserver_args.AppendArgument("--native-regs");
|
|
// make debugserver run in its own session so signals generated by
|
|
// special terminal key sequences (^C) don't affect debugserver
|
|
debugserver_args.AppendArgument("--setsid");
|
|
|
|
if (unix_socket_name && unix_socket_name[0])
|
|
{
|
|
debugserver_args.AppendArgument("--unix-socket");
|
|
debugserver_args.AppendArgument(unix_socket_name);
|
|
}
|
|
|
|
const char *env_debugserver_log_file = getenv("LLDB_DEBUGSERVER_LOG_FILE");
|
|
if (env_debugserver_log_file)
|
|
{
|
|
::snprintf (arg_cstr, sizeof(arg_cstr), "--log-file=%s", env_debugserver_log_file);
|
|
debugserver_args.AppendArgument(arg_cstr);
|
|
}
|
|
|
|
const char *env_debugserver_log_flags = getenv("LLDB_DEBUGSERVER_LOG_FLAGS");
|
|
if (env_debugserver_log_flags)
|
|
{
|
|
::snprintf (arg_cstr, sizeof(arg_cstr), "--log-flags=%s", env_debugserver_log_flags);
|
|
debugserver_args.AppendArgument(arg_cstr);
|
|
}
|
|
// debugserver_args.AppendArgument("--log-file=/tmp/debugserver.txt");
|
|
// debugserver_args.AppendArgument("--log-flags=0x802e0e");
|
|
|
|
// We currently send down all arguments, attach pids, or attach
|
|
// process names in dedicated GDB server packets, so we don't need
|
|
// to pass them as arguments. This is currently because of all the
|
|
// things we need to setup prior to launching: the environment,
|
|
// current working dir, file actions, etc.
|
|
#if 0
|
|
// Now append the program arguments
|
|
if (inferior_argv)
|
|
{
|
|
// Terminate the debugserver args so we can now append the inferior args
|
|
debugserver_args.AppendArgument("--");
|
|
|
|
for (int i = 0; inferior_argv[i] != NULL; ++i)
|
|
debugserver_args.AppendArgument (inferior_argv[i]);
|
|
}
|
|
else if (attach_pid != LLDB_INVALID_PROCESS_ID)
|
|
{
|
|
::snprintf (arg_cstr, sizeof(arg_cstr), "--attach=%u", attach_pid);
|
|
debugserver_args.AppendArgument (arg_cstr);
|
|
}
|
|
else if (attach_name && attach_name[0])
|
|
{
|
|
if (wait_for_launch)
|
|
debugserver_args.AppendArgument ("--waitfor");
|
|
else
|
|
debugserver_args.AppendArgument ("--attach");
|
|
debugserver_args.AppendArgument (attach_name);
|
|
}
|
|
#endif
|
|
|
|
// Close STDIN, STDOUT and STDERR. We might need to redirect them
|
|
// to "/dev/null" if we run into any problems.
|
|
// launch_info.AppendCloseFileAction (STDIN_FILENO);
|
|
// launch_info.AppendCloseFileAction (STDOUT_FILENO);
|
|
// launch_info.AppendCloseFileAction (STDERR_FILENO);
|
|
|
|
error = Host::LaunchProcess(launch_info);
|
|
}
|
|
else
|
|
{
|
|
error.SetErrorStringWithFormat ("unable to locate " DEBUGSERVER_BASENAME );
|
|
}
|
|
return error;
|
|
}
|
|
|
|
void
|
|
GDBRemoteCommunication::DumpHistory(Stream &strm)
|
|
{
|
|
m_history.Dump (strm);
|
|
}
|