2015-09-15 21:38:04 +00:00
|
|
|
"""
|
|
|
|
The LLVM Compiler Infrastructure
|
|
|
|
|
|
|
|
This file is distributed under the University of Illinois Open Source
|
|
|
|
License. See LICENSE.TXT for details.
|
|
|
|
|
|
|
|
Sync lldb and related source from a local machine to a remote machine.
|
|
|
|
|
|
|
|
This facilitates working on the lldb sourcecode on multiple machines
|
|
|
|
and multiple OS types, verifying changes across all.
|
|
|
|
|
|
|
|
|
|
|
|
This module provides asyncore channels used within the LLDB test
|
|
|
|
framework.
|
|
|
|
"""
|
|
|
|
|
2015-10-23 17:04:29 +00:00
|
|
|
from __future__ import print_function
|
Python 3 - Turn on absolute imports, and fix existing imports.
Absolute imports were introduced in Python 2.5 as a feature
(e.g. from __future__ import absolute_import), and made default
in Python 3.
When absolute imports are enabled, the import system changes in
a couple of ways:
1) The `import foo` syntax will *only* search sys.path. If `foo`
isn't in sys.path, it won't be found. Period. Without absolute
imports, the import system will also search the same directory
that the importing file resides in, so that you can easily
import from the same folder.
2) From inside a package, you can use a dot syntax to refer to higher
levels of the current package. For example, if you are in the
package lldbsuite.test.utility, then ..foo refers to
lldbsuite.test.foo. You can use this notation with the
`from X import Y` syntax to write intra-package references. For
example, using the previous locationa s a starting point, writing
`from ..support import seven` would import lldbsuite.support.seven
Since this is now the default behavior in Python 3, this means that
importing from the same directory with `import foo` *no longer works*.
As a result, the only way to have portable code is to force absolute
imports for all versions of Python.
See PEP 0328 [https://www.python.org/dev/peps/pep-0328/] for more
information about absolute and relative imports.
Differential Revision: http://reviews.llvm.org/D14342
Reviewed By: Todd Fiala
llvm-svn: 252191
2015-11-05 19:22:28 +00:00
|
|
|
from __future__ import absolute_import
|
2015-10-23 17:04:29 +00:00
|
|
|
|
2015-11-03 19:20:39 +00:00
|
|
|
|
Python 3 - Turn on absolute imports, and fix existing imports.
Absolute imports were introduced in Python 2.5 as a feature
(e.g. from __future__ import absolute_import), and made default
in Python 3.
When absolute imports are enabled, the import system changes in
a couple of ways:
1) The `import foo` syntax will *only* search sys.path. If `foo`
isn't in sys.path, it won't be found. Period. Without absolute
imports, the import system will also search the same directory
that the importing file resides in, so that you can easily
import from the same folder.
2) From inside a package, you can use a dot syntax to refer to higher
levels of the current package. For example, if you are in the
package lldbsuite.test.utility, then ..foo refers to
lldbsuite.test.foo. You can use this notation with the
`from X import Y` syntax to write intra-package references. For
example, using the previous locationa s a starting point, writing
`from ..support import seven` would import lldbsuite.support.seven
Since this is now the default behavior in Python 3, this means that
importing from the same directory with `import foo` *no longer works*.
As a result, the only way to have portable code is to force absolute
imports for all versions of Python.
See PEP 0328 [https://www.python.org/dev/peps/pep-0328/] for more
information about absolute and relative imports.
Differential Revision: http://reviews.llvm.org/D14342
Reviewed By: Todd Fiala
llvm-svn: 252191
2015-11-05 19:22:28 +00:00
|
|
|
# System modules
|
2015-09-15 21:38:04 +00:00
|
|
|
import asyncore
|
|
|
|
import socket
|
|
|
|
|
Python 3 - Turn on absolute imports, and fix existing imports.
Absolute imports were introduced in Python 2.5 as a feature
(e.g. from __future__ import absolute_import), and made default
in Python 3.
When absolute imports are enabled, the import system changes in
a couple of ways:
1) The `import foo` syntax will *only* search sys.path. If `foo`
isn't in sys.path, it won't be found. Period. Without absolute
imports, the import system will also search the same directory
that the importing file resides in, so that you can easily
import from the same folder.
2) From inside a package, you can use a dot syntax to refer to higher
levels of the current package. For example, if you are in the
package lldbsuite.test.utility, then ..foo refers to
lldbsuite.test.foo. You can use this notation with the
`from X import Y` syntax to write intra-package references. For
example, using the previous locationa s a starting point, writing
`from ..support import seven` would import lldbsuite.support.seven
Since this is now the default behavior in Python 3, this means that
importing from the same directory with `import foo` *no longer works*.
As a result, the only way to have portable code is to force absolute
imports for all versions of Python.
See PEP 0328 [https://www.python.org/dev/peps/pep-0328/] for more
information about absolute and relative imports.
Differential Revision: http://reviews.llvm.org/D14342
Reviewed By: Todd Fiala
llvm-svn: 252191
2015-11-05 19:22:28 +00:00
|
|
|
# Third-party modules
|
2015-10-21 17:48:52 +00:00
|
|
|
from six.moves import cPickle
|
2015-09-15 21:38:04 +00:00
|
|
|
|
Python 3 - Turn on absolute imports, and fix existing imports.
Absolute imports were introduced in Python 2.5 as a feature
(e.g. from __future__ import absolute_import), and made default
in Python 3.
When absolute imports are enabled, the import system changes in
a couple of ways:
1) The `import foo` syntax will *only* search sys.path. If `foo`
isn't in sys.path, it won't be found. Period. Without absolute
imports, the import system will also search the same directory
that the importing file resides in, so that you can easily
import from the same folder.
2) From inside a package, you can use a dot syntax to refer to higher
levels of the current package. For example, if you are in the
package lldbsuite.test.utility, then ..foo refers to
lldbsuite.test.foo. You can use this notation with the
`from X import Y` syntax to write intra-package references. For
example, using the previous locationa s a starting point, writing
`from ..support import seven` would import lldbsuite.support.seven
Since this is now the default behavior in Python 3, this means that
importing from the same directory with `import foo` *no longer works*.
As a result, the only way to have portable code is to force absolute
imports for all versions of Python.
See PEP 0328 [https://www.python.org/dev/peps/pep-0328/] for more
information about absolute and relative imports.
Differential Revision: http://reviews.llvm.org/D14342
Reviewed By: Todd Fiala
llvm-svn: 252191
2015-11-05 19:22:28 +00:00
|
|
|
# LLDB modules
|
|
|
|
|
2015-12-14 23:45:38 +00:00
|
|
|
|
2015-09-15 21:38:04 +00:00
|
|
|
class UnpicklingForwardingReaderChannel(asyncore.dispatcher):
|
|
|
|
"""Provides an unpickling, forwarding asyncore dispatch channel reader.
|
|
|
|
|
|
|
|
Inferior dotest.py processes with side-channel-based test results will
|
|
|
|
send test result event data in a pickled format, one event at a time.
|
|
|
|
This class supports reconstructing the pickled data and forwarding it
|
|
|
|
on to its final destination.
|
|
|
|
|
|
|
|
The channel data is written in the form:
|
|
|
|
{num_payload_bytes}#{payload_bytes}
|
|
|
|
|
|
|
|
The bulk of this class is devoted to reading and parsing out
|
|
|
|
the payload bytes.
|
|
|
|
"""
|
|
|
|
def __init__(self, file_object, async_map, forwarding_func):
|
|
|
|
asyncore.dispatcher.__init__(self, sock=file_object, map=async_map)
|
|
|
|
|
2015-12-02 23:07:33 +00:00
|
|
|
self.header_contents = b""
|
2015-09-15 21:38:04 +00:00
|
|
|
self.packet_bytes_remaining = 0
|
|
|
|
self.reading_header = True
|
2015-12-02 21:45:15 +00:00
|
|
|
self.ibuffer = b''
|
2015-09-15 21:38:04 +00:00
|
|
|
self.forwarding_func = forwarding_func
|
|
|
|
if forwarding_func is None:
|
|
|
|
# This whole class is useless if we do nothing with the
|
|
|
|
# unpickled results.
|
|
|
|
raise Exception("forwarding function must be set")
|
|
|
|
|
|
|
|
def deserialize_payload(self):
|
|
|
|
"""Unpickles the collected input buffer bytes and forwards."""
|
|
|
|
if len(self.ibuffer) > 0:
|
|
|
|
self.forwarding_func(cPickle.loads(self.ibuffer))
|
2015-12-02 21:45:15 +00:00
|
|
|
self.ibuffer = b''
|
2015-09-15 21:38:04 +00:00
|
|
|
|
|
|
|
def consume_header_bytes(self, data):
|
|
|
|
"""Consumes header bytes from the front of data.
|
|
|
|
@param data the incoming data stream bytes
|
|
|
|
@return any data leftover after consuming header bytes.
|
|
|
|
"""
|
|
|
|
# We're done if there is no content.
|
|
|
|
if not data or (len(data) == 0):
|
|
|
|
return None
|
|
|
|
|
2015-12-02 23:07:33 +00:00
|
|
|
full_header_len = 4
|
|
|
|
|
2015-12-14 23:45:38 +00:00
|
|
|
assert len(self.header_contents) < full_header_len
|
2015-12-02 23:07:33 +00:00
|
|
|
|
|
|
|
bytes_avail = len(data)
|
|
|
|
bytes_needed = full_header_len - len(self.header_contents)
|
|
|
|
header_bytes_avail = min(bytes_needed, bytes_avail)
|
|
|
|
self.header_contents += data[:header_bytes_avail]
|
|
|
|
if len(self.header_contents) == full_header_len:
|
|
|
|
import struct
|
|
|
|
# End of header.
|
2015-12-14 23:45:38 +00:00
|
|
|
self.packet_bytes_remaining = struct.unpack(
|
|
|
|
"!I", self.header_contents)[0]
|
2015-12-02 23:07:33 +00:00
|
|
|
self.header_contents = b""
|
|
|
|
self.reading_header = False
|
|
|
|
return data[header_bytes_avail:]
|
2015-09-15 21:38:04 +00:00
|
|
|
|
|
|
|
# If we made it here, we've exhausted the data and
|
|
|
|
# we're still parsing header content.
|
|
|
|
return None
|
|
|
|
|
|
|
|
def consume_payload_bytes(self, data):
|
|
|
|
"""Consumes payload bytes from the front of data.
|
|
|
|
@param data the incoming data stream bytes
|
|
|
|
@return any data leftover after consuming remaining payload bytes.
|
|
|
|
"""
|
|
|
|
if not data or (len(data) == 0):
|
|
|
|
# We're done and there's nothing to do.
|
|
|
|
return None
|
|
|
|
|
|
|
|
data_len = len(data)
|
|
|
|
if data_len <= self.packet_bytes_remaining:
|
|
|
|
# We're consuming all the data provided.
|
|
|
|
self.ibuffer += data
|
|
|
|
self.packet_bytes_remaining -= data_len
|
|
|
|
|
|
|
|
# If we're no longer waiting for payload bytes,
|
|
|
|
# we flip back to parsing header bytes and we
|
|
|
|
# unpickle the payload contents.
|
|
|
|
if self.packet_bytes_remaining < 1:
|
|
|
|
self.reading_header = True
|
|
|
|
self.deserialize_payload()
|
|
|
|
|
|
|
|
# We're done, no more data left.
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
# We're only consuming a portion of the data since
|
|
|
|
# the data contains more than the payload amount.
|
|
|
|
self.ibuffer += data[:self.packet_bytes_remaining]
|
|
|
|
data = data[self.packet_bytes_remaining:]
|
|
|
|
|
|
|
|
# We now move on to reading the header.
|
|
|
|
self.reading_header = True
|
|
|
|
self.packet_bytes_remaining = 0
|
|
|
|
|
|
|
|
# And we can deserialize the payload.
|
|
|
|
self.deserialize_payload()
|
|
|
|
|
|
|
|
# Return the remaining data.
|
|
|
|
return data
|
|
|
|
|
|
|
|
def handle_read(self):
|
2015-12-14 23:45:38 +00:00
|
|
|
# Read some data from the socket.
|
|
|
|
try:
|
|
|
|
data = self.recv(8192)
|
|
|
|
# print('driver socket READ: %d bytes' % len(data))
|
|
|
|
except socket.error as socket_error:
|
|
|
|
print(
|
|
|
|
"\nINFO: received socket error when reading data "
|
|
|
|
"from test inferior:\n{}".format(socket_error))
|
2015-12-15 23:51:27 +00:00
|
|
|
raise
|
2015-12-14 23:45:38 +00:00
|
|
|
except Exception as general_exception:
|
|
|
|
print(
|
|
|
|
"\nERROR: received non-socket error when reading data "
|
|
|
|
"from the test inferior:\n{}".format(general_exception))
|
2015-12-15 23:51:27 +00:00
|
|
|
raise
|
2015-12-14 23:45:38 +00:00
|
|
|
|
|
|
|
# Consume the message content.
|
2015-09-15 21:38:04 +00:00
|
|
|
while data and (len(data) > 0):
|
|
|
|
# If we're reading the header, gather header bytes.
|
|
|
|
if self.reading_header:
|
|
|
|
data = self.consume_header_bytes(data)
|
|
|
|
else:
|
|
|
|
data = self.consume_payload_bytes(data)
|
|
|
|
|
|
|
|
def handle_close(self):
|
2015-10-23 17:04:29 +00:00
|
|
|
# print("socket reader: closing port")
|
2015-09-15 21:38:04 +00:00
|
|
|
self.close()
|
|
|
|
|
|
|
|
|
|
|
|
class UnpicklingForwardingListenerChannel(asyncore.dispatcher):
|
|
|
|
"""Provides a socket listener asyncore channel for unpickling/forwarding.
|
|
|
|
|
|
|
|
This channel will listen on a socket port (use 0 for host-selected). Any
|
|
|
|
client that connects will have an UnpicklingForwardingReaderChannel handle
|
|
|
|
communication over the connection.
|
|
|
|
|
|
|
|
The dotest parallel test runners, when collecting test results, open the
|
|
|
|
test results side channel over a socket. This channel handles connections
|
|
|
|
from inferiors back to the test runner. Each worker fires up a listener
|
|
|
|
for each inferior invocation. This simplifies the asyncore.loop() usage,
|
|
|
|
one of the reasons for implementing with asyncore. This listener shuts
|
|
|
|
down once a single connection is made to it.
|
|
|
|
"""
|
2015-09-22 22:47:34 +00:00
|
|
|
def __init__(self, async_map, host, port, backlog_count, forwarding_func):
|
2015-09-15 21:38:04 +00:00
|
|
|
asyncore.dispatcher.__init__(self, map=async_map)
|
|
|
|
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
|
self.set_reuse_addr()
|
|
|
|
self.bind((host, port))
|
|
|
|
self.address = self.socket.getsockname()
|
2015-09-22 22:47:34 +00:00
|
|
|
self.listen(backlog_count)
|
2015-09-15 21:38:04 +00:00
|
|
|
self.handler = None
|
|
|
|
self.async_map = async_map
|
|
|
|
self.forwarding_func = forwarding_func
|
|
|
|
if forwarding_func is None:
|
|
|
|
# This whole class is useless if we do nothing with the
|
|
|
|
# unpickled results.
|
|
|
|
raise Exception("forwarding function must be set")
|
|
|
|
|
|
|
|
def handle_accept(self):
|
|
|
|
(sock, addr) = self.socket.accept()
|
|
|
|
if sock and addr:
|
2015-10-23 17:04:29 +00:00
|
|
|
# print('Incoming connection from %s' % repr(addr))
|
2015-09-15 21:38:04 +00:00
|
|
|
self.handler = UnpicklingForwardingReaderChannel(
|
|
|
|
sock, self.async_map, self.forwarding_func)
|
|
|
|
|
|
|
|
def handle_close(self):
|
|
|
|
self.close()
|