1# 2# Copyright (C) 2012 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15# 16 17# 18# GDB plugin to allow debugging of apps on remote Android systems using gdbserver. 19# 20# To use this plugin, source this file from a Python-enabled GDB client, then use: 21# load-android-app <app-source-dir> to tell GDB about the app you are debugging 22# run-android-app to start the app in a running state 23# start-android-app to start the app in a paused state 24# attach-android-ap to attach to an existing (running) instance of app 25# set-android-device to select a target (only if multiple devices are attached) 26 27import fnmatch 28import gdb 29import os 30import shutil 31import subprocess 32import tempfile 33import time 34 35be_verbose = False 36enable_renderscript_dumps = True 37local_symbols_library_directory = os.path.join(os.getenv('ANDROID_PRODUCT_OUT', 'out'), 38 'symbols', 'system', 'lib') 39local_library_directory = os.path.join(os.getenv('ANDROID_PRODUCT_OUT', 'out'), 40 'system', 'lib') 41 42# ADB - Basic ADB wrapper, far from complete 43# DebugAppInfo - App configuration struct, as far as GDB cares 44# StartAndroidApp - Implementation of GDB start (for android apps) 45# RunAndroidApp - Implementation of GDB run (for android apps) 46# AttachAndroidApp - GDB command to attach to an existing android app process 47# AndroidStatus - app status query command (not needed, mostly harmless) 48# LoadAndroidApp - Sets the package and intent names for an app 49 50def _interesting_libs(): 51 return ['libc', 'libbcc', 'libRS', 'libandroid_runtime', 'libdvm'] 52 53# In python 2.6, subprocess.check_output does not exist, so it is implemented here 54def check_output(*popenargs, **kwargs): 55 p = subprocess.Popen(stdout=subprocess.PIPE, stderr=subprocess.STDOUT, *popenargs, **kwargs) 56 out, err = p.communicate() 57 retcode = p.poll() 58 if retcode != 0: 59 c = kwargs.get("args") 60 if c is None: 61 c = popenargs[0] 62 e = subprocess.CalledProcessError(retcode, c) 63 e.output = str(out) + str(err) 64 raise e 65 return out 66 67class DebugAppInfo: 68 """Stores information from an app manifest""" 69 70 def __init__(self): 71 self.name = None 72 self.intent = None 73 74 def get_name(self): 75 return self.name 76 77 def get_intent(self): 78 return self.intent 79 80 def get_data_directory(self): 81 return self.data_directory 82 83 def get_gdbserver_path(self): 84 return os.path.join(self.data_directory, "lib", "gdbserver") 85 86 def set_info(self, name, intent, data_directory): 87 self.name = name 88 self.intent = intent 89 self.data_directory = data_directory 90 91 def unset_info(): 92 self.name = None 93 self.intent = None 94 self.data_directory = None 95 96class ADB: 97 """ 98 Python class implementing a basic ADB wrapper for useful commands. 99 Uses subprocess to invoke adb. 100 """ 101 102 def __init__(self, device=None, verbose=False): 103 self.verbose = verbose 104 self.current_device = device 105 self.temp_libdir = None 106 self.background_processes = [] 107 self.android_build_top = os.getenv('ANDROID_BUILD_TOP', None) 108 if not self.android_build_top: 109 raise gdb.GdbError("Unable to read ANDROID_BUILD_TOP. " \ 110 + "Is your environment setup correct?") 111 112 self.adb_path = os.path.join(self.android_build_top, 113 'out', 'host', 'linux-x86', 'bin', 'adb') 114 115 if not self.current_device: 116 devices = self.devices() 117 if len(devices) == 1: 118 self.set_current_device(devices[0]) 119 return 120 else: 121 msg = "" 122 if len(devices) == 0: 123 msg = "No devices detected. Please connect a device and " 124 else: 125 msg = "Too many devices (" + ", ".join(devices) + ") detected. " \ 126 + "Please " 127 128 print "Warning: " + msg + " use the set-android-device command." 129 130 131 def _prepare_adb_args(self, args): 132 largs = list(args) 133 134 # Prepare serial number option from current_device 135 if self.current_device and len(self.current_device) > 0: 136 largs.insert(0, self.current_device) 137 largs.insert(0, "-s") 138 139 largs.insert(0, self.adb_path) 140 return largs 141 142 143 def _background_adb(self, *args): 144 largs = self._prepare_adb_args(args) 145 p = None 146 try: 147 if self.verbose: 148 print "### " + str(largs) 149 p = subprocess.Popen(largs) 150 self.background_processes.append(p) 151 except CalledProcessError, e: 152 raise gdb.GdbError("Error starting background adb " + str(largs)) 153 except: 154 raise gdb.GdbError("Unknown error starting background adb " + str(largs)) 155 156 return p 157 158 def _call_adb(self, *args): 159 output = "" 160 largs = self._prepare_adb_args(args) 161 try: 162 if self.verbose: 163 print "### " + str(largs) 164 output = check_output(largs) 165 except subprocess.CalledProcessError, e: 166 raise gdb.GdbError("Error starting adb " + str(largs)) 167 except Exception as e: 168 raise gdb.GdbError("Unknown error starting adb " + str(largs)) 169 170 return output 171 172 def _shell(self, *args): 173 args = ["shell"] + list(args) 174 return self._call_adb(*args) 175 176 def _background_shell(self, *args): 177 args = ["shell"] + list(args) 178 return self._background_adb(*args) 179 180 def _cleanup_background_processes(self): 181 for handle in self.background_processes: 182 try: 183 handle.terminate() 184 except OSError, e: 185 # Background process died already 186 pass 187 188 def _cleanup_temp(self): 189 if self.temp_libdir: 190 shutil.rmtree(self.temp_libdir) 191 self.temp_libdir = None 192 193 def __del__(self): 194 self._cleanup_temp() 195 self._cleanup_background_processes() 196 197 def _get_local_libs(self): 198 ret = [] 199 for lib in _interesting_libs(): 200 lib_path = os.path.join(local_library_directory, lib + ".so") 201 if not os.path.exists(lib_path) and self.verbose: 202 print "Warning: unable to find expected library " \ 203 + lib_path + "." 204 ret.append(lib_path) 205 206 return ret 207 208 def _check_remote_libs_match_local_libs(self): 209 ret = [] 210 all_remote_libs = self._shell("ls", "/system/lib/*.so").split() 211 local_libs = self._get_local_libs() 212 213 self.temp_libdir = tempfile.mkdtemp() 214 215 for lib in _interesting_libs(): 216 lib += ".so" 217 for remote_lib in all_remote_libs: 218 if lib in remote_lib: 219 # Pull lib from device and compute hash 220 tmp_path = os.path.join(self.temp_libdir, lib) 221 self.pull(remote_lib, tmp_path) 222 remote_hash = self._md5sum(tmp_path) 223 224 # Find local lib and compute hash 225 built_library = filter(lambda l: lib in l, local_libs)[0] 226 built_hash = self._md5sum(built_library) 227 228 # Alert user if library mismatch is detected 229 if built_hash != remote_hash: 230 self._cleanup_temp() 231 raise gdb.GdbError("Library mismatch between:\n" \ 232 + "\t(" + remote_hash + ") " + tmp_path + " (from target) and\n " \ 233 + "\t(" + built_hash + ") " + built_library + " (on host)\n" \ 234 + "The target is running a different build than the host." \ 235 + " This situation is not debuggable.") 236 237 self._cleanup_temp() 238 239 def _md5sum(self, file): 240 try: 241 return check_output(["md5sum", file]).strip().split()[0] 242 except subprocess.CalledProcessError, e: 243 raise gdb.GdbError("Error invoking md5sum commandline utility") 244 245 # Returns the list of serial numbers of connected devices 246 def devices(self): 247 ret = [] 248 raw_output = self._call_adb("devices").split() 249 if len(raw_output) < 5: 250 return None 251 else: 252 for serial_num_index in range(4, len(raw_output), 2): 253 ret.append(raw_output[serial_num_index]) 254 return ret 255 256 def set_current_device(self, serial): 257 if self.current_device == str(serial): 258 print "Current device already is: " + str(serial) 259 return 260 261 # TODO: this function should probably check the serial is valid. 262 self.current_device = str(serial) 263 264 api_version = self.getprop("ro.build.version.sdk") 265 if api_version < 15: 266 print "Warning: untested API version. Upgrade to 15 or higher" 267 268 # Verify the local libraries loaded by GDB are identical to those 269 # sitting on the device actually executing. Alert the user if 270 # this is happening 271 self._check_remote_libs_match_local_libs() 272 273 # adb getprop [property] 274 # if property is not None, returns the given property, otherwise 275 # returns all properties. 276 def getprop(self, property=None): 277 if property == None: 278 # get all the props 279 return self._call_adb(*["shell", "getprop"]).split('\n') 280 else: 281 return str(self._call_adb(*["shell", "getprop", 282 str(property)]).split('\n')[0]) 283 284 # adb push 285 def push(self, source, destination): 286 self._call_adb(*["push", source, destination]) 287 288 # adb forward <source> <destination> 289 def forward(self, source, destination): 290 self._call_adb(*["forward", source, destination]) 291 292 # Returns true if filename exists on Android fs, false otherwise 293 def exists(self, filename): 294 raw_listing = self._shell(*["ls", filename]) 295 return "No such file or directory" not in raw_listing 296 297 # adb pull <remote_path> <local_path> 298 def pull(self, remote_path, local_path): 299 self._call_adb(*["pull", remote_path, local_path]) 300 301 #wrapper for adb shell ps. leave process_name=None for list of all processes 302 #Otherwise, returns triple with process name, pid and owner, 303 def get_process_info(self, process_name=None): 304 ret = [] 305 raw_output = self._shell("ps") 306 for raw_line in raw_output.splitlines()[1:]: 307 line = raw_line.split() 308 name = line[-1] 309 310 if process_name == None or name == process_name: 311 user = line[0] 312 pid = line[1] 313 314 if process_name != None: 315 return (pid, user) 316 else: 317 ret.append((pid, user)) 318 319 # No match in target process 320 if process_name != None: 321 return (None, None) 322 323 return ret 324 325 def kill_by_pid(self, pid): 326 self._shell(*["kill", "-9", pid]) 327 328 def kill_by_name(self, process_name): 329 (pid, user) = self.get_process_info(process_name) 330 while pid != None: 331 self.kill_by_pid(pid) 332 (pid, user) = self.get_process_info(process_name) 333 334class AndroidStatus(gdb.Command): 335 """Implements the android-status gdb command.""" 336 337 def __init__(self, adb, name="android-status", cat=gdb.COMMAND_OBSCURE, verbose=False): 338 super (AndroidStatus, self).__init__(name, cat) 339 self.verbose = verbose 340 self.adb = adb 341 342 def _update_status(self, process_name, gdbserver_process_name): 343 self._check_app_is_loaded() 344 345 # Update app status 346 (self.pid, self.owner_user) = \ 347 self.adb.get_process_info(process_name) 348 self.running = self.pid != None 349 350 # Update gdbserver status 351 (self.gdbserver_pid, self.gdbserver_user) = \ 352 self.adb.get_process_info(gdbserver_process_name) 353 self.gdbserver_running = self.gdbserver_pid != None 354 355 # Print results 356 if self.verbose: 357 print "--==Android GDB Plugin Status Update==--" 358 print "\tinferior name: " + process_name 359 print "\trunning: " + str(self.running) 360 print "\tpid: " + str(self.pid) 361 print "\tgdbserver running: " + str(self.gdbserver_running) 362 print "\tgdbserver pid: " + str(self.gdbserver_pid) 363 print "\tgdbserver user: " + str(self.gdbserver_user) 364 365 def _check_app_is_loaded(self): 366 if not currentAppInfo.get_name(): 367 raise gdb.GdbError("Error: no app loaded. Try load-android-app.") 368 369 def invoke(self, arg, from_tty): 370 self._check_app_is_loaded() 371 self._update_status(currentAppInfo.get_name(), 372 currentAppInfo.get_gdbserver_path()) 373 # TODO: maybe print something if verbose is off 374 375class StartAndroidApp (AndroidStatus): 376 """Implements the 'start-android-app' gdb command.""" 377 378 def _update_status(self): 379 AndroidStatus._update_status(self, self.process_name, \ 380 self.gdbserver_path) 381 382 # Calls adb shell ps every retry_delay seconds and returns 383 # the pid when process_name show up in output, or return 0 384 # after num_retries attempts. num_retries=0 means retry 385 # indefinitely. 386 def _wait_for_process(self, process_name, retry_delay=1, num_retries=10): 387 """ This function is a hack and should not be required""" 388 (pid, user) = self.adb.get_process_info(process_name) 389 retries_left = num_retries 390 while pid == None and retries_left != 0: 391 (pid, user) = self.adb.get_process_info(process_name) 392 time.sleep(retry_delay) 393 retries_left -= 1 394 395 return pid 396 397 def _gdbcmd(self, cmd, from_tty=False): 398 if self.verbose: 399 print '### GDB Command: ' + str(cmd) 400 401 gdb.execute(cmd, from_tty) 402 403 # Remove scratch directory if any 404 def _cleanup_temp(self): 405 if self.temp_dir: 406 shutil.rmtree(self.temp_dir) 407 self.temp_dir = None 408 409 def _cleanup_jdb(self): 410 if self.jdb_handle: 411 try: 412 self.jdb_handle.terminate() 413 except OSError, e: 414 # JDB process has likely died 415 pass 416 417 self.jdb_handle = None 418 419 def _load_local_libs(self): 420 for lib in _interesting_libs(): 421 self._gdbcmd("shar " + lib) 422 423 def __del__(self): 424 self._cleanup_temp() 425 self._cleanup_jdb() 426 427 def __init__ (self, adb, name="start-android-app", cat=gdb.COMMAND_RUNNING, verbose=False): 428 super (StartAndroidApp, self).__init__(adb, name, cat, verbose) 429 self.adb = adb 430 431 self.jdb_handle = None 432 # TODO: handle possibility that port 8700 is in use (may help with 433 # Eclipse problems) 434 self.jdwp_port = 8700 435 436 # Port for gdbserver 437 self.gdbserver_port = 5039 438 439 self.temp_dir = None 440 441 def start_process(self, start_running=False): 442 #TODO: implement libbcc cache removal if needed 443 444 args = ["am", "start"] 445 446 # If we are to start running, we can take advantage of am's -W flag to wait 447 # for the process to start before returning. That way, we don't have to 448 # emulate the behaviour (poorly) through the sleep-loop below. 449 if not start_running: 450 args.append("-D") 451 else: 452 args.append("-W") 453 454 args.append(self.process_name + "/" + self.intent) 455 am_output = self.adb._shell(*args) 456 if "Error:" in am_output: 457 raise gdb.GdbError("Cannot start app. Activity Manager returned:\n"\ 458 + am_output) 459 460 # Gotta wait until the process starts if we can't use -W 461 if not start_running: 462 self.pid = self._wait_for_process(self.process_name) 463 464 if not self.pid: 465 raise gdb.GdbError("Unable to detect running app remotely." \ 466 + "Is " + self.process_name + " installed correctly?") 467 468 if self.verbose: 469 print "--==Android App Started: " + self.process_name \ 470 + " (pid=" + self.pid + ")==--" 471 472 # Forward port for java debugger to Dalvik 473 self.adb.forward("tcp:" + str(self.jdwp_port), \ 474 "jdwp:" + str(self.pid)) 475 476 def start_gdbserver(self): 477 # TODO: adjust for architecture... 478 gdbserver_local_path = os.path.join(os.getenv('ANDROID_BUILD_TOP'), 479 'prebuilt', 'android-arm', 'gdbserver', 'gdbserver') 480 481 if not self.adb.exists(self.gdbserver_path): 482 # Install gdbserver 483 try: 484 self.adb.push(gdbserver_local_path, self.gdbserver_path) 485 except gdb.GdbError, e: 486 print "Unable to push gdbserver to device. Try re-installing app." 487 raise e 488 489 self.adb._background_shell(*[self.gdbserver_path, "--attach", 490 ":" + str(self.gdbserver_port), self.pid]) 491 492 self._wait_for_process(self.gdbserver_path) 493 self._update_status() 494 495 if self.verbose: 496 print "--==Remote gdbserver Started " \ 497 + " (pid=" + str(self.gdbserver_pid) \ 498 + " port=" + str(self.gdbserver_port) + ") ==--" 499 500 # Forward port for gdbserver 501 self.adb.forward("tcp:" + str(self.gdbserver_port), \ 502 "tcp:" + str(5039)) 503 504 def attach_gdb(self, from_tty): 505 self._gdbcmd("target remote :" + str(self.gdbserver_port), False) 506 if self.verbose: 507 print "--==GDB Plugin requested attach (port=" \ 508 + str(self.gdbserver_port) + ")==-" 509 510 # If GDB has no file set, things start breaking...so grab the same 511 # binary the NDK grabs from the filesystem and continue 512 self._cleanup_temp() 513 self.temp_dir = tempfile.mkdtemp() 514 self.gdb_inferior = os.path.join(self.temp_dir, 'app_process') 515 self.adb.pull("/system/bin/app_process", self.gdb_inferior) 516 self._gdbcmd('file ' + self.gdb_inferior) 517 518 def start_jdb(self, port): 519 # Kill if running 520 self._cleanup_jdb() 521 522 # Start the java debugger 523 args = ["jdb", "-connect", 524 "com.sun.jdi.SocketAttach:hostname=localhost,port=" + str(port)] 525 if self.verbose: 526 self.jdb_handle = subprocess.Popen(args, \ 527 stdin=subprocess.PIPE) 528 else: 529 # Unix-only bit here.. 530 self.jdb_handle = subprocess.Popen(args, \ 531 stdin=subprocess.PIPE, 532 stderr=subprocess.STDOUT, 533 stdout=open('/dev/null', 'w')) 534 535 def invoke (self, arg, from_tty): 536 # TODO: self._check_app_is_installed() 537 self._check_app_is_loaded() 538 539 self.intent = currentAppInfo.get_intent() 540 self.process_name = currentAppInfo.get_name() 541 self.data_directory = currentAppInfo.get_data_directory() 542 self.gdbserver_path = currentAppInfo.get_gdbserver_path() 543 544 self._update_status() 545 546 if self.gdbserver_running: 547 self.adb.kill_by_name(self.gdbserver_path) 548 if self.verbose: 549 print "--==Killed gdbserver process (pid=" \ 550 + str(self.gdbserver_pid) + ")==--" 551 self._update_status() 552 553 if self.running: 554 self.adb.kill_by_name(self.process_name) 555 if self.verbose: 556 print "--==Killed app process (pid=" + str(self.pid) + ")==--" 557 self._update_status() 558 559 self.start_process() 560 561 # Start remote gdbserver 562 self.start_gdbserver() 563 564 # Attach the gdb 565 self.attach_gdb(from_tty) 566 567 # Load symbolic libraries 568 self._load_local_libs() 569 570 # Set the debug output directory (for JIT debugging) 571 if enable_renderscript_dumps: 572 self._gdbcmd('set gDebugDumpDirectory="' + self.data_directory + '"') 573 574 # Start app 575 # unblock the gdb by connecting with jdb 576 self.start_jdb(self.jdwp_port) 577 578class RunAndroidApp(StartAndroidApp): 579 """Implements the run-android-app gdb command.""" 580 581 def __init__(self, adb, name="run-android-app", cat=gdb.COMMAND_RUNNING, verbose=False): 582 super (RunAndroidApp, self).__init__(adb, name, cat, verbose) 583 584 def invoke(self, arg, from_tty): 585 StartAndroidApp.invoke(self, arg, from_tty) 586 self._gdbcmd("continue") 587 588class AttachAndroidApp(StartAndroidApp): 589 """Implements the attach-android-app gdb command.""" 590 591 def __init__(self, adb, name="attach-android-app", cat=gdb.COMMAND_RUNNING, verbose=False): 592 super (AttachAndroidApp, self).__init__(adb, name, cat, verbose) 593 594 def invoke(self, arg, from_tty): 595 # TODO: self._check_app_is_installed() 596 self._check_app_is_loaded() 597 598 self.intent = currentAppInfo.get_intent() 599 self.process_name = currentAppInfo.get_name() 600 self.data_directory = currentAppInfo.get_data_directory() 601 self.gdbserver_path = currentAppInfo.get_gdbserver_path() 602 603 self._update_status() 604 605 if self.gdbserver_running: 606 self.adb.kill_by_name(self.gdbserver_path) 607 if self.verbose: 608 print "--==Killed gdbserver process (pid=" \ 609 + str(self.gdbserver_pid) + ")==--" 610 self._update_status() 611 612 # Start remote gdbserver 613 self.start_gdbserver() 614 615 # Attach the gdb 616 self.attach_gdb(from_tty) 617 618 # Load symbolic libraries 619 self._load_local_libs() 620 621 # Set the debug output directory (for JIT debugging) 622 if enable_renderscript_dumps: 623 self._gdbcmd('set gDebugDumpDirectory="' + self.data_directory + '"') 624 625class LoadApp(AndroidStatus): 626 """ Implements the load-android-app gbd command. 627 """ 628 def _awk_script_path(self, script_name): 629 if os.path.exists(script_name): 630 return script_name 631 632 script_root = os.path.join(os.getenv('ANDROID_BUILD_TOP'), \ 633 'ndk', 'build', 'awk') 634 635 path_in_root = os.path.join(script_root, script_name) 636 if os.path.exists(path_in_root): 637 return path_in_root 638 639 raise gdb.GdbError("Unable to find awk script " \ 640 + str(script_name) + " in " + path_in_root) 641 642 def _awk(self, script, command): 643 args = ["awk", "-f", self._awk_script_path(script), str(command)] 644 645 if self.verbose: 646 print "### awk command: " + str(args) 647 648 awk_output = "" 649 try: 650 awk_output = check_output(args) 651 except subprocess.CalledProcessError, e: 652 raise gdb.GdbError("### Error in subprocess awk " + str(args)) 653 except: 654 print "### Random error calling awk " + str(args) 655 656 return awk_output.rstrip() 657 658 def __init__(self, adb, name="load-android-app", cat=gdb.COMMAND_RUNNING, verbose=False): 659 super (LoadApp, self).__init__(adb, name, cat, verbose) 660 self.manifest_name = "AndroidManifest.xml" 661 self.verbose = verbose 662 self.adb = adb 663 self.temp_libdir = None 664 665 def _find_manifests(self, path): 666 manifests = [] 667 for root, dirnames, filenames in os.walk(path): 668 for filename in fnmatch.filter(filenames, self.manifest_name): 669 manifests.append(os.path.join(root, filename)) 670 return manifests 671 672 def _usage(self): 673 return "Usage: load-android-app [<path-to-AndroidManifest.xml>" \ 674 + " | <package-name> <intent-name>]" 675 676 def invoke(self, arg, from_tty): 677 678 package_name = '' 679 launchable = '' 680 args = arg.strip('"').split() 681 if len(args) == 2: 682 package_name = args[0] 683 launchable = args[1] 684 elif len(args) == 1: 685 if os.path.isfile(args[0]) and os.path.basename(args[0]) == self.manifest_name: 686 self.manifest_path = args[0] 687 elif os.path.isdir(args[0]): 688 manifests = self._find_manifests(args[0]) 689 if len(manifests) == 0: 690 raise gdb.GdbError(self.manifest_name + " not found in: " \ 691 + args[0] + "\n" + self._usage()) 692 elif len(manifests) > 1: 693 raise gdb.GdbError("Ambiguous argument! Found too many " \ 694 + self.manifest_name + " files found:\n" + "\n".join(manifests)) 695 else: 696 self.manifest_path = manifests[0] 697 else: 698 raise gdb.GdbError("Invalid path: " + args[0] + "\n" + self._usage()) 699 700 package_name = self._awk("extract-package-name.awk", 701 self.manifest_path) 702 launchable = self._awk("extract-launchable.awk", 703 self.manifest_path) 704 else: 705 raise gdb.GdbError(self._usage()) 706 707 708 data_directory = self.adb._shell("run-as", package_name, 709 "/system/bin/sh", "-c", "pwd").rstrip() 710 711 if not data_directory \ 712 or len(data_directory) == 0 \ 713 or not self.adb.exists(data_directory): 714 data_directory = os.path.join('/data', 'data', package_name) 715 print "Warning: unable to read data directory for package " \ 716 + package_name + ". Meh, defaulting to " + data_directory 717 718 currentAppInfo.set_info(package_name, launchable, data_directory) 719 720 if self.verbose: 721 print "--==Android App Loaded==--" 722 print "\tname=" + currentAppInfo.get_name() 723 print "\tintent=" + currentAppInfo.get_intent() 724 725 # TODO: Check status of app on device 726 727class SetAndroidDevice (gdb.Command): 728 def __init__(self, adb, name="set-android-device", cat=gdb.COMMAND_RUNNING, verbose=False): 729 super (SetAndroidDevice, self).__init__(name, cat) 730 self.verbose = verbose 731 self.adb = adb 732 733 def _usage(self): 734 return "Usage: set-android-device <serial>" 735 736 def invoke(self, arg, from_tty): 737 if not arg or len(arg) == 0: 738 raise gdb.GdbError(self._usage) 739 740 serial = str(arg) 741 devices = adb.devices() 742 if serial in devices: 743 adb.set_current_device(serial) 744 else: 745 raise gdb.GdbError("Invalid serial. Serial numbers of connected " \ 746 + "device(s): \n" + "\n".join(devices)) 747 748# Global initialization 749def initOnce(adb): 750 # Try to speed up startup by skipping most android shared objects 751 gdb.execute("set auto-solib-add 0", False); 752 753 # Set shared object search path 754 gdb.execute("set solib-search-path " + local_symbols_library_directory, False) 755 756# Global instance of the object containing the info for current app 757currentAppInfo = DebugAppInfo () 758 759# Global instance of ADB helper 760adb = ADB(verbose=be_verbose) 761 762# Perform global initialization 763initOnce(adb) 764 765# Command registration 766StartAndroidApp (adb, "start-android-app", gdb.COMMAND_RUNNING, be_verbose) 767RunAndroidApp (adb, "run-android-app", gdb.COMMAND_RUNNING, be_verbose) 768AndroidStatus (adb, "android-status", gdb.COMMAND_OBSCURE, be_verbose) 769LoadApp (adb, "load-android-app", gdb.COMMAND_RUNNING, be_verbose) 770SetAndroidDevice (adb, "set-android-device", gdb.COMMAND_RUNNING, be_verbose) 771AttachAndroidApp (adb, "attach-android-app", gdb.COMMAND_RUNNING, be_verbose) 772