blob: 92b771c5b9d244efbbdaa10869c90c450b1dea8c [file] [log] [blame]
Daniel Malea094881f2011-12-14 17:39:16 -05001#
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():
Brian Carlstromcb2d2e72014-06-19 13:55:16 -070051 return ['libc', 'libbcc', 'libRS', 'libandroid_runtime', 'libart']
Daniel Malea094881f2011-12-14 17:39:16 -050052
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'),
Stephen Hinesa663b772012-04-02 17:12:01 -0700479 'prebuilt', 'android-arm', 'gdbserver', 'gdbserver')
Daniel Malea094881f2011-12-14 17:39:16 -0500480
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)