blob: cac2101e4c051c6824bf5481dea26b447a8ac8c0 [file] [log] [blame]
//===-- 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
// C++ Includes
// Other libraries and framework includes
#include "lldb/Core/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/TimeValue.h"
// Project includes
#include "StringExtractorGDBRemote.h"
#include "ProcessGDBRemote.h"
#include "ProcessGDBRemoteLog.h"
using namespace lldb;
using namespace lldb_private;
//----------------------------------------------------------------------
// GDBRemoteCommunication constructor
//----------------------------------------------------------------------
GDBRemoteCommunication::GDBRemoteCommunication() :
Communication("gdb-remote.packets"),
m_send_acks (true),
m_rx_packet_listener ("gdbremote.rx_packet"),
m_sequence_mutex (Mutex::eMutexTypeRecursive),
m_is_running (false),
m_async_mutex (Mutex::eMutexTypeRecursive),
m_async_packet_predicate (false),
m_async_packet (),
m_async_response (),
m_async_timeout (UINT32_MAX),
m_async_signal (-1),
m_arch(),
m_os(),
m_vendor(),
m_byte_order(eByteOrderHost),
m_pointer_byte_size(0)
{
m_rx_packet_listener.StartListeningForEvents(this,
Communication::eBroadcastBitPacketAvailable |
Communication::eBroadcastBitReadThreadDidExit);
}
//----------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------
GDBRemoteCommunication::~GDBRemoteCommunication()
{
m_rx_packet_listener.StopListeningForEvents(this,
Communication::eBroadcastBitPacketAvailable |
Communication::eBroadcastBitReadThreadDidExit);
if (IsConnected())
{
StopReadThread();
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 (m_send_acks)
{
for (int i = 0; i < payload_length; ++i)
checksum += payload[i];
}
return checksum & 255;
}
size_t
GDBRemoteCommunication::SendAck (char ack_char)
{
Mutex::Locker locker(m_sequence_mutex);
ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "send packet: %c", ack_char);
ConnectionStatus status = eConnectionStatusSuccess;
return Write (&ack_char, 1, status, NULL) == 1;
}
size_t
GDBRemoteCommunication::SendPacketAndWaitForResponse
(
const char *payload,
StringExtractorGDBRemote &response,
uint32_t timeout_seconds,
bool send_async
)
{
return SendPacketAndWaitForResponse (payload,
::strlen (payload),
response,
timeout_seconds,
send_async);
}
size_t
GDBRemoteCommunication::SendPacketAndWaitForResponse
(
const char *payload,
size_t payload_length,
StringExtractorGDBRemote &response,
uint32_t timeout_seconds,
bool send_async
)
{
Mutex::Locker locker;
TimeValue timeout_time;
timeout_time = TimeValue::Now();
timeout_time.OffsetWithSeconds (timeout_seconds);
if (locker.TryLock (m_sequence_mutex.GetMutex()))
{
if (SendPacketNoLock (payload, strlen(payload)))
return WaitForPacketNoLock (response, &timeout_time);
}
else
{
if (send_async)
{
Mutex::Locker async_locker (m_async_mutex);
m_async_packet.assign(payload, payload_length);
m_async_timeout = timeout_seconds;
m_async_packet_predicate.SetValue (true, eBroadcastNever);
bool timed_out = false;
if (SendInterrupt(1, &timed_out))
{
if (m_async_packet_predicate.WaitForValueEqualTo (false, &timeout_time, &timed_out))
{
response = m_async_response;
return response.GetStringRef().size();
}
}
// if (timed_out)
// m_error.SetErrorString("Timeout.");
// else
// m_error.SetErrorString("Unknown error.");
}
else
{
// m_error.SetErrorString("Sequence mutex is locked.");
}
}
return 0;
}
//template<typename _Tp>
//class ScopedValueChanger
//{
//public:
// // Take a value reference and the value to assing it to when this class
// // instance goes out of scope.
// ScopedValueChanger (_Tp &value_ref, _Tp value) :
// m_value_ref (value_ref),
// m_value (value)
// {
// }
//
// // This object is going out of scope, change the value pointed to by
// // m_value_ref to the value we got during construction which was stored in
// // m_value;
// ~ScopedValueChanger ()
// {
// m_value_ref = m_value;
// }
//protected:
// _Tp &m_value_ref; // A reference to the value we wil change when this object destructs
// _Tp m_value; // The value to assign to m_value_ref when this goes out of scope.
//};
StateType
GDBRemoteCommunication::SendContinuePacketAndWaitForResponse
(
ProcessGDBRemote *process,
const char *payload,
size_t packet_length,
StringExtractorGDBRemote &response
)
{
Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS);
if (log)
log->Printf ("GDBRemoteCommunication::%s ()", __FUNCTION__);
Mutex::Locker locker(m_sequence_mutex);
m_is_running.SetValue (true, eBroadcastNever);
// ScopedValueChanger<bool> restore_running_to_false (m_is_running, false);
StateType state = eStateRunning;
if (SendPacket(payload, packet_length) == 0)
state = eStateInvalid;
while (state == eStateRunning)
{
if (log)
log->Printf ("GDBRemoteCommunication::%s () WaitForPacket(...)", __FUNCTION__);
if (WaitForPacket (response, (TimeValue*)NULL))
{
if (response.Empty())
state = eStateInvalid;
else
{
const char stop_type = response.GetChar();
if (log)
log->Printf ("GDBRemoteCommunication::%s () got '%c' packet", __FUNCTION__, stop_type);
switch (stop_type)
{
case 'T':
case 'S':
if (m_async_signal != -1)
{
// Save off the async signal we are supposed to send
const int async_signal = m_async_signal;
// Clear the async signal member so we don't end up
// sending the signal multiple times...
m_async_signal = -1;
// Check which signal we stopped with
uint8_t signo = response.GetHexU8(255);
if (signo == async_signal)
{
// We already stopped with a signal that we wanted
// to stop with, so we are done
response.SetFilePos (0);
}
else
{
// We stopped with a different signal that the one
// we wanted to stop with, so now we must resume
// with the signal we want
char signal_packet[32];
int signal_packet_len = 0;
signal_packet_len = ::snprintf (signal_packet,
sizeof (signal_packet),
"C%2.2x",
async_signal);
if (SendPacket(signal_packet, signal_packet_len) == 0)
{
state = eStateInvalid;
break;
}
else
continue;
}
}
else if (m_async_packet_predicate.GetValue())
{
// We are supposed to send an asynchronous packet while
// we are running.
m_async_response.Clear();
if (!m_async_packet.empty())
{
SendPacketAndWaitForResponse (m_async_packet.data(),
m_async_packet.size(),
m_async_response,
m_async_timeout,
false);
}
// Let the other thread that was trying to send the async
// packet know that the packet has been sent.
m_async_packet_predicate.SetValue(false, eBroadcastAlways);
// Continue again
if (SendPacket("c", 1) == 0)
{
state = eStateInvalid;
break;
}
else
continue;
}
// Stop with signal and thread info
state = eStateStopped;
break;
case 'W':
// process exited
state = eStateExited;
break;
case 'O':
// STDOUT
{
std::string inferior_stdout;
inferior_stdout.reserve(response.GetBytesLeft () / 2);
char ch;
while ((ch = response.GetHexU8()) != '\0')
inferior_stdout.append(1, ch);
process->AppendSTDOUT (inferior_stdout.c_str(), inferior_stdout.size());
}
break;
case 'E':
// ERROR
state = eStateInvalid;
break;
default:
if (log)
log->Printf ("GDBRemoteCommunication::%s () got unrecognized async packet: '%s'", __FUNCTION__, stop_type);
break;
}
}
}
else
{
if (log)
log->Printf ("GDBRemoteCommunication::%s () WaitForPacket(...) => false", __FUNCTION__);
state = eStateInvalid;
}
}
if (log)
log->Printf ("GDBRemoteCommunication::%s () => %s", __FUNCTION__, StateAsCString(state));
response.SetFilePos(0);
m_is_running.SetValue (false, eBroadcastOnChange);
return state;
}
size_t
GDBRemoteCommunication::SendPacket (const char *payload)
{
Mutex::Locker locker(m_sequence_mutex);
return SendPacketNoLock (payload, ::strlen (payload));
}
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));
ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "send packet: %s", packet.GetData());
ConnectionStatus status = eConnectionStatusSuccess;
size_t bytes_written = Write (packet.GetData(), packet.GetSize(), status, NULL);
if (bytes_written == packet.GetSize())
{
if (m_send_acks)
GetAck (1) == '+';
}
return bytes_written;
}
//m_error.SetErrorString("Not connected.");
return 0;
}
char
GDBRemoteCommunication::GetAck (uint32_t timeout_seconds)
{
StringExtractorGDBRemote response;
if (WaitForPacket (response, timeout_seconds) == 1)
return response.GetChar();
return 0;
}
bool
GDBRemoteCommunication::GetSequenceMutex (Mutex::Locker& locker)
{
return locker.TryLock (m_sequence_mutex.GetMutex());
}
bool
GDBRemoteCommunication::SendAsyncSignal (int signo)
{
m_async_signal = signo;
bool timed_out = false;
if (SendInterrupt(1, &timed_out))
return true;
m_async_signal = -1;
return false;
}
bool
GDBRemoteCommunication::SendInterrupt (uint32_t seconds_to_wait_for_stop, bool *timed_out)
{
if (timed_out)
*timed_out = false;
if (IsConnected() && IsRunning())
{
// Only send an interrupt if our debugserver is running...
if (m_sequence_mutex.TryLock() != 0)
{
// Someone has the mutex locked waiting for a response or for the
// inferior to stop, so send the interrupt on the down low...
char ctrl_c = '\x03';
ConnectionStatus status = eConnectionStatusSuccess;
TimeValue timeout;
if (seconds_to_wait_for_stop)
{
timeout = TimeValue::Now();
timeout.OffsetWithSeconds (seconds_to_wait_for_stop);
}
ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "send packet: \\x03");
if (Write (&ctrl_c, 1, status, NULL) > 0)
{
if (seconds_to_wait_for_stop)
m_is_running.WaitForValueEqualTo (false, &timeout, timed_out);
return true;
}
}
}
return false;
}
size_t
GDBRemoteCommunication::WaitForPacket (StringExtractorGDBRemote &response, uint32_t timeout_seconds)
{
TimeValue timeout_time;
timeout_time = TimeValue::Now();
timeout_time.OffsetWithSeconds (timeout_seconds);
return WaitForPacketNoLock (response, &timeout_time);
}
size_t
GDBRemoteCommunication::WaitForPacket (StringExtractorGDBRemote &response, TimeValue* timeout_time_ptr)
{
Mutex::Locker locker(m_sequence_mutex);
return WaitForPacketNoLock (response, timeout_time_ptr);
}
size_t
GDBRemoteCommunication::WaitForPacketNoLock (StringExtractorGDBRemote &response, TimeValue* timeout_time_ptr)
{
bool checksum_error = false;
response.Clear ();
EventSP event_sp;
if (m_rx_packet_listener.WaitForEvent (timeout_time_ptr, event_sp))
{
const uint32_t event_type = event_sp->GetType();
if (event_type | Communication::eBroadcastBitPacketAvailable)
{
const EventDataBytes *event_bytes = EventDataBytes::GetEventDataFromEvent(event_sp.get());
if (event_bytes)
{
const char * packet_data = (const char *)event_bytes->GetBytes();
ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "read packet: %s", packet_data);
const size_t packet_size = event_bytes->GetByteSize();
if (packet_data && packet_size > 0)
{
std::string &response_str = response.GetStringRef();
if (packet_data[0] == '$')
{
assert (packet_size >= 4); // Must have at least '$#CC' where CC is checksum
assert (packet_data[packet_size-3] == '#');
assert (::isxdigit (packet_data[packet_size-2])); // Must be checksum hex byte
assert (::isxdigit (packet_data[packet_size-1])); // Must be checksum hex byte
response_str.assign (packet_data + 1, packet_size - 4);
if (m_send_acks)
{
char packet_checksum = strtol (&packet_data[packet_size-2], NULL, 16);
char actual_checksum = CalculcateChecksum (response_str.data(), response_str.size());
checksum_error = packet_checksum != actual_checksum;
// Send the ack or nack if needed
if (checksum_error)
SendAck('-');
else
SendAck('+');
}
}
else
{
response_str.assign (packet_data, packet_size);
}
return response_str.size();
}
}
}
else if (Communication::eBroadcastBitReadThreadDidExit)
{
// Our read thread exited on us so just fall through and return zero...
}
}
return 0;
}
void
GDBRemoteCommunication::AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast)
{
// Put the packet data into the buffer in a thread safe fashion
Mutex::Locker locker(m_bytes_mutex);
m_bytes.append ((const char *)src, src_len);
// Parse up the packets into gdb remote packets
while (!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 end_idx = 0;
switch (m_bytes[0])
{
case '+': // Look for ack
case '-': // Look for cancel
case '\x03': // ^C to halt target
end_idx = 1; // The command is one byte long...
break;
case '$':
// Look for a standard gdb packet?
end_idx = m_bytes.find('#');
if (end_idx != std::string::npos)
{
if (end_idx + 2 < m_bytes.size())
{
end_idx += 3;
}
else
{
// Checksum bytes aren't all here yet
end_idx = std::string::npos;
}
}
break;
default:
break;
}
if (end_idx == std::string::npos)
{
//ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS | GDBR_LOG_VERBOSE, "GDBRemoteCommunication::%s packet not yet complete: '%s'",__FUNCTION__, m_bytes.c_str());
return;
}
else if (end_idx > 0)
{
// We have a valid packet...
assert (end_idx <= m_bytes.size());
std::auto_ptr<EventDataBytes> event_bytes_ap (new EventDataBytes (&m_bytes[0], end_idx));
ProcessGDBRemoteLog::LogIf (GDBR_LOG_COMM, "got full packet: %s", event_bytes_ap->GetBytes());
BroadcastEvent (eBroadcastBitPacketAvailable, event_bytes_ap.release());
m_bytes.erase(0, end_idx);
}
else
{
assert (1 <= m_bytes.size());
ProcessGDBRemoteLog::LogIf (GDBR_LOG_COMM, "GDBRemoteCommunication::%s tossing junk byte at %c",__FUNCTION__, m_bytes[0]);
m_bytes.erase(0, 1);
}
}
}
lldb::pid_t
GDBRemoteCommunication::GetCurrentProcessID (uint32_t timeout_seconds)
{
StringExtractorGDBRemote response;
if (SendPacketAndWaitForResponse("qC", strlen("qC"), response, timeout_seconds, false))
{
if (response.GetChar() == 'Q')
if (response.GetChar() == 'C')
return response.GetHexMaxU32 (false, LLDB_INVALID_PROCESS_ID);
}
return LLDB_INVALID_PROCESS_ID;
}
bool
GDBRemoteCommunication::GetLaunchSuccess (uint32_t timeout_seconds, std::string &error_str)
{
error_str.clear();
StringExtractorGDBRemote response;
if (SendPacketAndWaitForResponse("qLaunchSuccess", strlen("qLaunchSuccess"), response, timeout_seconds, false))
{
if (response.IsOKPacket())
return true;
if (response.GetChar() == 'E')
{
// A string the describes what failed when launching...
error_str = response.GetStringRef().substr(1);
}
else
{
error_str.assign ("unknown error occurred launching process");
}
}
else
{
error_str.assign ("failed to send the qLaunchSuccess packet");
}
return false;
}
int
GDBRemoteCommunication::SendArgumentsPacket (char const *argv[], uint32_t timeout_seconds)
{
if (argv && argv[0])
{
StreamString packet;
packet.PutChar('A');
const char *arg;
for (uint32_t i = 0; (arg = argv[i]) != NULL; ++i)
{
const int arg_len = strlen(arg);
if (i > 0)
packet.PutChar(',');
packet.Printf("%i,%i,", arg_len * 2, i);
packet.PutBytesAsRawHex8(arg, arg_len, eByteOrderHost, eByteOrderHost);
}
StringExtractorGDBRemote response;
if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, timeout_seconds, false))
{
if (response.IsOKPacket())
return 0;
uint8_t error = response.GetError();
if (error)
return error;
}
}
return -1;
}
int
GDBRemoteCommunication::SendEnvironmentPacket (char const *name_equal_value, uint32_t timeout_seconds)
{
if (name_equal_value && name_equal_value[0])
{
StreamString packet;
packet.Printf("QEnvironment:%s", name_equal_value);
StringExtractorGDBRemote response;
if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, timeout_seconds, false))
{
if (response.IsOKPacket())
return 0;
uint8_t error = response.GetError();
if (error)
return error;
}
}
return -1;
}
bool
GDBRemoteCommunication::GetHostInfo (uint32_t timeout_seconds)
{
m_arch.Clear();
m_os.Clear();
m_vendor.Clear();
m_byte_order = eByteOrderHost;
m_pointer_byte_size = 0;
StringExtractorGDBRemote response;
if (SendPacketAndWaitForResponse ("qHostInfo", response, timeout_seconds, false))
{
if (response.IsUnsupportedPacket())
return false;
std::string name;
std::string value;
while (response.GetNameColonValue(name, value))
{
if (name.compare("cputype") == 0)
{
// exception type in big endian hex
m_arch.SetCPUType(Args::StringToUInt32 (value.c_str(), LLDB_INVALID_CPUTYPE, 0));
}
else if (name.compare("cpusubtype") == 0)
{
// exception count in big endian hex
m_arch.SetCPUSubtype(Args::StringToUInt32 (value.c_str(), 0, 0));
}
else if (name.compare("ostype") == 0)
{
// exception data in big endian hex
m_os.SetCString(value.c_str());
}
else if (name.compare("vendor") == 0)
{
m_vendor.SetCString(value.c_str());
}
else if (name.compare("endian") == 0)
{
if (value.compare("little") == 0)
m_byte_order = eByteOrderLittle;
else if (value.compare("big") == 0)
m_byte_order = eByteOrderBig;
else if (value.compare("pdp") == 0)
m_byte_order = eByteOrderPDP;
}
else if (name.compare("ptrsize") == 0)
{
m_pointer_byte_size = Args::StringToUInt32 (value.c_str(), 0, 0);
}
}
}
return HostInfoIsValid();
}
int
GDBRemoteCommunication::SendAttach
(
lldb::pid_t pid,
uint32_t timeout_seconds,
StringExtractorGDBRemote& response
)
{
if (pid != LLDB_INVALID_PROCESS_ID)
{
StreamString packet;
packet.Printf("vAttach;%x", pid);
if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, timeout_seconds, false))
{
if (response.IsErrorPacket())
return response.GetError();
return 0;
}
}
return -1;
}
const lldb_private::ArchSpec &
GDBRemoteCommunication::GetHostArchitecture ()
{
if (!HostInfoIsValid ())
GetHostInfo (1);
return m_arch;
}
const lldb_private::ConstString &
GDBRemoteCommunication::GetOSString ()
{
if (!HostInfoIsValid ())
GetHostInfo (1);
return m_os;
}
const lldb_private::ConstString &
GDBRemoteCommunication::GetVendorString()
{
if (!HostInfoIsValid ())
GetHostInfo (1);
return m_vendor;
}
lldb::ByteOrder
GDBRemoteCommunication::GetByteOrder ()
{
if (!HostInfoIsValid ())
GetHostInfo (1);
return m_byte_order;
}
uint32_t
GDBRemoteCommunication::GetAddressByteSize ()
{
if (!HostInfoIsValid ())
GetHostInfo (1);
return m_pointer_byte_size;
}
addr_t
GDBRemoteCommunication::AllocateMemory (size_t size, uint32_t permissions, uint32_t timeout_seconds)
{
char packet[64];
::snprintf (packet, sizeof(packet), "_M%zx,%s%s%s", size,
permissions & lldb::ePermissionsReadable ? "r" : "",
permissions & lldb::ePermissionsWritable ? "w" : "",
permissions & lldb::ePermissionsExecutable ? "x" : "");
StringExtractorGDBRemote response;
if (SendPacketAndWaitForResponse (packet, response, timeout_seconds, false))
{
if (!response.IsErrorPacket())
return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS);
}
return LLDB_INVALID_ADDRESS;
}
bool
GDBRemoteCommunication::DeallocateMemory (addr_t addr, uint32_t timeout_seconds)
{
char packet[64];
snprintf(packet, sizeof(packet), "_m%llx", (uint64_t)addr);
StringExtractorGDBRemote response;
if (SendPacketAndWaitForResponse (packet, response, timeout_seconds, false))
{
if (!response.IsOKPacket())
return true;
}
return false;
}