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 from skypy.skyserver import SkyServer | 6 from skypy.skyserver import SkyServer |
7 import argparse | 7 import argparse |
8 import json | 8 import json |
9 import logging | 9 import logging |
10 import os | 10 import os |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
72 server_root = os.path.dirname(path) | 72 server_root = os.path.dirname(path) |
73 logging.warn( | 73 logging.warn( |
74 '%s is outside of mojo root, using %s as server root' % | 74 '%s is outside of mojo root, using %s as server root' % |
75 (path, server_root)) | 75 (path, server_root)) |
76 return server_root | 76 return server_root |
77 | 77 |
78 def _in_chromoting(self): | 78 def _in_chromoting(self): |
79 return os.environ.get('CHROME_REMOTE_DESKTOP_SESSION', False) | 79 return os.environ.get('CHROME_REMOTE_DESKTOP_SESSION', False) |
80 | 80 |
81 def _wrap_for_android(self, shell_args): | 81 def _wrap_for_android(self, shell_args): |
82 build_dir_url = SkyServer.url_for_path( | |
83 self.pids['remote_sky_server_port'], | |
84 self.pids['sky_server_root'], | |
85 self.pids['build_dir']) | |
86 shell_args += ['--origin=%s' % build_dir_url] | |
87 | |
88 # am shell --esa: (someone shoot me now) | 82 # am shell --esa: (someone shoot me now) |
89 # [--esa <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]] | 83 # [--esa <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]] |
90 # (to embed a comma into a string escape it using "\,") | 84 # (to embed a comma into a string escape it using "\,") |
91 escaped_args = map(lambda arg: arg.replace(',', '\\,'), shell_args) | 85 escaped_args = map(lambda arg: arg.replace(',', '\\,'), shell_args) |
92 return [ | 86 return [ |
93 'adb', 'shell', | 87 'adb', 'shell', |
94 'am', 'start', | 88 'am', 'start', |
95 '-W', | 89 '-W', |
96 '-S', | 90 '-S', |
97 '-a', 'android.intent.action.VIEW', | 91 '-a', 'android.intent.action.VIEW', |
98 '-n', ANDROID_ACTIVITY, | 92 '-n', ANDROID_ACTIVITY, |
99 # FIXME: This quoting is very error-prone. Perhaps we should read | 93 # FIXME: This quoting is very error-prone. Perhaps we should read |
100 # our args from a file instead? | 94 # our args from a file instead? |
101 '--esa', 'parameters', ','.join(escaped_args), | 95 '--esa', 'parameters', ','.join(escaped_args), |
102 ] | 96 ] |
103 | 97 |
104 def _build_mojo_shell_command(self, args, is_android): | 98 def _build_mojo_shell_command(self, args, is_android): |
105 content_handlers = ['%s,%s' % (mime_type, 'mojo:sky_viewer') | 99 content_handlers = ['%s,%s' % (mime_type, 'mojo:sky_viewer') |
106 for mime_type in SUPPORTED_MIME_TYPES] | 100 for mime_type in SUPPORTED_MIME_TYPES] |
107 | 101 |
108 remote_command_port = self.pids.get('remote_sky_command_port', self.pids
['sky_command_port']) | 102 remote_command_port = self.pids.get('remote_sky_command_port', self.pids
['sky_command_port']) |
| 103 remote_server_port = self.pids.get('remote_sky_server_port', self.pids['
sky_server_port']) |
109 | 104 |
110 shell_args = [ | 105 shell_args = [ |
111 '--v=1', | 106 '--v=1', |
112 '--content-handlers=%s' % ','.join(content_handlers), | 107 '--content-handlers=%s' % ','.join(content_handlers), |
113 '--url-mappings=mojo:window_manager=mojo:sky_debugger', | 108 '--url-mappings=mojo:window_manager=mojo:sky_debugger', |
114 '--args-for=mojo:sky_debugger_prompt %d' % remote_command_port, | 109 '--args-for=mojo:sky_debugger_prompt %d' % remote_command_port, |
115 'mojo:window_manager', | 110 'mojo:window_manager', |
116 ] | 111 ] |
117 | 112 |
| 113 # Map all mojo: urls to http: urls using the --origin command. |
| 114 build_dir_url = SkyServer.url_for_path( |
| 115 remote_server_port, |
| 116 self.pids['sky_server_root'], |
| 117 self.pids['build_dir']) |
| 118 shell_args += ['--origin=%s' % build_dir_url] |
| 119 |
118 # Desktop-only work-around for mojo crashing under chromoting. | 120 # Desktop-only work-around for mojo crashing under chromoting. |
119 if not is_android and args.use_osmesa: | 121 if not is_android and args.use_osmesa: |
120 shell_args.append( | 122 shell_args.append( |
121 '--args-for=mojo:native_viewport_service --use-osmesa') | 123 '--args-for=mojo:native_viewport_service --use-osmesa') |
122 | 124 |
123 if is_android and args.gdb: | 125 if is_android and args.gdb: |
124 shell_args.append('--wait_for_debugger') | 126 shell_args.append('--wait_for_debugger') |
125 | 127 |
126 if 'remote_sky_server_port' in self.pids: | 128 if 'remote_sky_server_port' in self.pids: |
127 shell_command = self._wrap_for_android(shell_args) | 129 shell_command = self._wrap_for_android(shell_args) |
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
315 if not urlparse.urlparse(args.url_or_path).scheme: | 317 if not urlparse.urlparse(args.url_or_path).scheme: |
316 # The load happens on the remote device, use the remote port. | 318 # The load happens on the remote device, use the remote port. |
317 remote_sky_server_port = self.pids.get('remote_sky_server_port', | 319 remote_sky_server_port = self.pids.get('remote_sky_server_port', |
318 self.pids['sky_server_port']) | 320 self.pids['sky_server_port']) |
319 url = SkyServer.url_for_path(remote_sky_server_port, | 321 url = SkyServer.url_for_path(remote_sky_server_port, |
320 self.pids['sky_server_root'], args.url_or_path) | 322 self.pids['sky_server_root'], args.url_or_path) |
321 else: | 323 else: |
322 url = args.url_or_path | 324 url = args.url_or_path |
323 self._send_command_to_sky('/load', url) | 325 self._send_command_to_sky('/load', url) |
324 | 326 |
| 327 def _read_mojo_map(self): |
| 328 # TODO(eseidel): Does not work for android. |
| 329 mojo_map_path = "/tmp/mojo_shell.%d.maps" % self.pids['mojo_shell_pid'] |
| 330 with open(mojo_map_path, 'r') as maps_file: |
| 331 lines = maps_file.read().strip().split('\n') |
| 332 return dict(map(lambda line: line.split(' '), lines)) |
| 333 |
325 def stop_profiling_command(self, args): | 334 def stop_profiling_command(self, args): |
326 self._send_command_to_sky('/stop_profiling') | 335 self._send_command_to_sky('/stop_profiling') |
327 # We need to munge the profile to replace foo.mojo with libfoo.so so | 336 mojo_map = self._read_mojo_map() |
328 # that pprof knows this represents an SO. | 337 |
329 with open("sky_viewer.pprof", "r+") as profile_file: | 338 # TODO(eseidel): We should have a helper for resolving urls, etc. |
330 data = profile_file.read() | 339 remote_server_port = self.pids.get('remote_sky_server_port', self.pids['
sky_server_port']) |
| 340 build_dir_url = SkyServer.url_for_path( |
| 341 remote_server_port, |
| 342 self.pids['sky_server_root'], |
| 343 self.pids['build_dir']) |
| 344 |
| 345 # Map /tmp cache paths to urls and then to local build_dir paths. |
| 346 def map_to_local_paths(match): |
| 347 path = match.group('mojo_path') |
| 348 url = mojo_map.get(path) |
| 349 if url and url.startswith(build_dir_url): |
| 350 return url.replace(build_dir_url, self.pids['build_dir']) |
| 351 return match.group(0) |
| 352 |
| 353 MOJO_PATH_RE = re.compile(r'(?P<mojo_path>\S+\.mojo)') |
| 354 MOJO_NAME_RE = re.compile(r'(?P<mojo_name>\w+)\.mojo') |
| 355 |
| 356 with open("sky_viewer.pprof", "rb+") as profile_file: |
| 357 # ISO-8859-1 can represent arbitrary binary while still keeping |
| 358 # ASCII characters in the ASCII range (allowing us to regexp). |
| 359 # http://en.wikipedia.org/wiki/ISO/IEC_8859-1 |
| 360 as_string = profile_file.read().decode('iso-8859-1') |
| 361 # Using the mojo_shell.PID.maps file tmp paths to build_dir paths. |
| 362 as_string = MOJO_PATH_RE.sub(map_to_local_paths, as_string) |
| 363 # In release foo.mojo is stripped but libfoo_library.so isn't. |
| 364 as_string = MOJO_NAME_RE.sub(r'lib\1_library.so', as_string) |
331 profile_file.seek(0) | 365 profile_file.seek(0) |
332 profile_file.write(re.sub(r'(\w+)\.mojo', r'lib\1_library.so', data)
) | 366 profile_file.write(as_string.encode('iso-8859-1')) |
333 profile_file.truncate() | 367 profile_file.truncate() |
334 | 368 |
335 def _command_base_url(self): | 369 def _command_base_url(self): |
336 return 'http://localhost:%s' % self.pids['sky_command_port'] | 370 return 'http://localhost:%s' % self.pids['sky_command_port'] |
337 | 371 |
338 def _send_command_to_sky(self, command_path, payload=None): | 372 def _send_command_to_sky(self, command_path, payload=None): |
339 url = 'http://localhost:%s%s' % ( | 373 url = 'http://localhost:%s%s' % ( |
340 self.pids['sky_command_port'], command_path) | 374 self.pids['sky_command_port'], command_path) |
341 if payload: | 375 if payload: |
342 response = requests.post(url, payload) | 376 response = requests.post(url, payload) |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
480 def print_crash_command(self, args): | 514 def print_crash_command(self, args): |
481 logcat_cmd = ['adb', 'logcat', '-d'] | 515 logcat_cmd = ['adb', 'logcat', '-d'] |
482 logcat = subprocess.Popen(logcat_cmd, stdout=subprocess.PIPE) | 516 logcat = subprocess.Popen(logcat_cmd, stdout=subprocess.PIPE) |
483 | 517 |
484 stack_path = os.path.join(SRC_ROOT, | 518 stack_path = os.path.join(SRC_ROOT, |
485 'tools', 'android_stack_parser', 'stack') | 519 'tools', 'android_stack_parser', 'stack') |
486 stack = subprocess.Popen([stack_path, '-'], stdin=logcat.stdout) | 520 stack = subprocess.Popen([stack_path, '-'], stdin=logcat.stdout) |
487 logcat.wait() | 521 logcat.wait() |
488 stack.wait() | 522 stack.wait() |
489 | 523 |
| 524 def pids_command(self, args): |
| 525 print json.dumps(self.pids, indent=1) |
| 526 |
490 def main(self): | 527 def main(self): |
491 logging.basicConfig(level=logging.WARNING) | 528 logging.basicConfig(level=logging.WARNING) |
492 logging.getLogger("requests").setLevel(logging.WARNING) | 529 logging.getLogger("requests").setLevel(logging.WARNING) |
493 | 530 |
494 self.pids = self._load_pid_file(PID_FILE_PATH) | 531 self.pids = self._load_pid_file(PID_FILE_PATH) |
495 | 532 |
496 parser = argparse.ArgumentParser(description='Sky launcher/debugger') | 533 parser = argparse.ArgumentParser(description='Sky launcher/debugger') |
497 subparsers = parser.add_subparsers(help='sub-command help') | 534 subparsers = parser.add_subparsers(help='sub-command help') |
498 | 535 |
499 start_parser = subparsers.add_parser('start', | 536 start_parser = subparsers.add_parser('start', |
500 help='launch a new mojo_shell with sky') | 537 help='launch a new mojo_shell with sky') |
501 start_parser.add_argument('--gdb', action='store_true') | 538 start_parser.add_argument('--gdb', action='store_true') |
502 start_parser.add_argument('--command-port', type=int, | 539 start_parser.add_argument('--command-port', type=int, |
503 default=DEFAULT_SKY_COMMAND_PORT) | 540 default=DEFAULT_SKY_COMMAND_PORT) |
504 start_parser.add_argument('--use-osmesa', action='store_true', | 541 start_parser.add_argument('--use-osmesa', action='store_true', |
505 default=self._in_chromoting()) | 542 default=self._in_chromoting()) |
506 start_parser.add_argument('build_dir', type=str) | 543 start_parser.add_argument('build_dir', type=str) |
507 start_parser.add_argument('url_or_path', nargs='?', type=str, | 544 start_parser.add_argument('url_or_path', nargs='?', type=str, |
508 default=DEFAULT_URL) | 545 default=DEFAULT_URL) |
509 start_parser.add_argument('--show-command', action='store_true', | 546 start_parser.add_argument('--show-command', action='store_true', |
510 help='Display the shell command and exit') | 547 help='Display the shell command and exit') |
511 start_parser.set_defaults(func=self.start_command) | 548 start_parser.set_defaults(func=self.start_command) |
512 | 549 |
513 stop_parser = subparsers.add_parser('stop', | 550 stop_parser = subparsers.add_parser('stop', |
514 help=('stop sky (as listed in %s)' % PID_FILE_PATH)) | 551 help=('stop sky (as listed in %s)' % PID_FILE_PATH)) |
515 stop_parser.set_defaults(func=self.stop_command) | 552 stop_parser.set_defaults(func=self.stop_command) |
516 | 553 |
| 554 pids_parser = subparsers.add_parser('pids', |
| 555 help='dump the current skydb pids file') |
| 556 pids_parser.set_defaults(func=self.pids_command) |
| 557 |
517 logcat_parser = subparsers.add_parser('logcat', | 558 logcat_parser = subparsers.add_parser('logcat', |
518 help=('dump sky-related logs from device')) | 559 help=('dump sky-related logs from device')) |
519 logcat_parser.set_defaults(func=self.logcat_command) | 560 logcat_parser.set_defaults(func=self.logcat_command) |
520 | 561 |
521 print_crash_parser = subparsers.add_parser('print_crash', | 562 print_crash_parser = subparsers.add_parser('print_crash', |
522 help=('dump (and symbolicate) recent crash-stacks')) | 563 help=('dump (and symbolicate) recent crash-stacks')) |
523 print_crash_parser.set_defaults(func=self.print_crash_command) | 564 print_crash_parser.set_defaults(func=self.print_crash_command) |
524 | 565 |
525 gdb_attach_parser = subparsers.add_parser('gdb_attach', | 566 gdb_attach_parser = subparsers.add_parser('gdb_attach', |
526 help='launch gdb and attach to gdbserver launched from start --gdb') | 567 help='launch gdb and attach to gdbserver launched from start --gdb') |
(...skipping 18 matching lines...) Expand all Loading... |
545 load_parser.set_defaults(func=self.load_command) | 586 load_parser.set_defaults(func=self.load_command) |
546 | 587 |
547 args = parser.parse_args() | 588 args = parser.parse_args() |
548 args.func(args) | 589 args.func(args) |
549 | 590 |
550 self._write_pid_file(PID_FILE_PATH, self.pids) | 591 self._write_pid_file(PID_FILE_PATH, self.pids) |
551 | 592 |
552 | 593 |
553 if __name__ == '__main__': | 594 if __name__ == '__main__': |
554 SkyDebugger().main() | 595 SkyDebugger().main() |
OLD | NEW |