Skip to content

Commit

Permalink
[lldb-vscode] Add logic to handle EOF when reading from lldb-vscode s…
Browse files Browse the repository at this point in the history
…tdout.

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
  • Loading branch information
slackito committed Apr 1, 2019
1 parent 66d7eb9 commit 4665aca
Showing 1 changed file with 45 additions and 31 deletions.
76 changes: 45 additions & 31 deletions lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
Expand Up @@ -52,11 +52,11 @@ def dump_memory(base_addr, data, num_per_line, outfile):

def read_packet(f, verbose=False, trace_file=None):
'''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")
if len(line) == 0:
return None
return None # EOF.

# Watch for line that starts with the prefix
prefix = 'Content-Length: '
Expand Down Expand Up @@ -91,10 +91,10 @@ def read_packet_thread(vs_comm):
done = False
while not done:
packet = read_packet(vs_comm.recv, trace_file=vs_comm.trace_file)
if packet:
done = not vs_comm.handle_recv_packet(packet)
else:
done = True
# `packet` will be `None` on EOF. We want to pass it down to
# handle_recv_packet anyway so the main thread can handle unexpected
# termination of lldb-vscode and stop waiting for new packets.
done = not vs_comm.handle_recv_packet(packet)


class DebugCommunication(object):
Expand Down Expand Up @@ -146,13 +146,24 @@ def get_output(self, category, timeout=0.0, clear=True):
self.output_condition.release()
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):
'''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
way. This function will then signal the "self.recv_condition" to
indicate a new packet is available. Returns True if the caller
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
keepGoing = True
packet_type = packet['type']
Expand Down Expand Up @@ -191,10 +202,7 @@ def handle_recv_packet(self, packet):
elif packet_type == 'response':
if packet['command'] == 'disconnect':
keepGoing = False
self.recv_condition.acquire()
self.recv_packets.append(packet)
self.recv_condition.notify()
self.recv_condition.release()
self.enqueue_recv_packet(packet)
return keepGoing

def send_packet(self, command_dict, set_sequence=True):
Expand Down Expand Up @@ -222,27 +230,33 @@ def recv_packet(self, filter_type=None, filter_event=None, timeout=None):
function will wait for the packet to arrive and return it when
it does.'''
while True:
self.recv_condition.acquire()
packet = None
while True:
for (i, curr_packet) in enumerate(self.recv_packets):
packet_type = curr_packet['type']
if filter_type is None or packet_type in filter_type:
if (filter_event is None or
(packet_type == 'event' and
curr_packet['event'] in filter_event)):
packet = self.recv_packets.pop(i)
break
if packet:
break
# Sleep until packet is received
len_before = len(self.recv_packets)
self.recv_condition.wait(timeout)
len_after = len(self.recv_packets)
if len_before == len_after:
return None # Timed out
self.recv_condition.release()
return packet
try:
self.recv_condition.acquire()
packet = None
while True:
for (i, curr_packet) in enumerate(self.recv_packets):
if not curr_packet:
raise EOFError
packet_type = curr_packet['type']
if filter_type is None or packet_type in filter_type:
if (filter_event is None or
(packet_type == 'event' and
curr_packet['event'] in filter_event)):
packet = self.recv_packets.pop(i)
break
if packet:
break
# Sleep until packet is received
len_before = len(self.recv_packets)
self.recv_condition.wait(timeout)
len_after = len(self.recv_packets)
if len_before == len_after:
return None # Timed out
return packet
except EOFError:
return None
finally:
self.recv_condition.release()

return None

Expand Down

0 comments on commit 4665aca

Please sign in to comment.