blob: 6b6f0be207ee323f03ff9cdfe98bfd844be878e6 [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 Turner814236d2015-10-21 17:48:52 +000017import lldb_shared
18
Todd Fiala68615ce2015-09-15 21:38:04 +000019import asyncore
Todd Fiala68615ce2015-09-15 21:38:04 +000020import socket
21
Zachary Turner814236d2015-10-21 17:48:52 +000022from six.moves import cPickle
Todd Fiala68615ce2015-09-15 21:38:04 +000023
24class UnpicklingForwardingReaderChannel(asyncore.dispatcher):
25 """Provides an unpickling, forwarding asyncore dispatch channel reader.
26
27 Inferior dotest.py processes with side-channel-based test results will
28 send test result event data in a pickled format, one event at a time.
29 This class supports reconstructing the pickled data and forwarding it
30 on to its final destination.
31
32 The channel data is written in the form:
33 {num_payload_bytes}#{payload_bytes}
34
35 The bulk of this class is devoted to reading and parsing out
36 the payload bytes.
37 """
38 def __init__(self, file_object, async_map, forwarding_func):
39 asyncore.dispatcher.__init__(self, sock=file_object, map=async_map)
40
41 self.header_contents = ''
42 self.packet_bytes_remaining = 0
43 self.reading_header = True
44 self.ibuffer = ''
45 self.forwarding_func = forwarding_func
46 if forwarding_func is None:
47 # This whole class is useless if we do nothing with the
48 # unpickled results.
49 raise Exception("forwarding function must be set")
50
51 def deserialize_payload(self):
52 """Unpickles the collected input buffer bytes and forwards."""
53 if len(self.ibuffer) > 0:
54 self.forwarding_func(cPickle.loads(self.ibuffer))
55 self.ibuffer = ''
56
57 def consume_header_bytes(self, data):
58 """Consumes header bytes from the front of data.
59 @param data the incoming data stream bytes
60 @return any data leftover after consuming header bytes.
61 """
62 # We're done if there is no content.
63 if not data or (len(data) == 0):
64 return None
65
66 for index in range(len(data)):
67 byte = data[index]
68 if byte != '#':
69 # Header byte.
70 self.header_contents += byte
71 else:
72 # End of header.
73 self.packet_bytes_remaining = int(self.header_contents)
74 self.header_contents = ''
75 self.reading_header = False
76 return data[(index+1):]
77
78 # If we made it here, we've exhausted the data and
79 # we're still parsing header content.
80 return None
81
82 def consume_payload_bytes(self, data):
83 """Consumes payload bytes from the front of data.
84 @param data the incoming data stream bytes
85 @return any data leftover after consuming remaining payload bytes.
86 """
87 if not data or (len(data) == 0):
88 # We're done and there's nothing to do.
89 return None
90
91 data_len = len(data)
92 if data_len <= self.packet_bytes_remaining:
93 # We're consuming all the data provided.
94 self.ibuffer += data
95 self.packet_bytes_remaining -= data_len
96
97 # If we're no longer waiting for payload bytes,
98 # we flip back to parsing header bytes and we
99 # unpickle the payload contents.
100 if self.packet_bytes_remaining < 1:
101 self.reading_header = True
102 self.deserialize_payload()
103
104 # We're done, no more data left.
105 return None
106 else:
107 # We're only consuming a portion of the data since
108 # the data contains more than the payload amount.
109 self.ibuffer += data[:self.packet_bytes_remaining]
110 data = data[self.packet_bytes_remaining:]
111
112 # We now move on to reading the header.
113 self.reading_header = True
114 self.packet_bytes_remaining = 0
115
116 # And we can deserialize the payload.
117 self.deserialize_payload()
118
119 # Return the remaining data.
120 return data
121
122 def handle_read(self):
123 data = self.recv(8192)
124 # print 'driver socket READ: %d bytes' % len(data)
125
126 while data and (len(data) > 0):
127 # If we're reading the header, gather header bytes.
128 if self.reading_header:
129 data = self.consume_header_bytes(data)
130 else:
131 data = self.consume_payload_bytes(data)
132
133 def handle_close(self):
134 # print "socket reader: closing port"
135 self.close()
136
137
138class UnpicklingForwardingListenerChannel(asyncore.dispatcher):
139 """Provides a socket listener asyncore channel for unpickling/forwarding.
140
141 This channel will listen on a socket port (use 0 for host-selected). Any
142 client that connects will have an UnpicklingForwardingReaderChannel handle
143 communication over the connection.
144
145 The dotest parallel test runners, when collecting test results, open the
146 test results side channel over a socket. This channel handles connections
147 from inferiors back to the test runner. Each worker fires up a listener
148 for each inferior invocation. This simplifies the asyncore.loop() usage,
149 one of the reasons for implementing with asyncore. This listener shuts
150 down once a single connection is made to it.
151 """
Todd Fiala871b2e52015-09-22 22:47:34 +0000152 def __init__(self, async_map, host, port, backlog_count, forwarding_func):
Todd Fiala68615ce2015-09-15 21:38:04 +0000153 asyncore.dispatcher.__init__(self, map=async_map)
154 self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
155 self.set_reuse_addr()
156 self.bind((host, port))
157 self.address = self.socket.getsockname()
Todd Fiala871b2e52015-09-22 22:47:34 +0000158 self.listen(backlog_count)
Todd Fiala68615ce2015-09-15 21:38:04 +0000159 self.handler = None
160 self.async_map = async_map
161 self.forwarding_func = forwarding_func
162 if forwarding_func is None:
163 # This whole class is useless if we do nothing with the
164 # unpickled results.
165 raise Exception("forwarding function must be set")
166
167 def handle_accept(self):
168 (sock, addr) = self.socket.accept()
169 if sock and addr:
170 # print 'Incoming connection from %s' % repr(addr)
171 self.handler = UnpicklingForwardingReaderChannel(
172 sock, self.async_map, self.forwarding_func)
173
174 def handle_close(self):
175 self.close()