Skip to content

Commit fab40d3

Browse files
committedJun 16, 2015
Add Read Thread to GDBRemoteCommunication
In order to support asynchronous notifications for non-stop mode this patch adds a packet read thread. This is done by implementing AppendBytesToCache() from the communications class, which continually reads packets into a packet queue. To initialize this thread StartReadThread() must be called by the client, so since llgs and platform tools use the GBDRemoteCommunicatos code they must also call this function as well as ProcessGDBRemote. When the read thread detects an async notify packet it broadcasts this event, where the matching listener will be added in the next non-stop patch. Packets are now accessed by calling ReadPacket() which pops a packet from the queue, instead of using WaitForPacketWithTimeoutMicroSecondsNoLock() Reviewers: vharron, clayborg Subscribers: lldb-commits, labath, ted, domipheus, deepak2427 Differential Revision: http://reviews.llvm.org/D10085 llvm-svn: 239824
1 parent c81f450 commit fab40d3

File tree

4 files changed

+167
-8
lines changed

4 files changed

+167
-8
lines changed
 

‎lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp

+113-1
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,12 @@ GDBRemoteCommunication::~GDBRemoteCommunication()
171171
{
172172
Disconnect();
173173
}
174+
175+
// Stop the communications read thread which is used to parse all
176+
// incoming packets. This function will block until the read
177+
// thread returns.
178+
if (m_read_thread_enabled)
179+
StopReadThread();
174180
}
175181

176182
char
@@ -295,7 +301,7 @@ GDBRemoteCommunication::PacketResult
295301
GDBRemoteCommunication::GetAck ()
296302
{
297303
StringExtractorGDBRemote packet;
298-
PacketResult result = WaitForPacketWithTimeoutMicroSecondsNoLock (packet, GetPacketTimeoutInMicroSeconds (), false);
304+
PacketResult result = ReadPacket (packet, GetPacketTimeoutInMicroSeconds (), false);
299305
if (result == PacketResult::Success)
300306
{
301307
if (packet.GetResponseType() == StringExtractorGDBRemote::ResponseType::eAck)
@@ -323,6 +329,62 @@ GDBRemoteCommunication::WaitForNotRunningPrivate (const TimeValue *timeout_ptr)
323329
return m_private_is_running.WaitForValueEqualTo (false, timeout_ptr, NULL);
324330
}
325331

332+
GDBRemoteCommunication::PacketResult
333+
GDBRemoteCommunication::ReadPacket (StringExtractorGDBRemote &response, uint32_t timeout_usec, bool sync_on_timeout)
334+
{
335+
if (m_read_thread_enabled)
336+
return PopPacketFromQueue (response, timeout_usec);
337+
else
338+
return WaitForPacketWithTimeoutMicroSecondsNoLock (response, timeout_usec, sync_on_timeout);
339+
}
340+
341+
342+
// This function is called when a packet is requested.
343+
// A whole packet is popped from the packet queue and returned to the caller.
344+
// Packets are placed into this queue from the communication read thread.
345+
// See GDBRemoteCommunication::AppendBytesToCache.
346+
GDBRemoteCommunication::PacketResult
347+
GDBRemoteCommunication::PopPacketFromQueue (StringExtractorGDBRemote &response, uint32_t timeout_usec)
348+
{
349+
// Calculate absolute timeout value
350+
TimeValue timeout = TimeValue::Now();
351+
timeout.OffsetWithMicroSeconds(timeout_usec);
352+
353+
do
354+
{
355+
// scope for the mutex
356+
{
357+
// lock down the packet queue
358+
Mutex::Locker locker(m_packet_queue_mutex);
359+
360+
// Wait on condition variable.
361+
if (m_packet_queue.size() == 0)
362+
m_condition_queue_not_empty.Wait(m_packet_queue_mutex, &timeout);
363+
364+
if (m_packet_queue.size() > 0)
365+
{
366+
// get the front element of the queue
367+
response = m_packet_queue.front();
368+
369+
// remove the front element
370+
m_packet_queue.pop();
371+
372+
// we got a packet
373+
return PacketResult::Success;
374+
}
375+
}
376+
377+
// Disconnected
378+
if (!IsConnected())
379+
return PacketResult::ErrorDisconnected;
380+
381+
// Loop while not timed out
382+
} while (TimeValue::Now() < timeout);
383+
384+
return PacketResult::ErrorReplyTimeout;
385+
}
386+
387+
326388
GDBRemoteCommunication::PacketResult
327389
GDBRemoteCommunication::WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtractorGDBRemote &packet, uint32_t timeout_usec, bool sync_on_timeout)
328390
{
@@ -1094,3 +1156,53 @@ GDBRemoteCommunication::ScopedTimeout::~ScopedTimeout ()
10941156
{
10951157
m_gdb_comm.SetPacketTimeout (m_saved_timeout);
10961158
}
1159+
1160+
// This function is called via the Communications class read thread when bytes become available
1161+
// for this connection. This function will consume all incoming bytes and try to parse whole
1162+
// packets as they become available. Full packets are placed in a queue, so that all packet
1163+
// requests can simply pop from this queue. Async notification packets will be dispatched
1164+
// immediately to the ProcessGDBRemote Async thread via an event.
1165+
void GDBRemoteCommunication::AppendBytesToCache (const uint8_t * bytes, size_t len, bool broadcast, lldb::ConnectionStatus status)
1166+
{
1167+
StringExtractorGDBRemote packet;
1168+
1169+
while (true)
1170+
{
1171+
PacketType type = CheckForPacket(bytes, len, packet);
1172+
1173+
// scrub the data so we do not pass it back to CheckForPacket
1174+
// on future passes of the loop
1175+
bytes = nullptr;
1176+
len = 0;
1177+
1178+
// we may have received no packet so lets bail out
1179+
if (type == PacketType::Invalid)
1180+
break;
1181+
1182+
if (type == PacketType::Standard)
1183+
{
1184+
// scope for the mutex
1185+
{
1186+
// lock down the packet queue
1187+
Mutex::Locker locker(m_packet_queue_mutex);
1188+
// push a new packet into the queue
1189+
m_packet_queue.push(packet);
1190+
// Signal condition variable that we have a packet
1191+
m_condition_queue_not_empty.Signal();
1192+
1193+
}
1194+
}
1195+
1196+
if (type == PacketType::Notify)
1197+
{
1198+
// put this packet into an event
1199+
const char *pdata = packet.GetStringRef().c_str();
1200+
1201+
// as the communication class, we are a broadcaster and the
1202+
// async thread is tuned to listen to us
1203+
BroadcastEvent(
1204+
eBroadcastBitGdbReadThreadGotNotify,
1205+
new EventDataBytes(pdata));
1206+
}
1207+
}
1208+
}

‎lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h

+27-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
// C++ Includes
1515
#include <list>
1616
#include <string>
17+
#include <queue>
1718

1819
// Other libraries and framework includes
1920
// Project includes
@@ -47,7 +48,8 @@ class GDBRemoteCommunication : public Communication
4748
public:
4849
enum
4950
{
50-
eBroadcastBitRunPacketSent = kLoUserBroadcastBit
51+
eBroadcastBitRunPacketSent = kLoUserBroadcastBit,
52+
eBroadcastBitGdbReadThreadGotNotify = kLoUserBroadcastBit << 1 // Sent when we received a notify packet.
5153
};
5254

5355
enum class PacketType
@@ -279,6 +281,13 @@ class GDBRemoteCommunication : public Communication
279281
SendPacketNoLock (const char *payload,
280282
size_t payload_length);
281283

284+
PacketResult
285+
ReadPacket (StringExtractorGDBRemote &response, uint32_t timeout_usec, bool sync_on_timeout);
286+
287+
// Pop a packet from the queue in a thread safe manner
288+
PacketResult
289+
PopPacketFromQueue (StringExtractorGDBRemote &response, uint32_t timeout_usec);
290+
282291
PacketResult
283292
WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtractorGDBRemote &response,
284293
uint32_t timeout_usec,
@@ -316,7 +325,24 @@ class GDBRemoteCommunication : public Communication
316325
static lldb::thread_result_t
317326
ListenThread (lldb::thread_arg_t arg);
318327

328+
// GDB-Remote read thread
329+
// . this thread constantly tries to read from the communication
330+
// class and stores all packets received in a queue. The usual
331+
// threads read requests simply pop packets off the queue in the
332+
// usual order.
333+
// This setup allows us to intercept and handle async packets, such
334+
// as the notify packet.
335+
336+
// This method is defined as part of communication.h
337+
// when the read thread gets any bytes it will pass them on to this function
338+
virtual void AppendBytesToCache (const uint8_t * bytes, size_t len, bool broadcast, lldb::ConnectionStatus status);
339+
319340
private:
341+
342+
std::queue<StringExtractorGDBRemote> m_packet_queue; // The packet queue
343+
lldb_private::Mutex m_packet_queue_mutex; // Mutex for accessing queue
344+
Condition m_condition_queue_not_empty; // Condition variable to wait for packets
345+
320346
HostThread m_listen_thread;
321347
std::string m_listen_url;
322348

‎lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp

+21-6
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ GDBRemoteCommunicationClient::HandshakeWithServer (Error *error_ptr)
145145
PacketResult packet_result = PacketResult::Success;
146146
const uint32_t timeout_usec = 10 * 1000; // Wait for 10 ms for a response
147147
while (packet_result == PacketResult::Success)
148-
packet_result = WaitForPacketWithTimeoutMicroSecondsNoLock (response, timeout_usec, false);
148+
packet_result = ReadPacket (response, timeout_usec, false);
149149

150150
// The return value from QueryNoAckModeSupported() is true if the packet
151151
// was sent and _any_ response (including UNIMPLEMENTED) was received),
@@ -654,7 +654,7 @@ GDBRemoteCommunicationClient::SendPacketAndWaitForResponseNoLock (const char *pa
654654
{
655655
PacketResult packet_result = SendPacketNoLock (payload, payload_length);
656656
if (packet_result == PacketResult::Success)
657-
packet_result = WaitForPacketWithTimeoutMicroSecondsNoLock (response, GetPacketTimeoutInMicroSeconds (), true);
657+
packet_result = ReadPacket (response, GetPacketTimeoutInMicroSeconds (), true);
658658
return packet_result;
659659
}
660660

@@ -670,6 +670,12 @@ GDBRemoteCommunicationClient::SendPacketAndWaitForResponse
670670
PacketResult packet_result = PacketResult::ErrorSendFailed;
671671
Mutex::Locker locker;
672672
Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS));
673+
674+
// In order to stop async notifications from being processed in the middle of the
675+
// send/recieve sequence Hijack the broadcast. Then rebroadcast any events when we are done.
676+
static Listener hijack_listener("lldb.NotifyHijacker");
677+
HijackBroadcaster(&hijack_listener, eBroadcastBitGdbReadThreadGotNotify);
678+
673679
if (GetSequenceMutex (locker))
674680
{
675681
packet_result = SendPacketAndWaitForResponseNoLock (payload, payload_length, response);
@@ -761,6 +767,15 @@ GDBRemoteCommunicationClient::SendPacketAndWaitForResponse
761767
log->Printf("error: failed to get packet sequence mutex, not sending packet '%*s'", (int) payload_length, payload);
762768
}
763769
}
770+
771+
// Remove our Hijacking listner from the broadcast.
772+
RestoreBroadcaster();
773+
774+
// If a notification event occured, rebroadcast since it can now be processed safely.
775+
EventSP event_sp;
776+
if (hijack_listener.GetNextEvent(event_sp))
777+
BroadcastEvent(event_sp);
778+
764779
return packet_result;
765780
}
766781

@@ -902,9 +917,9 @@ GDBRemoteCommunicationClient::SendContinuePacketAndWaitForResponse
902917
got_async_packet = false;
903918

904919
if (log)
905-
log->Printf ("GDBRemoteCommunicationClient::%s () WaitForPacket(%s)", __FUNCTION__, continue_packet.c_str());
920+
log->Printf ("GDBRemoteCommunicationClient::%s () ReadPacket(%s)", __FUNCTION__, continue_packet.c_str());
906921

907-
if (WaitForPacketWithTimeoutMicroSecondsNoLock(response, UINT32_MAX, false) == PacketResult::Success)
922+
if (ReadPacket(response, UINT32_MAX, false) == PacketResult::Success)
908923
{
909924
if (response.Empty())
910925
state = eStateInvalid;
@@ -961,7 +976,7 @@ GDBRemoteCommunicationClient::SendContinuePacketAndWaitForResponse
961976
// packet to make sure it doesn't get in the way
962977
StringExtractorGDBRemote extra_stop_reply_packet;
963978
uint32_t timeout_usec = 1000;
964-
if (WaitForPacketWithTimeoutMicroSecondsNoLock (extra_stop_reply_packet, timeout_usec, false) == PacketResult::Success)
979+
if (ReadPacket (extra_stop_reply_packet, timeout_usec, false) == PacketResult::Success)
965980
{
966981
switch (extra_stop_reply_packet.GetChar())
967982
{
@@ -1139,7 +1154,7 @@ GDBRemoteCommunicationClient::SendContinuePacketAndWaitForResponse
11391154
else
11401155
{
11411156
if (log)
1142-
log->Printf ("GDBRemoteCommunicationClient::%s () WaitForPacket(...) => false", __FUNCTION__);
1157+
log->Printf ("GDBRemoteCommunicationClient::%s () ReadPacket(...) => false", __FUNCTION__);
11431158
state = eStateInvalid;
11441159
}
11451160
}

‎lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -1151,6 +1151,12 @@ ProcessGDBRemote::ConnectToDebugserver (const char *connect_url)
11511151
return error;
11521152
}
11531153

1154+
1155+
// Start the communications read thread so all incoming data can be
1156+
// parsed into packets and queued as they arrive.
1157+
if (GetTarget().GetNonStopModeEnabled())
1158+
m_gdb_comm.StartReadThread();
1159+
11541160
// We always seem to be able to open a connection to a local port
11551161
// so we need to make sure we can then send data to it. If we can't
11561162
// then we aren't actually connected to anything, so try and do the

0 commit comments

Comments
 (0)
Please sign in to comment.