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 27 import fnmatch 28 import gdb 29 import os 30 import shutil 31 import subprocess 32 import tempfile 33 import time 34 35 be_verbose = False 36 enable_renderscript_dumps = True 37 local_symbols_library_directory = os.path.join(os.getenv('ANDROID_PRODUCT_OUT', 'out'), 38 'symbols', 'system', 'lib') 39 local_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 50 def _interesting_libs(): 51 return ['libc', 'libbcc', 'libRS', 'libandroid_runtime', 'libart'] 52 53 # In python 2.6, subprocess.check_output does not exist, so it is implemented here 54 def 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 67 class 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 96 class 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 334 class 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 375 class 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 578 class 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 588 class 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 625 class 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 727 class 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 749 def 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 757 currentAppInfo = DebugAppInfo () 758 759 # Global instance of ADB helper 760 adb = ADB(verbose=be_verbose) 761 762 # Perform global initialization 763 initOnce(adb) 764 765 # Command registration 766 StartAndroidApp (adb, "start-android-app", gdb.COMMAND_RUNNING, be_verbose) 767 RunAndroidApp (adb, "run-android-app", gdb.COMMAND_RUNNING, be_verbose) 768 AndroidStatus (adb, "android-status", gdb.COMMAND_OBSCURE, be_verbose) 769 LoadApp (adb, "load-android-app", gdb.COMMAND_RUNNING, be_verbose) 770 SetAndroidDevice (adb, "set-android-device", gdb.COMMAND_RUNNING, be_verbose) 771 AttachAndroidApp (adb, "attach-android-app", gdb.COMMAND_RUNNING, be_verbose) 772