| Index: mojo/devtools/common/debugger
|
| diff --git a/mojo/devtools/common/debugger b/mojo/devtools/common/debugger
|
| index b323f4ac6b2f1221cd9658f81716b46dc235dca1..23898eb328f7a4bace7d60ad7213761469b7c21e 100755
|
| --- a/mojo/devtools/common/debugger
|
| +++ b/mojo/devtools/common/debugger
|
| @@ -4,12 +4,65 @@
|
| # found in the LICENSE file.
|
|
|
| import argparse
|
| +import codecs
|
| +import logging
|
| import os.path
|
| import requests
|
| +import signal
|
| import subprocess
|
| import sys
|
| +import tempfile
|
| +
|
| +
|
| +from android_gdb.install_remote_file_reader import install
|
| +
|
|
|
| _MOJO_DEBUGGER_PORT = 7777
|
| +_DEFAULT_PACKAGE_NAME = 'org.chromium.mojo.shell'
|
| +
|
| +
|
| +# TODO(etiennej): Refactor with similar methods in subdirectories
|
| +class DirectoryNotFoundException(Exception):
|
| + """Directory has not been found."""
|
| + pass
|
| +
|
| +
|
| +def _get_dir_above(dirname):
|
| + """Returns the directory "above" this file containing |dirname|."""
|
| + path = os.path.abspath(__file__)
|
| + while True:
|
| + path, tail = os.path.split(path)
|
| + if not tail:
|
| + raise DirectoryNotFoundException(dirname)
|
| + if dirname in os.listdir(path):
|
| + return path
|
| +
|
| +
|
| +# The two methods below are taken from //tools/find_depot_tools.py.
|
| +def _is_real_depot_tools(path):
|
| + return os.path.isfile(os.path.join(path, 'gclient.py'))
|
| +
|
| +
|
| +def _get_depot_tools_path():
|
| + """Searches for depot_tools."""
|
| + # First look if depot_tools is already in PYTHONPATH.
|
| + for i in sys.path:
|
| + if i.rstrip(os.sep).endswith('depot_tools') and _is_real_depot_tools(i):
|
| + return i
|
| + # Then look if depot_tools is in PATH, common case.
|
| + for i in os.environ['PATH'].split(os.pathsep):
|
| + if _is_real_depot_tools(i):
|
| + return i
|
| + # Rare case, it's not even in PATH, look upward up to root.
|
| + root_dir = os.path.dirname(os.path.abspath(__file__))
|
| + previous_dir = os.path.abspath(__file__)
|
| + while root_dir and root_dir != previous_dir:
|
| + i = os.path.join(root_dir, 'depot_tools')
|
| + if _is_real_depot_tools(i):
|
| + return i
|
| + previous_dir = root_dir
|
| + root_dir = os.path.dirname(root_dir)
|
| + return None
|
|
|
|
|
| def _send_request(request, payload=None):
|
| @@ -132,25 +185,141 @@ def _device_stack(args):
|
| return 0
|
|
|
|
|
| +def _gdb_attach(args):
|
| + """Run GDB on an instance of Mojo Shell on an android device."""
|
| + if args.ndk_dir:
|
| + ndk_dir = args.ndk_dir
|
| + else:
|
| + try:
|
| + ndk_dir = os.path.join(_get_dir_above('third_party'), 'third_party',
|
| + 'android_tools', 'ndk')
|
| + if not os.path.exists(ndk_dir):
|
| + raise DirectoryNotFoundException()
|
| + except DirectoryNotFoundException:
|
| + logging.fatal("Unable to find the Android NDK, please specify its path "
|
| + "with --ndk-dir.")
|
| + return
|
| +
|
| + install_args = {}
|
| + if args.gsutil_dir:
|
| + install_args['gsutil'] = os.path.join(args.gsutil_dir, 'gsutil')
|
| + else:
|
| + try:
|
| + install_args['gsutil'] = os.path.join(
|
| + _get_depot_tools_path(), 'third_party', 'gsutil',
|
| + 'gsutil')
|
| + if not os.path.exists(install_args['gsutil']):
|
| + raise DirectoryNotFoundException()
|
| + except DirectoryNotFoundException:
|
| + logging.fatal("Unable to find gsutil, please specify its path with " "--gsutil-dir.")
|
| + return
|
| +
|
| + if args.adb_path:
|
| + install_args['adb'] = args.adb_path
|
| + install(**install_args)
|
| +
|
| + gdb_path = os.path.join(
|
| + ndk_dir,
|
| + 'toolchains',
|
| + # TODO(etiennej): Always select the most recent toolchain?
|
| + 'arm-linux-androideabi-4.9',
|
| + 'prebuilt',
|
| + # TODO(etiennej): DEPS mac NDK and use it on macs.
|
| + 'linux-x86_64',
|
| + 'bin',
|
| + 'arm-linux-androideabi-gdb')
|
| + python_gdb_script_path = os.path.join(os.path.dirname(__file__),
|
| + 'android_gdb', 'session.py')
|
| + debug_session_arguments = {}
|
| + if args.build_dir:
|
| + debug_session_arguments["build_directory"] = args.build_dir
|
| + else:
|
| + try:
|
| + debug_session_arguments["build_directory"] = os.path.join(
|
| + _get_dir_above('out'), 'out', 'android_Debug')
|
| + if not os.path.exists(debug_session_arguments["build_directory"]):
|
| + raise DirectoryNotFoundException()
|
| + except DirectoryNotFoundException:
|
| + logging.fatal("Unable to find the build directory, please specify it "
|
| + "using --build-dir.")
|
| + return
|
| +
|
| + if args.package_name:
|
| + debug_session_arguments["package_name"] = args.package_name
|
| + else:
|
| + debug_session_arguments["package_name"] = _DEFAULT_PACKAGE_NAME
|
| + if args.pyelftools_dir:
|
| + debug_session_arguments["pyelftools_dir"] = args.pyelftools_dir
|
| + else:
|
| + debug_session_arguments["pyelftools_dir"] = os.path.join(
|
| + _get_dir_above('third_party'), 'third_party', 'pyelftools')
|
| +
|
| + debug_session_arguments_str = ', '.join(
|
| + [k + '="' + codecs.encode(v, 'string_escape') + '"'
|
| + for k, v in debug_session_arguments.items()])
|
| +
|
| + # We need to pass some commands to GDB at startup.
|
| + gdb_commands_file = tempfile.NamedTemporaryFile()
|
| + gdb_commands_file.write('source ' + python_gdb_script_path + '\n')
|
| + gdb_commands_file.write('py d = DebugSession(' + debug_session_arguments_str
|
| + + ')\n')
|
| + gdb_commands_file.write('py d.start()\n')
|
| + gdb_commands_file.flush()
|
| +
|
| + gdb_proc = subprocess.Popen([gdb_path, '-x', gdb_commands_file.name],
|
| + stdin=sys.stdin,
|
| + stdout=sys.stdout,
|
| + stderr=sys.stderr)
|
| +
|
| + # We don't want SIGINT to stop this program. It is automatically propagated by
|
| + # the system to gdb.
|
| + signal.signal(signal.SIGINT, signal.SIG_IGN)
|
| + gdb_proc.wait()
|
| + signal.signal(signal.SIGINT, signal.SIG_DFL)
|
| +
|
| +
|
| def _add_device_command(subparsers):
|
| """Sets up the parser for the 'device' command."""
|
| device_parser = subparsers.add_parser('device',
|
| help='interact with the Android device (requires adb in PATH or passing '
|
| '--adb-path)')
|
| device_parser.add_argument('--adb-path', type=str,
|
| - help='path to the adb tool from the Android SDK')
|
| + help='path to the adb tool from the Android SDK (optional)')
|
| device_subparser = device_parser.add_subparsers(
|
| help='the command to run')
|
|
|
| device_stack_parser = device_subparser.add_parser('stack',
|
| help='symbolize the crash stacktraces from the device log')
|
| - device_stack_parser.add_argument('--build-dir', type=str,
|
| - help='path to the build directory')
|
| device_stack_parser.add_argument('--ndk-dir', type=str,
|
| help='path to the directory containing the Android NDK')
|
| + device_stack_parser.add_argument('--build-dir', type=str,
|
| + help='path to the build directory')
|
| device_stack_parser.set_defaults(func=_device_stack)
|
|
|
|
|
| +def _add_gdb_command(subparsers):
|
| + gdb_parser = subparsers.add_parser(
|
| + 'gdb', help='Debug Mojo Shell and its apps using GDB')
|
| + gdb_subparser = gdb_parser.add_subparsers(
|
| + help='Commands to GDB')
|
| +
|
| + gdb_attach_parser = gdb_subparser.add_parser(
|
| + 'attach', help='Attach GDB to a running Mojo Shell process')
|
| + gdb_attach_parser.add_argument('--adb-path', type=str,
|
| + help='path to the adb tool from the Android SDK (optional)')
|
| + gdb_attach_parser.add_argument('--ndk-dir', type=str,
|
| + help='path to the directory containing the Android NDK')
|
| + gdb_attach_parser.add_argument('--build-dir', type=str,
|
| + help='path to the build directory')
|
| + gdb_attach_parser.add_argument('--pyelftools-dir', type=str,
|
| + help='Path to a directory containing third party libraries')
|
| + gdb_attach_parser.add_argument('--gsutil-dir', type=str,
|
| + help='Path to a directory containing gsutil')
|
| + gdb_attach_parser.add_argument('--package-name', type=str,
|
| + help='Name of the Mojo Shell android package to debug')
|
| + gdb_attach_parser.set_defaults(func=_gdb_attach)
|
| +
|
| +
|
| def main():
|
| parser = argparse.ArgumentParser(description='Command-line interface for '
|
| 'mojo:debugger')
|
| @@ -158,6 +327,7 @@ def main():
|
| _add_device_command(subparsers)
|
| _add_tracing_command(subparsers)
|
| _add_wm_command(subparsers)
|
| + _add_gdb_command(subparsers)
|
|
|
| args = parser.parse_args()
|
| return args.func(args)
|
|
|