//===-- 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/Log.h" #include "lldb/Core/StreamString.h" #include "lldb/Host/TimeValue.h" // Project includes #include "ProcessGDBRemoteLog.h" using namespace lldb; using namespace lldb_private; //---------------------------------------------------------------------- // GDBRemoteCommunication constructor //---------------------------------------------------------------------- GDBRemoteCommunication::GDBRemoteCommunication(const char *comm_name, const char *listener_name) : Communication(comm_name), m_packet_timeout (1), m_rx_packet_listener (listener_name), m_sequence_mutex (Mutex::eMutexTypeRecursive), m_public_is_running (false), m_private_is_running (false), m_send_acks (true) { 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 (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)); if (log) log->Printf ("send packet: +"); ConnectionStatus status = eConnectionStatusSuccess; char ack_char = '+'; return Write (&ack_char, 1, status, NULL) == 1; } size_t GDBRemoteCommunication::SendNack () { LogSP log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); if (log) log->Printf ("send packet: -"); ConnectionStatus status = eConnectionStatusSuccess; char nack_char = '-'; return Write (&nack_char, 1, status, NULL) == 1; } 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)); LogSP log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); if (log) log->Printf ("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 (GetSendAcks ()) { if (GetAck () != '+') { printf("get ack failed..."); return 0; } } } else { LogSP log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); if (log) log->Printf ("error: failed to send packet: %s", packet.GetData()); } return bytes_written; } return 0; } char GDBRemoteCommunication::GetAck () { StringExtractorGDBRemote packet; if (WaitForPacket (packet, m_packet_timeout) == 1) return packet.GetChar(); return 0; } bool GDBRemoteCommunication::GetSequenceMutex (Mutex::Locker& locker) { return locker.TryLock (m_sequence_mutex.GetMutex()); } bool GDBRemoteCommunication::WaitForNotRunningPrivate (const TimeValue *timeout_ptr) { return m_private_is_running.WaitForValueEqualTo (false, timeout_ptr, NULL); } size_t GDBRemoteCommunication::WaitForPacket (StringExtractorGDBRemote &packet, uint32_t timeout_seconds) { Mutex::Locker locker(m_sequence_mutex); TimeValue timeout_time; timeout_time = TimeValue::Now(); timeout_time.OffsetWithSeconds (timeout_seconds); return WaitForPacketNoLock (packet, &timeout_time); } size_t GDBRemoteCommunication::WaitForPacket (StringExtractorGDBRemote &packet, const TimeValue* timeout_time_ptr) { Mutex::Locker locker(m_sequence_mutex); return WaitForPacketNoLock (packet, timeout_time_ptr); } size_t GDBRemoteCommunication::WaitForPacketNoLock (StringExtractorGDBRemote &packet, const TimeValue* timeout_time_ptr) { bool checksum_error = false; packet.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(); LogSP log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); if (log) log->Printf ("read packet: %s", packet_data); const size_t packet_size = event_bytes->GetByteSize(); if (packet_data && packet_size > 0) { std::string &packet_str = packet.GetStringRef(); if (packet_data[0] == '$') { bool success = false; if (packet_size < 4) ::fprintf (stderr, "Packet that starts with $ is too short: '%s'\n", packet_data); else if (packet_data[packet_size-3] != '#' || !::isxdigit (packet_data[packet_size-2]) || !::isxdigit (packet_data[packet_size-1])) ::fprintf (stderr, "Invalid checksum footer for packet: '%s'\n", packet_data); else success = true; if (success) packet_str.assign (packet_data + 1, packet_size - 4); if (GetSendAcks ()) { char packet_checksum = strtol (&packet_data[packet_size-2], NULL, 16); char actual_checksum = CalculcateChecksum (&packet_str[0], packet_str.size()); checksum_error = packet_checksum != actual_checksum; // Send the ack or nack if needed if (checksum_error || !success) SendNack(); else SendAck(); } } else { packet_str.assign (packet_data, packet_size); } return packet_str.size(); } } } else if (event_type | Communication::eBroadcastBitReadThreadDidExit) { // Our read thread exited on us so just fall through and return zero... Disconnect(); } } return 0; } void GDBRemoteCommunication::AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast, ConnectionStatus status) { // 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 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); } } }