blob: 3797ab7697d7419ca2d40e34142439137f38d792 [file] [log] [blame]
Todd Fiala68615ce2015-09-15 21:38:04 +00001"""
2 The LLVM Compiler Infrastructure
3
4This file is distributed under the University of Illinois Open Source
5License. See LICENSE.TXT for details.
6
7Sync lldb and related source from a local machine to a remote machine.
8
9This facilitates working on the lldb sourcecode on multiple machines
10and multiple OS types, verifying changes across all.
11
12
13This module provides asyncore channels used within the LLDB test
14framework.
15"""
16
Zachary Turner35d017f2015-10-23 17:04:29 +000017from __future__ import print_function
Zachary Turnerc1b7cd72015-11-05 19:22:28 +000018from __future__ import absolute_import
Zachary Turner35d017f2015-10-23 17:04:29 +000019
Zachary Turner19474e12015-11-03 19:20:39 +000020
Zachary Turnerc1b7cd72015-11-05 19:22:28 +000021# System modules
Todd Fiala68615ce2015-09-15 21:38:04 +000022import asyncore
Todd Fiala68615ce2015-09-15 21:38:04 +000023import socket
24
Zachary Turnerc1b7cd72015-11-05 19:22:28 +000025# Third-party modules
Zachary Turner814236d2015-10-21 17:48:52 +000026from six.moves import cPickle
Todd Fiala68615ce2015-09-15 21:38:04 +000027
Zachary Turnerc1b7cd72015-11-05 19:22:28 +000028# LLDB modules
29
Todd Fiala68615ce2015-09-15 21:38:04 +000030class UnpicklingForwardingReaderChannel(asyncore.dispatcher):
31 """Provides an unpickling, forwarding asyncore dispatch channel reader.
32
33 Inferior dotest.py processes with side-channel-based test results will
34 send test result event data in a pickled format, one event at a time.
35 This class supports reconstructing the pickled data and forwarding it
36 on to its final destination.
37
38 The channel data is written in the form:
39 {num_payload_bytes}#{payload_bytes}
40
41 The bulk of this class is devoted to reading and parsing out
42 the payload bytes.
43 """
44 def __init__(self, file_object, async_map, forwarding_func):
45 asyncore.dispatcher.__init__(self, sock=file_object, map=async_map)
46
47 self.header_contents = ''
48 self.packet_bytes_remaining = 0
49 self.reading_header = True
50 self.ibuffer = ''
51 self.forwarding_func = forwarding_func
52 if forwarding_func is None:
53 # This whole class is useless if we do nothing with the
54 # unpickled results.
55 raise Exception("forwarding function must be set")
56
57 def deserialize_payload(self):
58 """Unpickles the collected input buffer bytes and forwards."""
59 if len(self.ibuffer) > 0:
60 self.forwarding_func(cPickle.loads(self.ibuffer))
61 self.ibuffer = ''
62
63 def consume_header_bytes(self, data):
64 """Consumes header bytes from the front of data.
65 @param data the incoming data stream bytes
66 @return any data leftover after consuming header bytes.
67 """
68 # We're done if there is no content.
69 if not data or (len(data) == 0):
70 return None
71
72 for index in range(len(data)):
73 byte = data[index]
74 if byte != '#':
75 # Header byte.
76 self.header_contents += byte
77 else:
78 # End of header.
79 self.packet_bytes_remaining = int(self.header_contents)
80 self.header_contents = ''
81 self.reading_header = False
82 return data[(index+1):]
83
84 # If we made it here, we've exhausted the data and
85 # we're still parsing header content.
86 return None
87
88 def consume_payload_bytes(self, data):
89 """Consumes payload bytes from the front of data.
90 @param data the incoming data stream bytes
91 @return any data leftover after consuming remaining payload bytes.
92 """
93 if not data or (len(data) == 0):
94 # We're done and there's nothing to do.
95 return None
96
97 data_len = len(data)
98 if data_len <= self.packet_bytes_remaining:
99 # We're consuming all the data provided.
100 self.ibuffer += data
101 self.packet_bytes_remaining -= data_len
102
103 # If we're no longer waiting for payload bytes,
104 # we flip back to parsing header bytes and we
105 # unpickle the payload contents.
106 if self.packet_bytes_remaining < 1:
107 self.reading_header = True
108 self.deserialize_payload()
109
110 # We're done, no more data left.
111 return None
112 else:
113 # We're only consuming a portion of the data since
114 # the data contains more than the payload amount.
115 self.ibuffer += data[:self.packet_bytes_remaining]
116 data = data[self.packet_bytes_remaining:]
117
118 # We now move on to reading the header.
119 self.reading_header = True
120 self.packet_bytes_remaining = 0
121
122 # And we can deserialize the payload.
123 self.deserialize_payload()
124
125 # Return the remaining data.
126 return data
127
128 def handle_read(self):
129 data = self.recv(8192)
Zachary Turner35d017f2015-10-23 17:04:29 +0000130 # print('driver socket READ: %d bytes' % len(data))
Todd Fiala68615ce2015-09-15 21:38:04 +0000131
132 while data and (len(data) > 0):
133 # If we're reading the header, gather header bytes.
134 if self.reading_header:
135 data = self.consume_header_bytes(data)
136 else:
137 data = self.consume_payload_bytes(data)
138
139 def handle_close(self):
Zachary Turner35d017f2015-10-23 17:04:29 +0000140 # print("socket reader: closing port")
Todd Fiala68615ce2015-09-15 21:38:04 +0000141 self.close()
142
143
144class UnpicklingForwardingListenerChannel(asyncore.dispatcher):
145 """Provides a socket listener asyncore channel for unpickling/forwarding.
146
147 This channel will listen on a socket port (use 0 for host-selected). Any
148 client that connects will have an UnpicklingForwardingReaderChannel handle
149 communication over the connection.
150
151 The dotest parallel test runners, when collecting test results, open the
152 test results side channel over a socket. This channel handles connections
153 from inferiors back to the test runner. Each worker fires up a listener
154 for each inferior invocation. This simplifies the asyncore.loop() usage,
155 one of the reasons for implementing with asyncore. This listener shuts
156 down once a single connection is made to it.
157 """
Todd Fiala871b2e52015-09-22 22:47:34 +0000158 def __init__(self, async_map, host, port, backlog_count, forwarding_func):
Todd Fiala68615ce2015-09-15 21:38:04 +0000159 asyncore.dispatcher.__init__(self, map=async_map)
160 self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
161 self.set_reuse_addr()
162 self.bind((host, port))
163 self.address = self.socket.getsockname()
Todd Fiala871b2e52015-09-22 22:47:34 +0000164 self.listen(backlog_count)
Todd Fiala68615ce2015-09-15 21:38:04 +0000165 self.handler = None
166 self.async_map = async_map
167 self.forwarding_func = forwarding_func
168 if forwarding_func is None:
169 # This whole class is useless if we do nothing with the
170 # unpickled results.
171 raise Exception("forwarding function must be set")
172
173 def handle_accept(self):
174 (sock, addr) = self.socket.accept()
175 if sock and addr:
Zachary Turner35d017f2015-10-23 17:04:29 +0000176 # print('Incoming connection from %s' % repr(addr))
Todd Fiala68615ce2015-09-15 21:38:04 +0000177 self.handler = UnpicklingForwardingReaderChannel(
178 sock, self.async_map, self.forwarding_func)
179
180 def handle_close(self):
181 self.close()