teak-llvm/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
Todd Fiala b8b49ec92b Modified GDBProcessCommunicationServer to launch via the platform.
GDBProcessCommunicationServer now optionally takes a PlatformSP that
defaults to the default platform for the host.
GDBProcessCommunicationServer::LaunchProcess () now uses the platform
to launch the process.

lldb-gdbserver now takes an optional --platform={platform_plugin_name}
or -p {platform_plugin_name} command line option. If no platform is
specified, the default platform for the host is used; otherwise, if
the platform_plugin_name matches a registered platform plugin or
matches the default platform's name (which is not necessarily
registered by name in the case of 'host'), that platform is used. If
the platform name cannot be resolved, lldb-gdbserver exits after
printing all the available platform plugin names and the default
platform plugin name.

llvm-svn: 200266
2014-01-28 00:34:23 +00:00

1614 lines
54 KiB
C++

//===-- GDBRemoteCommunicationServer.cpp ------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <errno.h>
#include "GDBRemoteCommunicationServer.h"
#include "lldb/Core/StreamGDBRemote.h"
// C Includes
// C++ Includes
// Other libraries and framework includes
#include "llvm/ADT/Triple.h"
#include "lldb/Interpreter/Args.h"
#include "lldb/Core/ConnectionFileDescriptor.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/State.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Host/Endian.h"
#include "lldb/Host/File.h"
#include "lldb/Host/Host.h"
#include "lldb/Host/TimeValue.h"
#include "lldb/Target/Platform.h"
#include "lldb/Target/Process.h"
// Project includes
#include "Utility/StringExtractorGDBRemote.h"
#include "ProcessGDBRemote.h"
#include "ProcessGDBRemoteLog.h"
using namespace lldb;
using namespace lldb_private;
//----------------------------------------------------------------------
// GDBRemoteCommunicationServer constructor
//----------------------------------------------------------------------
GDBRemoteCommunicationServer::GDBRemoteCommunicationServer(bool is_platform) :
GDBRemoteCommunication ("gdb-remote.server", "gdb-remote.server.rx_packet", is_platform),
m_platform_sp (Platform::GetDefaultPlatform ()),
m_async_thread (LLDB_INVALID_HOST_THREAD),
m_process_launch_info (),
m_process_launch_error (),
m_spawned_pids (),
m_spawned_pids_mutex (Mutex::eMutexTypeRecursive),
m_proc_infos (),
m_proc_infos_index (0),
m_port_map (),
m_port_offset(0)
{
}
GDBRemoteCommunicationServer::GDBRemoteCommunicationServer(bool is_platform,
const lldb::PlatformSP& platform_sp) :
GDBRemoteCommunication ("gdb-remote.server", "gdb-remote.server.rx_packet", is_platform),
m_platform_sp (platform_sp),
m_async_thread (LLDB_INVALID_HOST_THREAD),
m_process_launch_info (),
m_process_launch_error (),
m_spawned_pids (),
m_spawned_pids_mutex (Mutex::eMutexTypeRecursive),
m_proc_infos (),
m_proc_infos_index (0),
m_port_map (),
m_port_offset(0)
{
assert(platform_sp);
}
//----------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------
GDBRemoteCommunicationServer::~GDBRemoteCommunicationServer()
{
}
//void *
//GDBRemoteCommunicationServer::AsyncThread (void *arg)
//{
// GDBRemoteCommunicationServer *server = (GDBRemoteCommunicationServer*) arg;
//
// Log *log;// (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS));
// if (log)
// log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) thread starting...", __FUNCTION__, arg, process->GetID());
//
// StringExtractorGDBRemote packet;
//
// while ()
// {
// if (packet.
// }
//
// if (log)
// log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) thread exiting...", __FUNCTION__, arg, process->GetID());
//
// process->m_async_thread = LLDB_INVALID_HOST_THREAD;
// return NULL;
//}
//
bool
GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec,
Error &error,
bool &interrupt,
bool &quit)
{
StringExtractorGDBRemote packet;
PacketResult packet_result = WaitForPacketWithTimeoutMicroSecondsNoLock (packet, timeout_usec);
if (packet_result == PacketResult::Success)
{
const StringExtractorGDBRemote::ServerPacketType packet_type = packet.GetServerPacketType ();
switch (packet_type)
{
case StringExtractorGDBRemote::eServerPacketType_nack:
case StringExtractorGDBRemote::eServerPacketType_ack:
break;
case StringExtractorGDBRemote::eServerPacketType_invalid:
error.SetErrorString("invalid packet");
quit = true;
break;
case StringExtractorGDBRemote::eServerPacketType_interrupt:
error.SetErrorString("interrupt received");
interrupt = true;
break;
default:
case StringExtractorGDBRemote::eServerPacketType_unimplemented:
packet_result = SendUnimplementedResponse (packet.GetStringRef().c_str());
break;
case StringExtractorGDBRemote::eServerPacketType_A:
packet_result = Handle_A (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_qfProcessInfo:
packet_result = Handle_qfProcessInfo (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_qsProcessInfo:
packet_result = Handle_qsProcessInfo (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_qC:
packet_result = Handle_qC (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_qHostInfo:
packet_result = Handle_qHostInfo (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer:
packet_result = Handle_qLaunchGDBServer (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_qKillSpawnedProcess:
packet_result = Handle_qKillSpawnedProcess (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_k:
packet_result = Handle_k (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_qLaunchSuccess:
packet_result = Handle_qLaunchSuccess (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_qGroupName:
packet_result = Handle_qGroupName (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_qProcessInfoPID:
packet_result = Handle_qProcessInfoPID (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_qSpeedTest:
packet_result = Handle_qSpeedTest (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_qUserName:
packet_result = Handle_qUserName (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir:
packet_result = Handle_qGetWorkingDir(packet);
break;
case StringExtractorGDBRemote::eServerPacketType_QEnvironment:
packet_result = Handle_QEnvironment (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_QLaunchArch:
packet_result = Handle_QLaunchArch (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_QSetDisableASLR:
packet_result = Handle_QSetDisableASLR (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_QSetSTDIN:
packet_result = Handle_QSetSTDIN (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_QSetSTDOUT:
packet_result = Handle_QSetSTDOUT (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_QSetSTDERR:
packet_result = Handle_QSetSTDERR (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir:
packet_result = Handle_QSetWorkingDir (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_QStartNoAckMode:
packet_result = Handle_QStartNoAckMode (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_qPlatform_mkdir:
packet_result = Handle_qPlatform_mkdir (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_qPlatform_chmod:
packet_result = Handle_qPlatform_chmod (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_qPlatform_shell:
packet_result = Handle_qPlatform_shell (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_vFile_open:
packet_result = Handle_vFile_Open (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_vFile_close:
packet_result = Handle_vFile_Close (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_vFile_pread:
packet_result = Handle_vFile_pRead (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_vFile_pwrite:
packet_result = Handle_vFile_pWrite (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_vFile_size:
packet_result = Handle_vFile_Size (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_vFile_mode:
packet_result = Handle_vFile_Mode (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_vFile_exists:
packet_result = Handle_vFile_Exists (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_vFile_stat:
packet_result = Handle_vFile_Stat (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_vFile_md5:
packet_result = Handle_vFile_MD5 (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_vFile_symlink:
packet_result = Handle_vFile_symlink (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_vFile_unlink:
packet_result = Handle_vFile_unlink (packet);
break;
}
}
else
{
if (!IsConnected())
{
error.SetErrorString("lost connection");
quit = true;
}
else
{
error.SetErrorString("timeout");
}
}
return packet_result == PacketResult::Success;
}
lldb_private::Error
GDBRemoteCommunicationServer::SetLaunchArguments (const char *const args[], int argc)
{
if ((argc < 1) || !args || !args[0] || !args[0][0])
return lldb_private::Error ("%s: no process command line specified to launch", __FUNCTION__);
m_process_launch_info.SetArguments (const_cast<const char**> (args), true);
return lldb_private::Error ();
}
lldb_private::Error
GDBRemoteCommunicationServer::SetLaunchFlags (unsigned int launch_flags)
{
m_process_launch_info.GetFlags ().Set (launch_flags);
return lldb_private::Error ();
}
lldb_private::Error
GDBRemoteCommunicationServer::LaunchProcess ()
{
if (!m_process_launch_info.GetArguments ().GetArgumentCount ())
return lldb_private::Error ("%s: no process command line specified to launch", __FUNCTION__);
// specify the process monitor if not already set. This should
// generally be what happens since we need to reap started
// processes.
if (!m_process_launch_info.GetMonitorProcessCallback ())
m_process_launch_info.SetMonitorProcessCallback(ReapDebuggedProcess, this, false);
lldb_private::Error error = m_platform_sp->LaunchProcess (m_process_launch_info);
if (!error.Success ())
{
fprintf (stderr, "%s: failed to launch executable %s", __FUNCTION__, m_process_launch_info.GetArguments ().GetArgumentAtIndex (0));
return error;
}
printf ("Launched '%s' as process %" PRIu64 "...\n", m_process_launch_info.GetArguments ().GetArgumentAtIndex (0), m_process_launch_info.GetProcessID());
// add to list of spawned processes. On an lldb-gdbserver, we
// would expect there to be only one.
lldb::pid_t pid;
if ( (pid = m_process_launch_info.GetProcessID()) != LLDB_INVALID_PROCESS_ID )
{
Mutex::Locker locker (m_spawned_pids_mutex);
m_spawned_pids.insert(pid);
}
return error;
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::SendUnimplementedResponse (const char *)
{
// TODO: Log the packet we aren't handling...
return SendPacketNoLock ("", 0);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::SendErrorResponse (uint8_t err)
{
char packet[16];
int packet_len = ::snprintf (packet, sizeof(packet), "E%2.2x", err);
assert (packet_len < (int)sizeof(packet));
return SendPacketNoLock (packet, packet_len);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::SendOKResponse ()
{
return SendPacketNoLock ("OK", 2);
}
bool
GDBRemoteCommunicationServer::HandshakeWithClient(Error *error_ptr)
{
return GetAck() == PacketResult::Success;
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_qHostInfo (StringExtractorGDBRemote &packet)
{
StreamString response;
// $cputype:16777223;cpusubtype:3;ostype:Darwin;vendor:apple;endian:little;ptrsize:8;#00
ArchSpec host_arch (Host::GetArchitecture ());
const llvm::Triple &host_triple = host_arch.GetTriple();
response.PutCString("triple:");
response.PutCStringAsRawHex8(host_triple.getTriple().c_str());
response.Printf (";ptrsize:%u;",host_arch.GetAddressByteSize());
const char* distribution_id = host_arch.GetDistributionId ().AsCString ();
if (distribution_id)
{
response.PutCString("distribution_id:");
response.PutCStringAsRawHex8(distribution_id);
response.PutCString(";");
}
uint32_t cpu = host_arch.GetMachOCPUType();
uint32_t sub = host_arch.GetMachOCPUSubType();
if (cpu != LLDB_INVALID_CPUTYPE)
response.Printf ("cputype:%u;", cpu);
if (sub != LLDB_INVALID_CPUTYPE)
response.Printf ("cpusubtype:%u;", sub);
if (cpu == ArchSpec::kCore_arm_any)
response.Printf("watchpoint_exceptions_received:before;"); // On armv7 we use "synchronous" watchpoints which means the exception is delivered before the instruction executes.
else
response.Printf("watchpoint_exceptions_received:after;");
switch (lldb::endian::InlHostByteOrder())
{
case eByteOrderBig: response.PutCString ("endian:big;"); break;
case eByteOrderLittle: response.PutCString ("endian:little;"); break;
case eByteOrderPDP: response.PutCString ("endian:pdp;"); break;
default: response.PutCString ("endian:unknown;"); break;
}
uint32_t major = UINT32_MAX;
uint32_t minor = UINT32_MAX;
uint32_t update = UINT32_MAX;
if (Host::GetOSVersion (major, minor, update))
{
if (major != UINT32_MAX)
{
response.Printf("os_version:%u", major);
if (minor != UINT32_MAX)
{
response.Printf(".%u", minor);
if (update != UINT32_MAX)
response.Printf(".%u", update);
}
response.PutChar(';');
}
}
std::string s;
if (Host::GetOSBuildString (s))
{
response.PutCString ("os_build:");
response.PutCStringAsRawHex8(s.c_str());
response.PutChar(';');
}
if (Host::GetOSKernelDescription (s))
{
response.PutCString ("os_kernel:");
response.PutCStringAsRawHex8(s.c_str());
response.PutChar(';');
}
#if defined(__APPLE__)
#if defined(__arm__)
// For iOS devices, we are connected through a USB Mux so we never pretend
// to actually have a hostname as far as the remote lldb that is connecting
// to this lldb-platform is concerned
response.PutCString ("hostname:");
response.PutCStringAsRawHex8("localhost");
response.PutChar(';');
#else // #if defined(__arm__)
if (Host::GetHostname (s))
{
response.PutCString ("hostname:");
response.PutCStringAsRawHex8(s.c_str());
response.PutChar(';');
}
#endif // #if defined(__arm__)
#else // #if defined(__APPLE__)
if (Host::GetHostname (s))
{
response.PutCString ("hostname:");
response.PutCStringAsRawHex8(s.c_str());
response.PutChar(';');
}
#endif // #if defined(__APPLE__)
return SendPacketNoLock (response.GetData(), response.GetSize());
}
static void
CreateProcessInfoResponse (const ProcessInstanceInfo &proc_info, StreamString &response)
{
response.Printf ("pid:%" PRIu64 ";ppid:%" PRIu64 ";uid:%i;gid:%i;euid:%i;egid:%i;",
proc_info.GetProcessID(),
proc_info.GetParentProcessID(),
proc_info.GetUserID(),
proc_info.GetGroupID(),
proc_info.GetEffectiveUserID(),
proc_info.GetEffectiveGroupID());
response.PutCString ("name:");
response.PutCStringAsRawHex8(proc_info.GetName());
response.PutChar(';');
const ArchSpec &proc_arch = proc_info.GetArchitecture();
if (proc_arch.IsValid())
{
const llvm::Triple &proc_triple = proc_arch.GetTriple();
response.PutCString("triple:");
response.PutCStringAsRawHex8(proc_triple.getTriple().c_str());
response.PutChar(';');
}
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_qProcessInfoPID (StringExtractorGDBRemote &packet)
{
// Packet format: "qProcessInfoPID:%i" where %i is the pid
packet.SetFilePos(::strlen ("qProcessInfoPID:"));
lldb::pid_t pid = packet.GetU32 (LLDB_INVALID_PROCESS_ID);
if (pid != LLDB_INVALID_PROCESS_ID)
{
ProcessInstanceInfo proc_info;
if (Host::GetProcessInfo(pid, proc_info))
{
StreamString response;
CreateProcessInfoResponse (proc_info, response);
return SendPacketNoLock (response.GetData(), response.GetSize());
}
}
return SendErrorResponse (1);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_qfProcessInfo (StringExtractorGDBRemote &packet)
{
m_proc_infos_index = 0;
m_proc_infos.Clear();
ProcessInstanceInfoMatch match_info;
packet.SetFilePos(::strlen ("qfProcessInfo"));
if (packet.GetChar() == ':')
{
std::string key;
std::string value;
while (packet.GetNameColonValue(key, value))
{
bool success = true;
if (key.compare("name") == 0)
{
StringExtractor extractor;
extractor.GetStringRef().swap(value);
extractor.GetHexByteString (value);
match_info.GetProcessInfo().GetExecutableFile().SetFile(value.c_str(), false);
}
else if (key.compare("name_match") == 0)
{
if (value.compare("equals") == 0)
{
match_info.SetNameMatchType (eNameMatchEquals);
}
else if (value.compare("starts_with") == 0)
{
match_info.SetNameMatchType (eNameMatchStartsWith);
}
else if (value.compare("ends_with") == 0)
{
match_info.SetNameMatchType (eNameMatchEndsWith);
}
else if (value.compare("contains") == 0)
{
match_info.SetNameMatchType (eNameMatchContains);
}
else if (value.compare("regex") == 0)
{
match_info.SetNameMatchType (eNameMatchRegularExpression);
}
else
{
success = false;
}
}
else if (key.compare("pid") == 0)
{
match_info.GetProcessInfo().SetProcessID (Args::StringToUInt32(value.c_str(), LLDB_INVALID_PROCESS_ID, 0, &success));
}
else if (key.compare("parent_pid") == 0)
{
match_info.GetProcessInfo().SetParentProcessID (Args::StringToUInt32(value.c_str(), LLDB_INVALID_PROCESS_ID, 0, &success));
}
else if (key.compare("uid") == 0)
{
match_info.GetProcessInfo().SetUserID (Args::StringToUInt32(value.c_str(), UINT32_MAX, 0, &success));
}
else if (key.compare("gid") == 0)
{
match_info.GetProcessInfo().SetGroupID (Args::StringToUInt32(value.c_str(), UINT32_MAX, 0, &success));
}
else if (key.compare("euid") == 0)
{
match_info.GetProcessInfo().SetEffectiveUserID (Args::StringToUInt32(value.c_str(), UINT32_MAX, 0, &success));
}
else if (key.compare("egid") == 0)
{
match_info.GetProcessInfo().SetEffectiveGroupID (Args::StringToUInt32(value.c_str(), UINT32_MAX, 0, &success));
}
else if (key.compare("all_users") == 0)
{
match_info.SetMatchAllUsers(Args::StringToBoolean(value.c_str(), false, &success));
}
else if (key.compare("triple") == 0)
{
match_info.GetProcessInfo().GetArchitecture().SetTriple (value.c_str(), NULL);
}
else
{
success = false;
}
if (!success)
return SendErrorResponse (2);
}
}
if (Host::FindProcesses (match_info, m_proc_infos))
{
// We found something, return the first item by calling the get
// subsequent process info packet handler...
return Handle_qsProcessInfo (packet);
}
return SendErrorResponse (3);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_qsProcessInfo (StringExtractorGDBRemote &packet)
{
if (m_proc_infos_index < m_proc_infos.GetSize())
{
StreamString response;
CreateProcessInfoResponse (m_proc_infos.GetProcessInfoAtIndex(m_proc_infos_index), response);
++m_proc_infos_index;
return SendPacketNoLock (response.GetData(), response.GetSize());
}
return SendErrorResponse (4);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_qUserName (StringExtractorGDBRemote &packet)
{
// Packet format: "qUserName:%i" where %i is the uid
packet.SetFilePos(::strlen ("qUserName:"));
uint32_t uid = packet.GetU32 (UINT32_MAX);
if (uid != UINT32_MAX)
{
std::string name;
if (Host::GetUserName (uid, name))
{
StreamString response;
response.PutCStringAsRawHex8 (name.c_str());
return SendPacketNoLock (response.GetData(), response.GetSize());
}
}
return SendErrorResponse (5);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_qGroupName (StringExtractorGDBRemote &packet)
{
// Packet format: "qGroupName:%i" where %i is the gid
packet.SetFilePos(::strlen ("qGroupName:"));
uint32_t gid = packet.GetU32 (UINT32_MAX);
if (gid != UINT32_MAX)
{
std::string name;
if (Host::GetGroupName (gid, name))
{
StreamString response;
response.PutCStringAsRawHex8 (name.c_str());
return SendPacketNoLock (response.GetData(), response.GetSize());
}
}
return SendErrorResponse (6);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_qSpeedTest (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen ("qSpeedTest:"));
std::string key;
std::string value;
bool success = packet.GetNameColonValue(key, value);
if (success && key.compare("response_size") == 0)
{
uint32_t response_size = Args::StringToUInt32(value.c_str(), 0, 0, &success);
if (success)
{
if (response_size == 0)
return SendOKResponse();
StreamString response;
uint32_t bytes_left = response_size;
response.PutCString("data:");
while (bytes_left > 0)
{
if (bytes_left >= 26)
{
response.PutCString("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
bytes_left -= 26;
}
else
{
response.Printf ("%*.*s;", bytes_left, bytes_left, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
bytes_left = 0;
}
}
return SendPacketNoLock (response.GetData(), response.GetSize());
}
}
return SendErrorResponse (7);
}
static void *
AcceptPortFromInferior (void *arg)
{
const char *connect_url = (const char *)arg;
ConnectionFileDescriptor file_conn;
Error error;
if (file_conn.Connect (connect_url, &error) == eConnectionStatusSuccess)
{
char pid_str[256];
::memset (pid_str, 0, sizeof(pid_str));
ConnectionStatus status;
const size_t pid_str_len = file_conn.Read (pid_str, sizeof(pid_str), 0, status, NULL);
if (pid_str_len > 0)
{
int pid = atoi (pid_str);
return (void *)(intptr_t)pid;
}
}
return NULL;
}
//
//static bool
//WaitForProcessToSIGSTOP (const lldb::pid_t pid, const int timeout_in_seconds)
//{
// const int time_delta_usecs = 100000;
// const int num_retries = timeout_in_seconds/time_delta_usecs;
// for (int i=0; i<num_retries; i++)
// {
// struct proc_bsdinfo bsd_info;
// int error = ::proc_pidinfo (pid, PROC_PIDTBSDINFO,
// (uint64_t) 0,
// &bsd_info,
// PROC_PIDTBSDINFO_SIZE);
//
// switch (error)
// {
// case EINVAL:
// case ENOTSUP:
// case ESRCH:
// case EPERM:
// return false;
//
// default:
// break;
//
// case 0:
// if (bsd_info.pbi_status == SSTOP)
// return true;
// }
// ::usleep (time_delta_usecs);
// }
// return false;
//}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_A (StringExtractorGDBRemote &packet)
{
// The 'A' packet is the most over designed packet ever here with
// redundant argument indexes, redundant argument lengths and needed hex
// encoded argument string values. Really all that is needed is a comma
// separated hex encoded argument value list, but we will stay true to the
// documented version of the 'A' packet here...
packet.SetFilePos(1); // Skip the 'A'
bool success = true;
while (success && packet.GetBytesLeft() > 0)
{
// Decode the decimal argument string length. This length is the
// number of hex nibbles in the argument string value.
const uint32_t arg_len = packet.GetU32(UINT32_MAX);
if (arg_len == UINT32_MAX)
success = false;
else
{
// Make sure the argument hex string length is followed by a comma
if (packet.GetChar() != ',')
success = false;
else
{
// Decode the argument index. We ignore this really becuase
// who would really send down the arguments in a random order???
const uint32_t arg_idx = packet.GetU32(UINT32_MAX);
if (arg_idx == UINT32_MAX)
success = false;
else
{
// Make sure the argument index is followed by a comma
if (packet.GetChar() != ',')
success = false;
else
{
// Decode the argument string value from hex bytes
// back into a UTF8 string and make sure the length
// matches the one supplied in the packet
std::string arg;
if (packet.GetHexByteString(arg) != (arg_len / 2))
success = false;
else
{
// If there are any bytes lft
if (packet.GetBytesLeft())
{
if (packet.GetChar() != ',')
success = false;
}
if (success)
{
if (arg_idx == 0)
m_process_launch_info.GetExecutableFile().SetFile(arg.c_str(), false);
m_process_launch_info.GetArguments().AppendArgument(arg.c_str());
}
}
}
}
}
}
}
if (success)
{
// FIXME: remove linux restriction once eLaunchFlagDebug is supported
#if !defined (__linux__)
m_process_launch_info.GetFlags().Set (eLaunchFlagDebug);
#endif
m_process_launch_error = LaunchProcess ();
if (m_process_launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID)
{
return SendOKResponse ();
}
}
return SendErrorResponse (8);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_qC (StringExtractorGDBRemote &packet)
{
lldb::pid_t pid = m_process_launch_info.GetProcessID();
StreamString response;
response.Printf("QC%" PRIx64, pid);
if (m_is_platform)
{
// If we launch a process and this GDB server is acting as a platform,
// then we need to clear the process launch state so we can start
// launching another process. In order to launch a process a bunch or
// packets need to be sent: environment packets, working directory,
// disable ASLR, and many more settings. When we launch a process we
// then need to know when to clear this information. Currently we are
// selecting the 'qC' packet as that packet which seems to make the most
// sense.
if (pid != LLDB_INVALID_PROCESS_ID)
{
m_process_launch_info.Clear();
}
}
return SendPacketNoLock (response.GetData(), response.GetSize());
}
bool
GDBRemoteCommunicationServer::DebugserverProcessReaped (lldb::pid_t pid)
{
Mutex::Locker locker (m_spawned_pids_mutex);
FreePortForProcess(pid);
return m_spawned_pids.erase(pid) > 0;
}
bool
GDBRemoteCommunicationServer::ReapDebugserverProcess (void *callback_baton,
lldb::pid_t pid,
bool exited,
int signal, // Zero for no signal
int status) // Exit value of process if signal is zero
{
GDBRemoteCommunicationServer *server = (GDBRemoteCommunicationServer *)callback_baton;
server->DebugserverProcessReaped (pid);
return true;
}
bool
GDBRemoteCommunicationServer::DebuggedProcessReaped (lldb::pid_t pid)
{
// reap a process that we were debugging (but not debugserver)
Mutex::Locker locker (m_spawned_pids_mutex);
return m_spawned_pids.erase(pid) > 0;
}
bool
GDBRemoteCommunicationServer::ReapDebuggedProcess (void *callback_baton,
lldb::pid_t pid,
bool exited,
int signal, // Zero for no signal
int status) // Exit value of process if signal is zero
{
GDBRemoteCommunicationServer *server = (GDBRemoteCommunicationServer *)callback_baton;
server->DebuggedProcessReaped (pid);
return true;
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet)
{
#ifdef _WIN32
return SendErrorResponse(9);
#else
// Spawn a local debugserver as a platform so we can then attach or launch
// a process...
if (m_is_platform)
{
// Sleep and wait a bit for debugserver to start to listen...
ConnectionFileDescriptor file_conn;
Error error;
std::string hostname;
// TODO: /tmp/ should not be hardcoded. User might want to override /tmp
// with the TMPDIR environnement variable
packet.SetFilePos(::strlen ("qLaunchGDBServer;"));
std::string name;
std::string value;
uint16_t port = UINT16_MAX;
while (packet.GetNameColonValue(name, value))
{
if (name.compare ("host") == 0)
hostname.swap(value);
else if (name.compare ("port") == 0)
port = Args::StringToUInt32(value.c_str(), 0, 0);
}
if (port == UINT16_MAX)
port = GetNextAvailablePort();
// Spawn a new thread to accept the port that gets bound after
// binding to port 0 (zero).
if (error.Success())
{
// Spawn a debugserver and try to get the port it listens to.
ProcessLaunchInfo debugserver_launch_info;
if (hostname.empty())
hostname = "localhost";
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
if (log)
log->Printf("Launching debugserver with: %s:%u...\n", hostname.c_str(), port);
debugserver_launch_info.SetMonitorProcessCallback(ReapDebugserverProcess, this, false);
error = StartDebugserverProcess (hostname.empty() ? NULL : hostname.c_str(),
port,
debugserver_launch_info,
port);
lldb::pid_t debugserver_pid = debugserver_launch_info.GetProcessID();
if (debugserver_pid != LLDB_INVALID_PROCESS_ID)
{
Mutex::Locker locker (m_spawned_pids_mutex);
m_spawned_pids.insert(debugserver_pid);
if (port > 0)
AssociatePortWithProcess(port, debugserver_pid);
}
else
{
if (port > 0)
FreePort (port);
}
if (error.Success())
{
char response[256];
const int response_len = ::snprintf (response, sizeof(response), "pid:%" PRIu64 ";port:%u;", debugserver_pid, port + m_port_offset);
assert (response_len < sizeof(response));
PacketResult packet_result = SendPacketNoLock (response, response_len);
if (packet_result != PacketResult::Success)
{
if (debugserver_pid != LLDB_INVALID_PROCESS_ID)
::kill (debugserver_pid, SIGINT);
}
return packet_result;
}
}
}
return SendErrorResponse (9);
#endif
}
bool
GDBRemoteCommunicationServer::KillSpawnedProcess (lldb::pid_t pid)
{
// make sure we know about this process
{
Mutex::Locker locker (m_spawned_pids_mutex);
if (m_spawned_pids.find(pid) == m_spawned_pids.end())
return false;
}
// first try a SIGTERM (standard kill)
Host::Kill (pid, SIGTERM);
// check if that worked
for (size_t i=0; i<10; ++i)
{
{
Mutex::Locker locker (m_spawned_pids_mutex);
if (m_spawned_pids.find(pid) == m_spawned_pids.end())
{
// it is now killed
return true;
}
}
usleep (10000);
}
// check one more time after the final usleep
{
Mutex::Locker locker (m_spawned_pids_mutex);
if (m_spawned_pids.find(pid) == m_spawned_pids.end())
return true;
}
// the launched process still lives. Now try killling it again,
// this time with an unblockable signal.
Host::Kill (pid, SIGKILL);
for (size_t i=0; i<10; ++i)
{
{
Mutex::Locker locker (m_spawned_pids_mutex);
if (m_spawned_pids.find(pid) == m_spawned_pids.end())
{
// it is now killed
return true;
}
}
usleep (10000);
}
// check one more time after the final usleep
// Scope for locker
{
Mutex::Locker locker (m_spawned_pids_mutex);
if (m_spawned_pids.find(pid) == m_spawned_pids.end())
return true;
}
// no luck - the process still lives
return false;
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_qKillSpawnedProcess (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen ("qKillSpawnedProcess:"));
lldb::pid_t pid = packet.GetU64(LLDB_INVALID_PROCESS_ID);
// verify that we know anything about this pid.
// Scope for locker
{
Mutex::Locker locker (m_spawned_pids_mutex);
if (m_spawned_pids.find(pid) == m_spawned_pids.end())
{
// not a pid we know about
return SendErrorResponse (10);
}
}
// go ahead and attempt to kill the spawned process
if (KillSpawnedProcess (pid))
return SendOKResponse ();
else
return SendErrorResponse (11);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_k (StringExtractorGDBRemote &packet)
{
// ignore for now if we're lldb_platform
if (m_is_platform)
return SendUnimplementedResponse (packet.GetStringRef().c_str());
// shutdown all spawned processes
std::set<lldb::pid_t> spawned_pids_copy;
// copy pids
{
Mutex::Locker locker (m_spawned_pids_mutex);
spawned_pids_copy.insert (m_spawned_pids.begin (), m_spawned_pids.end ());
}
// nuke the spawned processes
for (auto it = spawned_pids_copy.begin (); it != spawned_pids_copy.end (); ++it)
{
lldb::pid_t spawned_pid = *it;
if (!KillSpawnedProcess (spawned_pid))
{
fprintf (stderr, "%s: failed to kill spawned pid %" PRIu64 ", ignoring.\n", __FUNCTION__, spawned_pid);
}
}
// TODO figure out how to shut down gracefully at this point
return SendOKResponse ();
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_qLaunchSuccess (StringExtractorGDBRemote &packet)
{
if (m_process_launch_error.Success())
return SendOKResponse();
StreamString response;
response.PutChar('E');
response.PutCString(m_process_launch_error.AsCString("<unknown error>"));
return SendPacketNoLock (response.GetData(), response.GetSize());
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_QEnvironment (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen ("QEnvironment:"));
const uint32_t bytes_left = packet.GetBytesLeft();
if (bytes_left > 0)
{
m_process_launch_info.GetEnvironmentEntries ().AppendArgument (packet.Peek());
return SendOKResponse ();
}
return SendErrorResponse (12);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_QLaunchArch (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen ("QLaunchArch:"));
const uint32_t bytes_left = packet.GetBytesLeft();
if (bytes_left > 0)
{
const char* arch_triple = packet.Peek();
ArchSpec arch_spec(arch_triple,NULL);
m_process_launch_info.SetArchitecture(arch_spec);
return SendOKResponse();
}
return SendErrorResponse(13);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_QSetDisableASLR (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen ("QSetDisableASLR:"));
if (packet.GetU32(0))
m_process_launch_info.GetFlags().Set (eLaunchFlagDisableASLR);
else
m_process_launch_info.GetFlags().Clear (eLaunchFlagDisableASLR);
return SendOKResponse ();
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_QSetWorkingDir (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen ("QSetWorkingDir:"));
std::string path;
packet.GetHexByteString(path);
if (m_is_platform)
{
#ifdef _WIN32
// Not implemented on Windows
return SendUnimplementedResponse("GDBRemoteCommunicationServer::Handle_QSetWorkingDir unimplemented");
#else
// If this packet is sent to a platform, then change the current working directory
if (::chdir(path.c_str()) != 0)
return SendErrorResponse(errno);
#endif
}
else
{
m_process_launch_info.SwapWorkingDirectory (path);
}
return SendOKResponse ();
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_qGetWorkingDir (StringExtractorGDBRemote &packet)
{
StreamString response;
if (m_is_platform)
{
// If this packet is sent to a platform, then change the current working directory
char cwd[PATH_MAX];
if (getcwd(cwd, sizeof(cwd)) == NULL)
{
return SendErrorResponse(errno);
}
else
{
response.PutBytesAsRawHex8(cwd, strlen(cwd));
return SendPacketNoLock(response.GetData(), response.GetSize());
}
}
else
{
const char *working_dir = m_process_launch_info.GetWorkingDirectory();
if (working_dir && working_dir[0])
{
response.PutBytesAsRawHex8(working_dir, strlen(working_dir));
return SendPacketNoLock(response.GetData(), response.GetSize());
}
else
{
return SendErrorResponse(14);
}
}
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_QSetSTDIN (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen ("QSetSTDIN:"));
ProcessLaunchInfo::FileAction file_action;
std::string path;
packet.GetHexByteString(path);
const bool read = false;
const bool write = true;
if (file_action.Open(STDIN_FILENO, path.c_str(), read, write))
{
m_process_launch_info.AppendFileAction(file_action);
return SendOKResponse ();
}
return SendErrorResponse (15);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_QSetSTDOUT (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen ("QSetSTDOUT:"));
ProcessLaunchInfo::FileAction file_action;
std::string path;
packet.GetHexByteString(path);
const bool read = true;
const bool write = false;
if (file_action.Open(STDOUT_FILENO, path.c_str(), read, write))
{
m_process_launch_info.AppendFileAction(file_action);
return SendOKResponse ();
}
return SendErrorResponse (16);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_QSetSTDERR (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen ("QSetSTDERR:"));
ProcessLaunchInfo::FileAction file_action;
std::string path;
packet.GetHexByteString(path);
const bool read = true;
const bool write = false;
if (file_action.Open(STDERR_FILENO, path.c_str(), read, write))
{
m_process_launch_info.AppendFileAction(file_action);
return SendOKResponse ();
}
return SendErrorResponse (17);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_QStartNoAckMode (StringExtractorGDBRemote &packet)
{
// Send response first before changing m_send_acks to we ack this packet
PacketResult packet_result = SendOKResponse ();
m_send_acks = false;
return packet_result;
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_qPlatform_mkdir (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen("qPlatform_mkdir:"));
mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX);
if (packet.GetChar() == ',')
{
std::string path;
packet.GetHexByteString(path);
Error error = Host::MakeDirectory(path.c_str(),mode);
if (error.Success())
return SendPacketNoLock ("OK", 2);
else
return SendErrorResponse(error.GetError());
}
return SendErrorResponse(20);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_qPlatform_chmod (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen("qPlatform_chmod:"));
mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX);
if (packet.GetChar() == ',')
{
std::string path;
packet.GetHexByteString(path);
Error error = Host::SetFilePermissions (path.c_str(), mode);
if (error.Success())
return SendPacketNoLock ("OK", 2);
else
return SendErrorResponse(error.GetError());
}
return SendErrorResponse(19);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_vFile_Open (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen("vFile:open:"));
std::string path;
packet.GetHexByteStringTerminatedBy(path,',');
if (!path.empty())
{
if (packet.GetChar() == ',')
{
uint32_t flags = packet.GetHexMaxU32(false, 0);
if (packet.GetChar() == ',')
{
mode_t mode = packet.GetHexMaxU32(false, 0600);
Error error;
int fd = ::open (path.c_str(), flags, mode);
const int save_errno = fd == -1 ? errno : 0;
StreamString response;
response.PutChar('F');
response.Printf("%i", fd);
if (save_errno)
response.Printf(",%i", save_errno);
return SendPacketNoLock(response.GetData(), response.GetSize());
}
}
}
return SendErrorResponse(18);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_vFile_Close (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen("vFile:close:"));
int fd = packet.GetS32(-1);
Error error;
int err = -1;
int save_errno = 0;
if (fd >= 0)
{
err = close(fd);
save_errno = err == -1 ? errno : 0;
}
else
{
save_errno = EINVAL;
}
StreamString response;
response.PutChar('F');
response.Printf("%i", err);
if (save_errno)
response.Printf(",%i", save_errno);
return SendPacketNoLock(response.GetData(), response.GetSize());
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_vFile_pRead (StringExtractorGDBRemote &packet)
{
#ifdef _WIN32
// Not implemented on Windows
return SendUnimplementedResponse("GDBRemoteCommunicationServer::Handle_vFile_pRead() unimplemented");
#else
StreamGDBRemote response;
packet.SetFilePos(::strlen("vFile:pread:"));
int fd = packet.GetS32(-1);
if (packet.GetChar() == ',')
{
uint64_t count = packet.GetU64(UINT64_MAX);
if (packet.GetChar() == ',')
{
uint64_t offset = packet.GetU64(UINT32_MAX);
if (count == UINT64_MAX)
{
response.Printf("F-1:%i", EINVAL);
return SendPacketNoLock(response.GetData(), response.GetSize());
}
std::string buffer(count, 0);
const ssize_t bytes_read = ::pread (fd, &buffer[0], buffer.size(), offset);
const int save_errno = bytes_read == -1 ? errno : 0;
response.PutChar('F');
response.Printf("%zi", bytes_read);
if (save_errno)
response.Printf(",%i", save_errno);
else
{
response.PutChar(';');
response.PutEscapedBytes(&buffer[0], bytes_read);
}
return SendPacketNoLock(response.GetData(), response.GetSize());
}
}
return SendErrorResponse(21);
#endif
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_vFile_pWrite (StringExtractorGDBRemote &packet)
{
#ifdef _WIN32
return SendUnimplementedResponse("GDBRemoteCommunicationServer::Handle_vFile_pWrite() unimplemented");
#else
packet.SetFilePos(::strlen("vFile:pwrite:"));
StreamGDBRemote response;
response.PutChar('F');
int fd = packet.GetU32(UINT32_MAX);
if (packet.GetChar() == ',')
{
off_t offset = packet.GetU64(UINT32_MAX);
if (packet.GetChar() == ',')
{
std::string buffer;
if (packet.GetEscapedBinaryData(buffer))
{
const ssize_t bytes_written = ::pwrite (fd, buffer.data(), buffer.size(), offset);
const int save_errno = bytes_written == -1 ? errno : 0;
response.Printf("%zi", bytes_written);
if (save_errno)
response.Printf(",%i", save_errno);
}
else
{
response.Printf ("-1,%i", EINVAL);
}
return SendPacketNoLock(response.GetData(), response.GetSize());
}
}
return SendErrorResponse(27);
#endif
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_vFile_Size (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen("vFile:size:"));
std::string path;
packet.GetHexByteString(path);
if (!path.empty())
{
lldb::user_id_t retcode = Host::GetFileSize(FileSpec(path.c_str(), false));
StreamString response;
response.PutChar('F');
response.PutHex64(retcode);
if (retcode == UINT64_MAX)
{
response.PutChar(',');
response.PutHex64(retcode); // TODO: replace with Host::GetSyswideErrorCode()
}
return SendPacketNoLock(response.GetData(), response.GetSize());
}
return SendErrorResponse(22);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_vFile_Mode (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen("vFile:mode:"));
std::string path;
packet.GetHexByteString(path);
if (!path.empty())
{
Error error;
const uint32_t mode = File::GetPermissions(path.c_str(), error);
StreamString response;
response.Printf("F%u", mode);
if (mode == 0 || error.Fail())
response.Printf(",%i", (int)error.GetError());
return SendPacketNoLock(response.GetData(), response.GetSize());
}
return SendErrorResponse(23);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_vFile_Exists (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen("vFile:exists:"));
std::string path;
packet.GetHexByteString(path);
if (!path.empty())
{
bool retcode = Host::GetFileExists(FileSpec(path.c_str(), false));
StreamString response;
response.PutChar('F');
response.PutChar(',');
if (retcode)
response.PutChar('1');
else
response.PutChar('0');
return SendPacketNoLock(response.GetData(), response.GetSize());
}
return SendErrorResponse(24);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_vFile_symlink (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen("vFile:symlink:"));
std::string dst, src;
packet.GetHexByteStringTerminatedBy(dst, ',');
packet.GetChar(); // Skip ',' char
packet.GetHexByteString(src);
Error error = Host::Symlink(src.c_str(), dst.c_str());
StreamString response;
response.Printf("F%u,%u", error.GetError(), error.GetError());
return SendPacketNoLock(response.GetData(), response.GetSize());
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_vFile_unlink (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen("vFile:unlink:"));
std::string path;
packet.GetHexByteString(path);
Error error = Host::Unlink(path.c_str());
StreamString response;
response.Printf("F%u,%u", error.GetError(), error.GetError());
return SendPacketNoLock(response.GetData(), response.GetSize());
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_qPlatform_shell (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen("qPlatform_shell:"));
std::string path;
std::string working_dir;
packet.GetHexByteStringTerminatedBy(path,',');
if (!path.empty())
{
if (packet.GetChar() == ',')
{
// FIXME: add timeout to qPlatform_shell packet
// uint32_t timeout = packet.GetHexMaxU32(false, 32);
uint32_t timeout = 10;
if (packet.GetChar() == ',')
packet.GetHexByteString(working_dir);
int status, signo;
std::string output;
Error err = Host::RunShellCommand(path.c_str(),
working_dir.empty() ? NULL : working_dir.c_str(),
&status, &signo, &output, timeout);
StreamGDBRemote response;
if (err.Fail())
{
response.PutCString("F,");
response.PutHex32(UINT32_MAX);
}
else
{
response.PutCString("F,");
response.PutHex32(status);
response.PutChar(',');
response.PutHex32(signo);
response.PutChar(',');
response.PutEscapedBytes(output.c_str(), output.size());
}
return SendPacketNoLock(response.GetData(), response.GetSize());
}
}
return SendErrorResponse(24);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_vFile_Stat (StringExtractorGDBRemote &packet)
{
return SendUnimplementedResponse("GDBRemoteCommunicationServer::Handle_vFile_Stat() unimplemented");
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_vFile_MD5 (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen("vFile:MD5:"));
std::string path;
packet.GetHexByteString(path);
if (!path.empty())
{
uint64_t a,b;
StreamGDBRemote response;
if (Host::CalculateMD5(FileSpec(path.c_str(),false),a,b) == false)
{
response.PutCString("F,");
response.PutCString("x");
}
else
{
response.PutCString("F,");
response.PutHex64(a);
response.PutHex64(b);
}
return SendPacketNoLock(response.GetData(), response.GetSize());
}
return SendErrorResponse(25);
}