aboutsummaryrefslogtreecommitdiff
path: root/ndk-gdb.py
diff options
context:
space:
mode:
authorRay Donnelly <mingw.android@gmail.com>2012-12-05 01:01:42 +0000
committerRay Donnelly <mingw.android@gmail.com>2013-01-08 22:45:02 +0000
commitc2f40d9f17b5535bec8f92134848de91d54931d5 (patch)
tree9bd25a0ae1ec231083ccabf3a0c5bae73e0d1fa3 /ndk-gdb.py
parentf38259dde215610ad366f8c1072ff96dddfee9b7 (diff)
downloadndk-c2f40d9f17b5535bec8f92134848de91d54931d5.tar.gz
Added ndk-gdb{.py|-py|-py.cmd}
Pythonic replacements for the ndk-gdb shell script. Attempts to emulate the old behaviour, including verbose mode and logging and also the general layout of the shell script. Added a new command line option, --tui/-t to launch gdb in text user interface mode, if the gdb is built with support for this. Change-Id: Ic02feb18d70fe449bf54a531ffb973513dc82783
Diffstat (limited to 'ndk-gdb.py')
-rwxr-xr-xndk-gdb.py692
1 files changed, 692 insertions, 0 deletions
diff --git a/ndk-gdb.py b/ndk-gdb.py
new file mode 100755
index 000000000..9cd3794e0
--- /dev/null
+++ b/ndk-gdb.py
@@ -0,0 +1,692 @@
+#!/bin/env python
+
+r'''
+ Copyright (C) 2010 The Android Open Source Project
+ Copyright (C) 2012 Ray Donnelly <mingw.android@gmail.com>
+
+ 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.
+
+
+ This wrapper script is used to launch a native debugging session
+ on a given NDK application. The application must be debuggable, i.e.
+ its android:debuggable attribute must be set to 'true' in the
+ <application> element of its manifest.
+
+ See docs/NDK-GDB.TXT for usage description. Essentially, you just
+ need to launch ndk-gdb-py from your application project directory
+ after doing ndk-build && ant debug && \
+ adb install && <start-application-on-device>
+'''
+
+import sys, os, argparse, subprocess, types
+import xml.etree.cElementTree as ElementTree
+import shutil
+from threading import Thread
+try:
+ from Queue import Queue, Empty
+except ImportError:
+ from queue import Queue, Empty # python 3.x
+
+def find_program(program, extra_paths = []):
+ ''' extra_paths are searched before PATH '''
+ PATHS = extra_paths+os.environ['PATH'].split(os.pathsep)
+ exts = ['']
+ if sys.platform.startswith('win'):
+ exts += ['.exe', '.bat', '.cmd']
+ for path in PATHS:
+ if os.path.isdir(path):
+ for ext in exts:
+ full = path + os.sep + program + ext
+ if os.path.isfile(full):
+ return True, full
+ return False, None
+
+# Return the prebuilt bin path for the host os.
+def ndk_bin_path(ndk):
+ if sys.platform.startswith('linux'):
+ return ndk+os.sep+'prebuilt/linux-x86/bin'
+ elif sys.platform.startswith('darwin'):
+ return ndk+os.sep+'prebuilt/darwin-x86/bin'
+ elif sys.platform.startswith('win'):
+ return ndk+os.sep+'prebuilt/windows/bin'
+ return ndk+os.sep+'prebuilt/UNKNOWN/bin'
+
+VERBOSE = False
+PROJECT = None
+PYTHON_CMD = None
+ADB_CMD = None
+GNUMAKE_CMD = None
+
+OPTION_FORCE = None
+OPTION_EXEC = None
+OPTION_START = None
+OPTION_LAUNCH = None
+OPTION_LAUNCH_LIST = None
+OPTION_TUI = None
+
+
+DEBUG_PORT = 5039
+
+# Name of the manifest file
+MANIFEST = 'AndroidManifest.xml'
+
+# Delay in seconds between launching the activity and attaching gdbserver on it.
+# This is needed because there is no way to know when the activity has really
+# started, and sometimes this takes a few seconds.
+#
+DELAY = 2.0
+NDK = os.path.abspath(os.path.dirname(sys.argv[0]))
+DEVICE_SERIAL = ''
+ADB_FLAGS = ''
+
+def log(string):
+ global VERBOSE
+ if VERBOSE:
+ print(string)
+
+def error(string, errcode=1):
+ print('ERROR: %s' % (string))
+ exit(errcode)
+
+def handle_args():
+ global VERBOSE, DEBUG_PORT, DELAY, DEVICE_SERIAL
+ global PYTHON_CMD, GNUMAKE_CMD, ADB_CMD, ADB_FLAGS
+ global PROJECT, NDK
+ global OPTION_START, OPTION_LAUNCH, OPTION_LAUNCH_LIST
+ global OPTION_FORCE, OPTION_EXEC, OPTION_TUI
+
+ parser = argparse.ArgumentParser(description='''
+ Setup a gdb debugging session for your Android NDK application.
+ Read ''' + NDK + '''/docs/NDK-GDB.html for complete usage instructions.''')
+
+ parser.add_argument( '--verbose',
+ help='Enable verbose mode', action='store_true', dest='verbose')
+
+ parser.add_argument( '--force',
+ help='Kill existing debug session if it exists',
+ action='store_true')
+
+ parser.add_argument( '--start',
+ help='Launch application instead of attaching to existing one',
+ action='store_true')
+
+ parser.add_argument( '--launch',
+ help='Same as --start, but specify activity name (see below)',
+ dest='launch_name', nargs=1)
+
+ parser.add_argument( '--launch-list',
+ help='List all launchable activity names from manifest',
+ action='store_true')
+
+ parser.add_argument( '--delay',
+ help='Delay in seconds between activity start and gdbserver attach',
+ type=float, default=DELAY,
+ dest='delay')
+
+ parser.add_argument( '-p', '--project',
+ help='Specify application project path',
+ dest='project')
+
+ parser.add_argument( '--port',
+ help='Use tcp:localhost:<DEBUG_PORT> to communicate with gdbserver',
+ type=int, default=DEBUG_PORT,
+ dest='debug_port')
+
+ parser.add_argument( '-x', '--exec',
+ help='Execute gdb initialization commands in <EXEC_FILE> after connection',
+ dest='exec_file')
+
+ parser.add_argument( '--adb',
+ help='Use specific adb command',
+ dest='adb_cmd')
+
+ parser.add_argument( '--awk',
+ help='Use specific awk command (unused flag retained for compatability)')
+
+ parser.add_argument( '-e',
+ help='Connect to single emulator instance....(either this,)',
+ action='store_true', dest='emulator')
+
+ parser.add_argument( '-d',
+ help='Connect to single target device........(this,)',
+ action='store_true', dest='device')
+
+ parser.add_argument( '-s',
+ help='Connect to specific emulator or device.(or this)',
+ default=DEVICE_SERIAL,
+ dest='device_serial')
+
+ parser.add_argument( '-t','--tui',
+ help='Use tui mode',
+ action='store_true', dest='tui')
+
+ args = parser.parse_args()
+
+ VERBOSE = args.verbose
+
+ ndk_bin = ndk_bin_path(NDK)
+ (found_python, PYTHON_CMD) = find_program('python', [ndk_bin])
+ (found_adb, ADB_CMD) = find_program('adb', [ndk_bin])
+ (found_gnumake, GNUMAKE_CMD) = find_program('make', [ndk_bin])
+
+ if not found_gnumake:
+ error('Failed to find GNU make')
+
+ log('Android NDK installation path: %s' % (NDK))
+
+ if args.device:
+ ADB_FLAGS = '-d'
+ if args.emulator:
+ if ADB_FLAGS != '':
+ parser.print_help()
+ exit(1)
+ ADB_FLAGS = '-e'
+ if args.device_serial != '':
+ DEVICE_SERIAL = args.device_serial
+ if ADB_FLAGS != '':
+ parser.print_help()
+ exit(1)
+ ADB_FLAGS = '-s'
+ if args.adb_cmd != None:
+ log('Using specific adb command: %s' % (args.adb_cmd))
+ ADB_CMD = args.adb_cmd
+ if ADB_CMD is None:
+ error('''The 'adb' tool is not in your path.
+ You can change your PATH variable, or use
+ --adb=<executable> to point to a valid one.''')
+ if not os.path.isfile(ADB_CMD):
+ error('Could not run ADB with: %s' % (ADB_CMD))
+
+ if args.project != None:
+ PROJECT = args.project
+
+ if args.start != None:
+ OPTION_START = args.start
+
+ if args.launch_name != None:
+ OPTION_LAUNCH = args.launch_name
+
+ if args.launch_list != None:
+ OPTION_LAUNCH_LIST = args.launch_list
+
+ if args.force != None:
+ OPTION_FORCE = args.force
+
+ if args.exec_file != None:
+ OPTION_EXEC = args.exec_file
+
+ if args.tui != False:
+ OPTION_TUI = True
+
+ if args.delay != None:
+ DELAY = args.delay
+
+def get_build_var(var):
+ global GNUMAKE_CMD, NDK, PROJECT
+ text = subprocess.check_output([GNUMAKE_CMD,
+ '--no-print-dir',
+ '-f',
+ NDK+'/build/core/build-local.mk',
+ '-C',
+ PROJECT,
+ 'DUMP_'+var]
+ )
+ # replace('\r', '') due to Windows crlf (\r\n)
+ # ...universal_newlines=True causes bytes to be returned
+ # rather than a str
+ return text.decode('ascii').replace('\r', '').splitlines()[0]
+
+def get_build_var_for_abi(var, abi):
+ global GNUMAKE_CMD, NDK, PROJECT
+ text = subprocess.check_output([GNUMAKE_CMD,
+ '--no-print-dir',
+ '-f',
+ NDK+'/build/core/build-local.mk',
+ '-C',
+ PROJECT,
+ 'DUMP_'+var,
+ 'APP_ABI='+abi],
+ )
+ return text.decode('ascii').replace('\r', '').splitlines()[0]
+
+# Silent if gdb is running in tui mode to keep things tidy.
+def output_gdbserver(text):
+ if not OPTION_TUI or OPTION_TUI != 'running':
+ print(text)
+
+def background_spawn(args, redirect_stderr, output_fn):
+
+ def async_stdout(out, queue, output_fn):
+ for line in iter(out.readline, b''):
+ output_fn(line.replace('\r', '').replace('\n', ''))
+ out.close()
+
+ def async_stderr(out, queue, output_fn):
+ for line in iter(out.readline, b''):
+ output_fn(line.replace('\r', '').replace('\n', ''))
+ out.close()
+
+ if redirect_stderr:
+ used_stderr = subprocess.PIPE
+ else:
+ used_stderr = subprocess.STDOUT
+ p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=used_stderr,
+ bufsize=1, close_fds='posix' in sys.builtin_module_names)
+ qo = Queue()
+ to = Thread(target=async_stdout, args=(p.stdout, qo, output_fn))
+ to.daemon = True
+ to.start()
+ if redirect_stderr:
+ te = Thread(target=async_stderr, args=(p.stderr, qo, output_fn))
+ te.daemon = True
+ te.start()
+
+def adb_cmd(redirect_stderr, args, log_command=False, adb_trace=False, background=False):
+ global ADB_CMD, ADB_FLAGS, DEVICE_SERIAL
+ fullargs = [ADB_CMD]
+ if ADB_FLAGS != '':
+ fullargs += [ADB_FLAGS]
+ if DEVICE_SERIAL != '':
+ fullargs += [DEVICE_SERIAL]
+ if isinstance(args, str):
+ fullargs.append(args)
+ else:
+ fullargs += [arg for arg in args]
+ new_env = os.environ.copy()
+ retval = 0
+ if adb_trace:
+ new_env["ADB_TRACE"] = "1"
+ if background:
+ if log_command:
+ log('## COMMAND: adb_cmd %s [BACKGROUND]' % (' '.join(args)))
+ background_spawn(fullargs, redirect_stderr, output_gdbserver)
+ return 0, ''
+ else:
+ if log_command:
+ log('## COMMAND: adb_cmd %s' % (' '.join(args)))
+ try:
+ if redirect_stderr:
+ text = subprocess.check_output(fullargs,
+ stderr=subprocess.STDOUT,
+ env=new_env
+ )
+ else:
+ text = subprocess.check_output(fullargs,
+ env=new_env
+ )
+ except subprocess.CalledProcessError as e:
+ retval = e.returncode
+ text = e.output
+ # rstrip() because of final newline.
+ return retval, text.decode('ascii').replace('\r', '').rstrip()
+
+def _adb_var_shell(args, redirect_stderr=False, log_command=True):
+ if log_command:
+ log('## COMMAND: adb_cmd shell %s' % (' '.join(args)))
+ arg_str = str(' '.join(args)+' ; echo $?')
+ adb_ret,output = adb_cmd(redirect_stderr=redirect_stderr,
+ args=['shell', arg_str], log_command=False)
+ output = output.splitlines()
+ retcode = int(output.pop())
+ return retcode,'\n'.join(output)
+
+def adb_var_shell(args, log_command=False):
+ return _adb_var_shell(args, redirect_stderr=False, log_command=log_command)
+
+def adb_var_shell2(args, log_command=False):
+ return _adb_var_shell(args, redirect_stderr=True, log_command=log_command)
+
+# Return the PID of a given package or program, or 0 if it doesn't run
+# $1: Package name ("com.example.hellojni") or program name ("/lib/gdbserver")
+# Out: PID number, or 0 if not running
+#
+def get_pid_of(package_name):
+ '''
+ Some custom ROMs use busybox instead of toolbox for ps.
+ Without -w, busybox truncates the output, and very long
+ package names like com.exampleisverylongtoolongbyfar.plasma
+ exceed the limit.
+ '''
+ ps_command = 'ps'
+ retcode,output = adb_cmd(False, ['shell', 'readlink $(which ps)'])
+ if output:
+ output = output.replace('\r', '').splitlines()[0]
+ if output == 'busybox':
+ ps_command = 'ps -w'
+ retcode,output = adb_cmd(False,['shell', ps_command])
+ output = output.replace('\r', '').splitlines()
+ columns = output.pop(0).split()
+ try:
+ PID_column = columns.index('PID')
+ except:
+ PID_column = 1
+ while output:
+ columns = output.pop().split()
+ if columns.pop() == package_name:
+ return 0,int(columns[PID_column])
+ return 1,0
+
+def extract_package_name(xmlfile):
+ '''
+ The name itself is the value of the 'package' attribute in the
+ 'manifest' element.
+ '''
+ tree = ElementTree.ElementTree(file=xmlfile)
+ root = tree.getroot()
+ if 'package' in root.attrib:
+ return root.attrib['package']
+ return None
+
+def extract_debuggable(xmlfile):
+ '''
+ simply extract the 'android:debuggable' attribute value from
+ the first <manifest><application> element we find.
+ '''
+ tree = ElementTree.ElementTree(file=xmlfile)
+ root = tree.getroot()
+ for application in root.iter('application'):
+ for k in application.attrib.keys():
+ if str(k).endswith('debuggable'):
+ return application.attrib[k] == 'true'
+ return False
+
+def extract_launchable(xmlfile):
+ '''
+ A given application can have several activities, and each activity
+ can have several intent filters. We want to only list, in the final
+ output, the activities which have a intent-filter that contains the
+ following elements:
+
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ '''
+ tree = ElementTree.ElementTree(file=xmlfile)
+ root = tree.getroot()
+ launchable_activities = []
+ for application in root.iter('application'):
+ for activity in application.iter('activity'):
+ for intent_filter in activity.iter('intent-filter'):
+ found_action_MAIN = False
+ found_category_LAUNCHER = False
+ for child in intent_filter:
+ if child.tag == 'action':
+ if True in [str(child.attrib[k]).endswith('MAIN') for k in child.attrib.keys()]:
+ found_action_MAIN = True
+ if child.tag == 'category':
+ if True in [str(child.attrib[k]).endswith('LAUNCHER') for k in child.attrib.keys()]:
+ found_category_LAUNCHER = True
+ if found_action_MAIN and found_category_LAUNCHER:
+ names = [str(activity.attrib[k]) for k in activity.attrib.keys() if str(k).endswith('name')]
+ for name in names:
+ if name[0] != '.':
+ name = '.'+name
+ launchable_activities.append(name)
+ return launchable_activities
+
+def main():
+ global ADB_CMD, NDK, PROJECT
+ global OPTION_START, OPTION_LAUNCH, OPTION_LAUNCH_LIST
+ global OPTION_FORCE, OPTION_EXEC, OPTION_TUI
+
+ if NDK.find(' ')!=-1:
+ error('NDK path cannot contain space')
+ handle_args()
+ if OPTION_EXEC:
+ if not os.path.isfile(OPTION_EXEC):
+ error('Invalid initialization file: %s' % (OPTION_EXEC))
+ ADB_VERSION = subprocess.check_output([ADB_CMD, 'version'],
+ ).decode('ascii').replace('\r', '').splitlines()[0]
+ log('ADB version found: %s' % (ADB_VERSION))
+ if DEVICE_SERIAL == '':
+ log('Using ADB flags: %s' % (ADB_FLAGS))
+ else:
+ log('Using ADB flags: %s "%s"' % (ADB_FLAGS,DEVICE_SERIAL))
+
+ if PROJECT != None:
+ log('Using specified project path: %s' % (PROJECT))
+ if not os.path.isdir(PROJECT):
+ error('Your --project option does not point to a directory!')
+ if not os.path.isfile(PROJECT+os.sep+MANIFEST):
+ error('''Your --project does not point to an Android project path!
+ It is missing a %s file.''' % (MANIFEST))
+ else:
+ # Assume we are in the project directory
+ if os.path.isfile(MANIFEST):
+ PROJECT = '.'
+ else:
+ PROJECT = ''
+ CURDIR = os.getcwd()
+
+ while CURDIR != os.path.dirname(CURDIR):
+ if os.path.isfile(CURDIR+os.sep+MANIFEST):
+ PROJECT=CURDIR
+ break
+ CURDIR = os.path.dirname(CURDIR)
+
+ if not os.path.isdir(PROJECT):
+ error('Launch this script from an application project directory, or use --project=<path>.')
+ log('Using auto-detected project path: %s' % (PROJECT))
+
+ PACKAGE_NAME = extract_package_name(PROJECT+os.sep+MANIFEST)
+ if PACKAGE_NAME is None:
+ PACKAGE_NAME = '<none>'
+ log('Found package name: %s' % (PACKAGE_NAME))
+ if PACKAGE_NAME is '<none>':
+ error('''Could not extract package name from %s.
+ Please check that the file is well-formed!''' % (PROJECT+os.sep+MANIFEST))
+ if OPTION_LAUNCH_LIST:
+ log('Extracting list of launchable activities from manifest:')
+ print(' '.join(extract_launchable(PROJECT+os.sep+MANIFEST)))
+ exit(0)
+ APP_ABIS = get_build_var('APP_ABI').split(' ')
+ if 'all' in APP_ABIS:
+ ALL_ABIS = get_build_var('NDK_ALL_ABIS').split(' ')
+ APP_ABIS = APP_ABIS[:APP_ABIS.index('all')]+ALL_ABIS+APP_ABIS[APP_ABIS.index('all')+1:]
+ log('ABIs targetted by application: %s' % (' '.join(APP_ABIS)))
+
+ retcode,ADB_TEST = adb_cmd(True,['shell', 'ls'])
+ if retcode != 0:
+ print(ADB_TEST)
+ error('''Could not connect to device or emulator!
+ Please check that an emulator is running or a device is connected
+ through USB to this machine. You can use -e, -d and -s <serial>
+ in case of multiple ones.''')
+
+ retcode,API_LEVEL = adb_var_shell(['getprop', 'ro.build.version.sdk'])
+ if retcode != 0 or API_LEVEL == '':
+ error('''Could not find target device's supported API level!
+ndk-gdb will only work if your device is running Android 2.2 or higher.''')
+ API_LEVEL = int(API_LEVEL)
+ log('Device API Level: %d' % (API_LEVEL))
+ if API_LEVEL < 8:
+ error('''ndk-gdb requires a target device running Android 2.2 (API level 8) or higher.
+The target device is running API level %d!''' % (API_LEVEL))
+ COMPAT_ABI = []
+ _,CPU_ABI1 = adb_var_shell(['getprop', 'ro.product.cpu.abi'])
+ _,CPU_ABI2 = adb_var_shell(['getprop', 'ro.product.cpu.abi2'])
+ # Both CPU_ABI1 and CPU_ABI2 may contain multiple comma-delimited abis.
+ # Concatanate CPU_ABI1 and CPU_ABI2.
+ CPU_ABIS = CPU_ABI1.split(',')+CPU_ABI2.split(',')
+ log('Device CPU ABIs: %s' % (' '.join(CPU_ABIS)))
+ COMPAT_ABI = [ABI for ABI in CPU_ABIS if ABI in APP_ABIS]
+
+ if not len(COMPAT_ABI):
+ error('''The device does not support the application's targetted CPU ABIs!
+ Device supports: %s
+ Package supports: %s''' % (' '.join(CPU_ABIS),' '.join(APP_ABIS)))
+ COMPAT_ABI = COMPAT_ABI[0]
+ log('Compatible device ABI: %s' % (COMPAT_ABI))
+ GDBSETUP_INIT = get_build_var_for_abi('NDK_APP_GDBSETUP', COMPAT_ABI)
+ log('Using gdb setup init: %s' % (GDBSETUP_INIT))
+
+ TOOLCHAIN_PREFIX = get_build_var_for_abi('TOOLCHAIN_PREFIX', COMPAT_ABI)
+ log('Using toolchain prefix: %s' % (TOOLCHAIN_PREFIX))
+
+ APP_OUT = get_build_var_for_abi('TARGET_OUT', COMPAT_ABI)
+ log('Using app out directory: %s' % (APP_OUT))
+ DEBUGGABLE = extract_debuggable(PROJECT+os.sep+MANIFEST)
+ log('Found debuggable flag: %s' % ('true' if DEBUGGABLE==True else 'false'))
+ # If gdbserver exists, then we built with 'ndk-build NDK_DEBUG=1' and it's
+ # ok to not have android:debuggable set to true in the original manifest.
+ # However, if this is not the case, then complain!!
+ #
+ gdbserver_path = os.path.join(PROJECT,'libs',COMPAT_ABI,'gdbserver')
+ if not DEBUGGABLE:
+ if os.path.isfile(gdbserver_path):
+ log('Found gdbserver under libs/%s, assuming app was built with NDK_DEBUG=1' % (COMPAT_ABI))
+ else:
+ error('''Package %s is not debuggable ! You can fix that in two ways:
+
+ - Rebuilt with the NDK_DEBUG=1 option when calling 'ndk-build'.
+
+ - Modify your manifest to set android:debuggable attribute to "true",
+ then rebuild normally.
+
+After one of these, re-install to the device!''' % (PACKAGE_NAME))
+ elif not os.path.isfile(gdbserver_path):
+ error('''Could not find gdbserver binary under %s/libs/%s
+ This usually means you modified your AndroidManifest.xml to set
+ the android:debuggable flag to 'true' but did not rebuild the
+ native binaries. Please call 'ndk-build' to do so,
+ *then* re-install to the device!''' % (PROJECT,COMPAT_ABI))
+
+ # Let's check that 'gdbserver' is properly installed on the device too. If this
+ # is not the case, the user didn't install the proper package after rebuilding.
+ #
+ retcode,DEVICE_GDBSERVER = adb_var_shell2(['ls', '/data/data/%s/lib/gdbserver' % (PACKAGE_NAME)])
+ if retcode:
+ error('''Non-debuggable application installed on the target device.
+ Please re-install the debuggable version!''')
+ log('Found device gdbserver: %s' % (DEVICE_GDBSERVER))
+
+ # Find the <dataDir> of the package on the device
+ retcode,DATA_DIR = adb_var_shell2(['run-as', PACKAGE_NAME, '/system/bin/sh', '-c', 'pwd'])
+ if retcode or DATA_DIR == '':
+ error('''Could not extract package's data directory. Are you sure that
+ your installed application is debuggable?''')
+ log("Found data directory: '%s'" % (DATA_DIR))
+
+ # Launch the activity if needed
+ if OPTION_START:
+ if not OPTION_LAUNCH:
+ OPTION_LAUNCH = extract_launchable(PROJECT+os.sep+MANIFEST)
+ if not len(OPTION_LAUNCH):
+ error('''Could not extract name of launchable activity from manifest!
+ Try to use --launch=<name> directly instead as a work-around.''')
+ log('Found first launchable activity: %s' % (OPTION_LAUNCH[0]))
+ if not len(OPTION_LAUNCH):
+ error('''It seems that your Application does not have any launchable activity!
+ Please fix your manifest file and rebuild/re-install your application.''')
+
+ if len(OPTION_LAUNCH):
+ log('Launching activity: %s/%s' % (PACKAGE_NAME,OPTION_LAUNCH[0]))
+ retcode,LAUNCH_OUTPUT=adb_cmd(True,
+ ['shell', 'am', 'start', '-n', '%s/%s' % (PACKAGE_NAME,OPTION_LAUNCH[0])],
+ log_command=True)
+ if retcode:
+ error('''Could not launch specified activity: %s
+ Use --launch-list to dump a list of valid values.''' % (OPTION_LAUNCH[0]))
+
+ # Sleep a bit, it sometimes take one second to start properly
+ # Note that we use the 'sleep' command on the device here.
+ #
+ adb_cmd(True, ['shell', 'sleep', '%f' % (DELAY)], log_command=True)
+
+ # Find the PID of the application being run
+ retcode,PID = get_pid_of(PACKAGE_NAME)
+ log('Found running PID: %d' % (PID))
+ if retcode or PID == 0:
+ if len(OPTION_LAUNCH):
+ error('''Could not extract PID of application on device/emulator.
+ Weird, this probably means one of these:
+
+ - The installed package does not match your current manifest.
+ - The application process was terminated.
+
+ Try using the --verbose option and look at its output for details.''')
+ else:
+ error('''Could not extract PID of application on device/emulator.
+ Are you sure the application is already started?
+ Consider using --start or --launch=<name> if not.''')
+
+ # Check that there is no other instance of gdbserver running
+ retcode,GDBSERVER_PID = get_pid_of('lib/gdbserver')
+ if not retcode and not GDBSERVER_PID == 0:
+ if not OPTION_FORCE:
+ error('Another debug session running, Use --force to kill it.')
+ log('Killing existing debugging session')
+ adb_cmd(False, ['shell', 'kill -9 %s' % (GDBSERVER_PID)])
+
+ # Launch gdbserver now
+ DEBUG_SOCKET = 'debug-socket'
+ adb_cmd(False,
+ ['shell', 'run-as', PACKAGE_NAME, 'lib/gdbserver', '+%s' % (DEBUG_SOCKET), '--attach', str(PID)],
+ log_command=True, adb_trace=True, background=True)
+ log('Launched gdbserver succesfully')
+
+# Make sure gdbserver was launched - debug check.
+# adb_var_shell(['sleep', '0.1'], log_command=False)
+# retcode,GDBSERVER_PID = get_pid_of('lib/gdbserver')
+# if retcode or GDBSERVER_PID == 0:
+# error('Could not launch gdbserver on the device?')
+# log('Launched gdbserver succesfully (PID=%s)' % (GDBSERVER_PID))
+
+ # Setup network redirection
+ log('Setup network redirection')
+ retcode,_ = adb_cmd(False,
+ ['forward', 'tcp:%d' % (DEBUG_PORT), 'localfilesystem:%s/%s' % (DATA_DIR,DEBUG_SOCKET)],
+ log_command=True)
+ if retcode:
+ error('''Could not setup network redirection to gdbserver?
+ Maybe using --port=<port> to use a different TCP port might help?''')
+
+ # Get the app_server binary from the device
+ APP_PROCESS = '%s/app_process' % (APP_OUT)
+ adb_cmd(False, ['pull', '/system/bin/app_process', APP_PROCESS], log_command=True)
+ log('Pulled app_process from device/emulator.')
+
+ adb_cmd(False, ['pull', '/system/bin/linker', '%s/linker' % (APP_OUT)], log_command=True)
+ log('Pulled linker from device/emulator.')
+
+ adb_cmd(False, ['pull', '/system/lib/libc.so', '%s/libc.so' % (APP_OUT)], log_command=True)
+ log('Pulled libc.so from device/emulator.')
+
+ # Now launch the appropriate gdb client with the right init commands
+ #
+ GDBCLIENT = '%sgdb' % (TOOLCHAIN_PREFIX)
+ GDBSETUP = '%s/gdb.setup' % (APP_OUT)
+ shutil.copyfile(GDBSETUP_INIT, GDBSETUP)
+ with open(GDBSETUP, "a") as gdbsetup:
+ #uncomment the following to debug the remote connection only
+ #echo "set debug remote 1" >> $GDBSETUP
+ gdbsetup.write('file '+APP_PROCESS+'\n')
+ gdbsetup.write('target remote :%d\n' % (DEBUG_PORT))
+ if OPTION_EXEC:
+ with open(OPTION_EXEC, 'r') as execfile:
+ for line in execfile.readline():
+ gdbsetup.write(line)
+ gdbsetup.close()
+
+ gdbargs = [GDBCLIENT, '-x', '%s' % (GDBSETUP)]
+ if OPTION_TUI:
+ gdbhelp = subprocess.check_output([GDBCLIENT, '--help']).decode('ascii')
+ try:
+ gdbhelp.index('--tui')
+ gdbargs.append('--tui')
+ OPTION_TUI = 'running'
+ except:
+ print('Warning: Disabled tui mode as %s does not support it' % (os.path.basename(GDBCLIENT)))
+ subprocess.call(gdbargs)
+
+if __name__ == '__main__':
+ main()