From 4665aca8ca769f1389d83f59e8774c749b2f9d88 Mon Sep 17 00:00:00 2001 From: Jorge Gorbe Moya Date: Mon, 1 Apr 2019 20:37:22 +0000 Subject: [PATCH] [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 --- .../test/tools/lldb-vscode/vscode.py | 76 +++++++++++-------- 1 file changed, 45 insertions(+), 31 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py index 99986bc96a23..4e09e6a43a47 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py @@ -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: ' @@ -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): @@ -146,6 +146,12 @@ class DebugCommunication(object): 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 @@ -153,6 +159,11 @@ class DebugCommunication(object): 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'] @@ -191,10 +202,7 @@ class DebugCommunication(object): 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): @@ -222,27 +230,33 @@ class DebugCommunication(object): 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