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

Unified Diff: mojo/tools/mopy/android.py

Issue 1152663002: Makes android.py support gdb (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: review feedback and symbols Created 5 years, 7 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « mandoline/tools/android_run_mandoline.py ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: mojo/tools/mopy/android.py
diff --git a/mojo/tools/mopy/android.py b/mojo/tools/mopy/android.py
index 74499b20447a6dcd72f011ca3f182bfaf2670bcb..1bf66df429bdcc0a45e7e67d2553806da17bf902 100644
--- a/mojo/tools/mopy/android.py
+++ b/mojo/tools/mopy/android.py
@@ -13,8 +13,11 @@ import math
import os
import os.path
import random
+import shutil
+import signal
import subprocess
import sys
+import tempfile
import threading
import time
import urlparse
@@ -185,11 +188,13 @@ class AndroidShell(object):
local_dir: directory where locally build Mojo apps will be served, optional
adb_path: path to adb, optional if adb is in PATH
target_device: device to run on, if multiple devices are connected
+ src_root: root of the source tree
"""
def __init__(
self, shell_apk_path, local_dir=None, adb_path="adb", target_device=None,
- target_package=MOJO_SHELL_PACKAGE_NAME):
+ target_package=MOJO_SHELL_PACKAGE_NAME, src_root=None):
self.shell_apk_path = shell_apk_path
+ self.src_root = src_root
self.adb_path = adb_path
self.local_dir = local_dir
self.target_device = target_device
@@ -310,7 +315,7 @@ class AndroidShell(object):
result.append(self._StartHttpServerForOriginMapping(value, 0))
return [MAPPING_PREFIX + ','.join(result)]
- def PrepareShellRun(self, origin=None):
+ def PrepareShellRun(self, origin=None, install=True, gdb=False):
""" Prepares for StartShell: runs adb as root and installs the apk. If the
origin specified is 'localhost', a local http server will be set up to serve
files from the build directory along with port forwarding.
@@ -322,9 +327,11 @@ class AndroidShell(object):
subprocess.check_output(self._CreateADBCommand(['devices'])))
subprocess.check_call(self._CreateADBCommand(['root']))
- subprocess.check_call(
- self._CreateADBCommand(['install', '-r', self.shell_apk_path, '-i',
- self.target_package]))
+ if install:
+ subprocess.check_call(
+ self._CreateADBCommand(['install', '-r', self.shell_apk_path, '-i',
+ self.target_package]))
+
atexit.register(self.StopShell)
extra_args = []
@@ -332,18 +339,81 @@ class AndroidShell(object):
origin = self._StartHttpServerForDirectory(self.local_dir, 0)
if origin:
extra_args.append("--origin=" + origin)
+
+ if gdb:
+ # Remote debugging needs a port forwarded.
+ subprocess.check_call(self._CreateADBCommand(['forward', 'tcp:5039',
+ 'tcp:5039']))
+
return extra_args
+ def _GetProcessId(self, process):
+ """Returns the process id of the process on the remote device."""
+ while True:
+ line = process.stdout.readline()
+ pid_command = 'launcher waiting for GDB. pid: '
+ index = line.find(pid_command)
+ if index != -1:
+ return line[index + len(pid_command):].strip()
+ return 0
+
+ def _GetLocalGdbPath(self):
+ """Returns the path to the android gdb."""
+ return os.path.join(self.src_root, "third_party", "android_tools", "ndk",
+ "toolchains", "arm-linux-androideabi-4.9", "prebuilt",
+ "linux-x86_64", "bin", "arm-linux-androideabi-gdb")
+
+ def _WaitForProcessIdAndStartGdb(self, process):
+ """Waits until we see the process id from the remote device, starts up
+ gdbserver on the remote device, and gdb on the local device."""
+ # Wait until we see "PID"
+ pid = self._GetProcessId(process)
+ assert pid != 0
+ # No longer need the logcat process.
+ process.kill()
+ # Disable python's processing of SIGINT while running gdb. Otherwise
+ # control-c doesn't work well in gdb.
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
+ gdbserver_process = subprocess.Popen(self._CreateADBCommand(['shell',
+ 'gdbserver',
+ '--attach',
+ ':5039',
+ pid]))
+ atexit.register(_ExitIfNeeded, gdbserver_process)
+
+ temp_dir = tempfile.mkdtemp()
+ atexit.register(shutil.rmtree, temp_dir, True)
+
+ gdbinit_path = os.path.join(temp_dir, 'gdbinit')
+ _CreateGdbInit(temp_dir, gdbinit_path, self.local_dir)
+
+ _CreateSOLinks(temp_dir, self.local_dir)
+
+ # Wait a second for gdb to start up on the device. Without this the local
+ # gdb starts before the remote side has registered the port.
+ # TODO(sky): maybe we should try a couple of times and then give up?
+ time.sleep(1)
+
+ local_gdb_process = subprocess.Popen([self._GetLocalGdbPath(),
+ "-x",
+ gdbinit_path],
+ cwd=temp_dir)
+ atexit.register(_ExitIfNeeded, local_gdb_process)
+ local_gdb_process.wait()
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+
def StartShell(self,
arguments,
stdout=None,
- on_application_stop=None):
+ on_application_stop=None,
+ gdb=False):
"""
Starts the mojo shell, passing it the given arguments.
The |arguments| list must contain the "--origin=" arg from PrepareShellRun.
If |stdout| is not None, it should be a valid argument for subprocess.Popen.
"""
+
STDOUT_PIPE = "/data/data/%s/stdout.fifo" % self.target_package
cmd = self._CreateADBCommand([
@@ -355,6 +425,12 @@ class AndroidShell(object):
'-n', '%s/%s.MojoShellActivity' % (self.target_package,
MOJO_SHELL_PACKAGE_NAME)])
+ logcat_process = None
+
+ if gdb:
+ arguments += ['--wait-for-debugger']
+ logcat_process = self.ShowLogs(stdout=subprocess.PIPE)
+
parameters = []
if stdout or on_application_stop:
subprocess.check_call(self._CreateADBCommand(
@@ -376,7 +452,10 @@ class AndroidShell(object):
cmd += ['--es', 'encodedParameters', encodedParameters]
with open(os.devnull, 'w') as devnull:
- subprocess.Popen(cmd, stdout=devnull).wait()
+ cmd_process = subprocess.Popen(cmd, stdout=devnull)
+ if logcat_process:
+ self._WaitForProcessIdAndStartGdb(logcat_process)
+ cmd_process.wait()
def StopShell(self):
"""
@@ -393,7 +472,7 @@ class AndroidShell(object):
"""
subprocess.check_call(self._CreateADBCommand(['logcat', '-c']))
- def ShowLogs(self):
+ def ShowLogs(self, stdout=sys.stdout):
"""
Displays the log for the mojo shell.
@@ -403,6 +482,58 @@ class AndroidShell(object):
'logcat',
'-s',
' '.join(LOGCAT_TAGS)]),
- stdout=sys.stdout)
+ stdout=stdout)
atexit.register(_ExitIfNeeded, logcat)
return logcat
+
+
+def _CreateGdbInit(tmp_dir, gdb_init_path, build_dir):
+ """
+ Creates the gdbinit file.
+ Args:
+ tmp_dir: the directory where the gdbinit and other files lives.
+ gdb_init_path: path to gdbinit
+ build_dir: path where build files are located.
+ """
+ gdbinit = ('target remote localhost:5039\n'
+ 'def reload-symbols\n'
+ ' set solib-search-path %s:%s\n'
+ 'end\n'
+ 'def info-symbols\n'
+ ' info sharedlibrary\n'
+ 'end\n'
+ 'reload-symbols\n'
+ 'echo \\n\\n'
+ 'You are now in gdb and need to type continue (or c) to continue '
+ 'execution.\\n'
+ 'gdb is in the directory %s\\n'
+ 'The following functions have been defined:\\n'
+ 'reload-symbols: forces reloading symbols. If after a crash you\\n'
+ 'still do not see symbols you likely need to create a link in\\n'
+ 'the directory you are in.\\n'
+ 'info-symbols: shows status of current shared libraries.\\n'
+ 'NOTE: you may need to type reload-symbols again after a '
+ 'crash.\\n\\n' % (tmp_dir, build_dir, tmp_dir))
+ with open(gdb_init_path, 'w') as f:
+ f.write(gdbinit)
+
+
+def _CreateSOLinks(dest_dir, build_dir):
+ """
+ Creates links from files (such as mojo files) to the real .so so that gdb can
+ find them.
+ """
+ # The files to create links for. The key is the name as seen on the device,
+ # and the target an array of path elements as to where the .so lives (relative
+ # to the output directory).
+ # TODO(sky): come up with some way to automate this.
+ files_to_link = {
+ 'html_viewer.mojo': ['libhtml_viewer', 'html_viewer_library.so'],
+ 'libmandoline_runner.so': ['mandoline_runner'],
+ }
+ for android_name, so_path in files_to_link.iteritems():
+ src = os.path.join(build_dir, *so_path)
+ if not os.path.isfile(src):
+ print 'Expected file not found', src
+ sys.exit(-1)
+ os.symlink(src, os.path.join(dest_dir, android_name))
« no previous file with comments | « mandoline/tools/android_run_mandoline.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698