Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: sky/tools/shelldb

Issue 1167513003: Make --gdb and gdb_attach mostly work in shelldb (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Fix reverse/forward typo Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2015 The Chromium Authors. All rights reserved. 2 # Copyright 2015 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 hashlib
8 import json 9 import json
9 import logging 10 import logging
10 import os 11 import os
12 import pipes
13 import platform
11 import re 14 import re
12 import signal 15 import signal
13 import subprocess 16 import subprocess
14 import sys 17 import sys
18 import tempfile
15 import time 19 import time
16 import urlparse 20 import urlparse
17 import hashlib
18 import tempfile
19 21
20 SKY_TOOLS_DIR = os.path.dirname(os.path.abspath(__file__)) 22 SKY_TOOLS_DIR = os.path.dirname(os.path.abspath(__file__))
21 SKY_ROOT = os.path.dirname(SKY_TOOLS_DIR) 23 SKY_ROOT = os.path.dirname(SKY_TOOLS_DIR)
22 SRC_ROOT = os.path.dirname(SKY_ROOT) 24 SRC_ROOT = os.path.dirname(SKY_ROOT)
23 25
26 GDB_PORT = 8888
24 SKY_SERVER_PORT = 9888 27 SKY_SERVER_PORT = 9888
25 OBSERVATORY_PORT = 8181 28 OBSERVATORY_PORT = 8181
26 DEFAULT_URL = "sky://domokit.github.io/sky_home" 29 DEFAULT_URL = "sky://domokit.github.io/sky_home"
27 APK_NAME = 'SkyDemo.apk' 30 APK_NAME = 'SkyDemo.apk'
28 ADB_PATH = os.path.join(SRC_ROOT, 31 ADB_PATH = os.path.join(SRC_ROOT,
29 'third_party/android_tools/sdk/platform-tools/adb') 32 'third_party/android_tools/sdk/platform-tools/adb')
30 ANDROID_PACKAGE = "org.domokit.sky.demo" 33 ANDROID_PACKAGE = "org.domokit.sky.demo"
31 SHA1_PATH = '/sdcard/%s/%s.sha1' % (ANDROID_PACKAGE, APK_NAME) 34 SHA1_PATH = '/sdcard/%s/%s.sha1' % (ANDROID_PACKAGE, APK_NAME)
32 35
33 PID_FILE_PATH = "/tmp/skydemo.pids" 36 PID_FILE_PATH = "/tmp/skydemo.pids"
34 PID_FILE_KEYS = frozenset([ 37 PID_FILE_KEYS = frozenset([
35 'remote_sky_server_port', 38 'remote_sky_server_port',
36 'sky_server_pid', 39 'sky_server_pid',
37 'sky_server_port', 40 'sky_server_port',
38 'sky_server_root', 41 'sky_server_root',
39 'build_dir', 42 'build_dir',
43 'sky_shell_pid',
44 'remote_gdbserver_port',
40 ]) 45 ])
41 46
47 # TODO(iansf): Fix undefined behavior when you have more than one device attache d.
48 SYSTEM_LIBS_ROOT_PATH = '/tmp/device_libs/%s' % (subprocess.check_output(['adb', 'get-serialno']).strip())
49
42 _IGNORED_PATTERNS = [ 50 _IGNORED_PATTERNS = [
43 # Ignored because they're not indicative of specific errors. 51 # Ignored because they're not indicative of specific errors.
44 re.compile(r'^$'), 52 re.compile(r'^$'),
45 re.compile(r'^Analyzing \['), 53 re.compile(r'^Analyzing \['),
46 re.compile(r'^No issues found'), 54 re.compile(r'^No issues found'),
47 re.compile(r'^[0-9]+ errors? and [0-9]+ warnings? found.'), 55 re.compile(r'^[0-9]+ errors? and [0-9]+ warnings? found.'),
48 re.compile(r'^([0-9]+|No) (error|warning|issue)s? found.'), 56 re.compile(r'^([0-9]+|No) (error|warning|issue)s? found.'),
49 57
50 # TODO: Remove once sdk-extensions are in place 58 # TODO: Remove once sdk-extensions are in place
51 re.compile(r'^\[error\] Native functions can only be declared in'), 59 re.compile(r'^\[error\] Native functions can only be declared in'),
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
146 'Running `download_material_design_icons` for you.') 154 'Running `download_material_design_icons` for you.')
147 subprocess.check_call( 155 subprocess.check_call(
148 [os.path.join(sky_pkg_lib_dir, 'download_material_design_icons')]) 156 [os.path.join(sky_pkg_lib_dir, 'download_material_design_icons')])
149 157
150 158
151 class StartSky(object): 159 class StartSky(object):
152 def add_subparser(self, subparsers): 160 def add_subparser(self, subparsers):
153 start_parser = subparsers.add_parser('start', 161 start_parser = subparsers.add_parser('start',
154 help='launch SkyShell.apk on the device') 162 help='launch SkyShell.apk on the device')
155 start_parser.add_argument('build_dir', type=str) 163 start_parser.add_argument('build_dir', type=str)
164 start_parser.add_argument('--gdb', action="store_true")
156 start_parser.add_argument('url_or_path', nargs='?', type=str, 165 start_parser.add_argument('url_or_path', nargs='?', type=str,
157 default=DEFAULT_URL) 166 default=DEFAULT_URL)
158 start_parser.add_argument('--no_install', action="store_false", 167 start_parser.add_argument('--no_install', action="store_false",
159 default=True, dest="install", 168 default=True, dest="install",
160 help="Don't install SkyDemo.apk before starting") 169 help="Don't install SkyDemo.apk before starting")
161 start_parser.set_defaults(func=self.run) 170 start_parser.set_defaults(func=self.run)
162 171
163 def _server_root_for_url(self, url_or_path): 172 def _server_root_for_url(self, url_or_path):
164 path = os.path.abspath(url_or_path) 173 path = os.path.abspath(url_or_path)
165 if os.path.commonprefix([path, SRC_ROOT]) == SRC_ROOT: 174 if os.path.commonprefix([path, SRC_ROOT]) == SRC_ROOT:
166 server_root = SRC_ROOT 175 server_root = SRC_ROOT
167 else: 176 else:
168 server_root = os.path.dirname(path) 177 server_root = os.path.dirname(path)
169 logging.warn( 178 logging.warn(
170 '%s is outside of mojo root, using %s as server root' % 179 '%s is outside of mojo root, using %s as server root' %
171 (path, server_root)) 180 (path, server_root))
172 return server_root 181 return server_root
173 182
174 def _sky_server_for_args(self, args, packages_root): 183 def _sky_server_for_args(self, args, packages_root):
175 # FIXME: This is a hack. sky_server should just take a build_dir 184 # FIXME: This is a hack. sky_server should just take a build_dir
176 # not a magical "configuration" name. 185 # not a magical "configuration" name.
177 configuration = os.path.basename(os.path.normpath(args.build_dir)) 186 configuration = os.path.basename(os.path.normpath(args.build_dir))
178 server_root = self._server_root_for_url(args.url_or_path) 187 server_root = self._server_root_for_url(args.url_or_path)
179 sky_server = SkyServer(SKY_SERVER_PORT, configuration, server_root, pack ages_root) 188 sky_server = SkyServer(SKY_SERVER_PORT, configuration, server_root, pack ages_root)
180 return sky_server 189 return sky_server
181 190
191 def _find_remote_pid_for_package(self, package):
192 ps_output = subprocess.check_output([ADB_PATH, 'shell', 'ps'])
193 for line in ps_output.split('\n'):
194 fields = line.split()
195 if fields and fields[-1] == package:
196 return fields[1]
197 return None
198
199 def _find_install_location_for_package(self, package):
200 pm_command = [ADB_PATH, 'shell', 'pm', 'path', package]
201 pm_output = subprocess.check_output(pm_command)
202 # e.g. package:/data/app/org.chromium.mojo.shell-1/base.apk
203 return pm_output.split(':')[-1]
204
182 def run(self, args, pids): 205 def run(self, args, pids):
183 apk_path = os.path.join(args.build_dir, 'apks', APK_NAME) 206 apk_path = os.path.join(args.build_dir, 'apks', APK_NAME)
184 if not os.path.exists(apk_path): 207 if not os.path.exists(apk_path):
185 print "'%s' does not exist?" % apk_path 208 print "'%s' does not exist?" % apk_path
186 return 2 209 return 2
187 210
188 ensure_assets_are_downloaded(args.build_dir) 211 ensure_assets_are_downloaded(args.build_dir)
189 212
190 packages_root = dev_packages_root(args.build_dir) 213 packages_root = dev_packages_root(args.build_dir)
191 sky_server = self._sky_server_for_args(args, packages_root) 214 sky_server = self._sky_server_for_args(args, packages_root)
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
229 subprocess.check_call([ 252 subprocess.check_call([
230 ADB_PATH, 'forward', port_string, port_string 253 ADB_PATH, 'forward', port_string, port_string
231 ]) 254 ])
232 255
233 port_string = 'tcp:%s' % sky_server.port 256 port_string = 'tcp:%s' % sky_server.port
234 subprocess.check_call([ 257 subprocess.check_call([
235 ADB_PATH, 'reverse', port_string, port_string 258 ADB_PATH, 'reverse', port_string, port_string
236 ]) 259 ])
237 pids['remote_sky_server_port'] = sky_server.port 260 pids['remote_sky_server_port'] = sky_server.port
238 261
262
239 subprocess.check_call([ADB_PATH, 'shell', 263 subprocess.check_call([ADB_PATH, 'shell',
240 'am', 'start', 264 'am', 'start',
241 '-a', 'android.intent.action.VIEW', 265 '-a', 'android.intent.action.VIEW',
242 '-d', _url_from_args(args, pids)]) 266 '-d', _url_from_args(args, pids)])
243 267
268 if not args.gdb:
269 return
270
271 # TODO(eseidel): am start -W does not seem to work?
272 pid_tries = 0
273 while True:
274 pid = self._find_remote_pid_for_package(ANDROID_PACKAGE)
275 if pid or pid_tries > 3:
276 break
277 logging.debug('No pid for %s yet, waiting' % ANDROID_PACKAGE)
278 time.sleep(5)
279 pid_tries += 1
280
281 if not pid:
282 logging.error('Failed to find pid on device!')
283 return
284
285 pids['sky_shell_pid'] = pid
286
287 # We push our own copy of gdbserver with the package since
288 # the default gdbserver is a different version from our gdb.
289 package_path = \
290 self._find_install_location_for_package(ANDROID_PACKAGE)
291 gdb_server_path = os.path.join(
292 os.path.dirname(package_path), 'lib/arm/gdbserver')
293 gdbserver_cmd = [
294 ADB_PATH, 'shell',
295 gdb_server_path, '--attach',
296 ':%d' % GDB_PORT,
297 str(pid)
298 ]
299 print ' '.join(map(pipes.quote, gdbserver_cmd))
300 subprocess.Popen(gdbserver_cmd)
301
302 port_string = 'tcp:%d' % GDB_PORT
303 subprocess.check_call([
304 ADB_PATH, 'forward', port_string, port_string
305 ])
306 pids['remote_gdbserver_port'] = GDB_PORT
307
308
309 class GDBAttach(object):
310 def add_subparser(self, subparsers):
311 start_parser = subparsers.add_parser('gdb_attach',
312 help='attach to gdbserver running on device')
313 start_parser.set_defaults(func=self.run)
314
315 def _pull_system_libraries(self, pids, system_libs_root):
316 # Pull down the system libraries this pid has already mapped in.
317 # TODO(eseidel): This does not handle dynamic loads.
318 library_cacher_path = os.path.join(
319 SKY_TOOLS_DIR, 'android_library_cacher.py')
320 subprocess.call([
321 library_cacher_path, system_libs_root, pids['sky_shell_pid']
322 ])
323
324 # TODO(eseidel): adb_gdb does, this, unclear why solib-absolute-prefix
325 # doesn't make this explicit listing not necessary?
326 return subprocess.check_output([
327 'find', system_libs_root,
328 '-mindepth', '1',
329 '-maxdepth', '4',
330 '-type', 'd',
331 ]).strip().split('\n')
332
333 def run(self, args, pids):
334 symbol_search_paths = [
335 pids['build_dir'],
336 ]
337 gdb_path = '/usr/bin/gdb'
338
339 eval_commands = [
340 'directory %s' % SRC_ROOT,
341 # TODO(eseidel): What file do I point it at? The apk?
342 #'file %s' % self.paths.mojo_shell_path,
343 'target remote localhost:%s' % GDB_PORT,
344 ]
345
346 system_lib_dirs = self._pull_system_libraries(pids,
347 SYSTEM_LIBS_ROOT_PATH)
348 eval_commands.append(
349 'set solib-absolute-prefix %s' % SYSTEM_LIBS_ROOT_PATH)
350
351 symbol_search_paths = system_lib_dirs + symbol_search_paths
352
353 # TODO(eseidel): We need to look up the toolchain somehow?
354 if platform.system() == 'Darwin':
355 gdb_path = os.path.join(SRC_ROOT, 'third_party/android_tools/ndk/'
356 'toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/'
357 'bin/arm-linux-androideabi-gdb')
358 else:
359 gdb_path = os.path.join(SRC_ROOT, 'third_party/android_tools/ndk/'
360 'toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/'
361 'bin/arm-linux-androideabi-gdb')
362
363 # Set solib-search-path after letting android modify symbol_search_paths
364 eval_commands.append(
365 'set solib-search-path %s' % ':'.join(symbol_search_paths))
366
367 exec_command = [gdb_path]
368 for command in eval_commands:
369 exec_command += ['--eval-command', command]
370
371 print " ".join(exec_command)
372
373 # Write out our pid file before we exec ourselves.
374 pids.write_to(PID_FILE_PATH)
375
376 # Exec gdb directly to avoid python intercepting symbols, etc.
377 os.execv(exec_command[0], exec_command)
378
379
244 380
245 class StopSky(object): 381 class StopSky(object):
246 def add_subparser(self, subparsers): 382 def add_subparser(self, subparsers):
247 stop_parser = subparsers.add_parser('stop', 383 stop_parser = subparsers.add_parser('stop',
248 help=('kill all running SkyShell.apk processes')) 384 help=('kill all running SkyShell.apk processes'))
249 stop_parser.set_defaults(func=self.run) 385 stop_parser.set_defaults(func=self.run)
250 386
251 def _kill_if_exists(self, pids, key, name): 387 def _kill_if_exists(self, pids, key, name):
252 pid = pids.pop(key, None) 388 pid = pids.pop(key, None)
253 if not pid: 389 if not pid:
254 logging.info('No pid for %s, nothing to do.' % name) 390 logging.info('No pid for %s, nothing to do.' % name)
255 return 391 return
256 logging.info('Killing %s (%d).' % (name, pid)) 392 logging.info('Killing %s (%d).' % (name, pid))
257 try: 393 try:
258 os.kill(pid, signal.SIGTERM) 394 os.kill(pid, signal.SIGTERM)
259 except OSError: 395 except OSError:
260 logging.info('%s (%d) already gone.' % (name, pid)) 396 logging.info('%s (%d) already gone.' % (name, pid))
261 397
398 def _adb_reverse_remove(self, port):
399 port_string = 'tcp:%s' % port
400 subprocess.call([ADB_PATH, 'reverse', '--remove', port_string])
401
402 def _adb_forward_remove(self, port):
403 port_string = 'tcp:%s' % port
404 subprocess.call([ADB_PATH, 'forward', '--remove', port_string])
405
262 def run(self, args, pids): 406 def run(self, args, pids):
263 self._kill_if_exists(pids, 'sky_server_pid', 'sky_server') 407 self._kill_if_exists(pids, 'sky_server_pid', 'sky_server')
264 408
265 if 'remote_sky_server_port' in pids: 409 if 'remote_sky_server_port' in pids:
266 port_string = 'tcp:%s' % pids['remote_sky_server_port'] 410 self._adb_reverse_remove(pids['remote_sky_server_port'])
267 subprocess.call([ADB_PATH, 'reverse', '--remove', port_string]) 411
412 if 'remote_gdbserver_port' in pids:
413 self._kill_if_exists('adb_shell_gdbserver_pid',
414 'adb shell gdbserver')
415 self._adb_forward_remove(pids['remote_gdbserver_port'])
268 416
269 subprocess.call([ 417 subprocess.call([
270 ADB_PATH, 'shell', 'am', 'force-stop', ANDROID_PACKAGE]) 418 ADB_PATH, 'shell', 'am', 'force-stop', ANDROID_PACKAGE])
271 419
272 pids.clear() 420 pids.clear()
273 421
274 422
275 class Analyze(object): 423 class Analyze(object):
276 def add_subparser(self, subparsers): 424 def add_subparser(self, subparsers):
277 analyze_parser = subparsers.add_parser('analyze', 425 analyze_parser = subparsers.add_parser('analyze',
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
366 def main(self): 514 def main(self):
367 logging.basicConfig(level=logging.WARNING) 515 logging.basicConfig(level=logging.WARNING)
368 516
369 parser = argparse.ArgumentParser(description='Sky Shell Runner') 517 parser = argparse.ArgumentParser(description='Sky Shell Runner')
370 subparsers = parser.add_subparsers(help='sub-command help') 518 subparsers = parser.add_subparsers(help='sub-command help')
371 519
372 commands = [ 520 commands = [
373 StartSky(), 521 StartSky(),
374 StopSky(), 522 StopSky(),
375 Analyze(), 523 Analyze(),
524 GDBAttach(),
376 StartTracing(), 525 StartTracing(),
377 StopTracing(), 526 StopTracing(),
378 ] 527 ]
379 528
380 for command in commands: 529 for command in commands:
381 command.add_subparser(subparsers) 530 command.add_subparser(subparsers)
382 531
383 args = parser.parse_args() 532 args = parser.parse_args()
384 pids = Pids.read_from(PID_FILE_PATH, PID_FILE_KEYS) 533 pids = Pids.read_from(PID_FILE_PATH, PID_FILE_KEYS)
385 exit_code = args.func(args, pids) 534 exit_code = args.func(args, pids)
386 # We could do this with an at-exit handler instead? 535 # We could do this with an at-exit handler instead?
387 pids.write_to(PID_FILE_PATH) 536 pids.write_to(PID_FILE_PATH)
388 sys.exit(exit_code) 537 sys.exit(exit_code)
389 538
390 539
391 if __name__ == '__main__': 540 if __name__ == '__main__':
392 SkyShellRunner().main() 541 SkyShellRunner().main()
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698