[lldb-vscode] Add logic to handle EOF when reading from lldb-vscode stdout.

Summary:
This change prevents the lldb-vscode test harness from hanging up waiting for
new messages when the lldb-vscode subprocess crashes.

Now, when an EOF from the subprocess pipe is detected we enqueue a `None` packet
in the received packets list. Then, during the message processing loop, we can
use this `None` packet to tell apart the case where lldb-vscode has terminated
unexpectedly from the normal situation where no pending messages means blocking
and waiting for more data.

I believe this should be enough to fix the issues with these tests hanging on
multiple platforms. Once this lands, I'll prepare and test a separate change
removing the @skipIfLinux annotations.

Reviewers: clayborg, zturner

Subscribers: lldb-commits

Tags: #lldb

Differential Revision: https://reviews.llvm.org/D59849

llvm-svn: 357426
This commit is contained in:
Jorge Gorbe Moya 2019-04-01 20:37:22 +00:00
parent 66d7eb9704
commit 4665aca8ca
1 changed files with 45 additions and 31 deletions

View File

@ -52,11 +52,11 @@ def dump_memory(base_addr, data, num_per_line, outfile):
def read_packet(f, verbose=False, trace_file=None): def read_packet(f, verbose=False, trace_file=None):
'''Decode a JSON packet that starts with the content length and is '''Decode a JSON packet that starts with the content length and is
followed by the JSON bytes from a file 'f' followed by the JSON bytes from a file 'f'. Returns None on EOF.
''' '''
line = f.readline().decode("utf-8") line = f.readline().decode("utf-8")
if len(line) == 0: if len(line) == 0:
return None return None # EOF.
# Watch for line that starts with the prefix # Watch for line that starts with the prefix
prefix = 'Content-Length: ' prefix = 'Content-Length: '
@ -91,10 +91,10 @@ def read_packet_thread(vs_comm):
done = False done = False
while not done: while not done:
packet = read_packet(vs_comm.recv, trace_file=vs_comm.trace_file) packet = read_packet(vs_comm.recv, trace_file=vs_comm.trace_file)
if packet: # `packet` will be `None` on EOF. We want to pass it down to
done = not vs_comm.handle_recv_packet(packet) # handle_recv_packet anyway so the main thread can handle unexpected
else: # termination of lldb-vscode and stop waiting for new packets.
done = True done = not vs_comm.handle_recv_packet(packet)
class DebugCommunication(object): class DebugCommunication(object):
@ -146,6 +146,12 @@ class DebugCommunication(object):
self.output_condition.release() self.output_condition.release()
return output return output
def enqueue_recv_packet(self, packet):
self.recv_condition.acquire()
self.recv_packets.append(packet)
self.recv_condition.notify()
self.recv_condition.release()
def handle_recv_packet(self, packet): def handle_recv_packet(self, packet):
'''Called by the read thread that is waiting for all incoming packets '''Called by the read thread that is waiting for all incoming packets
to store the incoming packet in "self.recv_packets" in a thread safe to store the incoming packet in "self.recv_packets" in a thread safe
@ -153,6 +159,11 @@ class DebugCommunication(object):
indicate a new packet is available. Returns True if the caller indicate a new packet is available. Returns True if the caller
should keep calling this function for more packets. should keep calling this function for more packets.
''' '''
# If EOF, notify the read thread by enqueing a None.
if not packet:
self.enqueue_recv_packet(None)
return False
# Check the packet to see if is an event packet # Check the packet to see if is an event packet
keepGoing = True keepGoing = True
packet_type = packet['type'] packet_type = packet['type']
@ -191,10 +202,7 @@ class DebugCommunication(object):
elif packet_type == 'response': elif packet_type == 'response':
if packet['command'] == 'disconnect': if packet['command'] == 'disconnect':
keepGoing = False keepGoing = False
self.recv_condition.acquire() self.enqueue_recv_packet(packet)
self.recv_packets.append(packet)
self.recv_condition.notify()
self.recv_condition.release()
return keepGoing return keepGoing
def send_packet(self, command_dict, set_sequence=True): def send_packet(self, command_dict, set_sequence=True):
@ -222,27 +230,33 @@ class DebugCommunication(object):
function will wait for the packet to arrive and return it when function will wait for the packet to arrive and return it when
it does.''' it does.'''
while True: while True:
self.recv_condition.acquire() try:
packet = None self.recv_condition.acquire()
while True: packet = None
for (i, curr_packet) in enumerate(self.recv_packets): while True:
packet_type = curr_packet['type'] for (i, curr_packet) in enumerate(self.recv_packets):
if filter_type is None or packet_type in filter_type: if not curr_packet:
if (filter_event is None or raise EOFError
(packet_type == 'event' and packet_type = curr_packet['type']
curr_packet['event'] in filter_event)): if filter_type is None or packet_type in filter_type:
packet = self.recv_packets.pop(i) if (filter_event is None or
break (packet_type == 'event' and
if packet: curr_packet['event'] in filter_event)):
break packet = self.recv_packets.pop(i)
# Sleep until packet is received break
len_before = len(self.recv_packets) if packet:
self.recv_condition.wait(timeout) break
len_after = len(self.recv_packets) # Sleep until packet is received
if len_before == len_after: len_before = len(self.recv_packets)
return None # Timed out self.recv_condition.wait(timeout)
self.recv_condition.release() len_after = len(self.recv_packets)
return packet if len_before == len_after:
return None # Timed out
return packet
except EOFError:
return None
finally:
self.recv_condition.release()
return None return None