aboutsummaryrefslogtreecommitdiff
path: root/gdb_plugin
diff options
context:
space:
mode:
authorDaniel Malea <daniel.malea@intel.com>2011-12-14 17:39:16 -0500
committerDaniel Malea <daniel.malea@intel.com>2012-03-01 18:20:38 -0500
commit094881f513ab366f7ffd0b2c7778ab50281ca59e (patch)
tree2c50858954063c84b90ff5f7db1c9087fcaab3c3 /gdb_plugin
parenteb4509bf00a5d3be4caaa8f1b6a062a2d0311e9a (diff)
downloadlibbcc-094881f513ab366f7ffd0b2c7778ab50281ca59e.tar.gz
Enable debugging of RS code under GDB
- Add/integrate GDBJITRegistrar support class for interfacing with GDB -- Once the GDBJITRegistrar is merged into LLVM trunk (and AOSP upgrades LLVM) all files GDB* should be removed, and replaced with appropriate includes - Basic [host|target]-side integration tests -- host-tests: use bcc driver and clang to verify gdb output -- target-tests: run skeleton apk on target and verify gdb output - Add support for optimization_level metadata in bcinfo, libbcc -- Disabled some LTO passes when optimization_level = 0 -- move register allocator registration after metadata inspection - Initial version of android-commands.py GDB plugin (for test infrastructure) -- relevant commands: load-android-app, run-android-app, start-android-app -- tested versions: gdb (7.2, 7.3), python (2.6, 2.7) - build 'bcc' driver tool by default in eng builds Change-Id: I99e0c11c8591c6d911632c1dcc82dd8fbe1244a8
Diffstat (limited to 'gdb_plugin')
-rw-r--r--gdb_plugin/android-commands.py771
1 files changed, 771 insertions, 0 deletions
diff --git a/gdb_plugin/android-commands.py b/gdb_plugin/android-commands.py
new file mode 100644
index 0000000..b512ed0
--- /dev/null
+++ b/gdb_plugin/android-commands.py
@@ -0,0 +1,771 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# GDB plugin to allow debugging of apps on remote Android systems using gdbserver.
+#
+# To use this plugin, source this file from a Python-enabled GDB client, then use:
+# load-android-app <app-source-dir> to tell GDB about the app you are debugging
+# run-android-app to start the app in a running state
+# start-android-app to start the app in a paused state
+# attach-android-ap to attach to an existing (running) instance of app
+# set-android-device to select a target (only if multiple devices are attached)
+
+import fnmatch
+import gdb
+import os
+import shutil
+import subprocess
+import tempfile
+import time
+
+be_verbose = False
+enable_renderscript_dumps = True
+local_symbols_library_directory = os.path.join(os.getenv('ANDROID_PRODUCT_OUT', 'out'),
+ 'symbols', 'system', 'lib')
+local_library_directory = os.path.join(os.getenv('ANDROID_PRODUCT_OUT', 'out'),
+ 'system', 'lib')
+
+# ADB - Basic ADB wrapper, far from complete
+# DebugAppInfo - App configuration struct, as far as GDB cares
+# StartAndroidApp - Implementation of GDB start (for android apps)
+# RunAndroidApp - Implementation of GDB run (for android apps)
+# AttachAndroidApp - GDB command to attach to an existing android app process
+# AndroidStatus - app status query command (not needed, mostly harmless)
+# LoadAndroidApp - Sets the package and intent names for an app
+
+def _interesting_libs():
+ return ['libc', 'libbcc', 'libRS', 'libandroid_runtime', 'libdvm']
+
+# In python 2.6, subprocess.check_output does not exist, so it is implemented here
+def check_output(*popenargs, **kwargs):
+ p = subprocess.Popen(stdout=subprocess.PIPE, stderr=subprocess.STDOUT, *popenargs, **kwargs)
+ out, err = p.communicate()
+ retcode = p.poll()
+ if retcode != 0:
+ c = kwargs.get("args")
+ if c is None:
+ c = popenargs[0]
+ e = subprocess.CalledProcessError(retcode, c)
+ e.output = str(out) + str(err)
+ raise e
+ return out
+
+class DebugAppInfo:
+ """Stores information from an app manifest"""
+
+ def __init__(self):
+ self.name = None
+ self.intent = None
+
+ def get_name(self):
+ return self.name
+
+ def get_intent(self):
+ return self.intent
+
+ def get_data_directory(self):
+ return self.data_directory
+
+ def get_gdbserver_path(self):
+ return os.path.join(self.data_directory, "lib", "gdbserver")
+
+ def set_info(self, name, intent, data_directory):
+ self.name = name
+ self.intent = intent
+ self.data_directory = data_directory
+
+ def unset_info():
+ self.name = None
+ self.intent = None
+ self.data_directory = None
+
+class ADB:
+ """
+ Python class implementing a basic ADB wrapper for useful commands.
+ Uses subprocess to invoke adb.
+ """
+
+ def __init__(self, device=None, verbose=False):
+ self.verbose = verbose
+ self.current_device = device
+ self.temp_libdir = None
+ self.background_processes = []
+ self.android_build_top = os.getenv('ANDROID_BUILD_TOP', None)
+ if not self.android_build_top:
+ raise gdb.GdbError("Unable to read ANDROID_BUILD_TOP. " \
+ + "Is your environment setup correct?")
+
+ self.adb_path = os.path.join(self.android_build_top,
+ 'out', 'host', 'linux-x86', 'bin', 'adb')
+
+ if not self.current_device:
+ devices = self.devices()
+ if len(devices) == 1:
+ self.set_current_device(devices[0])
+ return
+ else:
+ msg = ""
+ if len(devices) == 0:
+ msg = "No devices detected. Please connect a device and "
+ else:
+ msg = "Too many devices (" + ", ".join(devices) + ") detected. " \
+ + "Please "
+
+ print "Warning: " + msg + " use the set-android-device command."
+
+
+ def _prepare_adb_args(self, args):
+ largs = list(args)
+
+ # Prepare serial number option from current_device
+ if self.current_device and len(self.current_device) > 0:
+ largs.insert(0, self.current_device)
+ largs.insert(0, "-s")
+
+ largs.insert(0, self.adb_path)
+ return largs
+
+
+ def _background_adb(self, *args):
+ largs = self._prepare_adb_args(args)
+ p = None
+ try:
+ if self.verbose:
+ print "### " + str(largs)
+ p = subprocess.Popen(largs)
+ self.background_processes.append(p)
+ except CalledProcessError, e:
+ raise gdb.GdbError("Error starting background adb " + str(largs))
+ except:
+ raise gdb.GdbError("Unknown error starting background adb " + str(largs))
+
+ return p
+
+ def _call_adb(self, *args):
+ output = ""
+ largs = self._prepare_adb_args(args)
+ try:
+ if self.verbose:
+ print "### " + str(largs)
+ output = check_output(largs)
+ except subprocess.CalledProcessError, e:
+ raise gdb.GdbError("Error starting adb " + str(largs))
+ except Exception as e:
+ raise gdb.GdbError("Unknown error starting adb " + str(largs))
+
+ return output
+
+ def _shell(self, *args):
+ args = ["shell"] + list(args)
+ return self._call_adb(*args)
+
+ def _background_shell(self, *args):
+ args = ["shell"] + list(args)
+ return self._background_adb(*args)
+
+ def _cleanup_background_processes(self):
+ for handle in self.background_processes:
+ try:
+ handle.terminate()
+ except OSError, e:
+ # Background process died already
+ pass
+
+ def _cleanup_temp(self):
+ if self.temp_libdir:
+ shutil.rmtree(self.temp_libdir)
+ self.temp_libdir = None
+
+ def __del__(self):
+ self._cleanup_temp()
+ self._cleanup_background_processes()
+
+ def _get_local_libs(self):
+ ret = []
+ for lib in _interesting_libs():
+ lib_path = os.path.join(local_library_directory, lib + ".so")
+ if not os.path.exists(lib_path) and self.verbose:
+ print "Warning: unable to find expected library " \
+ + lib_path + "."
+ ret.append(lib_path)
+
+ return ret
+
+ def _check_remote_libs_match_local_libs(self):
+ ret = []
+ all_remote_libs = self._shell("ls", "/system/lib/*.so").split()
+ local_libs = self._get_local_libs()
+
+ self.temp_libdir = tempfile.mkdtemp()
+
+ for lib in _interesting_libs():
+ lib += ".so"
+ for remote_lib in all_remote_libs:
+ if lib in remote_lib:
+ # Pull lib from device and compute hash
+ tmp_path = os.path.join(self.temp_libdir, lib)
+ self.pull(remote_lib, tmp_path)
+ remote_hash = self._md5sum(tmp_path)
+
+ # Find local lib and compute hash
+ built_library = filter(lambda l: lib in l, local_libs)[0]
+ built_hash = self._md5sum(built_library)
+
+ # Alert user if library mismatch is detected
+ if built_hash != remote_hash:
+ self._cleanup_temp()
+ raise gdb.GdbError("Library mismatch between:\n" \
+ + "\t(" + remote_hash + ") " + tmp_path + " (from target) and\n " \
+ + "\t(" + built_hash + ") " + built_library + " (on host)\n" \
+ + "The target is running a different build than the host." \
+ + " This situation is not debuggable.")
+
+ self._cleanup_temp()
+
+ def _md5sum(self, file):
+ try:
+ return check_output(["md5sum", file]).strip().split()[0]
+ except subprocess.CalledProcessError, e:
+ raise gdb.GdbError("Error invoking md5sum commandline utility")
+
+ # Returns the list of serial numbers of connected devices
+ def devices(self):
+ ret = []
+ raw_output = self._call_adb("devices").split()
+ if len(raw_output) < 5:
+ return None
+ else:
+ for serial_num_index in range(4, len(raw_output), 2):
+ ret.append(raw_output[serial_num_index])
+ return ret
+
+ def set_current_device(self, serial):
+ if self.current_device == str(serial):
+ print "Current device already is: " + str(serial)
+ return
+
+ # TODO: this function should probably check the serial is valid.
+ self.current_device = str(serial)
+
+ api_version = self.getprop("ro.build.version.sdk")
+ if api_version < 15:
+ print "Warning: untested API version. Upgrade to 15 or higher"
+
+ # Verify the local libraries loaded by GDB are identical to those
+ # sitting on the device actually executing. Alert the user if
+ # this is happening
+ self._check_remote_libs_match_local_libs()
+
+ # adb getprop [property]
+ # if property is not None, returns the given property, otherwise
+ # returns all properties.
+ def getprop(self, property=None):
+ if property == None:
+ # get all the props
+ return self._call_adb(*["shell", "getprop"]).split('\n')
+ else:
+ return str(self._call_adb(*["shell", "getprop",
+ str(property)]).split('\n')[0])
+
+ # adb push
+ def push(self, source, destination):
+ self._call_adb(*["push", source, destination])
+
+ # adb forward <source> <destination>
+ def forward(self, source, destination):
+ self._call_adb(*["forward", source, destination])
+
+ # Returns true if filename exists on Android fs, false otherwise
+ def exists(self, filename):
+ raw_listing = self._shell(*["ls", filename])
+ return "No such file or directory" not in raw_listing
+
+ # adb pull <remote_path> <local_path>
+ def pull(self, remote_path, local_path):
+ self._call_adb(*["pull", remote_path, local_path])
+
+ #wrapper for adb shell ps. leave process_name=None for list of all processes
+ #Otherwise, returns triple with process name, pid and owner,
+ def get_process_info(self, process_name=None):
+ ret = []
+ raw_output = self._shell("ps")
+ for raw_line in raw_output.splitlines()[1:]:
+ line = raw_line.split()
+ name = line[-1]
+
+ if process_name == None or name == process_name:
+ user = line[0]
+ pid = line[1]
+
+ if process_name != None:
+ return (pid, user)
+ else:
+ ret.append((pid, user))
+
+ # No match in target process
+ if process_name != None:
+ return (None, None)
+
+ return ret
+
+ def kill_by_pid(self, pid):
+ self._shell(*["kill", "-9", pid])
+
+ def kill_by_name(self, process_name):
+ (pid, user) = self.get_process_info(process_name)
+ while pid != None:
+ self.kill_by_pid(pid)
+ (pid, user) = self.get_process_info(process_name)
+
+class AndroidStatus(gdb.Command):
+ """Implements the android-status gdb command."""
+
+ def __init__(self, adb, name="android-status", cat=gdb.COMMAND_OBSCURE, verbose=False):
+ super (AndroidStatus, self).__init__(name, cat)
+ self.verbose = verbose
+ self.adb = adb
+
+ def _update_status(self, process_name, gdbserver_process_name):
+ self._check_app_is_loaded()
+
+ # Update app status
+ (self.pid, self.owner_user) = \
+ self.adb.get_process_info(process_name)
+ self.running = self.pid != None
+
+ # Update gdbserver status
+ (self.gdbserver_pid, self.gdbserver_user) = \
+ self.adb.get_process_info(gdbserver_process_name)
+ self.gdbserver_running = self.gdbserver_pid != None
+
+ # Print results
+ if self.verbose:
+ print "--==Android GDB Plugin Status Update==--"
+ print "\tinferior name: " + process_name
+ print "\trunning: " + str(self.running)
+ print "\tpid: " + str(self.pid)
+ print "\tgdbserver running: " + str(self.gdbserver_running)
+ print "\tgdbserver pid: " + str(self.gdbserver_pid)
+ print "\tgdbserver user: " + str(self.gdbserver_user)
+
+ def _check_app_is_loaded(self):
+ if not currentAppInfo.get_name():
+ raise gdb.GdbError("Error: no app loaded. Try load-android-app.")
+
+ def invoke(self, arg, from_tty):
+ self._check_app_is_loaded()
+ self._update_status(currentAppInfo.get_name(),
+ currentAppInfo.get_gdbserver_path())
+ # TODO: maybe print something if verbose is off
+
+class StartAndroidApp (AndroidStatus):
+ """Implements the 'start-android-app' gdb command."""
+
+ def _update_status(self):
+ AndroidStatus._update_status(self, self.process_name, \
+ self.gdbserver_path)
+
+ # Calls adb shell ps every retry_delay seconds and returns
+ # the pid when process_name show up in output, or return 0
+ # after num_retries attempts. num_retries=0 means retry
+ # indefinitely.
+ def _wait_for_process(self, process_name, retry_delay=1, num_retries=10):
+ """ This function is a hack and should not be required"""
+ (pid, user) = self.adb.get_process_info(process_name)
+ retries_left = num_retries
+ while pid == None and retries_left != 0:
+ (pid, user) = self.adb.get_process_info(process_name)
+ time.sleep(retry_delay)
+ retries_left -= 1
+
+ return pid
+
+ def _gdbcmd(self, cmd, from_tty=False):
+ if self.verbose:
+ print '### GDB Command: ' + str(cmd)
+
+ gdb.execute(cmd, from_tty)
+
+ # Remove scratch directory if any
+ def _cleanup_temp(self):
+ if self.temp_dir:
+ shutil.rmtree(self.temp_dir)
+ self.temp_dir = None
+
+ def _cleanup_jdb(self):
+ if self.jdb_handle:
+ try:
+ self.jdb_handle.terminate()
+ except OSError, e:
+ # JDB process has likely died
+ pass
+
+ self.jdb_handle = None
+
+ def _load_local_libs(self):
+ for lib in _interesting_libs():
+ self._gdbcmd("shar " + lib)
+
+ def __del__(self):
+ self._cleanup_temp()
+ self._cleanup_jdb()
+
+ def __init__ (self, adb, name="start-android-app", cat=gdb.COMMAND_RUNNING, verbose=False):
+ super (StartAndroidApp, self).__init__(adb, name, cat, verbose)
+ self.adb = adb
+
+ self.jdb_handle = None
+ # TODO: handle possibility that port 8700 is in use (may help with
+ # Eclipse problems)
+ self.jdwp_port = 8700
+
+ # Port for gdbserver
+ self.gdbserver_port = 5039
+
+ self.temp_dir = None
+
+ def start_process(self, start_running=False):
+ #TODO: implement libbcc cache removal if needed
+
+ args = ["am", "start"]
+
+ # If we are to start running, we can take advantage of am's -W flag to wait
+ # for the process to start before returning. That way, we don't have to
+ # emulate the behaviour (poorly) through the sleep-loop below.
+ if not start_running:
+ args.append("-D")
+ else:
+ args.append("-W")
+
+ args.append(self.process_name + "/" + self.intent)
+ am_output = self.adb._shell(*args)
+ if "Error:" in am_output:
+ raise gdb.GdbError("Cannot start app. Activity Manager returned:\n"\
+ + am_output)
+
+ # Gotta wait until the process starts if we can't use -W
+ if not start_running:
+ self.pid = self._wait_for_process(self.process_name)
+
+ if not self.pid:
+ raise gdb.GdbError("Unable to detect running app remotely." \
+ + "Is " + self.process_name + " installed correctly?")
+
+ if self.verbose:
+ print "--==Android App Started: " + self.process_name \
+ + " (pid=" + self.pid + ")==--"
+
+ # Forward port for java debugger to Dalvik
+ self.adb.forward("tcp:" + str(self.jdwp_port), \
+ "jdwp:" + str(self.pid))
+
+ def start_gdbserver(self):
+ # TODO: adjust for architecture...
+ gdbserver_local_path = os.path.join(os.getenv('ANDROID_BUILD_TOP'),
+ 'prebuilt', 'android-x86', 'gdbserver', 'gdbserver')
+
+ if not self.adb.exists(self.gdbserver_path):
+ # Install gdbserver
+ try:
+ self.adb.push(gdbserver_local_path, self.gdbserver_path)
+ except gdb.GdbError, e:
+ print "Unable to push gdbserver to device. Try re-installing app."
+ raise e
+
+ self.adb._background_shell(*[self.gdbserver_path, "--attach",
+ ":" + str(self.gdbserver_port), self.pid])
+
+ self._wait_for_process(self.gdbserver_path)
+ self._update_status()
+
+ if self.verbose:
+ print "--==Remote gdbserver Started " \
+ + " (pid=" + str(self.gdbserver_pid) \
+ + " port=" + str(self.gdbserver_port) + ") ==--"
+
+ # Forward port for gdbserver
+ self.adb.forward("tcp:" + str(self.gdbserver_port), \
+ "tcp:" + str(5039))
+
+ def attach_gdb(self, from_tty):
+ self._gdbcmd("target remote :" + str(self.gdbserver_port), False)
+ if self.verbose:
+ print "--==GDB Plugin requested attach (port=" \
+ + str(self.gdbserver_port) + ")==-"
+
+ # If GDB has no file set, things start breaking...so grab the same
+ # binary the NDK grabs from the filesystem and continue
+ self._cleanup_temp()
+ self.temp_dir = tempfile.mkdtemp()
+ self.gdb_inferior = os.path.join(self.temp_dir, 'app_process')
+ self.adb.pull("/system/bin/app_process", self.gdb_inferior)
+ self._gdbcmd('file ' + self.gdb_inferior)
+
+ def start_jdb(self, port):
+ # Kill if running
+ self._cleanup_jdb()
+
+ # Start the java debugger
+ args = ["jdb", "-connect",
+ "com.sun.jdi.SocketAttach:hostname=localhost,port=" + str(port)]
+ if self.verbose:
+ self.jdb_handle = subprocess.Popen(args, \
+ stdin=subprocess.PIPE)
+ else:
+ # Unix-only bit here..
+ self.jdb_handle = subprocess.Popen(args, \
+ stdin=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ stdout=open('/dev/null', 'w'))
+
+ def invoke (self, arg, from_tty):
+ # TODO: self._check_app_is_installed()
+ self._check_app_is_loaded()
+
+ self.intent = currentAppInfo.get_intent()
+ self.process_name = currentAppInfo.get_name()
+ self.data_directory = currentAppInfo.get_data_directory()
+ self.gdbserver_path = currentAppInfo.get_gdbserver_path()
+
+ self._update_status()
+
+ if self.gdbserver_running:
+ self.adb.kill_by_name(self.gdbserver_path)
+ if self.verbose:
+ print "--==Killed gdbserver process (pid=" \
+ + str(self.gdbserver_pid) + ")==--"
+ self._update_status()
+
+ if self.running:
+ self.adb.kill_by_name(self.process_name)
+ if self.verbose:
+ print "--==Killed app process (pid=" + str(self.pid) + ")==--"
+ self._update_status()
+
+ self.start_process()
+
+ # Start remote gdbserver
+ self.start_gdbserver()
+
+ # Attach the gdb
+ self.attach_gdb(from_tty)
+
+ # Load symbolic libraries
+ self._load_local_libs()
+
+ # Set the debug output directory (for JIT debugging)
+ if enable_renderscript_dumps:
+ self._gdbcmd('set gDebugDumpDirectory="' + self.data_directory + '"')
+
+ # Start app
+ # unblock the gdb by connecting with jdb
+ self.start_jdb(self.jdwp_port)
+
+class RunAndroidApp(StartAndroidApp):
+ """Implements the run-android-app gdb command."""
+
+ def __init__(self, adb, name="run-android-app", cat=gdb.COMMAND_RUNNING, verbose=False):
+ super (RunAndroidApp, self).__init__(adb, name, cat, verbose)
+
+ def invoke(self, arg, from_tty):
+ StartAndroidApp.invoke(self, arg, from_tty)
+ self._gdbcmd("continue")
+
+class AttachAndroidApp(StartAndroidApp):
+ """Implements the attach-android-app gdb command."""
+
+ def __init__(self, adb, name="attach-android-app", cat=gdb.COMMAND_RUNNING, verbose=False):
+ super (AttachAndroidApp, self).__init__(adb, name, cat, verbose)
+
+ def invoke(self, arg, from_tty):
+ # TODO: self._check_app_is_installed()
+ self._check_app_is_loaded()
+
+ self.intent = currentAppInfo.get_intent()
+ self.process_name = currentAppInfo.get_name()
+ self.data_directory = currentAppInfo.get_data_directory()
+ self.gdbserver_path = currentAppInfo.get_gdbserver_path()
+
+ self._update_status()
+
+ if self.gdbserver_running:
+ self.adb.kill_by_name(self.gdbserver_path)
+ if self.verbose:
+ print "--==Killed gdbserver process (pid=" \
+ + str(self.gdbserver_pid) + ")==--"
+ self._update_status()
+
+ # Start remote gdbserver
+ self.start_gdbserver()
+
+ # Attach the gdb
+ self.attach_gdb(from_tty)
+
+ # Load symbolic libraries
+ self._load_local_libs()
+
+ # Set the debug output directory (for JIT debugging)
+ if enable_renderscript_dumps:
+ self._gdbcmd('set gDebugDumpDirectory="' + self.data_directory + '"')
+
+class LoadApp(AndroidStatus):
+ """ Implements the load-android-app gbd command.
+ """
+ def _awk_script_path(self, script_name):
+ if os.path.exists(script_name):
+ return script_name
+
+ script_root = os.path.join(os.getenv('ANDROID_BUILD_TOP'), \
+ 'ndk', 'build', 'awk')
+
+ path_in_root = os.path.join(script_root, script_name)
+ if os.path.exists(path_in_root):
+ return path_in_root
+
+ raise gdb.GdbError("Unable to find awk script " \
+ + str(script_name) + " in " + path_in_root)
+
+ def _awk(self, script, command):
+ args = ["awk", "-f", self._awk_script_path(script), str(command)]
+
+ if self.verbose:
+ print "### awk command: " + str(args)
+
+ awk_output = ""
+ try:
+ awk_output = check_output(args)
+ except subprocess.CalledProcessError, e:
+ raise gdb.GdbError("### Error in subprocess awk " + str(args))
+ except:
+ print "### Random error calling awk " + str(args)
+
+ return awk_output.rstrip()
+
+ def __init__(self, adb, name="load-android-app", cat=gdb.COMMAND_RUNNING, verbose=False):
+ super (LoadApp, self).__init__(adb, name, cat, verbose)
+ self.manifest_name = "AndroidManifest.xml"
+ self.verbose = verbose
+ self.adb = adb
+ self.temp_libdir = None
+
+ def _find_manifests(self, path):
+ manifests = []
+ for root, dirnames, filenames in os.walk(path):
+ for filename in fnmatch.filter(filenames, self.manifest_name):
+ manifests.append(os.path.join(root, filename))
+ return manifests
+
+ def _usage(self):
+ return "Usage: load-android-app [<path-to-AndroidManifest.xml>" \
+ + " | <package-name> <intent-name>]"
+
+ def invoke(self, arg, from_tty):
+
+ package_name = ''
+ launchable = ''
+ args = arg.strip('"').split()
+ if len(args) == 2:
+ package_name = args[0]
+ launchable = args[1]
+ elif len(args) == 1:
+ if os.path.isfile(args[0]) and os.path.basename(args[0]) == self.manifest_name:
+ self.manifest_path = args[0]
+ elif os.path.isdir(args[0]):
+ manifests = self._find_manifests(args[0])
+ if len(manifests) == 0:
+ raise gdb.GdbError(self.manifest_name + " not found in: " \
+ + args[0] + "\n" + self._usage())
+ elif len(manifests) > 1:
+ raise gdb.GdbError("Ambiguous argument! Found too many " \
+ + self.manifest_name + " files found:\n" + "\n".join(manifests))
+ else:
+ self.manifest_path = manifests[0]
+ else:
+ raise gdb.GdbError("Invalid path: " + args[0] + "\n" + self._usage())
+
+ package_name = self._awk("extract-package-name.awk",
+ self.manifest_path)
+ launchable = self._awk("extract-launchable.awk",
+ self.manifest_path)
+ else:
+ raise gdb.GdbError(self._usage())
+
+
+ data_directory = self.adb._shell("run-as", package_name,
+ "/system/bin/sh", "-c", "pwd").rstrip()
+
+ if not data_directory \
+ or len(data_directory) == 0 \
+ or not self.adb.exists(data_directory):
+ data_directory = os.path.join('/data', 'data', package_name)
+ print "Warning: unable to read data directory for package " \
+ + package_name + ". Meh, defaulting to " + data_directory
+
+ currentAppInfo.set_info(package_name, launchable, data_directory)
+
+ if self.verbose:
+ print "--==Android App Loaded==--"
+ print "\tname=" + currentAppInfo.get_name()
+ print "\tintent=" + currentAppInfo.get_intent()
+
+ # TODO: Check status of app on device
+
+class SetAndroidDevice (gdb.Command):
+ def __init__(self, adb, name="set-android-device", cat=gdb.COMMAND_RUNNING, verbose=False):
+ super (SetAndroidDevice, self).__init__(name, cat)
+ self.verbose = verbose
+ self.adb = adb
+
+ def _usage(self):
+ return "Usage: set-android-device <serial>"
+
+ def invoke(self, arg, from_tty):
+ if not arg or len(arg) == 0:
+ raise gdb.GdbError(self._usage)
+
+ serial = str(arg)
+ devices = adb.devices()
+ if serial in devices:
+ adb.set_current_device(serial)
+ else:
+ raise gdb.GdbError("Invalid serial. Serial numbers of connected " \
+ + "device(s): \n" + "\n".join(devices))
+
+# Global initialization
+def initOnce(adb):
+ # Try to speed up startup by skipping most android shared objects
+ gdb.execute("set auto-solib-add 0", False);
+
+ # Set shared object search path
+ gdb.execute("set solib-search-path " + local_symbols_library_directory, False)
+
+# Global instance of the object containing the info for current app
+currentAppInfo = DebugAppInfo ()
+
+# Global instance of ADB helper
+adb = ADB(verbose=be_verbose)
+
+# Perform global initialization
+initOnce(adb)
+
+# Command registration
+StartAndroidApp (adb, "start-android-app", gdb.COMMAND_RUNNING, be_verbose)
+RunAndroidApp (adb, "run-android-app", gdb.COMMAND_RUNNING, be_verbose)
+AndroidStatus (adb, "android-status", gdb.COMMAND_OBSCURE, be_verbose)
+LoadApp (adb, "load-android-app", gdb.COMMAND_RUNNING, be_verbose)
+SetAndroidDevice (adb, "set-android-device", gdb.COMMAND_RUNNING, be_verbose)
+AttachAndroidApp (adb, "attach-android-app", gdb.COMMAND_RUNNING, be_verbose)