1#!/bin/env python 2 3r''' 4 Copyright (C) 2010 The Android Open Source Project 5 Copyright (C) 2012 Ray Donnelly <mingw.android@gmail.com> 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 19 20 This wrapper script is used to launch a native debugging session 21 on a given NDK application. The application must be debuggable, i.e. 22 its android:debuggable attribute must be set to 'true' in the 23 <application> element of its manifest. 24 25 See docs/NDK-GDB.TXT for usage description. Essentially, you just 26 need to launch ndk-gdb-py from your application project directory 27 after doing ndk-build && ant debug && \ 28 adb install && <start-application-on-device> 29''' 30 31import sys, os, argparse, subprocess, types 32import xml.etree.cElementTree as ElementTree 33import shutil 34from threading import Thread 35try: 36 from Queue import Queue, Empty 37except ImportError: 38 from queue import Queue, Empty # python 3.x 39 40def find_program(program, extra_paths = []): 41 ''' extra_paths are searched before PATH ''' 42 PATHS = extra_paths+os.environ['PATH'].split(os.pathsep) 43 exts = [''] 44 if sys.platform.startswith('win'): 45 exts += ['.exe', '.bat', '.cmd'] 46 for path in PATHS: 47 if os.path.isdir(path): 48 for ext in exts: 49 full = path + os.sep + program + ext 50 if os.path.isfile(full): 51 return True, full 52 return False, None 53 54# Return the prebuilt bin path for the host os. 55def ndk_bin_path(ndk): 56 if sys.platform.startswith('linux'): 57 return ndk+os.sep+'prebuilt/linux-x86/bin' 58 elif sys.platform.startswith('darwin'): 59 return ndk+os.sep+'prebuilt/darwin-x86/bin' 60 elif sys.platform.startswith('win'): 61 return ndk+os.sep+'prebuilt/windows/bin' 62 return ndk+os.sep+'prebuilt/UNKNOWN/bin' 63 64VERBOSE = False 65PROJECT = None 66PYTHON_CMD = None 67ADB_CMD = None 68GNUMAKE_CMD = None 69 70OPTION_FORCE = None 71OPTION_EXEC = None 72OPTION_START = None 73OPTION_LAUNCH = None 74OPTION_LAUNCH_LIST = None 75OPTION_TUI = None 76 77 78DEBUG_PORT = 5039 79 80# Name of the manifest file 81MANIFEST = 'AndroidManifest.xml' 82 83# Delay in seconds between launching the activity and attaching gdbserver on it. 84# This is needed because there is no way to know when the activity has really 85# started, and sometimes this takes a few seconds. 86# 87DELAY = 2.0 88NDK = os.path.abspath(os.path.dirname(sys.argv[0])) 89DEVICE_SERIAL = '' 90ADB_FLAGS = '' 91 92def log(string): 93 global VERBOSE 94 if VERBOSE: 95 print(string) 96 97def error(string, errcode=1): 98 print('ERROR: %s' % (string)) 99 exit(errcode) 100 101def handle_args(): 102 global VERBOSE, DEBUG_PORT, DELAY, DEVICE_SERIAL 103 global PYTHON_CMD, GNUMAKE_CMD, ADB_CMD, ADB_FLAGS 104 global PROJECT, NDK 105 global OPTION_START, OPTION_LAUNCH, OPTION_LAUNCH_LIST 106 global OPTION_FORCE, OPTION_EXEC, OPTION_TUI 107 108 parser = argparse.ArgumentParser(description=''' 109 Setup a gdb debugging session for your Android NDK application. 110 Read ''' + NDK + '''/docs/NDK-GDB.html for complete usage instructions.''') 111 112 parser.add_argument( '--verbose', 113 help='Enable verbose mode', action='store_true', dest='verbose') 114 115 parser.add_argument( '--force', 116 help='Kill existing debug session if it exists', 117 action='store_true') 118 119 parser.add_argument( '--start', 120 help='Launch application instead of attaching to existing one', 121 action='store_true') 122 123 parser.add_argument( '--launch', 124 help='Same as --start, but specify activity name (see below)', 125 dest='launch_name', nargs=1) 126 127 parser.add_argument( '--launch-list', 128 help='List all launchable activity names from manifest', 129 action='store_true') 130 131 parser.add_argument( '--delay', 132 help='Delay in seconds between activity start and gdbserver attach', 133 type=float, default=DELAY, 134 dest='delay') 135 136 parser.add_argument( '-p', '--project', 137 help='Specify application project path', 138 dest='project') 139 140 parser.add_argument( '--port', 141 help='Use tcp:localhost:<DEBUG_PORT> to communicate with gdbserver', 142 type=int, default=DEBUG_PORT, 143 dest='debug_port') 144 145 parser.add_argument( '-x', '--exec', 146 help='Execute gdb initialization commands in <EXEC_FILE> after connection', 147 dest='exec_file') 148 149 parser.add_argument( '--adb', 150 help='Use specific adb command', 151 dest='adb_cmd') 152 153 parser.add_argument( '--awk', 154 help='Use specific awk command (unused flag retained for compatability)') 155 156 parser.add_argument( '-e', 157 help='Connect to single emulator instance....(either this,)', 158 action='store_true', dest='emulator') 159 160 parser.add_argument( '-d', 161 help='Connect to single target device........(this,)', 162 action='store_true', dest='device') 163 164 parser.add_argument( '-s', 165 help='Connect to specific emulator or device.(or this)', 166 default=DEVICE_SERIAL, 167 dest='device_serial') 168 169 parser.add_argument( '-t','--tui', 170 help='Use tui mode', 171 action='store_true', dest='tui') 172 173 args = parser.parse_args() 174 175 VERBOSE = args.verbose 176 177 ndk_bin = ndk_bin_path(NDK) 178 (found_python, PYTHON_CMD) = find_program('python', [ndk_bin]) 179 (found_adb, ADB_CMD) = find_program('adb', [ndk_bin]) 180 (found_gnumake, GNUMAKE_CMD) = find_program('make', [ndk_bin]) 181 182 if not found_gnumake: 183 error('Failed to find GNU make') 184 185 log('Android NDK installation path: %s' % (NDK)) 186 187 if args.device: 188 ADB_FLAGS = '-d' 189 if args.emulator: 190 if ADB_FLAGS != '': 191 parser.print_help() 192 exit(1) 193 ADB_FLAGS = '-e' 194 if args.device_serial != '': 195 DEVICE_SERIAL = args.device_serial 196 if ADB_FLAGS != '': 197 parser.print_help() 198 exit(1) 199 ADB_FLAGS = '-s' 200 if args.adb_cmd != None: 201 log('Using specific adb command: %s' % (args.adb_cmd)) 202 ADB_CMD = args.adb_cmd 203 if ADB_CMD is None: 204 error('''The 'adb' tool is not in your path. 205 You can change your PATH variable, or use 206 --adb=<executable> to point to a valid one.''') 207 if not os.path.isfile(ADB_CMD): 208 error('Could not run ADB with: %s' % (ADB_CMD)) 209 210 if args.project != None: 211 PROJECT = args.project 212 213 if args.start != None: 214 OPTION_START = args.start 215 216 if args.launch_name != None: 217 OPTION_LAUNCH = args.launch_name 218 219 if args.launch_list != None: 220 OPTION_LAUNCH_LIST = args.launch_list 221 222 if args.force != None: 223 OPTION_FORCE = args.force 224 225 if args.exec_file != None: 226 OPTION_EXEC = args.exec_file 227 228 if args.tui != False: 229 OPTION_TUI = True 230 231 if args.delay != None: 232 DELAY = args.delay 233 234def get_build_var(var): 235 global GNUMAKE_CMD, NDK, PROJECT 236 text = subprocess.check_output([GNUMAKE_CMD, 237 '--no-print-dir', 238 '-f', 239 NDK+'/build/core/build-local.mk', 240 '-C', 241 PROJECT, 242 'DUMP_'+var] 243 ) 244 # replace('\r', '') due to Windows crlf (\r\n) 245 # ...universal_newlines=True causes bytes to be returned 246 # rather than a str 247 return text.decode('ascii').replace('\r', '').splitlines()[0] 248 249def get_build_var_for_abi(var, abi): 250 global GNUMAKE_CMD, NDK, PROJECT 251 text = subprocess.check_output([GNUMAKE_CMD, 252 '--no-print-dir', 253 '-f', 254 NDK+'/build/core/build-local.mk', 255 '-C', 256 PROJECT, 257 'DUMP_'+var, 258 'APP_ABI='+abi], 259 ) 260 return text.decode('ascii').replace('\r', '').splitlines()[0] 261 262# Silent if gdb is running in tui mode to keep things tidy. 263def output_gdbserver(text): 264 if not OPTION_TUI or OPTION_TUI != 'running': 265 print(text) 266 267def background_spawn(args, redirect_stderr, output_fn): 268 269 def async_stdout(out, queue, output_fn): 270 for line in iter(out.readline, b''): 271 output_fn(line.replace('\r', '').replace('\n', '')) 272 out.close() 273 274 def async_stderr(out, queue, output_fn): 275 for line in iter(out.readline, b''): 276 output_fn(line.replace('\r', '').replace('\n', '')) 277 out.close() 278 279 if redirect_stderr: 280 used_stderr = subprocess.PIPE 281 else: 282 used_stderr = subprocess.STDOUT 283 p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=used_stderr, 284 bufsize=1, close_fds='posix' in sys.builtin_module_names) 285 qo = Queue() 286 to = Thread(target=async_stdout, args=(p.stdout, qo, output_fn)) 287 to.daemon = True 288 to.start() 289 if redirect_stderr: 290 te = Thread(target=async_stderr, args=(p.stderr, qo, output_fn)) 291 te.daemon = True 292 te.start() 293 294def adb_cmd(redirect_stderr, args, log_command=False, adb_trace=False, background=False): 295 global ADB_CMD, ADB_FLAGS, DEVICE_SERIAL 296 fullargs = [ADB_CMD] 297 if ADB_FLAGS != '': 298 fullargs += [ADB_FLAGS] 299 if DEVICE_SERIAL != '': 300 fullargs += [DEVICE_SERIAL] 301 if isinstance(args, str): 302 fullargs.append(args) 303 else: 304 fullargs += [arg for arg in args] 305 new_env = os.environ.copy() 306 retval = 0 307 if adb_trace: 308 new_env["ADB_TRACE"] = "1" 309 if background: 310 if log_command: 311 log('## COMMAND: adb_cmd %s [BACKGROUND]' % (' '.join(args))) 312 background_spawn(fullargs, redirect_stderr, output_gdbserver) 313 return 0, '' 314 else: 315 if log_command: 316 log('## COMMAND: adb_cmd %s' % (' '.join(args))) 317 try: 318 if redirect_stderr: 319 text = subprocess.check_output(fullargs, 320 stderr=subprocess.STDOUT, 321 env=new_env 322 ) 323 else: 324 text = subprocess.check_output(fullargs, 325 env=new_env 326 ) 327 except subprocess.CalledProcessError as e: 328 retval = e.returncode 329 text = e.output 330 # rstrip() because of final newline. 331 return retval, text.decode('ascii').replace('\r', '').rstrip() 332 333def _adb_var_shell(args, redirect_stderr=False, log_command=True): 334 if log_command: 335 log('## COMMAND: adb_cmd shell %s' % (' '.join(args))) 336 arg_str = str(' '.join(args)+' ; echo $?') 337 adb_ret,output = adb_cmd(redirect_stderr=redirect_stderr, 338 args=['shell', arg_str], log_command=False) 339 output = output.splitlines() 340 retcode = int(output.pop()) 341 return retcode,'\n'.join(output) 342 343def adb_var_shell(args, log_command=False): 344 return _adb_var_shell(args, redirect_stderr=False, log_command=log_command) 345 346def adb_var_shell2(args, log_command=False): 347 return _adb_var_shell(args, redirect_stderr=True, log_command=log_command) 348 349# Return the PID of a given package or program, or 0 if it doesn't run 350# $1: Package name ("com.example.hellojni") or program name ("/lib/gdbserver") 351# Out: PID number, or 0 if not running 352# 353def get_pid_of(package_name): 354 ''' 355 Some custom ROMs use busybox instead of toolbox for ps. 356 Without -w, busybox truncates the output, and very long 357 package names like com.exampleisverylongtoolongbyfar.plasma 358 exceed the limit. 359 ''' 360 ps_command = 'ps' 361 retcode,output = adb_cmd(False, ['shell', 'readlink $(which ps)']) 362 if output: 363 output = output.replace('\r', '').splitlines()[0] 364 if output == 'busybox': 365 ps_command = 'ps -w' 366 retcode,output = adb_cmd(False,['shell', ps_command]) 367 output = output.replace('\r', '').splitlines() 368 columns = output.pop(0).split() 369 try: 370 PID_column = columns.index('PID') 371 except: 372 PID_column = 1 373 while output: 374 columns = output.pop().split() 375 if columns.pop() == package_name: 376 return 0,int(columns[PID_column]) 377 return 1,0 378 379def extract_package_name(xmlfile): 380 ''' 381 The name itself is the value of the 'package' attribute in the 382 'manifest' element. 383 ''' 384 tree = ElementTree.ElementTree(file=xmlfile) 385 root = tree.getroot() 386 if 'package' in root.attrib: 387 return root.attrib['package'] 388 return None 389 390def extract_debuggable(xmlfile): 391 ''' 392 simply extract the 'android:debuggable' attribute value from 393 the first <manifest><application> element we find. 394 ''' 395 tree = ElementTree.ElementTree(file=xmlfile) 396 root = tree.getroot() 397 for application in root.iter('application'): 398 for k in application.attrib.keys(): 399 if str(k).endswith('debuggable'): 400 return application.attrib[k] == 'true' 401 return False 402 403def extract_launchable(xmlfile): 404 ''' 405 A given application can have several activities, and each activity 406 can have several intent filters. We want to only list, in the final 407 output, the activities which have a intent-filter that contains the 408 following elements: 409 410 <action android:name="android.intent.action.MAIN" /> 411 <category android:name="android.intent.category.LAUNCHER" /> 412 ''' 413 tree = ElementTree.ElementTree(file=xmlfile) 414 root = tree.getroot() 415 launchable_activities = [] 416 for application in root.iter('application'): 417 for activity in application.iter('activity'): 418 for intent_filter in activity.iter('intent-filter'): 419 found_action_MAIN = False 420 found_category_LAUNCHER = False 421 for child in intent_filter: 422 if child.tag == 'action': 423 if True in [str(child.attrib[k]).endswith('MAIN') for k in child.attrib.keys()]: 424 found_action_MAIN = True 425 if child.tag == 'category': 426 if True in [str(child.attrib[k]).endswith('LAUNCHER') for k in child.attrib.keys()]: 427 found_category_LAUNCHER = True 428 if found_action_MAIN and found_category_LAUNCHER: 429 names = [str(activity.attrib[k]) for k in activity.attrib.keys() if str(k).endswith('name')] 430 for name in names: 431 if name[0] != '.': 432 name = '.'+name 433 launchable_activities.append(name) 434 return launchable_activities 435 436def main(): 437 global ADB_CMD, NDK, PROJECT 438 global OPTION_START, OPTION_LAUNCH, OPTION_LAUNCH_LIST 439 global OPTION_FORCE, OPTION_EXEC, OPTION_TUI 440 441 if NDK.find(' ')!=-1: 442 error('NDK path cannot contain space') 443 handle_args() 444 if OPTION_EXEC: 445 if not os.path.isfile(OPTION_EXEC): 446 error('Invalid initialization file: %s' % (OPTION_EXEC)) 447 ADB_VERSION = subprocess.check_output([ADB_CMD, 'version'], 448 ).decode('ascii').replace('\r', '').splitlines()[0] 449 log('ADB version found: %s' % (ADB_VERSION)) 450 if DEVICE_SERIAL == '': 451 log('Using ADB flags: %s' % (ADB_FLAGS)) 452 else: 453 log('Using ADB flags: %s "%s"' % (ADB_FLAGS,DEVICE_SERIAL)) 454 455 if PROJECT != None: 456 log('Using specified project path: %s' % (PROJECT)) 457 if not os.path.isdir(PROJECT): 458 error('Your --project option does not point to a directory!') 459 if not os.path.isfile(PROJECT+os.sep+MANIFEST): 460 error('''Your --project does not point to an Android project path! 461 It is missing a %s file.''' % (MANIFEST)) 462 else: 463 # Assume we are in the project directory 464 if os.path.isfile(MANIFEST): 465 PROJECT = '.' 466 else: 467 PROJECT = '' 468 CURDIR = os.getcwd() 469 470 while CURDIR != os.path.dirname(CURDIR): 471 if os.path.isfile(CURDIR+os.sep+MANIFEST): 472 PROJECT=CURDIR 473 break 474 CURDIR = os.path.dirname(CURDIR) 475 476 if not os.path.isdir(PROJECT): 477 error('Launch this script from an application project directory, or use --project=<path>.') 478 log('Using auto-detected project path: %s' % (PROJECT)) 479 480 PACKAGE_NAME = extract_package_name(PROJECT+os.sep+MANIFEST) 481 if PACKAGE_NAME is None: 482 PACKAGE_NAME = '<none>' 483 log('Found package name: %s' % (PACKAGE_NAME)) 484 if PACKAGE_NAME is '<none>': 485 error('''Could not extract package name from %s. 486 Please check that the file is well-formed!''' % (PROJECT+os.sep+MANIFEST)) 487 if OPTION_LAUNCH_LIST: 488 log('Extracting list of launchable activities from manifest:') 489 print(' '.join(extract_launchable(PROJECT+os.sep+MANIFEST))) 490 exit(0) 491 APP_ABIS = get_build_var('APP_ABI').split(' ') 492 if 'all' in APP_ABIS: 493 ALL_ABIS = get_build_var('NDK_ALL_ABIS').split(' ') 494 APP_ABIS = APP_ABIS[:APP_ABIS.index('all')]+ALL_ABIS+APP_ABIS[APP_ABIS.index('all')+1:] 495 log('ABIs targetted by application: %s' % (' '.join(APP_ABIS))) 496 497 retcode,ADB_TEST = adb_cmd(True,['shell', 'ls']) 498 if retcode != 0: 499 print(ADB_TEST) 500 error('''Could not connect to device or emulator! 501 Please check that an emulator is running or a device is connected 502 through USB to this machine. You can use -e, -d and -s <serial> 503 in case of multiple ones.''') 504 505 retcode,API_LEVEL = adb_var_shell(['getprop', 'ro.build.version.sdk']) 506 if retcode != 0 or API_LEVEL == '': 507 error('''Could not find target device's supported API level! 508ndk-gdb will only work if your device is running Android 2.2 or higher.''') 509 API_LEVEL = int(API_LEVEL) 510 log('Device API Level: %d' % (API_LEVEL)) 511 if API_LEVEL < 8: 512 error('''ndk-gdb requires a target device running Android 2.2 (API level 8) or higher. 513The target device is running API level %d!''' % (API_LEVEL)) 514 COMPAT_ABI = [] 515 _,CPU_ABI1 = adb_var_shell(['getprop', 'ro.product.cpu.abi']) 516 _,CPU_ABI2 = adb_var_shell(['getprop', 'ro.product.cpu.abi2']) 517 # Both CPU_ABI1 and CPU_ABI2 may contain multiple comma-delimited abis. 518 # Concatanate CPU_ABI1 and CPU_ABI2. 519 CPU_ABIS = CPU_ABI1.split(',')+CPU_ABI2.split(',') 520 log('Device CPU ABIs: %s' % (' '.join(CPU_ABIS))) 521 COMPAT_ABI = [ABI for ABI in CPU_ABIS if ABI in APP_ABIS] 522 523 if not len(COMPAT_ABI): 524 error('''The device does not support the application's targetted CPU ABIs! 525 Device supports: %s 526 Package supports: %s''' % (' '.join(CPU_ABIS),' '.join(APP_ABIS))) 527 COMPAT_ABI = COMPAT_ABI[0] 528 log('Compatible device ABI: %s' % (COMPAT_ABI)) 529 GDBSETUP_INIT = get_build_var_for_abi('NDK_APP_GDBSETUP', COMPAT_ABI) 530 log('Using gdb setup init: %s' % (GDBSETUP_INIT)) 531 532 TOOLCHAIN_PREFIX = get_build_var_for_abi('TOOLCHAIN_PREFIX', COMPAT_ABI) 533 log('Using toolchain prefix: %s' % (TOOLCHAIN_PREFIX)) 534 535 APP_OUT = get_build_var_for_abi('TARGET_OUT', COMPAT_ABI) 536 log('Using app out directory: %s' % (APP_OUT)) 537 DEBUGGABLE = extract_debuggable(PROJECT+os.sep+MANIFEST) 538 log('Found debuggable flag: %s' % ('true' if DEBUGGABLE==True else 'false')) 539 # If gdbserver exists, then we built with 'ndk-build NDK_DEBUG=1' and it's 540 # ok to not have android:debuggable set to true in the original manifest. 541 # However, if this is not the case, then complain!! 542 # 543 gdbserver_path = os.path.join(PROJECT,'libs',COMPAT_ABI,'gdbserver') 544 if not DEBUGGABLE: 545 if os.path.isfile(gdbserver_path): 546 log('Found gdbserver under libs/%s, assuming app was built with NDK_DEBUG=1' % (COMPAT_ABI)) 547 else: 548 error('''Package %s is not debuggable ! You can fix that in two ways: 549 550 - Rebuilt with the NDK_DEBUG=1 option when calling 'ndk-build'. 551 552 - Modify your manifest to set android:debuggable attribute to "true", 553 then rebuild normally. 554 555After one of these, re-install to the device!''' % (PACKAGE_NAME)) 556 elif not os.path.isfile(gdbserver_path): 557 error('''Could not find gdbserver binary under %s/libs/%s 558 This usually means you modified your AndroidManifest.xml to set 559 the android:debuggable flag to 'true' but did not rebuild the 560 native binaries. Please call 'ndk-build' to do so, 561 *then* re-install to the device!''' % (PROJECT,COMPAT_ABI)) 562 563 # Let's check that 'gdbserver' is properly installed on the device too. If this 564 # is not the case, the user didn't install the proper package after rebuilding. 565 # 566 retcode,DEVICE_GDBSERVER = adb_var_shell2(['ls', '/data/data/%s/lib/gdbserver' % (PACKAGE_NAME)]) 567 if retcode: 568 error('''Non-debuggable application installed on the target device. 569 Please re-install the debuggable version!''') 570 log('Found device gdbserver: %s' % (DEVICE_GDBSERVER)) 571 572 # Find the <dataDir> of the package on the device 573 retcode,DATA_DIR = adb_var_shell2(['run-as', PACKAGE_NAME, '/system/bin/sh', '-c', 'pwd']) 574 if retcode or DATA_DIR == '': 575 error('''Could not extract package's data directory. Are you sure that 576 your installed application is debuggable?''') 577 log("Found data directory: '%s'" % (DATA_DIR)) 578 579 # Launch the activity if needed 580 if OPTION_START: 581 if not OPTION_LAUNCH: 582 OPTION_LAUNCH = extract_launchable(PROJECT+os.sep+MANIFEST) 583 if not len(OPTION_LAUNCH): 584 error('''Could not extract name of launchable activity from manifest! 585 Try to use --launch=<name> directly instead as a work-around.''') 586 log('Found first launchable activity: %s' % (OPTION_LAUNCH[0])) 587 if not len(OPTION_LAUNCH): 588 error('''It seems that your Application does not have any launchable activity! 589 Please fix your manifest file and rebuild/re-install your application.''') 590 591 if len(OPTION_LAUNCH): 592 log('Launching activity: %s/%s' % (PACKAGE_NAME,OPTION_LAUNCH[0])) 593 retcode,LAUNCH_OUTPUT=adb_cmd(True, 594 ['shell', 'am', 'start', '-n', '%s/%s' % (PACKAGE_NAME,OPTION_LAUNCH[0])], 595 log_command=True) 596 if retcode: 597 error('''Could not launch specified activity: %s 598 Use --launch-list to dump a list of valid values.''' % (OPTION_LAUNCH[0])) 599 600 # Sleep a bit, it sometimes take one second to start properly 601 # Note that we use the 'sleep' command on the device here. 602 # 603 adb_cmd(True, ['shell', 'sleep', '%f' % (DELAY)], log_command=True) 604 605 # Find the PID of the application being run 606 retcode,PID = get_pid_of(PACKAGE_NAME) 607 log('Found running PID: %d' % (PID)) 608 if retcode or PID == 0: 609 if len(OPTION_LAUNCH): 610 error('''Could not extract PID of application on device/emulator. 611 Weird, this probably means one of these: 612 613 - The installed package does not match your current manifest. 614 - The application process was terminated. 615 616 Try using the --verbose option and look at its output for details.''') 617 else: 618 error('''Could not extract PID of application on device/emulator. 619 Are you sure the application is already started? 620 Consider using --start or --launch=<name> if not.''') 621 622 # Check that there is no other instance of gdbserver running 623 retcode,GDBSERVER_PID = get_pid_of('lib/gdbserver') 624 if not retcode and not GDBSERVER_PID == 0: 625 if not OPTION_FORCE: 626 error('Another debug session running, Use --force to kill it.') 627 log('Killing existing debugging session') 628 adb_cmd(False, ['shell', 'kill -9 %s' % (GDBSERVER_PID)]) 629 630 # Launch gdbserver now 631 DEBUG_SOCKET = 'debug-socket' 632 adb_cmd(False, 633 ['shell', 'run-as', PACKAGE_NAME, 'lib/gdbserver', '+%s' % (DEBUG_SOCKET), '--attach', str(PID)], 634 log_command=True, adb_trace=True, background=True) 635 log('Launched gdbserver succesfully') 636 637# Make sure gdbserver was launched - debug check. 638# adb_var_shell(['sleep', '0.1'], log_command=False) 639# retcode,GDBSERVER_PID = get_pid_of('lib/gdbserver') 640# if retcode or GDBSERVER_PID == 0: 641# error('Could not launch gdbserver on the device?') 642# log('Launched gdbserver succesfully (PID=%s)' % (GDBSERVER_PID)) 643 644 # Setup network redirection 645 log('Setup network redirection') 646 retcode,_ = adb_cmd(False, 647 ['forward', 'tcp:%d' % (DEBUG_PORT), 'localfilesystem:%s/%s' % (DATA_DIR,DEBUG_SOCKET)], 648 log_command=True) 649 if retcode: 650 error('''Could not setup network redirection to gdbserver? 651 Maybe using --port=<port> to use a different TCP port might help?''') 652 653 # Get the app_server binary from the device 654 APP_PROCESS = '%s/app_process' % (APP_OUT) 655 adb_cmd(False, ['pull', '/system/bin/app_process', APP_PROCESS], log_command=True) 656 log('Pulled app_process from device/emulator.') 657 658 adb_cmd(False, ['pull', '/system/bin/linker', '%s/linker' % (APP_OUT)], log_command=True) 659 log('Pulled linker from device/emulator.') 660 661 adb_cmd(False, ['pull', '/system/lib/libc.so', '%s/libc.so' % (APP_OUT)], log_command=True) 662 log('Pulled libc.so from device/emulator.') 663 664 # Now launch the appropriate gdb client with the right init commands 665 # 666 GDBCLIENT = '%sgdb' % (TOOLCHAIN_PREFIX) 667 GDBSETUP = '%s/gdb.setup' % (APP_OUT) 668 shutil.copyfile(GDBSETUP_INIT, GDBSETUP) 669 with open(GDBSETUP, "a") as gdbsetup: 670 #uncomment the following to debug the remote connection only 671 #echo "set debug remote 1" >> $GDBSETUP 672 gdbsetup.write('file '+APP_PROCESS+'\n') 673 gdbsetup.write('target remote :%d\n' % (DEBUG_PORT)) 674 if OPTION_EXEC: 675 with open(OPTION_EXEC, 'r') as execfile: 676 for line in execfile.readline(): 677 gdbsetup.write(line) 678 gdbsetup.close() 679 680 gdbargs = [GDBCLIENT, '-x', '%s' % (GDBSETUP)] 681 if OPTION_TUI: 682 gdbhelp = subprocess.check_output([GDBCLIENT, '--help']).decode('ascii') 683 try: 684 gdbhelp.index('--tui') 685 gdbargs.append('--tui') 686 OPTION_TUI = 'running' 687 except: 688 print('Warning: Disabled tui mode as %s does not support it' % (os.path.basename(GDBCLIENT))) 689 subprocess.call(gdbargs) 690 691if __name__ == '__main__': 692 main() 693