mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Unfortunately, using --origin on Linux is slow and too painful to use at the moment. We should work on fixing that, but currently many of us have this in our local working copy anyway. R=eseidel@chromium.org Review URL: https://codereview.chromium.org/922823002
610 lines
24 KiB
Python
Executable File
610 lines
24 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# Copyright 2014 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.
|
|
|
|
from skypy.skyserver import SkyServer
|
|
import argparse
|
|
import json
|
|
import logging
|
|
import os
|
|
import pipes
|
|
import re
|
|
import requests
|
|
import signal
|
|
import skypy.paths
|
|
import StringIO
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
import urlparse
|
|
|
|
SUPPORTED_MIME_TYPES = [
|
|
'text/html',
|
|
'text/sky',
|
|
'text/plain',
|
|
]
|
|
|
|
DEFAULT_SKY_COMMAND_PORT = 7777
|
|
GDB_PORT = 8888
|
|
SKY_SERVER_PORT = 9999
|
|
DEFAULT_URL = "https://raw.githubusercontent.com/domokit/mojo/master/sky/examples/home.sky"
|
|
|
|
ANDROID_PACKAGE = "org.chromium.mojo.shell"
|
|
ANDROID_ACTIVITY = "%s/.MojoShellActivity" % ANDROID_PACKAGE
|
|
ANDROID_APK_NAME = 'MojoShell.apk'
|
|
|
|
PID_FILE_PATH = "/tmp/skydb.pids"
|
|
CACHE_LINKS_PATH = '/tmp/mojo_cache_links'
|
|
SYSTEM_LIBS_ROOT_PATH = '/tmp/device_libs'
|
|
|
|
SRC_ROOT = skypy.paths.Paths('ignored').src_root
|
|
ADB_PATH = os.path.join(SRC_ROOT,
|
|
'third_party/android_tools/sdk/platform-tools/adb')
|
|
|
|
|
|
# FIXME: Move this into mopy.config
|
|
def gn_args_from_build_dir(build_dir):
|
|
gn_cmd = [
|
|
'gn', 'args',
|
|
build_dir,
|
|
'--list', '--short'
|
|
]
|
|
config = {}
|
|
for line in subprocess.check_output(gn_cmd).strip().split('\n'):
|
|
# FIXME: This doesn't handle = in values.
|
|
key, value = line.split(' = ')
|
|
config[key] = value
|
|
return config
|
|
|
|
|
|
class SkyDebugger(object):
|
|
def __init__(self):
|
|
self.pids = {}
|
|
self.paths = None
|
|
|
|
def _server_root_for_url(self, url_or_path):
|
|
path = os.path.abspath(url_or_path)
|
|
if os.path.commonprefix([path, SRC_ROOT]) == SRC_ROOT:
|
|
server_root = SRC_ROOT
|
|
else:
|
|
server_root = os.path.dirname(path)
|
|
logging.warn(
|
|
'%s is outside of mojo root, using %s as server root' %
|
|
(path, server_root))
|
|
return server_root
|
|
|
|
def _in_chromoting(self):
|
|
return os.environ.get('CHROME_REMOTE_DESKTOP_SESSION', False)
|
|
|
|
def _wrap_for_android(self, shell_args):
|
|
# am shell --esa: (someone shoot me now)
|
|
# [--esa <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]
|
|
# (to embed a comma into a string escape it using "\,")
|
|
escaped_args = map(lambda arg: arg.replace(',', '\\,'), shell_args)
|
|
return [
|
|
ADB_PATH, 'shell',
|
|
'am', 'start',
|
|
'-W',
|
|
'-S',
|
|
'-a', 'android.intent.action.VIEW',
|
|
'-n', ANDROID_ACTIVITY,
|
|
# FIXME: This quoting is very error-prone. Perhaps we should read
|
|
# our args from a file instead?
|
|
'--esa', 'parameters', ','.join(escaped_args),
|
|
]
|
|
|
|
def _build_mojo_shell_command(self, args, is_android):
|
|
content_handlers = ['%s,%s' % (mime_type, 'mojo:sky_viewer')
|
|
for mime_type in SUPPORTED_MIME_TYPES]
|
|
|
|
remote_command_port = self.pids.get('remote_sky_command_port', self.pids['sky_command_port'])
|
|
remote_server_port = self.pids.get('remote_sky_server_port', self.pids['sky_server_port'])
|
|
|
|
shell_args = [
|
|
'--v=1',
|
|
'--content-handlers=%s' % ','.join(content_handlers),
|
|
'--url-mappings=mojo:window_manager=mojo:kiosk_wm',
|
|
'--args-for=mojo:sky_debugger %d' % remote_command_port,
|
|
'mojo:sky_debugger',
|
|
]
|
|
|
|
if args.url_or_path:
|
|
shell_args.append(
|
|
'--args-for=mojo:window_manager %s' % self._url_from_args(args))
|
|
|
|
# Map all mojo: urls to http: urls using the --origin command.
|
|
build_dir_url = SkyServer.url_for_path(
|
|
remote_server_port,
|
|
self.pids['sky_server_root'],
|
|
self.pids['build_dir'])
|
|
|
|
# TODO(eseidel): We should do this on linux, but we need to fix
|
|
# mojo http loading to be faster first.
|
|
if is_android:
|
|
shell_args += ['--origin=%s' % build_dir_url]
|
|
|
|
# Desktop-only work-around for mojo crashing under chromoting.
|
|
if not is_android and args.use_osmesa:
|
|
shell_args.append(
|
|
'--args-for=mojo:native_viewport_service --use-osmesa')
|
|
|
|
if is_android and args.gdb:
|
|
shell_args.append('--wait-for-debugger')
|
|
|
|
if 'remote_sky_server_port' in self.pids:
|
|
shell_command = self._wrap_for_android(shell_args)
|
|
else:
|
|
shell_command = [self.paths.mojo_shell_path] + shell_args
|
|
|
|
return shell_command
|
|
|
|
def sky_server_for_args(self, args):
|
|
# FIXME: This is a hack. sky_server should just take a build_dir
|
|
# not a magical "configuration" name.
|
|
configuration = os.path.basename(os.path.normpath(self.paths.build_dir))
|
|
server_root = self._server_root_for_url(args.url_or_path)
|
|
sky_server = SkyServer(self.paths, SKY_SERVER_PORT,
|
|
configuration, server_root)
|
|
return sky_server
|
|
|
|
def _create_paths_for_build_dir(self, build_dir):
|
|
# skypy.paths.Paths takes a root-relative build_dir argument. :(
|
|
abs_build_dir = os.path.abspath(build_dir)
|
|
root_relative_build_dir = os.path.relpath(abs_build_dir, SRC_ROOT)
|
|
return skypy.paths.Paths(root_relative_build_dir)
|
|
|
|
def _find_remote_pid_for_package(self, package):
|
|
ps_output = subprocess.check_output([ADB_PATH, 'shell', 'ps'])
|
|
for line in ps_output.split('\n'):
|
|
fields = line.split()
|
|
if fields and fields[-1] == package:
|
|
return fields[1]
|
|
return None
|
|
|
|
def _find_install_location_for_package(self, package):
|
|
pm_command = [ADB_PATH, 'shell', 'pm', 'path', package]
|
|
pm_output = subprocess.check_output(pm_command)
|
|
# e.g. package:/data/app/org.chromium.mojo.shell-1/base.apk
|
|
return pm_output.split(':')[-1]
|
|
|
|
def start_command(self, args):
|
|
# FIXME: Lame that we use self for a command-specific variable.
|
|
self.paths = self._create_paths_for_build_dir(args.build_dir)
|
|
self.stop_command(None) # Quit any existing process.
|
|
|
|
if not os.path.exists(self.paths.mojo_shell_path):
|
|
print "mojo_shell not found in build_dir '%s'" % args.build_dir
|
|
print "Are you sure you sure that's a valid build_dir location?"
|
|
print "See skydb start --help for more info"
|
|
sys.exit(2)
|
|
|
|
# FIXME: This is probably not the right way to compute is_android
|
|
# from the build directory?
|
|
gn_args = gn_args_from_build_dir(self.paths.build_dir)
|
|
is_android = 'android_sdk_version' in gn_args
|
|
|
|
if is_android and args.gdb and not 'is_debug' in gn_args:
|
|
# FIXME: We don't include gdbserver in the release APK...
|
|
print "Cannot debug Release builds on Android"
|
|
sys.exit(2)
|
|
|
|
sky_server = self.sky_server_for_args(args)
|
|
self.pids['sky_server_pid'] = sky_server.start()
|
|
self.pids['sky_server_port'] = sky_server.port
|
|
self.pids['sky_server_root'] = sky_server.root
|
|
|
|
self.pids['build_dir'] = self.paths.build_dir
|
|
self.pids['sky_command_port'] = args.command_port
|
|
|
|
if is_android:
|
|
# TODO(eseidel): This should move into a helper method and handle
|
|
# failures with nice messages explaining how to get root.
|
|
subprocess.check_call([ADB_PATH, 'root'])
|
|
|
|
# We could make installing conditional on an argument.
|
|
apk_path = os.path.join(self.paths.build_dir, 'apks',
|
|
ANDROID_APK_NAME)
|
|
subprocess.check_call([ADB_PATH, 'install', '-r', apk_path])
|
|
|
|
port_string = 'tcp:%s' % sky_server.port
|
|
subprocess.check_call([
|
|
ADB_PATH, 'reverse', port_string, port_string
|
|
])
|
|
self.pids['remote_sky_server_port'] = sky_server.port
|
|
|
|
port_string = 'tcp:%s' % args.command_port
|
|
subprocess.check_call([
|
|
ADB_PATH, 'forward', port_string, port_string
|
|
])
|
|
self.pids['remote_sky_command_port'] = args.command_port
|
|
|
|
shell_command = self._build_mojo_shell_command(args, is_android)
|
|
|
|
# On android we can't launch inside gdb, but rather have to attach.
|
|
if not is_android and args.gdb:
|
|
shell_command = ['gdbserver', ':%d' % GDB_PORT] + shell_command
|
|
|
|
print ' '.join(map(pipes.quote, shell_command))
|
|
# This pid is meaningless on android (it's the adb shell pid)
|
|
start_command_pid = subprocess.Popen(shell_command).pid
|
|
|
|
if is_android:
|
|
# TODO(eseidel): am start -W does not seem to work?
|
|
pid_tries = 0
|
|
while True:
|
|
pid = self._find_remote_pid_for_package(ANDROID_PACKAGE)
|
|
if pid or pid_tries > 3:
|
|
break
|
|
logging.debug('No pid for %s yet, waiting' % ANDROID_PACKAGE)
|
|
time.sleep(5)
|
|
pid_tries += 1
|
|
|
|
if not pid:
|
|
logging.error('Failed to find mojo_shell pid on device!')
|
|
return
|
|
self.pids['mojo_shell_pid'] = pid
|
|
else:
|
|
self.pids['mojo_shell_pid'] = start_command_pid
|
|
|
|
if args.gdb and is_android:
|
|
# We push our own copy of gdbserver with the package since
|
|
# the default gdbserver is a different version from our gdb.
|
|
package_path = \
|
|
self._find_install_location_for_package(ANDROID_PACKAGE)
|
|
gdb_server_path = os.path.join(
|
|
os.path.dirname(package_path), 'lib/arm/gdbserver')
|
|
gdbserver_cmd = [
|
|
ADB_PATH, 'shell',
|
|
gdb_server_path, '--attach',
|
|
':%d' % GDB_PORT,
|
|
str(self.pids['mojo_shell_pid'])
|
|
]
|
|
print ' '.join(map(pipes.quote, gdbserver_cmd))
|
|
self.pids['adb_shell_gdbserver_pid'] = \
|
|
subprocess.Popen(gdbserver_cmd).pid
|
|
|
|
port_string = 'tcp:%d' % GDB_PORT
|
|
subprocess.check_call([
|
|
ADB_PATH, 'forward', port_string, port_string
|
|
])
|
|
self.pids['remote_gdbserver_port'] = GDB_PORT
|
|
|
|
if not args.gdb:
|
|
if not self._wait_for_sky_command_port():
|
|
logging.error('Failed to start sky')
|
|
self.stop_command(None)
|
|
else:
|
|
# We could just run gdb_attach_command here, but when I do that
|
|
# it auto-suspends in my zsh. Unclear why.
|
|
# self.gdb_attach_command(args)
|
|
print "Run 'skydb gdb_attach' to attach."
|
|
|
|
def _kill_if_exists(self, key, name):
|
|
pid = self.pids.pop(key, None)
|
|
if not pid:
|
|
logging.info('No pid for %s, nothing to do.' % name)
|
|
return
|
|
logging.info('Killing %s (%d).' % (name, pid))
|
|
try:
|
|
os.kill(pid, signal.SIGTERM)
|
|
except OSError:
|
|
logging.info('%s (%d) already gone.' % (name, pid))
|
|
|
|
def stop_command(self, args):
|
|
# TODO(eseidel): mojo_shell crashes when attempting graceful shutdown.
|
|
# self._run_basic_command('/quit')
|
|
|
|
self._kill_if_exists('sky_server_pid', 'sky_server')
|
|
|
|
if 'remote_sky_server_port' in self.pids:
|
|
port_string = 'tcp:%s' % self.pids['remote_sky_server_port']
|
|
subprocess.call([ADB_PATH, 'reverse', '--remove', port_string])
|
|
|
|
if 'remote_sky_command_port' in self.pids:
|
|
# adb forward --remove takes the *host* port, not the remote port.
|
|
port_string = 'tcp:%s' % self.pids['sky_command_port']
|
|
subprocess.call([ADB_PATH, 'forward', '--remove', port_string])
|
|
|
|
subprocess.call([
|
|
ADB_PATH, 'shell', 'am', 'force-stop', ANDROID_PACKAGE])
|
|
else:
|
|
# Only try to kill mojo_shell if it's running locally.
|
|
self._kill_if_exists('mojo_shell_pid', 'mojo_shell')
|
|
|
|
if 'remote_gdbserver_port' in self.pids:
|
|
self._kill_if_exists('adb_shell_gdbserver_pid',
|
|
'adb shell gdbserver')
|
|
|
|
port_string = 'tcp:%s' % self.pids['remote_gdbserver_port']
|
|
subprocess.call([ADB_PATH, 'forward', '--remove', port_string])
|
|
self.pids = {} # Clear out our pid file.
|
|
|
|
def _url_from_args(self, args):
|
|
if urlparse.urlparse(args.url_or_path).scheme:
|
|
return args.url_or_path
|
|
# The load happens on the remote device, use the remote port.
|
|
remote_sky_server_port = self.pids.get('remote_sky_server_port',
|
|
self.pids['sky_server_port'])
|
|
return SkyServer.url_for_path(remote_sky_server_port,
|
|
self.pids['sky_server_root'], args.url_or_path)
|
|
|
|
def load_command(self, args):
|
|
self._run_basic_command('/load', self._url_from_args(args))
|
|
|
|
def _read_mojo_map(self):
|
|
# TODO(eseidel): Does not work for android.
|
|
mojo_map_path = "/tmp/mojo_shell.%d.maps" % self.pids['mojo_shell_pid']
|
|
with open(mojo_map_path, 'r') as maps_file:
|
|
lines = maps_file.read().strip().split('\n')
|
|
return dict(map(lambda line: line.split(' '), lines))
|
|
|
|
def stop_tracing_command(self, args):
|
|
file_name = args.file_name
|
|
trace = self._send_command_to_sky('/stop_tracing').content
|
|
with open(file_name, "wb") as trace_file:
|
|
trace_file.write(trace)
|
|
print "Trace saved in %s" % file_name
|
|
|
|
def stop_profiling_command(self, args):
|
|
self._run_basic_command('/stop_profiling')
|
|
mojo_map = self._read_mojo_map()
|
|
|
|
# TODO(eseidel): We should have a helper for resolving urls, etc.
|
|
remote_server_port = self.pids.get('remote_sky_server_port', self.pids['sky_server_port'])
|
|
build_dir_url = SkyServer.url_for_path(
|
|
remote_server_port,
|
|
self.pids['sky_server_root'],
|
|
self.pids['build_dir'])
|
|
|
|
# Map /tmp cache paths to urls and then to local build_dir paths.
|
|
def map_to_local_paths(match):
|
|
path = match.group('mojo_path')
|
|
url = mojo_map.get(path)
|
|
if url and url.startswith(build_dir_url):
|
|
return url.replace(build_dir_url, self.pids['build_dir'])
|
|
return match.group(0)
|
|
|
|
MOJO_PATH_RE = re.compile(r'(?P<mojo_path>\S+\.mojo)')
|
|
MOJO_NAME_RE = re.compile(r'(?P<mojo_name>\w+)\.mojo')
|
|
|
|
with open("sky_viewer.pprof", "rb+") as profile_file:
|
|
# ISO-8859-1 can represent arbitrary binary while still keeping
|
|
# ASCII characters in the ASCII range (allowing us to regexp).
|
|
# http://en.wikipedia.org/wiki/ISO/IEC_8859-1
|
|
as_string = profile_file.read().decode('iso-8859-1')
|
|
# Using the mojo_shell.PID.maps file tmp paths to build_dir paths.
|
|
as_string = MOJO_PATH_RE.sub(map_to_local_paths, as_string)
|
|
# In release foo.mojo is stripped but libfoo_library.so isn't.
|
|
as_string = MOJO_NAME_RE.sub(r'lib\1_library.so', as_string)
|
|
profile_file.seek(0)
|
|
profile_file.write(as_string.encode('iso-8859-1'))
|
|
profile_file.truncate()
|
|
|
|
def _command_base_url(self):
|
|
return 'http://localhost:%s' % self.pids['sky_command_port']
|
|
|
|
def _send_command_to_sky(self, command_path, payload=None):
|
|
url = 'http://localhost:%s%s' % (
|
|
self.pids['sky_command_port'], command_path)
|
|
if payload:
|
|
response = requests.post(url, payload)
|
|
else:
|
|
response = requests.get(url)
|
|
return response
|
|
|
|
def _run_basic_command(self, command_path, payload=None):
|
|
print self._send_command_to_sky(command_path, payload=payload).text
|
|
|
|
# FIXME: These could be made into a context object with __enter__/__exit__.
|
|
def _load_pid_file(self, path):
|
|
try:
|
|
with open(path, 'r') as pid_file:
|
|
return json.load(pid_file)
|
|
except:
|
|
if os.path.exists(path):
|
|
logging.warn('Failed to read pid file: %s' % path)
|
|
return {}
|
|
|
|
def _write_pid_file(self, path, pids):
|
|
try:
|
|
with open(path, 'w') as pid_file:
|
|
json.dump(pids, pid_file, indent=2, sort_keys=True)
|
|
except:
|
|
logging.warn('Failed to write pid file: %s' % path)
|
|
|
|
def _add_basic_command(self, subparsers, name, url_path, help_text):
|
|
parser = subparsers.add_parser(name, help=help_text)
|
|
command = lambda args: self._run_basic_command(url_path)
|
|
parser.set_defaults(func=command)
|
|
|
|
def _wait_for_sky_command_port(self):
|
|
tries = 0
|
|
while True:
|
|
try:
|
|
self._run_basic_command('/')
|
|
return True
|
|
except:
|
|
tries += 1
|
|
if tries == 3:
|
|
logging.warn('Still waiting for sky on port %s' %
|
|
self.pids['sky_command_port'])
|
|
if tries > 10:
|
|
return False
|
|
time.sleep(1)
|
|
|
|
def logcat_command(self, args):
|
|
TAGS = [
|
|
'AndroidHandler',
|
|
'MojoMain',
|
|
'MojoShellActivity',
|
|
'MojoShellApplication',
|
|
'chromium',
|
|
]
|
|
subprocess.call([ADB_PATH, 'logcat', '-d', '-s'] + TAGS)
|
|
|
|
def _pull_system_libraries(self, system_libs_root):
|
|
# Pull down the system libraries this pid has already mapped in.
|
|
# TODO(eseidel): This does not handle dynamic loads.
|
|
library_cacher_path = os.path.join(
|
|
self.paths.sky_tools_directory, 'android_library_cacher.py')
|
|
subprocess.call([
|
|
library_cacher_path, system_libs_root, self.pids['mojo_shell_pid']
|
|
])
|
|
|
|
# TODO(eseidel): adb_gdb does, this, unclear why solib-absolute-prefix
|
|
# doesn't make this explicit listing not necessary?
|
|
return subprocess.check_output([
|
|
'find', system_libs_root,
|
|
'-mindepth', '1',
|
|
'-maxdepth', '4',
|
|
'-type', 'd',
|
|
]).strip().split('\n')
|
|
|
|
def _add_android_library_links(self, links_path):
|
|
# TODO(eseidel): This might not match mojo_shell on the device?
|
|
# TODO(eseidel): Should we pass libmojo_shell.so as 'file' to gdb?
|
|
shell_link_path = os.path.join(links_path, 'libmojo_shell.so')
|
|
if os.path.lexists(shell_link_path):
|
|
os.unlink(shell_link_path)
|
|
os.symlink(self.paths.mojo_shell_path, shell_link_path)
|
|
|
|
def gdb_attach_command(self, args):
|
|
self.paths = self._create_paths_for_build_dir(self.pids['build_dir'])
|
|
|
|
if not os.path.exists(CACHE_LINKS_PATH):
|
|
os.makedirs(CACHE_LINKS_PATH)
|
|
cache_linker_path = os.path.join(
|
|
self.paths.sky_tools_directory, 'mojo_cache_linker.py')
|
|
subprocess.check_call([
|
|
cache_linker_path, CACHE_LINKS_PATH, self.paths.build_dir])
|
|
|
|
symbol_search_paths = [
|
|
self.pids['build_dir'],
|
|
CACHE_LINKS_PATH,
|
|
]
|
|
gdb_path = '/usr/bin/gdb'
|
|
|
|
eval_commands = [
|
|
'directory %s' % self.paths.src_root,
|
|
'file %s' % self.paths.mojo_shell_path,
|
|
'target remote localhost:%s' % GDB_PORT,
|
|
]
|
|
|
|
# A bunch of extra work is needed for android:
|
|
if 'remote_sky_server_port' in self.pids:
|
|
self._add_android_library_links(CACHE_LINKS_PATH)
|
|
|
|
system_lib_dirs = self._pull_system_libraries(SYSTEM_LIBS_ROOT_PATH)
|
|
eval_commands.append(
|
|
'set solib-absolute-prefix %s' % SYSTEM_LIBS_ROOT_PATH)
|
|
|
|
symbol_search_paths = system_lib_dirs + symbol_search_paths
|
|
|
|
# TODO(eseidel): We need to look up the toolchain somehow?
|
|
gdb_path = os.path.join(SRC_ROOT, 'third_party/android_tools/ndk/'
|
|
'toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/'
|
|
'bin/arm-linux-androideabi-gdb')
|
|
|
|
# Set solib-search-path after letting android modify symbol_search_paths
|
|
eval_commands.append(
|
|
'set solib-search-path %s' % ':'.join(symbol_search_paths))
|
|
|
|
exec_command = [gdb_path]
|
|
for command in eval_commands:
|
|
exec_command += ['--eval-command', command]
|
|
|
|
print " ".join(exec_command)
|
|
|
|
# Write out our pid file before we exec ourselves.
|
|
self._write_pid_file(PID_FILE_PATH, self.pids)
|
|
|
|
# Exec gdb directly to avoid python intercepting symbols, etc.
|
|
os.execv(exec_command[0], exec_command)
|
|
|
|
def print_crash_command(self, args):
|
|
logcat_cmd = [ADB_PATH, 'logcat', '-d']
|
|
logcat = subprocess.Popen(logcat_cmd, stdout=subprocess.PIPE)
|
|
|
|
stack_path = os.path.join(SRC_ROOT,
|
|
'tools', 'android_stack_parser', 'stack')
|
|
stack = subprocess.Popen([stack_path, '-'], stdin=logcat.stdout)
|
|
logcat.wait()
|
|
stack.wait()
|
|
|
|
def pids_command(self, args):
|
|
print json.dumps(self.pids, indent=1)
|
|
|
|
def main(self):
|
|
logging.basicConfig(level=logging.WARNING)
|
|
logging.getLogger("requests").setLevel(logging.WARNING)
|
|
|
|
self.pids = self._load_pid_file(PID_FILE_PATH)
|
|
|
|
parser = argparse.ArgumentParser(description='Sky launcher/debugger')
|
|
subparsers = parser.add_subparsers(help='sub-command help')
|
|
|
|
start_parser = subparsers.add_parser('start',
|
|
help='launch a new mojo_shell with sky')
|
|
start_parser.add_argument('--gdb', action='store_true')
|
|
start_parser.add_argument('--command-port', type=int,
|
|
default=DEFAULT_SKY_COMMAND_PORT)
|
|
start_parser.add_argument('--use-osmesa', action='store_true',
|
|
default=self._in_chromoting())
|
|
start_parser.add_argument('build_dir', type=str)
|
|
start_parser.add_argument('url_or_path', nargs='?', type=str,
|
|
default=DEFAULT_URL)
|
|
start_parser.add_argument('--show-command', action='store_true',
|
|
help='Display the shell command and exit')
|
|
start_parser.set_defaults(func=self.start_command)
|
|
|
|
stop_parser = subparsers.add_parser('stop',
|
|
help=('stop sky (as listed in %s)' % PID_FILE_PATH))
|
|
stop_parser.set_defaults(func=self.stop_command)
|
|
|
|
pids_parser = subparsers.add_parser('pids',
|
|
help='dump the current skydb pids file')
|
|
pids_parser.set_defaults(func=self.pids_command)
|
|
|
|
logcat_parser = subparsers.add_parser('logcat',
|
|
help=('dump sky-related logs from device'))
|
|
logcat_parser.set_defaults(func=self.logcat_command)
|
|
|
|
print_crash_parser = subparsers.add_parser('print_crash',
|
|
help=('dump (and symbolicate) recent crash-stacks'))
|
|
print_crash_parser.set_defaults(func=self.print_crash_command)
|
|
|
|
gdb_attach_parser = subparsers.add_parser('gdb_attach',
|
|
help='launch gdb and attach to gdbserver launched from start --gdb')
|
|
gdb_attach_parser.set_defaults(func=self.gdb_attach_command)
|
|
|
|
self._add_basic_command(subparsers, 'start_tracing', '/start_tracing',
|
|
'starts tracing the running sky instance')
|
|
self._add_basic_command(subparsers, 'reload', '/reload',
|
|
'reload the current page')
|
|
self._add_basic_command(subparsers, 'start_profiling', '/start_profiling',
|
|
'starts profiling the running sky instance (Linux only)')
|
|
|
|
stop_tracing_parser = subparsers.add_parser('stop_tracing',
|
|
help='stops tracing the running sky instance')
|
|
stop_tracing_parser.add_argument('file_name', type=str, default='sky_viewer.trace')
|
|
stop_tracing_parser.set_defaults(func=self.stop_tracing_command)
|
|
|
|
stop_profiling_parser = subparsers.add_parser('stop_profiling',
|
|
help='stops profiling the running sky instance (Linux only)')
|
|
stop_profiling_parser.set_defaults(func=self.stop_profiling_command)
|
|
|
|
load_parser = subparsers.add_parser('load',
|
|
help='load a new page in the currently running sky')
|
|
load_parser.add_argument('url_or_path', type=str)
|
|
load_parser.set_defaults(func=self.load_command)
|
|
|
|
args = parser.parse_args()
|
|
args.func(args)
|
|
|
|
self._write_pid_file(PID_FILE_PATH, self.pids)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
SkyDebugger().main()
|