| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2014 The Chromium Authors. All rights reserved. | 2 # Copyright 2014 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 import argparse | 6 import argparse |
| 7 import codecs |
| 8 import logging |
| 7 import os.path | 9 import os.path |
| 8 import requests | 10 import requests |
| 11 import signal |
| 9 import subprocess | 12 import subprocess |
| 10 import sys | 13 import sys |
| 14 import tempfile |
| 15 |
| 16 |
| 17 from android_gdb.install_remote_file_reader import install |
| 18 |
| 11 | 19 |
| 12 _MOJO_DEBUGGER_PORT = 7777 | 20 _MOJO_DEBUGGER_PORT = 7777 |
| 21 _DEFAULT_PACKAGE_NAME = 'org.chromium.mojo.shell' |
| 22 |
| 23 |
| 24 # TODO(etiennej): Refactor with similar methods in subdirectories |
| 25 class DirectoryNotFoundException(Exception): |
| 26 """Directory has not been found.""" |
| 27 pass |
| 28 |
| 29 |
| 30 def _get_dir_above(dirname): |
| 31 """Returns the directory "above" this file containing |dirname|.""" |
| 32 path = os.path.abspath(__file__) |
| 33 while True: |
| 34 path, tail = os.path.split(path) |
| 35 if not tail: |
| 36 raise DirectoryNotFoundException(dirname) |
| 37 if dirname in os.listdir(path): |
| 38 return path |
| 39 |
| 40 |
| 41 # The two methods below are taken from //tools/find_depot_tools.py. |
| 42 def _is_real_depot_tools(path): |
| 43 return os.path.isfile(os.path.join(path, 'gclient.py')) |
| 44 |
| 45 |
| 46 def _get_depot_tools_path(): |
| 47 """Searches for depot_tools.""" |
| 48 # First look if depot_tools is already in PYTHONPATH. |
| 49 for i in sys.path: |
| 50 if i.rstrip(os.sep).endswith('depot_tools') and _is_real_depot_tools(i): |
| 51 return i |
| 52 # Then look if depot_tools is in PATH, common case. |
| 53 for i in os.environ['PATH'].split(os.pathsep): |
| 54 if _is_real_depot_tools(i): |
| 55 return i |
| 56 # Rare case, it's not even in PATH, look upward up to root. |
| 57 root_dir = os.path.dirname(os.path.abspath(__file__)) |
| 58 previous_dir = os.path.abspath(__file__) |
| 59 while root_dir and root_dir != previous_dir: |
| 60 i = os.path.join(root_dir, 'depot_tools') |
| 61 if _is_real_depot_tools(i): |
| 62 return i |
| 63 previous_dir = root_dir |
| 64 root_dir = os.path.dirname(root_dir) |
| 65 return None |
| 13 | 66 |
| 14 | 67 |
| 15 def _send_request(request, payload=None): | 68 def _send_request(request, payload=None): |
| 16 """Sends a request to mojo:debugger.""" | 69 """Sends a request to mojo:debugger.""" |
| 17 try: | 70 try: |
| 18 url = 'http://localhost:%s/%s' % (_MOJO_DEBUGGER_PORT, request) | 71 url = 'http://localhost:%s/%s' % (_MOJO_DEBUGGER_PORT, request) |
| 19 if payload: | 72 if payload: |
| 20 return requests.post(url, payload) | 73 return requests.post(url, payload) |
| 21 else: | 74 else: |
| 22 return requests.get(url) | 75 return requests.get(url) |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 125 stack.wait() | 178 stack.wait() |
| 126 | 179 |
| 127 if logcat.returncode: | 180 if logcat.returncode: |
| 128 print 'adb logcat failed, make sure the device is connected and available' | 181 print 'adb logcat failed, make sure the device is connected and available' |
| 129 return logcat.returncode | 182 return logcat.returncode |
| 130 if stack.returncode: | 183 if stack.returncode: |
| 131 return stack.returncode | 184 return stack.returncode |
| 132 return 0 | 185 return 0 |
| 133 | 186 |
| 134 | 187 |
| 188 def _gdb_attach(args): |
| 189 """Run GDB on an instance of Mojo Shell on an android device.""" |
| 190 if args.ndk_dir: |
| 191 ndk_dir = args.ndk_dir |
| 192 else: |
| 193 try: |
| 194 ndk_dir = os.path.join(_get_dir_above('third_party'), 'third_party', |
| 195 'android_tools', 'ndk') |
| 196 if not os.path.exists(ndk_dir): |
| 197 raise DirectoryNotFoundException() |
| 198 except DirectoryNotFoundException: |
| 199 logging.fatal("Unable to find the Android NDK, please specify its path " |
| 200 "with --ndk-dir.") |
| 201 return |
| 202 |
| 203 install_args = {} |
| 204 if args.gsutil_dir: |
| 205 install_args['gsutil'] = os.path.join(args.gsutil_dir, 'gsutil') |
| 206 else: |
| 207 try: |
| 208 install_args['gsutil'] = os.path.join( |
| 209 _get_depot_tools_path(), 'third_party', 'gsutil', |
| 210 'gsutil') |
| 211 if not os.path.exists(install_args['gsutil']): |
| 212 raise DirectoryNotFoundException() |
| 213 except DirectoryNotFoundException: |
| 214 logging.fatal("Unable to find gsutil, please specify its path with " "--gs
util-dir.") |
| 215 return |
| 216 |
| 217 if args.adb_path: |
| 218 install_args['adb'] = args.adb_path |
| 219 install(**install_args) |
| 220 |
| 221 gdb_path = os.path.join( |
| 222 ndk_dir, |
| 223 'toolchains', |
| 224 # TODO(etiennej): Always select the most recent toolchain? |
| 225 'arm-linux-androideabi-4.9', |
| 226 'prebuilt', |
| 227 # TODO(etiennej): DEPS mac NDK and use it on macs. |
| 228 'linux-x86_64', |
| 229 'bin', |
| 230 'arm-linux-androideabi-gdb') |
| 231 python_gdb_script_path = os.path.join(os.path.dirname(__file__), |
| 232 'android_gdb', 'session.py') |
| 233 debug_session_arguments = {} |
| 234 if args.build_dir: |
| 235 debug_session_arguments["build_directory"] = args.build_dir |
| 236 else: |
| 237 try: |
| 238 debug_session_arguments["build_directory"] = os.path.join( |
| 239 _get_dir_above('out'), 'out', 'android_Debug') |
| 240 if not os.path.exists(debug_session_arguments["build_directory"]): |
| 241 raise DirectoryNotFoundException() |
| 242 except DirectoryNotFoundException: |
| 243 logging.fatal("Unable to find the build directory, please specify it " |
| 244 "using --build-dir.") |
| 245 return |
| 246 |
| 247 if args.package_name: |
| 248 debug_session_arguments["package_name"] = args.package_name |
| 249 else: |
| 250 debug_session_arguments["package_name"] = _DEFAULT_PACKAGE_NAME |
| 251 if args.pyelftools_dir: |
| 252 debug_session_arguments["pyelftools_dir"] = args.pyelftools_dir |
| 253 else: |
| 254 debug_session_arguments["pyelftools_dir"] = os.path.join( |
| 255 _get_dir_above('third_party'), 'third_party', 'pyelftools') |
| 256 |
| 257 debug_session_arguments_str = ', '.join( |
| 258 [k + '="' + codecs.encode(v, 'string_escape') + '"' |
| 259 for k, v in debug_session_arguments.items()]) |
| 260 |
| 261 # We need to pass some commands to GDB at startup. |
| 262 gdb_commands_file = tempfile.NamedTemporaryFile() |
| 263 gdb_commands_file.write('source ' + python_gdb_script_path + '\n') |
| 264 gdb_commands_file.write('py d = DebugSession(' + debug_session_arguments_str |
| 265 + ')\n') |
| 266 gdb_commands_file.write('py d.start()\n') |
| 267 gdb_commands_file.flush() |
| 268 |
| 269 gdb_proc = subprocess.Popen([gdb_path, '-x', gdb_commands_file.name], |
| 270 stdin=sys.stdin, |
| 271 stdout=sys.stdout, |
| 272 stderr=sys.stderr) |
| 273 |
| 274 # We don't want SIGINT to stop this program. It is automatically propagated by |
| 275 # the system to gdb. |
| 276 signal.signal(signal.SIGINT, signal.SIG_IGN) |
| 277 gdb_proc.wait() |
| 278 signal.signal(signal.SIGINT, signal.SIG_DFL) |
| 279 |
| 280 |
| 135 def _add_device_command(subparsers): | 281 def _add_device_command(subparsers): |
| 136 """Sets up the parser for the 'device' command.""" | 282 """Sets up the parser for the 'device' command.""" |
| 137 device_parser = subparsers.add_parser('device', | 283 device_parser = subparsers.add_parser('device', |
| 138 help='interact with the Android device (requires adb in PATH or passing ' | 284 help='interact with the Android device (requires adb in PATH or passing ' |
| 139 '--adb-path)') | 285 '--adb-path)') |
| 140 device_parser.add_argument('--adb-path', type=str, | 286 device_parser.add_argument('--adb-path', type=str, |
| 141 help='path to the adb tool from the Android SDK') | 287 help='path to the adb tool from the Android SDK (optional)') |
| 142 device_subparser = device_parser.add_subparsers( | 288 device_subparser = device_parser.add_subparsers( |
| 143 help='the command to run') | 289 help='the command to run') |
| 144 | 290 |
| 145 device_stack_parser = device_subparser.add_parser('stack', | 291 device_stack_parser = device_subparser.add_parser('stack', |
| 146 help='symbolize the crash stacktraces from the device log') | 292 help='symbolize the crash stacktraces from the device log') |
| 293 device_stack_parser.add_argument('--ndk-dir', type=str, |
| 294 help='path to the directory containing the Android NDK') |
| 147 device_stack_parser.add_argument('--build-dir', type=str, | 295 device_stack_parser.add_argument('--build-dir', type=str, |
| 148 help='path to the build directory') | 296 help='path to the build directory') |
| 149 device_stack_parser.add_argument('--ndk-dir', type=str, | 297 device_stack_parser.set_defaults(func=_device_stack) |
| 298 |
| 299 |
| 300 def _add_gdb_command(subparsers): |
| 301 gdb_parser = subparsers.add_parser( |
| 302 'gdb', help='Debug Mojo Shell and its apps using GDB') |
| 303 gdb_subparser = gdb_parser.add_subparsers( |
| 304 help='Commands to GDB') |
| 305 |
| 306 gdb_attach_parser = gdb_subparser.add_parser( |
| 307 'attach', help='Attach GDB to a running Mojo Shell process') |
| 308 gdb_attach_parser.add_argument('--adb-path', type=str, |
| 309 help='path to the adb tool from the Android SDK (optional)') |
| 310 gdb_attach_parser.add_argument('--ndk-dir', type=str, |
| 150 help='path to the directory containing the Android NDK') | 311 help='path to the directory containing the Android NDK') |
| 151 device_stack_parser.set_defaults(func=_device_stack) | 312 gdb_attach_parser.add_argument('--build-dir', type=str, |
| 313 help='path to the build directory') |
| 314 gdb_attach_parser.add_argument('--pyelftools-dir', type=str, |
| 315 help='Path to a directory containing third party libraries') |
| 316 gdb_attach_parser.add_argument('--gsutil-dir', type=str, |
| 317 help='Path to a directory containing gsutil') |
| 318 gdb_attach_parser.add_argument('--package-name', type=str, |
| 319 help='Name of the Mojo Shell android package to debug') |
| 320 gdb_attach_parser.set_defaults(func=_gdb_attach) |
| 152 | 321 |
| 153 | 322 |
| 154 def main(): | 323 def main(): |
| 155 parser = argparse.ArgumentParser(description='Command-line interface for ' | 324 parser = argparse.ArgumentParser(description='Command-line interface for ' |
| 156 'mojo:debugger') | 325 'mojo:debugger') |
| 157 subparsers = parser.add_subparsers(help='the tool to run') | 326 subparsers = parser.add_subparsers(help='the tool to run') |
| 158 _add_device_command(subparsers) | 327 _add_device_command(subparsers) |
| 159 _add_tracing_command(subparsers) | 328 _add_tracing_command(subparsers) |
| 160 _add_wm_command(subparsers) | 329 _add_wm_command(subparsers) |
| 330 _add_gdb_command(subparsers) |
| 161 | 331 |
| 162 args = parser.parse_args() | 332 args = parser.parse_args() |
| 163 return args.func(args) | 333 return args.func(args) |
| 164 | 334 |
| 165 if __name__ == '__main__': | 335 if __name__ == '__main__': |
| 166 sys.exit(main()) | 336 sys.exit(main()) |
| OLD | NEW |