コード例 #1
0
ファイル: controller.py プロジェクト: spirali/aislinn
class Controller:
    """ The main class for controlling AVT - Aislinn Valgrind Tool

        Terminology:
        client - a verified process
        client's memory - a memory visible to a verified process
        buffer - a memory allocated in AVT by controller; invisible to client
    """


    # TODO: Universal architecture detection
    POINTER_SIZE = 8
    INT_SIZE = 4

    FUNCTION_INT = 0
    FUNCTION_4_POINTER = 1
    FUNCTION_2_INT_2_POINTER = 2
    FUNCTION_2_INT_4_POINTER = 3

    debug = False
    debug_by_valgrind_tool = None

    stdout_file = None
    stderr_file = None
    profile = False
    extra_env = None

    heap_size = None
    redzone_size = None
    verbose = None

    name = ""  # For debug purpose

    def __init__(self, valgrind_bin, args, cwd=None):
        self.process = None
        self.socket = None
        self.recv_buffer = ""
        self.args = tuple(args)
        self.cwd = cwd
        self.server_socket = None
        self.running = False
        self.valgrind_bin = valgrind_bin

    def start(self, capture_syscalls=()):
        """ Start Valgrind with Aislinn plugin (AVT)
        and initilizes connection """
        assert self.process is None  # Nothing is running
        assert self.socket is None

        self.server_socket = self._start_server()
        port = self.server_socket.getsockname()[1]
        self._start_valgrind(port, capture_syscalls)

    def connect(self):
        """ Connect to AVT """
        self.server_socket.settimeout(5.0)
        try:
            sock, addr = self.server_socket.accept()
        except socket.timeout:
            logging.error("Aislinn client was not started")
            return False
        self.socket = SocketWrapper(sock)
        self.socket.set_no_delay()
        self.server_socket.close()
        self.server_socket = None
        return True
        # User has to call receive_line after calling this method
        # But it may take some time to initialize vgclient, hence
        # it is not build in in connect to allow polling

    def start_and_connect(self, *args, **kw):
        """ Calls 'start' and 'connect' and receives the first line,
        returns None if connection failed"""
        self.start(*args, **kw)
        if not self.connect():
            return None
        return self.receive_line()

    def kill(self):
        """ Kills running AVT """
        if self.process and self.process.poll() is None:
            self.process.kill()
            self.process = None

    def set_capture_syscall(self, syscall, value):
        """ Switches on/off a capturing a syscall """
        self.send_and_receive_ok(
            "SET syscall {0} {1}\n".format(syscall, "on" if value else "off"))

    def save_state(self):
        """ Save a current process state """
        return self.send_and_receive_int("SAVE\n")

    def restore_state(self, state_id):
        """ Restores a saved process state """
        self.send_and_receive_ok("RESTORE {0}\n".format(state_id))

    def free_state(self, state_id):
        """ Frees a saved state """
        self.send_command("FREE {0}\n".format(state_id))

    def run_process(self):
        """ Resumes the paused process and wait until new event,
        then returns the event """
        return self.send_and_receive("RUN\n")

    def run_drop_syscall(self, return_value):
        """ When process is paused in syscall,
        it skips the syscall and then behaves as 'run' """
        return self.send_and_receive(
            "RUN_DROP_SYSCALL {0}\n".format(return_value))

    def run_process_async(self):
        """ Asynchronous version of 'run'. It does not wait for the
           next event and returns immediately """
        self.running = True
        self.send_command("RUN\n")

    def run_drop_syscall_async(self, return_value):
        """ Asynchornous version of 'run_drop_syscall'. It does not wait
           for the next event and retusn immediately. """
        self.running = True
        self.send_command("RUN_DROP_SYSCALL {0}\n".format(return_value))

    def finish_async(self):
        """ Finishes an asynchronous call """
        assert self.running
        self.running = False
        return self.receive_line()

    def run_function(self, fn_pointer, fn_type, *args):
        """ Executes a function in client """
        command = "RUN_FUNCTION {0} {1} {2} {3} \n".format(
                  fn_pointer, fn_type, len(args), " ".join(map(str, args)))
        return self.send_and_receive(command)

    def client_malloc(self, size):
        """ Calls "malloc" in client, i.e. allocate a memory
            that is visible for the verified process """
        return self.send_and_receive_int("CLIENT_MALLOC {0}\n".format(size))

    def client_free(self, mem):
        """ Calls "free" in client (an opposite function to client_malloc) """
        self.send_and_receive_ok("CLIENT_FREE {0}\n".format(mem))

    def client_malloc_from_buffer(self, buffer_id):
        """ Allocate a client's memory with the same size as buffer and
            copy buffer into this memory. """
        return self.send_and_receive_int(
            "CLIENT_MALLOC_FROM_BUFFER {0}\n".format(buffer_id))

    def memcpy(self, addr, source, size, check=True):
        """ Copies a non-overlapping block memory """
        self.send_and_receive_ok("WRITE {0} {1} addr {2} {3}\n"
                                 .format(check_str(check), addr, source, size))

    def write_into_buffer(self, buffer_id, index, addr, size):
        """ Writes a client memory into a buffer """
        # Copy a memory from client addres to the buffer
        self.send_and_receive_ok("WRITE_BUFFER {0} {1} {2} {3}\n"
                                 .format(buffer_id, index, addr, size))

    def write_data(self, addr, data, check=True):
        """ Writes data (str) into client's memory """
        size = len(data)
        if size == 0:
            return
        # TODO: the following constant should be benchmarked
        if size < 8192:
            command = "WRITE {0} {1} mem {2}\n{3}" \
                .format(check_str(check), addr, len(data), data)
            self.send_data_and_receive_ok(command)
        else:
            command = "WRITE {0} {1} mem {2}" \
                .format(check_str(check), addr, len(data))
            self.send_command(command)
            self.send_data_and_receive_ok(data)

    def write_data_into_buffer(self, buffer_addr, index, data):
        """ Writes data (str) into buffer """
        size = len(data)
        if size == 0:
            return
        # TODO: the following constant should be benchmarked
        if size < 8192:
            self.send_data_and_receive_ok(
                "WRITE_BUFFER_DATA {0} {1} {2}\n{3}"
                .format(buffer_addr, index, len(data), data))
        else:
            self.send_command("WRITE_BUFFER_DATA {0} {1} {2}\n"
                              .format(buffer_addr, index, len(data)))
            self.send_data_and_receive_ok(data)

    def write_buffer(self, addr, buffer_addr,
                     index=None, size=None, check=True):
        """ Copies a buffer into client's memory """
        if index is None or size is None:
            self.send_and_receive_ok(
                "WRITE {0} {1} buffer {2}\n"
                .format(check_str(check), addr, buffer_addr))
        else:
            assert size is not None
            if size == 0:
                return
            self.send_and_receive_ok(
                "WRITE {0} {1} buffer-part {2} {3} {4}\n"
                .format(check_str(check), addr, buffer_addr, index, size))

    def write_int(self, addr, value, check=True):
        """ Writes int into client's memory """
        self.send_and_receive_ok("WRITE {0} {1} int {2}\n"
                                 .format(check_str(check), addr, value))

    def write_string(self, addr, value, check=True):
        """ Writes string into client's memory """
        self.send_and_receive_ok("WRITE {0} {1} string {2}\n"
                                 .format(check_str(check), addr, value))

    def write_pointer(self, addr, value, check=True):
        """ Writes pointer into client's memory """
        self.send_and_receive_ok("WRITE {0} {1} pointer {2}\n"
                                 .format(check_str(check), addr, value))

    def write_ints(self, addr, values, check=True):
        """ Writes an array of ints into client's memory """
        self.send_and_receive_ok(
            "WRITE {0} {1} ints {2} {3}\n"
            .format(check_str(check),
                    addr,
                    len(values),
                    " ".join(map(str, values))))

    def read_mem(self, addr, size):
        """ Reads client's memory """
        return self.send_and_receive_data("READ {0} mem {1}\n"
                                          .format(addr, size))

    def read_int(self, addr):
        """ Reads int from client's memory """
        return self.send_and_receive_int("READ {0} int\n".format(addr))

    def read_pointer(self, addr):
        """ Reads pointer from client's memory """
        return self.send_and_receive_int("READ {0} pointer\n".format(addr))

    def read_ints(self, addr, count):
        """ Reads an array of ints from client's memory """
        line = self.send_and_receive("READ {0} ints {1}\n".format(addr, count))
        results = map(int, line.split())
        assert len(results) == count
        return results

    def read_pointers(self, addr, count):
        """ Reads an array of pointers from client's memory """
        line = self.send_and_receive("READ {0} pointers {1}\n"
                                     .format(addr, count))
        results = map(int, line.split())
        assert len(results) == count
        return results

    def read_string(self, addr):
        """ Reads a string from client's memory """
        return self.send_and_receive_data("READ {0} string\n".format(addr))

    def read_buffer(self, buffer_id):
        """ Reads a buffer """
        return self.send_and_receive_data("READ_BUFFER {0}\n"
                                          .format(buffer_id))

    def hash_state(self):
        """ Hashes current process state """
        h = self.send_and_receive("HASH\n")
        return h

    def hash_buffer(self, buffer_id):
        """ Hashes a buffer """
        return self.send_and_receive("HASH_BUFFER {0}\n".format(buffer_id))

    def get_stacktrace(self):
        """ Returns stack trace (each item separeted by ';') """
        return self.send_and_receive("STACKTRACE\n")

    def get_stats(self):
        """ Gets internal statistic from client
           (number of states, buffers, etc ...) """
        self.send_command("STATS\n")
        result = {}
        for entry in self.receive_line().split("|"):
            name, value = entry.split()
            result[name] = int(value)
        return result

    def is_writable(self, addr, size):
        """ Returns True if an client's address is writable """
        return self.send_and_receive("CHECK write {0} {1}\n"
                                     .format(addr, size))

    def is_readable(self, addr, size):
        """ Returns True if an client's address is readable """
        return self.send_and_receive("CHECK read {0} {1}\n".format(addr, size))

    def lock_memory(self, addr, size):
        """ Marks a client's memory as read only """
        self.send_and_receive_ok("LOCK {0} {1}\n".format(addr, size))

    def unlock_memory(self, addr, size):
        """ Marks a client's memory as defined """
        self.send_and_receive_ok("UNLOCK {0} {1}\n".format(addr, size))

    def get_allocations(self):
        """ Get list of client's allocations on heap """
        return self.send_and_receive("ALLOCATIONS\n")

    def interconn_listen(self):
        """ Clients start to listen for a connection on a free port,
            that is returned from the method.
            This blocks AVT but not controller.
            Method 'interconn_listen_finish' has to be called
            after this method """
        port = self.send_and_receive_int("CONN_LISTEN\n")
        self.running = True
        return port

    def interconn_listen_finish(self):
        """ This has to be called after interconn_listen.
            It blocks until the client is not connected and then
            returns socket id """
        return int(self.finish_async())

    def interconn_connect(self, host):
        """ Initializes a connection to another AVT that is listening by
            'interconn_listen'. This has be followed by
            'interconn_connect_finish'. """
        self.running = True
        return self.send_command("CONN_CONNECT {0}\n".format(host))

    def interconn_connect_finish(self):
        """ This method has to follow 'interconn_connect'.
            Blocks until connection is not finished.
            Returns socket id."""
        return int(self.finish_async())

    def push_state(self, socket, state_id):
        """ Send a state through an AVT interconnection """
        self.send_command("CONN_PUSH_STATE {0} {1}\n".format(socket, state_id))

    def pull_state(self, socket):
        """ Receives a state through an AVT interconnection """
        return self.send_and_receive_int("CONN_PULL_STATE {0}\n"
                                         .format(socket))

    def send_command(self, command):
        """ Send a command to AVT """
        assert command[-1] == "\n", "Command does not end with new line"
        self.socket.send_data(command)

    def send_data(self, data):
        """ Send data to AVT """
        self.socket.send_data(data)

    def receive_line(self):
        """ Receives a line (string) from AVT """
        line = self.socket.read_line()
        if line.startswith("Error:"):
            raise Exception("Received line: " + line)
        return line

    def receive_data(self):
        """ Receives a data from AVT """
        args = self.socket.read_line().split()
        return self.socket.read_data(int(args[1]))

    def send_and_receive(self, command):
        """ Sends a command and waits for the answer."""
        self.send_command(command)
        assert not self.running
        return self.receive_line()

    def send_and_receive_data(self, command):
        """ Sends a command and waits for the answer as data."""
        self.send_command(command)
        assert not self.running
        return self.receive_data()

    def send_and_receive_ok(self, command):
        """ Sends a command and waits for its confirmation (string "Ok\n") """
        self.send_command(command)
        assert not self.running
        r = self.receive_line()
        if r != "Ok":
            raise self.on_unexpected_output(r)

    def send_data_and_receive_ok(self, data):
        """ Sends data and waits for its confirmation (string "Ok\n") """
        self.send_data(data)
        assert not self.running
        r = self.receive_line()
        if r != "Ok":
            raise self.on_unexpected_output(r)

    def on_unexpected_output(self, line):
        """ This method is called when unexpected output is received,
            by default it throws UnexpectedOutput exception"""
        raise UnexpectedOutput(line)

    def receive_until_ok(self):
        """ Receives lines until "Ok" is not received """
        result = []
        line = self.receive_line()
        while line != "Ok":
            result.append(line)
            line = self.receive_line()
        return result

    def send_and_receive_int(self, command):
        """ Sends a command and waits for int """
        return int(self.send_and_receive(command))

    def debug_compare(self, state_id1, state_id2):
        """ Compares two saved states in AVT """
        self.send_and_receive_ok("DEBUG_COMPARE {0} {1}\n"
                                 .format(state_id1, state_id2))

    def debug_dump_state(self, state_id):
        """ Dumps a saved state on stderr """
        self.send_and_receive_ok("DEBUG_DUMP_STATE {0}\n"
                                 .format(state_id))

    def make_buffer(self, buffer_id, size):
        """ Creates a new buffer """
        self.send_and_receive_ok("NEW_BUFFER {0} {1}\n"
                                 .format(buffer_id, size))

    def free_buffer(self, buffer_id):
        """ Frees a buffer """
        self.send_command("FREE_BUFFER {0}\n".format(buffer_id))

    def _start_valgrind(self, port, capture_syscalls):
        args = (
            self.valgrind_bin,
            "-q",
            "--tool=aislinn",
            "--port={0}".format(port),
            "--identification={0}".format(self.name),
        ) + tuple(["--capture-syscall=" + name for name in capture_syscalls])

        if self.profile:
            args += ("--profile=yes",)

        if self.heap_size is not None:
            args += ("--heap-size={0}".format(self.heap_size),)

        if self.redzone_size is not None:
            args += ("--alloc-redzone-size={0}".format(self.redzone_size),)

        if self.verbose is not None:
            args += ("--verbose={0}".format(self.verbose),)

        args += self.args

        if self.debug_by_valgrind_tool:
            args = (
                "valgrind",
                "--tool=" + self.debug_by_valgrind_tool,
                "--sim-hints=enable-outer",
                "--trace-children=yes",
                "--smc-check=all-non-file",
                "--run-libc-freeres=no") + args

        logging.debug("Starting valgrind with %s", args)

        if self.extra_env:
            env = os.environ.copy()
            for v in self.extra_env:
                env[v] = self.extra_env[v]
        else:
            env = None
        self.process = subprocess.Popen(
            args, cwd=self.cwd, env=env,
            stdout=self.stdout_file, stderr=self.stderr_file)

    def _start_server(self):
        HOST = "127.0.0.1"  # Connection only from localhost
        PORT = 0  # Alloc arbirary empty port
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.bind((HOST, PORT))
        s.listen(1)
        return s

    def __repr__(self):
        return "<Controller '{0}'>".format(self.name)
コード例 #2
0
ファイル: controller.py プロジェクト: msurkovsky/aislinn
class Controller:

    # TODO: Universal architecture detection
    POINTER_SIZE = 8
    INT_SIZE = 4

    FUNCTION_INT = 0
    FUNCTION_4_POINTER = 1
    FUNCTION_2_INT_2_POINTER = 2
    FUNCTION_2_INT_4_POINTER = 3

    debug_by_valgrind_tool = None

    stdout_arg = None
    buffer_server_port = None
    profile = False

    name = "" # For debug purpose

    def __init__(self, args, cwd=None):
        self.process = None
        self.socket = None
        self.recv_buffer = ""
        self.args = tuple(args)
        self.cwd = cwd
        self.valgrind_args = ()
        self.server_socket = None
        self.running = False

    def start(self, capture_syscalls=()):
        assert self.process is None # Nothing is running
        assert self.socket is None

        self.server_socket = self._start_server()
        port = self.server_socket.getsockname()[1]
        self._start_valgrind(port, capture_syscalls)

    def start_and_connect(self, *args, **kw):
        self.start(*args, **kw)
        self.connect()
        return self.receive_line()

    def connect(self):
        self.server_socket.settimeout(5.0)
        try:
            sock, addr = self.server_socket.accept()
        except socket.timeout:
            logging.error("Aislinn client was not started")
            return False
        self.socket = SocketWrapper(sock)
        self.socket.set_no_delay()
        self.server_socket.close()
        self.server_socket = None
        return True
        # User has to call receive_line after calling this method
        # But it may take some time to initialize vgclient, hence
        # it is not build in in connect to allow polling

    def kill(self):
        if self.process and self.process.poll() is None:
            self.process.kill()
            self.process = None

    def run_process(self):
        return self.send_and_receive("RUN\n")

    def run_drop_syscall(self):
        return self.send_and_receive("RUN_DROP_SYSCALL\n")

    def run_process_async(self):
        self.running = True
        self.send_command("RUN\n")

    def run_drop_syscall_async(self):
        self.running = True
        self.send_command("RUN_DROP_SYSCALL\n")

    def run_function(self, fn_pointer, fn_type, *args):
        command = "RUN_FUNCTION {0} {1} {2} {3} \n".format(
                fn_pointer, fn_type, len(args), " ".join(map(str, args)))
        return self.send_and_receive(command)

    def client_malloc(self, size):
        return self.send_and_receive_int("CLIENT_MALLOC {0}\n".format(size))

    def client_free(self, mem):
        self.send_and_receive_ok("CLIENT_FREE {0}\n".format(mem))

    def client_malloc_from_buffer(self, buffer_id):
        return self.send_and_receive_int(
                "CLIENT_MALLOC_FROM_BUFFER {0}\n".format(buffer_id))

    def memcpy(self, addr, source, size, check=True):
        self.send_and_receive_ok("WRITE {0} {1} addr {2} {3}\n" \
                .format(check_str(check), addr, source, size))

    def write_into_buffer(self, buffer_addr, index, addr, size):
        # Copy a memory from client addres to the buffer
        self.send_and_receive_ok("WRITE_BUFFER {0} {1} {2} {3}\n" \
                    .format(buffer_addr, index, addr, size))

    def write_data_into_buffer(self, buffer_addr, index, data):
        # Write a literal data into the buffer
        size = len(data)
        if size == 0:
            return
        # TODO: the following constant should be benchmarked
        if size < 8192:
            self.send_data_and_receive_ok(
                    "WRITE_BUFFER_DATA {0} {1} {2}\n{3}" \
                            .format(buffer_addr, index, len(data), data))
        else:
            self.send_command("WRITE_BUFFER_DATA {0} {1} {2}\n" \
                        .format(buffer_addr, index, len(data)))
            self.send_data_and_receive_ok(data)

    def write_buffer(self, addr, buffer_addr,
                     index=None, size=None, check=True):
        if index is None or size is None:
            self.send_and_receive_ok("WRITE {0} {1} buffer {2}\n" \
                    .format(check_str(check), addr, buffer_addr))
        else:
            assert size is not None
            if size == 0:
                return
            self.send_and_receive_ok("WRITE {0} {1} buffer-part {2} {3} {4}\n" \
                    .format(check_str(check), addr, buffer_addr, index, size))

    def write_int(self, addr, value, check=True):
        self.send_and_receive_ok("WRITE {0} {1} int {2}\n" \
                .format(check_str(check), addr, value))

    def write_string(self, addr, value, check=True):
        self.send_and_receive_ok("WRITE {0} {1} string {2}\n" \
                .format(check_str(check), addr, value))


    def write_pointer(self, addr, value, check=True):
        self.send_and_receive_ok("WRITE {0} {1} pointer {2}\n" \
                .format(check_str(check), addr, value))

    def write_ints(self, addr, values, check=True):
        self.send_and_receive_ok("WRITE {0} {1} ints {2} {3}\n" \
                .format(check_str(check), addr, len(values), " ".join(map(str, values))))

    def read_mem(self, addr, size):
        return self.send_and_receive_data("READ {0} mem {1}\n" \
                .format(addr, size))

    def read_int(self, addr):
        return self.send_and_receive_int("READ {0} int\n".format(addr))

    def read_pointer(self, addr):
        return self.send_and_receive_int("READ {0} pointer\n".format(addr))

    def read_ints(self, addr, count):
        line = self.send_and_receive("READ {0} ints {1}\n".format(addr, count))
        results = map(int, line.split())
        assert len(results) == count
        return results

    def read_pointers(self, addr, count):
        line = self.send_and_receive("READ {0} pointers {1}\n" \
                .format(addr, count))
        results = map(int, line.split())
        assert len(results) == count
        return results

    def read_string(self, addr):
        return self.send_and_receive_data("READ {0} string\n".format(addr))

    def hash_state(self):
        #s = time.time()
        h = self.send_and_receive("HASH\n")
        #e = time.time()
        #print e - s
        return h

    def hash_buffer(self, buffer_id):
        return self.send_and_receive("HASH_BUFFER {0}\n".format(buffer_id))

    def get_stacktrace(self):
        return self.send_and_receive("STACKTRACE\n")

    def get_stats(self):
        self.send_command("STATS\n")
        result = {}
        for entry in self.receive_line().split("|"):
            name, value = entry.split()
            result[name] = int(value)
        return result

    def is_writable(self, addr, size):
        return self.send_and_receive("CHECK write {0} {1}\n".format(addr, size))

    def is_readable(self, addr, size):
        return self.send_and_receive("CHECK read {0} {1}\n".format(addr, size))

    def lock_memory(self, addr, size):
        self.send_and_receive_ok("LOCK {0} {1}\n".format(addr, size))

    def unlock_memory(self, addr, size):
        self.send_and_receive_ok("UNLOCK {0} {1}\n".format(addr, size))

    def get_allocations(self):
        return self.send_and_receive("ALLOCATIONS\n");

    ### Semi-internal functions

    def send_command(self, command):
        assert command[-1] == "\n", "Command does not end with new line"
        self.socket.send_data(command)

    def send_data(self, data):
       self.socket.send_data(data)

    def receive_line(self):
        line = self.socket.read_line()
        if line.startswith("Error:"):
            raise Exception("Received line: " + line)
        return line

    def finish_async(self):
        assert self.running
        self.running = False
        return self.receive_line()

    def receive_data(self):
        args = self.socket.read_line().split()
        return self.socket.read_data(int(args[1]))

    def send_and_receive(self, command):
        self.send_command(command)
        assert not self.running
        return self.receive_line()

    def send_and_receive_data(self, command):
        self.send_command(command)
        assert not self.running
        return self.receive_data()

    def send_and_receive_ok(self, command):
        self.send_command(command)
        assert not self.running
        r = self.receive_line()
        if r != "Ok":
            raise self.on_unexpected_output(r)

    def send_data_and_receive_ok(self, data):
        self.send_data(data)
        assert not self.running
        r = self.receive_line()
        if r != "Ok":
            raise self.on_unexpected_output(r)

    def on_unexpected_output(self, line):
        raise UnexpectedOutput(line)

    def receive_until_ok(self):
        result = []
        line = self.receive_line()
        while line != "Ok":
            result.append(line)
            line = self.receive_line()
        return result

    def send_and_receive_int(self, command):
        return int(self.send_and_receive(command))

    def debug_compare(self, state_id1, state_id2):
        self.send_and_receive_ok(
             "DEBUG_COMPARE {0} {1}\n".format(state_id1, state_id2))

    def set_capture_syscall(self, syscall, value):
        self.send_and_receive_ok(
             "SET syscall {0} {1}\n".format(syscall, "on" if value else "off"))

    def save_state(self):
        return self.send_and_receive_int("SAVE\n")

    def restore_state(self, state_id):
        self.send_and_receive_ok("RESTORE {0}\n".format(state_id))

    def make_buffer(self, buffer_id, size):
        self.send_and_receive_ok(
             "NEW_BUFFER {0} {1}\n".format(buffer_id, size))

    def start_remote_buffer(self, buffer_id):
        self.send_command("START_REMOTE_BUFFER {0}\n".format(buffer_id))

    def finish_remote_buffer(self):
        self.send_and_receive_ok("FINISH_REMOTE_BUFFER\n")

    def remote_buffer_upload(self, addr, size):
        self.send_command("UPLOAD {0} {1}\n".format(addr, size))

    def remote_buffer_download(self, buffer_id):
        self.send_command("DOWNLOAD {0}\n".format(buffer_id))

    def free_state(self, state_id):
        self.send_command("FREE {0}\n".format(state_id))

    def free_buffer(self, buffer_id):
        self.send_command("FREE_BUFFER {0}\n".format(buffer_id))

    def _start_valgrind(self, port, capture_syscalls):
        args = (
            paths.VALGRIND_BIN,
            "-q",
            "--tool=aislinn",
            "--port={0}".format(port),
            "--identification={0}".format(self.name),
        ) + tuple([ "--capture-syscall=" + name for name in capture_syscalls ])

        if self.buffer_server_port is not None:
            args += ("--bs-port={0}".format(self.buffer_server_port),)

        if self.profile:
            args += ("--profile=yes",)

        args += tuple(self.valgrind_args) + tuple(self.args)

        if self.debug_by_valgrind_tool:
            args = (
                "valgrind",
                "--tool=" + self.debug_by_valgrind_tool,
                "--sim-hints=enable-outer",
                "--trace-children=yes",
                "--smc-check=all-non-file",
                "--run-libc-freeres=no") + args

        logging.debug("Starting valgrind with %s", args)
        self.process = subprocess.Popen(
            args, cwd=self.cwd, stdout=self.stdout_arg)

    def _start_server(self):
        HOST = "127.0.0.1" # Connection only from localhost
        PORT = 0 # Alloc arbirary empty port
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.bind((HOST, PORT))
        s.listen(1)
        return s

    def __repr__(self):
        return "<Controller '{0}'>".format(self.name)