diff options
Diffstat (limited to 'lldb/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp')
-rw-r--r-- | lldb/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/lldb/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp b/lldb/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp new file mode 100644 index 00000000000..c3ac577febd --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp @@ -0,0 +1,403 @@ +//===-- CommunicationKDP.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "CommunicationKDP.h" + +// C Includes +#include <limits.h> +#include <string.h> + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/Log.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" +#include "Utility/StringExtractor.h" + +// Project includes +#include "ProcessKDPLog.h" + +#define DEBUGSERVER_BASENAME "debugserver" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// CommunicationKDP constructor +//---------------------------------------------------------------------- +CommunicationKDP::CommunicationKDP (const char *comm_name) : + Communication(comm_name), + m_packet_timeout (1), + m_sequence_mutex (Mutex::eMutexTypeRecursive), + m_public_is_running (false), + m_private_is_running (false), + m_send_acks (true) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommunicationKDP::~CommunicationKDP() +{ + if (IsConnected()) + { + Disconnect(); + } +} + +char +CommunicationKDP::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 +CommunicationKDP::SendAck () +{ + LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); + if (log) + log->Printf ("send packet: +"); + ConnectionStatus status = eConnectionStatusSuccess; + char ack_char = '+'; + return Write (&ack_char, 1, status, NULL); +} + +size_t +CommunicationKDP::SendNack () +{ + LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); + if (log) + log->Printf ("send packet: -"); + ConnectionStatus status = eConnectionStatusSuccess; + char nack_char = '-'; + return Write (&nack_char, 1, status, NULL); +} + +size_t +CommunicationKDP::SendPacket (lldb_private::StreamString &payload) +{ + Mutex::Locker locker(m_sequence_mutex); + const std::string &p (payload.GetString()); + return SendPacketNoLock (p.c_str(), p.size()); +} + +size_t +CommunicationKDP::SendPacket (const char *payload) +{ + Mutex::Locker locker(m_sequence_mutex); + return SendPacketNoLock (payload, ::strlen (payload)); +} + +size_t +CommunicationKDP::SendPacket (const char *payload, size_t payload_length) +{ + Mutex::Locker locker(m_sequence_mutex); + return SendPacketNoLock (payload, payload_length); +} + +size_t +CommunicationKDP::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 (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); + if (log) + log->Printf ("send packet: %.*s", (int)packet.GetSize(), 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 (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); + if (log) + log->Printf ("error: failed to send packet: %.*s", (int)packet.GetSize(), packet.GetData()); + } + return bytes_written; + } + return 0; +} + +char +CommunicationKDP::GetAck () +{ + StringExtractor packet; + if (WaitForPacketWithTimeoutMicroSeconds (packet, GetPacketTimeoutInMicroSeconds ()) == 1) + return packet.GetChar(); + return 0; +} + +bool +CommunicationKDP::GetSequenceMutex (Mutex::Locker& locker) +{ + return locker.TryLock (m_sequence_mutex.GetMutex()); +} + + +bool +CommunicationKDP::WaitForNotRunningPrivate (const TimeValue *timeout_ptr) +{ + return m_private_is_running.WaitForValueEqualTo (false, timeout_ptr, NULL); +} + +size_t +CommunicationKDP::WaitForPacketWithTimeoutMicroSeconds (StringExtractor &packet, uint32_t timeout_usec) +{ + Mutex::Locker locker(m_sequence_mutex); + return WaitForPacketWithTimeoutMicroSecondsNoLock (packet, timeout_usec); +} + +size_t +CommunicationKDP::WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtractor &packet, uint32_t timeout_usec) +{ + uint8_t buffer[8192]; + Error error; + + LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS | KDP_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; + 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 +CommunicationKDP::CheckForPacket (const uint8_t *src, size_t src_len, StringExtractor &packet) +{ + // Put the packet data into the buffer in a thread safe fashion + Mutex::Locker locker(m_bytes_mutex); + + LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); + + if (src && src_len > 0) + { + if (log && log->GetVerbose()) + { + StreamString s; + log->Printf ("CommunicationKDP::%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 ("CommunicationKDP::%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(); + 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(); + } + if (success) + { + if (log) + log->Printf ("read packet: %.*s", (int)(total_length), m_bytes.c_str()); + } + } + else + { + success = false; + if (log) + log->Printf ("error: invalid checksum in packet: '%s'\n", (int)(total_length), m_bytes.c_str()); + } + } + m_bytes.erase(0, total_length); + packet.SetFilePos(0); + return success; + } + } + packet.Clear(); + return false; +} + |