mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-24 22:36:09 +03:00
186 lines
6.1 KiB
Python
186 lines
6.1 KiB
Python
|
# Copyright 2018 The Chromium Authors. All rights reserved.
|
||
|
# Use of this source code is governed by a BSD-style license that can be
|
||
|
# found in the LICENSE file.
|
||
|
|
||
|
"""Contains a helper function for deploying and executing a packaged
|
||
|
executable on a Target."""
|
||
|
|
||
|
import common
|
||
|
import json
|
||
|
import logging
|
||
|
import multiprocessing
|
||
|
import os
|
||
|
import shutil
|
||
|
import subprocess
|
||
|
import tempfile
|
||
|
import threading
|
||
|
import uuid
|
||
|
import select
|
||
|
|
||
|
from symbolizer import FilterStream
|
||
|
|
||
|
FAR = os.path.join(common.SDK_ROOT, 'tools', 'far')
|
||
|
PM = os.path.join(common.SDK_ROOT, 'tools', 'pm')
|
||
|
|
||
|
# Amount of time to wait for the termination of the system log output thread.
|
||
|
_JOIN_TIMEOUT_SECS = 5
|
||
|
|
||
|
|
||
|
def _AttachKernelLogReader(target):
|
||
|
"""Attaches a kernel log reader as a long-running SSH task."""
|
||
|
|
||
|
logging.info('Attaching kernel logger.')
|
||
|
return target.RunCommandPiped(['dlog', '-f'], stdin=open(os.devnull, 'r'),
|
||
|
stdout=subprocess.PIPE)
|
||
|
|
||
|
|
||
|
def _ReadMergedLines(streams):
|
||
|
"""Creates a generator which merges the buffered line output from |streams|.
|
||
|
The generator is terminated when the primary (first in sequence) stream
|
||
|
signals EOF. Absolute output ordering is not guaranteed."""
|
||
|
|
||
|
assert len(streams) > 0
|
||
|
poll = select.poll()
|
||
|
streams_by_fd = {}
|
||
|
primary_fd = streams[0].fileno()
|
||
|
for s in streams:
|
||
|
poll.register(s.fileno(), select.POLLIN)
|
||
|
streams_by_fd[s.fileno()] = s
|
||
|
|
||
|
try:
|
||
|
while primary_fd != None:
|
||
|
events = poll.poll(1)
|
||
|
for fileno, event in events:
|
||
|
if event & select.POLLIN:
|
||
|
yield streams_by_fd[fileno].readline()
|
||
|
|
||
|
elif event & select.POLLHUP:
|
||
|
poll.unregister(fileno)
|
||
|
del streams_by_fd[fileno]
|
||
|
|
||
|
if fileno == primary_fd:
|
||
|
primary_fd = None
|
||
|
finally:
|
||
|
for fd_to_cleanup, _ in streams_by_fd.iteritems():
|
||
|
poll.unregister(fd_to_cleanup)
|
||
|
|
||
|
|
||
|
def DrainStreamToStdout(stream, quit_event):
|
||
|
"""Outputs the contents of |stream| until |quit_event| is set."""
|
||
|
|
||
|
poll = select.poll()
|
||
|
poll.register(stream.fileno(), select.POLLIN)
|
||
|
try:
|
||
|
while not quit_event.is_set():
|
||
|
events = poll.poll(1)
|
||
|
for fileno, event in events:
|
||
|
if event & select.POLLIN:
|
||
|
print stream.readline().rstrip()
|
||
|
elif event & select.POLLHUP:
|
||
|
break
|
||
|
|
||
|
finally:
|
||
|
poll.unregister(stream.fileno())
|
||
|
|
||
|
|
||
|
def RunPackage(output_dir, target, package_path, package_name, package_deps,
|
||
|
run_args, system_logging, install_only, symbolizer_config=None):
|
||
|
"""Copies the Fuchsia package at |package_path| to the target,
|
||
|
executes it with |run_args|, and symbolizes its output.
|
||
|
|
||
|
output_dir: The path containing the build output files.
|
||
|
target: The deployment Target object that will run the package.
|
||
|
package_path: The path to the .far package file.
|
||
|
package_name: The name of app specified by package metadata.
|
||
|
run_args: The arguments which will be passed to the Fuchsia process.
|
||
|
system_logging: If set, connects a system log reader to the target.
|
||
|
install_only: If set, skips the package execution step.
|
||
|
symbolizer_config: A newline delimited list of source files contained
|
||
|
in the package. Omitting this parameter will disable
|
||
|
symbolization.
|
||
|
|
||
|
Returns the exit code of the remote package process."""
|
||
|
|
||
|
|
||
|
system_logger = _AttachKernelLogReader(target) if system_logging else None
|
||
|
try:
|
||
|
if system_logger:
|
||
|
# Spin up a thread to asynchronously dump the system log to stdout
|
||
|
# for easier diagnoses of early, pre-execution failures.
|
||
|
log_output_quit_event = multiprocessing.Event()
|
||
|
log_output_thread = threading.Thread(
|
||
|
target=lambda: DrainStreamToStdout(system_logger.stdout,
|
||
|
log_output_quit_event))
|
||
|
log_output_thread.daemon = True
|
||
|
log_output_thread.start()
|
||
|
|
||
|
for next_package_path in ([package_path] + package_deps):
|
||
|
logging.info('Installing ' + os.path.basename(next_package_path) + '.')
|
||
|
|
||
|
# Copy the package archive.
|
||
|
install_path = os.path.join('/data', os.path.basename(next_package_path))
|
||
|
target.PutFile(next_package_path, install_path)
|
||
|
|
||
|
# Install the package.
|
||
|
p = target.RunCommandPiped(['pm', 'install', install_path],
|
||
|
stderr=subprocess.PIPE)
|
||
|
output = p.stderr.readlines()
|
||
|
p.wait()
|
||
|
if p.returncode != 0:
|
||
|
# Don't error out if the package already exists on the device.
|
||
|
if len(output) != 1 or 'ErrAlreadyExists' not in output[0]:
|
||
|
raise Exception('Error while installing: %s' % '\n'.join(output))
|
||
|
|
||
|
# Clean up the package archive.
|
||
|
target.RunCommand(['rm', install_path])
|
||
|
|
||
|
if system_logger:
|
||
|
log_output_quit_event.set()
|
||
|
log_output_thread.join(timeout=_JOIN_TIMEOUT_SECS)
|
||
|
|
||
|
if install_only:
|
||
|
logging.info('Installation complete.')
|
||
|
return
|
||
|
|
||
|
logging.info('Running application.')
|
||
|
command = ['run', package_name] + run_args
|
||
|
process = target.RunCommandPiped(command,
|
||
|
stdin=open(os.devnull, 'r'),
|
||
|
stdout=subprocess.PIPE,
|
||
|
stderr=subprocess.STDOUT)
|
||
|
|
||
|
if system_logger:
|
||
|
task_output = _ReadMergedLines([process.stdout, system_logger.stdout])
|
||
|
else:
|
||
|
task_output = process.stdout
|
||
|
|
||
|
if symbolizer_config:
|
||
|
# Decorate the process output stream with the symbolizer.
|
||
|
output = FilterStream(task_output, package_name, symbolizer_config,
|
||
|
output_dir)
|
||
|
else:
|
||
|
logging.warn('Symbolization is DISABLED.')
|
||
|
output = process.stdout
|
||
|
|
||
|
for next_line in output:
|
||
|
print next_line.rstrip()
|
||
|
|
||
|
process.wait()
|
||
|
if process.returncode == 0:
|
||
|
logging.info('Process exited normally with status code 0.')
|
||
|
else:
|
||
|
# The test runner returns an error status code if *any* tests fail,
|
||
|
# so we should proceed anyway.
|
||
|
logging.warning('Process exited with status code %d.' %
|
||
|
process.returncode)
|
||
|
|
||
|
finally:
|
||
|
if system_logger:
|
||
|
logging.info('Terminating kernel log reader.')
|
||
|
log_output_quit_event.set()
|
||
|
log_output_thread.join()
|
||
|
system_logger.kill()
|
||
|
|
||
|
|
||
|
return process.returncode
|