Index: include/lldb/Core/Communication.h =================================================================== --- include/lldb/Core/Communication.h +++ include/lldb/Core/Communication.h @@ -93,6 +93,7 @@ eBroadcastBitReadThreadShouldExit = (1u << 3), ///< Sent by clients that need to cancel the read thread. eBroadcastBitPacketAvailable = (1u << 4), ///< Sent when data received makes a complete packet. eBroadcastBitNoMorePendingInput = (1u << 5), ///< Sent by the read thread to indicate all pending input has been processed. + eBroadcastBitGdbReadThreadGotNotify = (1u << 6), ///< Send when we received a notify packet kLoUserBroadcastBit = (1u << 16),///< Subclasses can used bits 31:16 for any needed events. kHiUserBroadcastBit = (1u << 31), eAllEventBits = 0xffffffff Index: source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp =================================================================== --- source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp +++ source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp @@ -393,6 +393,11 @@ const ConnectionStatus status = m_gdb_client.Connect(url, &error); if (status == eConnectionStatusSuccess) { + + // Start the communications read thread so all incoming data can be + // parsed into packets and queued as they arrive. + m_gdb_client.StartReadThread(); + if (m_gdb_client.HandshakeWithServer(&error)) { m_gdb_client.GetHostInfo(); Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h @@ -14,6 +14,7 @@ // C++ Includes #include #include +#include // Other libraries and framework includes // Project includes @@ -163,7 +164,11 @@ void DumpHistory(Stream &strm); - + + // Pop a packet from the queue in a thread safe manner + PacketResult + ReadPacket(StringExtractorGDBRemote &out, uint32_t timeout_usec, const bool ignore_acks = false); + protected: class History @@ -313,7 +318,23 @@ static lldb::thread_result_t ListenThread (lldb::thread_arg_t arg); + // GDB-Remote read thread + // . this thread constantly trys to read from the communication + // class and stores all packets received in a queue. The usual + // threads read requests simply pop packets off the queue in the + // usual order. + // This setup allows us to intercept and handle async packets, such + // as the notify packet. + + // This method is defined as part of communication.h + // when the read thread gets any bytes it will pass them on to this function + virtual void AppendBytesToCache(const uint8_t * bytes, size_t len, bool broadcast, lldb::ConnectionStatus status); + private: + // The packet queue + std::queue m_packet_queue; + lldb_private::Mutex m_packet_queue_mutex; + HostThread m_listen_thread; std::string m_listen_url; Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -168,6 +168,11 @@ { Disconnect(); } + + // Stop the communications read thread which is used to parse all + // incoming packets. This function will block until the read + // thread returns. + StopReadThread(); } char @@ -292,7 +297,7 @@ GDBRemoteCommunication::GetAck () { StringExtractorGDBRemote packet; - PacketResult result = WaitForPacketWithTimeoutMicroSecondsNoLock (packet, GetPacketTimeoutInMicroSeconds ()); + PacketResult result = ReadPacket(packet, GetPacketTimeoutInMicroSeconds()); if (result == PacketResult::Success) { if (packet.GetResponseType() == StringExtractorGDBRemote::ResponseType::eAck) @@ -320,6 +325,8 @@ return m_private_is_running.WaitForValueEqualTo (false, timeout_ptr, NULL); } +// WaitForPacketWithTimeoutMicroSecondsNoLock() has been replaced by ReadPacket() in all cases since packets are +// now parsed by the communication read thread and queued. This was change took place to support non-stop mode. GDBRemoteCommunication::PacketResult GDBRemoteCommunication::WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtractorGDBRemote &packet, uint32_t timeout_usec) { @@ -993,3 +1000,115 @@ { m_gdb_comm.SetPacketTimeout (m_saved_timeout); } + +/// This function is called via the Communications class read thread when bytes become available +// for this connection. This function will consume all incoming bytes and try to parse whole +// packets as they become available. Full packets are placed in a queue, so that all packet +// requests can simply pop from this queue. Async notification packets will be dispatched +// immediately to the ProcessGDBRemote Async thread via and event. +void GDBRemoteCommunication::AppendBytesToCache(const uint8_t * bytes, size_t len, bool broadcast, lldb::ConnectionStatus status) +{ + StringExtractorGDBRemote packet; + + while (true) + { + PacketType type = CheckForPacket(bytes, len, packet); + + // scrub the data so we do not pass it back to CheckForPacket + // on future passes of the loop + bytes = nullptr; + len = 0; + + // we may have received no packet so lets bail out + if (type == PacketType::Invalid) + break; + + if (type == PacketType::Standard) + { + // scope for the mutex + { + // lock down the packet queue + Mutex::Locker locker(m_packet_queue_mutex); + // push a new packet into the queue + m_packet_queue.push(packet); + } + } + + if (type == PacketType::Notify) + { + // put this packet into an event + const char *pdata = packet.GetStringRef().c_str(); + lldb_private::EventDataBytes edata(pdata); + + // as the communication class, we are a broadcaster and the + // async thread is tuned to listen to us + BroadcastEvent( + eBroadcastBitGdbReadThreadGotNotify, + new EventDataBytes(pdata)); + } + } +} + +// This function is called when a packet is requested. +// A whole packets is popped from the packet queue and returned to the caller. +// Packets are placed into this queue from the communication read thread. +// See GDBRemoteCommunication::AppendBytesToCache. +GDBRemoteCommunication::PacketResult +GDBRemoteCommunication::ReadPacket +( + StringExtractorGDBRemote &out, + uint32_t timeout_usec, + const bool ignore_acks +) +{ + // get the timeout in clock ticks + if (timeout_usec < UINT32_MAX) + timeout_usec = (timeout_usec * CLOCKS_PER_SEC) / 1000000; + + // get the starting time of the clock + clock_t start = clock(); + clock_t dif = start; + + do + { + // scope for the mutex + { + // lock down the packet queue + Mutex::Locker locker(m_packet_queue_mutex); + + if (m_packet_queue.size() > 0) + { + // get the front element of the queue + out = m_packet_queue.front(); + + // Don't return ack packets. This is useful when we have multiple threads, to stop + // one thread reading the ack that the other is waiting for. + const StringExtractorGDBRemote::ServerPacketType packet_type = out.GetServerPacketType(); + if (ignore_acks && (packet_type == StringExtractorGDBRemote::eServerPacketType_nack || + packet_type == StringExtractorGDBRemote::eServerPacketType_ack)) + { + continue; + } + + // remove the front element + m_packet_queue.pop(); + // we got a packet + return PacketResult::Success; + } + } + + if (!IsConnected()) + return PacketResult::ErrorDisconnected; + + // sleep to avoid starving other threads of the queue + usleep(500); + + // get the current time difference + dif = clock() - start; + + // while in the timeout period + // check for a timeout + } while (dif < timeout_usec || timeout_usec == UINT32_MAX); + + return PacketResult::ErrorReplyTimeout; +} Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -145,7 +145,7 @@ PacketResult packet_result = PacketResult::Success; const uint32_t timeout_usec = 10 * 1000; // Wait for 10 ms for a response while (packet_result == PacketResult::Success) - packet_result = WaitForPacketWithTimeoutMicroSecondsNoLock (response, timeout_usec); + packet_result = ReadPacket(response, timeout_usec); // The return value from QueryNoAckModeSupported() is true if the packet // was sent and _any_ response (including UNIMPLEMENTED) was received), @@ -638,7 +638,7 @@ { PacketResult packet_result = SendPacketNoLock (payload, payload_length); if (packet_result == PacketResult::Success) - packet_result = WaitForPacketWithTimeoutMicroSecondsNoLock (response, GetPacketTimeoutInMicroSeconds ()); + packet_result = ReadPacket(response, GetPacketTimeoutInMicroSeconds()); return packet_result; } @@ -888,7 +888,7 @@ if (log) log->Printf ("GDBRemoteCommunicationClient::%s () WaitForPacket(%s)", __FUNCTION__, continue_packet.c_str()); - if (WaitForPacketWithTimeoutMicroSecondsNoLock(response, UINT32_MAX) == PacketResult::Success) + if (ReadPacket(response, UINT32_MAX) == PacketResult::Success) { if (response.Empty()) state = eStateInvalid; @@ -945,7 +945,7 @@ // packet to make sure it doesn't get in the way StringExtractorGDBRemote extra_stop_reply_packet; uint32_t timeout_usec = 1000; - if (WaitForPacketWithTimeoutMicroSecondsNoLock (extra_stop_reply_packet, timeout_usec) == PacketResult::Success) + if (ReadPacket(extra_stop_reply_packet, timeout_usec) == PacketResult::Success) { switch (extra_stop_reply_packet.GetChar()) { Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp @@ -51,7 +51,8 @@ { StringExtractorGDBRemote packet; - PacketResult packet_result = WaitForPacketWithTimeoutMicroSecondsNoLock (packet, timeout_usec); + // Read a non-ack packet from the packet queue. + PacketResult packet_result = ReadPacket(packet, timeout_usec, m_send_acks); if (packet_result == PacketResult::Success) { const StringExtractorGDBRemote::ServerPacketType packet_type = packet.GetServerPacketType (); Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp @@ -950,9 +950,12 @@ GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerCommon::Handle_QStartNoAckMode (StringExtractorGDBRemote &packet) { - // Send response first before changing m_send_acks to we ack this packet - PacketResult packet_result = SendOKResponse (); + + // Disable acks before sending the response, so ignoring matching ack. + // Otherwise there is a race condition where the packet read thread acks a packet + // recieved after OK is sent, but before m_send_acks can be set. m_send_acks = false; + PacketResult packet_result = SendOKResponse (); return packet_result; } Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -1153,6 +1153,10 @@ return error; } + // Start the communications read thread so all incoming data can be + // parsed into packets and queued as they arrive. + m_gdb_comm.StartReadThread(); + // We always seem to be able to open a connection to a local port // so we need to make sure we can then send data to it. If we can't // then we aren't actually connected to anything, so try and do the Index: tools/lldb-server/lldb-gdbserver.cpp =================================================================== --- tools/lldb-server/lldb-gdbserver.cpp +++ tools/lldb-server/lldb-gdbserver.cpp @@ -476,6 +476,11 @@ if (gdb_server.IsConnected()) { + + // Start the communications read thread so all incoming data can be + // parsed into packets and queued as they arrive. + gdb_server.StartReadThread(); + // After we connected, we need to get an initial ack from... if (gdb_server.HandshakeWithClient(&error)) { Index: tools/lldb-server/lldb-platform.cpp =================================================================== --- tools/lldb-server/lldb-platform.cpp +++ tools/lldb-server/lldb-platform.cpp @@ -309,6 +309,10 @@ if (platform.IsConnected()) { + // Start the communications read thread so all incoming data can be + // parsed into packets and queued as they arrive. + platform.StartReadThread(); + // After we connected, we need to get an initial ack from... if (platform.HandshakeWithClient(&error)) {