1094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea#
2094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# Copyright (C) 2012 The Android Open Source Project
3094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea#
4094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# Licensed under the Apache License, Version 2.0 (the "License");
5094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# you may not use this file except in compliance with the License.
6094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# You may obtain a copy of the License at
7094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea#
8094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea#      http://www.apache.org/licenses/LICENSE-2.0
9094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea#
10094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# Unless required by applicable law or agreed to in writing, software
11094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# distributed under the License is distributed on an "AS IS" BASIS,
12094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# See the License for the specific language governing permissions and
14094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# limitations under the License.
15094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea#
16094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
17094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea#
18094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# GDB plugin to allow debugging of apps on remote Android systems using gdbserver.
19094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea#
20094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# To use this plugin, source this file from a Python-enabled GDB client, then use:
21094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea#   load-android-app <app-source-dir>  to tell GDB about the app you are debugging
22094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea#   run-android-app to start the app in a running state
23094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea#   start-android-app to start the app in a paused state
24094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea#   attach-android-ap to attach to an existing (running) instance of app
25094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea#   set-android-device to select a target (only if multiple devices are attached)
26094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
27094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaimport fnmatch
28094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaimport gdb
29094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaimport os
30094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaimport shutil
31094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaimport subprocess
32094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaimport tempfile
33094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaimport time
34094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
35094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleabe_verbose = False
36094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaenable_renderscript_dumps = True
37094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malealocal_symbols_library_directory = os.path.join(os.getenv('ANDROID_PRODUCT_OUT', 'out'),
38094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      'symbols', 'system', 'lib')
39094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malealocal_library_directory = os.path.join(os.getenv('ANDROID_PRODUCT_OUT', 'out'),
40094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      'system', 'lib')
41094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
42094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# ADB              - Basic ADB wrapper, far from complete
43094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# DebugAppInfo     - App configuration struct, as far as GDB cares
44094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# StartAndroidApp  - Implementation of GDB start (for android apps)
45094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# RunAndroidApp    - Implementation of GDB run (for android apps)
46094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# AttachAndroidApp - GDB command to attach to an existing android app process
47094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# AndroidStatus    - app status query command (not needed, mostly harmless)
48094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# LoadAndroidApp   - Sets the package and intent names for an app
49094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
50094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleadef _interesting_libs():
51094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  return ['libc', 'libbcc', 'libRS', 'libandroid_runtime', 'libdvm']
52094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
53094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# In python 2.6, subprocess.check_output does not exist, so it is implemented here
54094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleadef check_output(*popenargs, **kwargs):
55094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  p = subprocess.Popen(stdout=subprocess.PIPE, stderr=subprocess.STDOUT, *popenargs, **kwargs)
56094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  out, err = p.communicate()
57094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  retcode = p.poll()
58094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  if retcode != 0:
59094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    c = kwargs.get("args")
60094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if c is None:
61094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      c = popenargs[0]
62094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    e = subprocess.CalledProcessError(retcode, c)
63094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    e.output = str(out) + str(err)
64094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    raise e
65094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  return out
66094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
67094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaclass DebugAppInfo:
68094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  """Stores information from an app manifest"""
69094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
70094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def __init__(self):
71094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.name = None
72094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.intent = None
73094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
74094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def get_name(self):
75094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    return self.name
76094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
77094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def get_intent(self):
78094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    return self.intent
79094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
80094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def get_data_directory(self):
81094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    return self.data_directory
82094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
83094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def get_gdbserver_path(self):
84094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    return os.path.join(self.data_directory, "lib", "gdbserver")
85094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
86094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def set_info(self, name, intent, data_directory):
87094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.name = name
88094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.intent = intent
89094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.data_directory = data_directory
90094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
91094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def unset_info():
92094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.name = None
93094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.intent = None
94094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.data_directory = None
95094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
96094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaclass ADB:
97094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  """
98094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  Python class implementing a basic ADB wrapper for useful commands.
99094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  Uses subprocess to invoke adb.
100094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  """
101094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
102094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def __init__(self, device=None, verbose=False):
103094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.verbose = verbose
104094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.current_device = device
105094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.temp_libdir = None
106094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.background_processes = []
107094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.android_build_top = os.getenv('ANDROID_BUILD_TOP', None)
108094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if not self.android_build_top:
109094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      raise gdb.GdbError("Unable to read ANDROID_BUILD_TOP. " \
110094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        + "Is your environment setup correct?")
111094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
112094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.adb_path = os.path.join(self.android_build_top,
113094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea                      'out', 'host', 'linux-x86', 'bin', 'adb')
114094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
115094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if not self.current_device:
116094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      devices = self.devices()
117094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      if len(devices) == 1:
118094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        self.set_current_device(devices[0])
119094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        return
120094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      else:
121094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        msg = ""
122094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        if len(devices) == 0:
123094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea          msg = "No devices detected. Please connect a device and "
124094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        else:
125094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea          msg = "Too many devices (" + ", ".join(devices) + ") detected. " \
126094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea              + "Please "
127094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
128094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        print "Warning: " + msg + " use the set-android-device command."
129094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
130094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
131094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _prepare_adb_args(self, args):
132094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    largs = list(args)
133094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
134094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Prepare serial number option from current_device
135094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if self.current_device and len(self.current_device) > 0:
136094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      largs.insert(0, self.current_device)
137094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      largs.insert(0, "-s")
138094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
139094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    largs.insert(0, self.adb_path)
140094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    return largs
141094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
142094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
143094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _background_adb(self, *args):
144094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    largs = self._prepare_adb_args(args)
145094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    p = None
146094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    try:
147094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      if self.verbose:
148094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        print "### " + str(largs)
149094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      p = subprocess.Popen(largs)
150094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self.background_processes.append(p)
151094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    except CalledProcessError, e:
152094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      raise gdb.GdbError("Error starting background adb " + str(largs))
153094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    except:
154094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      raise gdb.GdbError("Unknown error starting background adb " + str(largs))
155094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
156094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    return p
157094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
158094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _call_adb(self, *args):
159094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    output = ""
160094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    largs = self._prepare_adb_args(args)
161094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    try:
162094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      if self.verbose:
163094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        print "### " + str(largs)
164094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      output = check_output(largs)
165094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    except subprocess.CalledProcessError, e:
166094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      raise gdb.GdbError("Error starting adb " + str(largs))
167094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    except Exception as e:
168094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      raise gdb.GdbError("Unknown error starting adb " + str(largs))
169094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
170094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    return output
171094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
172094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _shell(self, *args):
173094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    args = ["shell"] + list(args)
174094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    return self._call_adb(*args)
175094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
176094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _background_shell(self, *args):
177094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    args = ["shell"] + list(args)
178094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    return self._background_adb(*args)
179094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
180094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _cleanup_background_processes(self):
181094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    for handle in self.background_processes:
182094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      try:
183094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        handle.terminate()
184094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      except OSError, e:
185094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        # Background process died already
186094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        pass
187094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
188094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _cleanup_temp(self):
189094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if self.temp_libdir:
190094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      shutil.rmtree(self.temp_libdir)
191094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self.temp_libdir = None
192094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
193094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def __del__(self):
194094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._cleanup_temp()
195094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._cleanup_background_processes()
196094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
197094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _get_local_libs(self):
198094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    ret = []
199094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    for lib in _interesting_libs():
200094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      lib_path = os.path.join(local_library_directory, lib + ".so")
201094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      if not os.path.exists(lib_path) and self.verbose:
202094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        print "Warning: unable to find expected library " \
203094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea          + lib_path + "."
204094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      ret.append(lib_path)
205094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
206094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    return ret
207094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
208094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _check_remote_libs_match_local_libs(self):
209094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    ret = []
210094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    all_remote_libs = self._shell("ls", "/system/lib/*.so").split()
211094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    local_libs = self._get_local_libs()
212094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
213094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.temp_libdir = tempfile.mkdtemp()
214094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
215094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    for lib in _interesting_libs():
216094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      lib += ".so"
217094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      for remote_lib in all_remote_libs:
218094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        if lib in remote_lib:
219094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea          # Pull lib from device and compute hash
220094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea          tmp_path = os.path.join(self.temp_libdir, lib)
221094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea          self.pull(remote_lib, tmp_path)
222094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea          remote_hash = self._md5sum(tmp_path)
223094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
224094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea          # Find local lib and compute hash
225094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea          built_library = filter(lambda l: lib in l, local_libs)[0]
226094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea          built_hash = self._md5sum(built_library)
227094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
228094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea          # Alert user if library mismatch is detected
229094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea          if built_hash != remote_hash:
230094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea            self._cleanup_temp()
231094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea            raise gdb.GdbError("Library mismatch between:\n" \
232094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea              + "\t(" + remote_hash + ") " + tmp_path + " (from target) and\n " \
233094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea              + "\t(" + built_hash + ") " + built_library + " (on host)\n" \
234094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea              + "The target is running a different build than the host." \
235094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea              + " This situation is not debuggable.")
236094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
237094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._cleanup_temp()
238094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
239094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _md5sum(self, file):
240094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    try:
241094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      return check_output(["md5sum", file]).strip().split()[0]
242094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    except subprocess.CalledProcessError, e:
243094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      raise gdb.GdbError("Error invoking md5sum commandline utility")
244094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
245094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  # Returns the list of serial numbers of connected devices
246094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def devices(self):
247094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    ret = []
248094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    raw_output = self._call_adb("devices").split()
249094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if len(raw_output) < 5:
250094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      return None
251094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    else:
252094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      for serial_num_index in range(4, len(raw_output), 2):
253094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        ret.append(raw_output[serial_num_index])
254094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    return ret
255094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
256094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def set_current_device(self, serial):
257094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if self.current_device == str(serial):
258094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      print "Current device already is: " + str(serial)
259094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      return
260094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
261094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # TODO: this function should probably check the serial is valid.
262094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.current_device = str(serial)
263094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
264094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    api_version = self.getprop("ro.build.version.sdk")
265094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if api_version < 15:
266094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      print "Warning: untested API version. Upgrade to 15 or higher"
267094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
268094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Verify the local libraries loaded by GDB are identical to those
269094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # sitting on the device actually executing. Alert the user if
270094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # this is happening
271094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._check_remote_libs_match_local_libs()
272094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
273094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  # adb getprop [property]
274094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  # if property is not None, returns the given property, otherwise
275094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  # returns all properties.
276094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def getprop(self, property=None):
277094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if property == None:
278094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      # get all the props
279094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      return self._call_adb(*["shell", "getprop"]).split('\n')
280094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    else:
281094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      return str(self._call_adb(*["shell", "getprop",
282094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        str(property)]).split('\n')[0])
283094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
284094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  # adb push
285094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def push(self, source, destination):
286094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._call_adb(*["push", source, destination])
287094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
288094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  # adb forward <source> <destination>
289094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def forward(self, source, destination):
290094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._call_adb(*["forward", source, destination])
291094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
292094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  # Returns true if filename exists on Android fs, false otherwise
293094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def exists(self, filename):
294094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    raw_listing = self._shell(*["ls", filename])
295094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    return "No such file or directory" not in raw_listing
296094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
297094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  # adb pull <remote_path> <local_path>
298094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def pull(self, remote_path, local_path):
299094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._call_adb(*["pull", remote_path, local_path])
300094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
301094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  #wrapper for adb shell ps. leave process_name=None for list of all processes
302094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  #Otherwise, returns triple with process name, pid and owner,
303094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def get_process_info(self, process_name=None):
304094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    ret = []
305094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    raw_output = self._shell("ps")
306094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    for raw_line in raw_output.splitlines()[1:]:
307094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      line = raw_line.split()
308094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      name = line[-1]
309094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
310094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      if process_name == None or name == process_name:
311094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        user = line[0]
312094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        pid = line[1]
313094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
314094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        if process_name != None:
315094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea          return (pid, user)
316094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        else:
317094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea          ret.append((pid, user))
318094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
319094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # No match in target process
320094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if process_name != None:
321094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      return (None, None)
322094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
323094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    return ret
324094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
325094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def kill_by_pid(self, pid):
326094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._shell(*["kill", "-9", pid])
327094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
328094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def kill_by_name(self, process_name):
329094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    (pid, user) = self.get_process_info(process_name)
330094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    while pid != None:
331094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self.kill_by_pid(pid)
332094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      (pid, user) = self.get_process_info(process_name)
333094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
334094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaclass AndroidStatus(gdb.Command):
335094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  """Implements the android-status gdb command."""
336094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
337094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def __init__(self, adb, name="android-status", cat=gdb.COMMAND_OBSCURE, verbose=False):
338094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    super (AndroidStatus, self).__init__(name, cat)
339094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.verbose = verbose
340094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.adb = adb
341094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
342094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _update_status(self, process_name, gdbserver_process_name):
343094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._check_app_is_loaded()
344094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
345094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Update app status
346094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    (self.pid, self.owner_user) = \
347094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self.adb.get_process_info(process_name)
348094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.running = self.pid != None
349094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
350094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Update gdbserver status
351094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    (self.gdbserver_pid, self.gdbserver_user) = \
352094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self.adb.get_process_info(gdbserver_process_name)
353094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.gdbserver_running = self.gdbserver_pid != None
354094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
355094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Print results
356094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if self.verbose:
357094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      print "--==Android GDB Plugin Status Update==--"
358094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      print "\tinferior name: " + process_name
359094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      print "\trunning: " + str(self.running)
360094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      print "\tpid: " + str(self.pid)
361094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      print "\tgdbserver running: " + str(self.gdbserver_running)
362094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      print "\tgdbserver pid: " + str(self.gdbserver_pid)
363094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      print "\tgdbserver user: " + str(self.gdbserver_user)
364094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
365094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _check_app_is_loaded(self):
366094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if not currentAppInfo.get_name():
367094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      raise gdb.GdbError("Error: no app loaded. Try load-android-app.")
368094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
369094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def invoke(self, arg, from_tty):
370094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._check_app_is_loaded()
371094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._update_status(currentAppInfo.get_name(),
372094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      currentAppInfo.get_gdbserver_path())
373094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # TODO: maybe print something if verbose is off
374094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
375094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaclass StartAndroidApp (AndroidStatus):
376094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  """Implements the 'start-android-app' gdb command."""
377094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
378094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _update_status(self):
379094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    AndroidStatus._update_status(self, self.process_name, \
380094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self.gdbserver_path)
381094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
382094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  # Calls adb shell ps every retry_delay seconds and returns
383094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  # the pid when process_name show up in output, or return 0
384094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  # after num_retries attempts. num_retries=0 means retry
385094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  # indefinitely.
386094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _wait_for_process(self, process_name, retry_delay=1, num_retries=10):
387094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    """ This function is a hack and should not be required"""
388094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    (pid, user) = self.adb.get_process_info(process_name)
389094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    retries_left = num_retries
390094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    while pid == None and retries_left != 0:
391094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      (pid, user) = self.adb.get_process_info(process_name)
392094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      time.sleep(retry_delay)
393094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      retries_left -= 1
394094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
395094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    return pid
396094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
397094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _gdbcmd(self, cmd, from_tty=False):
398094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if self.verbose:
399094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      print '### GDB Command: ' + str(cmd)
400094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
401094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    gdb.execute(cmd, from_tty)
402094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
403094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  # Remove scratch directory if any
404094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _cleanup_temp(self):
405094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if self.temp_dir:
406094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      shutil.rmtree(self.temp_dir)
407094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self.temp_dir = None
408094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
409094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _cleanup_jdb(self):
410094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if self.jdb_handle:
411094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      try:
412094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        self.jdb_handle.terminate()
413094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      except OSError, e:
414094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        # JDB process has likely died
415094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        pass
416094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
417094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self.jdb_handle = None
418094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
419094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _load_local_libs(self):
420094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    for lib in _interesting_libs():
421094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self._gdbcmd("shar " + lib)
422094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
423094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def __del__(self):
424094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._cleanup_temp()
425094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._cleanup_jdb()
426094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
427094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def __init__ (self, adb, name="start-android-app", cat=gdb.COMMAND_RUNNING, verbose=False):
428094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    super (StartAndroidApp, self).__init__(adb, name, cat, verbose)
429094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.adb = adb
430094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
431094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.jdb_handle = None
432094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # TODO: handle possibility that port 8700 is in use (may help with
433094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Eclipse problems)
434094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.jdwp_port = 8700
435094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
436094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Port for gdbserver
437094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.gdbserver_port = 5039
438094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
439094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.temp_dir = None
440094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
441094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def start_process(self, start_running=False):
442094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    #TODO: implement libbcc cache removal if needed
443094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
444094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    args = ["am", "start"]
445094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
446094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # If we are to start running, we can take advantage of am's -W flag to wait
447094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # for the process to start before returning. That way, we don't have to
448094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # emulate the behaviour (poorly) through the sleep-loop below.
449094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if not start_running:
450094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      args.append("-D")
451094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    else:
452094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      args.append("-W")
453094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
454094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    args.append(self.process_name + "/" + self.intent)
455094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    am_output = self.adb._shell(*args)
456094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if "Error:" in am_output:
457094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      raise gdb.GdbError("Cannot start app. Activity Manager returned:\n"\
458094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        + am_output)
459094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
460094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Gotta wait until the process starts if we can't use -W
461094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if not start_running:
462094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self.pid = self._wait_for_process(self.process_name)
463094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
464094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if not self.pid:
465094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      raise gdb.GdbError("Unable to detect running app remotely." \
466094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        + "Is " + self.process_name + " installed correctly?")
467094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
468094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if self.verbose:
469094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      print "--==Android App Started: " + self.process_name \
470094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        + " (pid=" + self.pid + ")==--"
471094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
472094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Forward port for java debugger to Dalvik
473094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.adb.forward("tcp:" + str(self.jdwp_port), \
474094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea                     "jdwp:" + str(self.pid))
475094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
476094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def start_gdbserver(self):
477094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # TODO: adjust for architecture...
478094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    gdbserver_local_path = os.path.join(os.getenv('ANDROID_BUILD_TOP'),
479a663b778625f3de523e251ca810f4dc722c706beStephen Hines      'prebuilt', 'android-arm', 'gdbserver', 'gdbserver')
480094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
481094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if not self.adb.exists(self.gdbserver_path):
482094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      # Install gdbserver
483094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      try:
484094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        self.adb.push(gdbserver_local_path, self.gdbserver_path)
485094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      except gdb.GdbError, e:
486094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        print "Unable to push gdbserver to device. Try re-installing app."
487094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        raise e
488094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
489094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.adb._background_shell(*[self.gdbserver_path, "--attach",
490094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      ":" + str(self.gdbserver_port), self.pid])
491094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
492094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._wait_for_process(self.gdbserver_path)
493094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._update_status()
494094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
495094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if self.verbose:
496094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      print "--==Remote gdbserver Started " \
497094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        + " (pid=" + str(self.gdbserver_pid) \
498094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        + " port=" + str(self.gdbserver_port) + ") ==--"
499094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
500094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Forward port for gdbserver
501094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.adb.forward("tcp:" + str(self.gdbserver_port), \
502094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea                     "tcp:" + str(5039))
503094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
504094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def attach_gdb(self, from_tty):
505094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._gdbcmd("target remote :" + str(self.gdbserver_port), False)
506094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if self.verbose:
507094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      print "--==GDB Plugin requested attach (port=" \
508094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        + str(self.gdbserver_port) + ")==-"
509094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
510094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # If GDB has no file set, things start breaking...so grab the same
511094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # binary the NDK grabs from the filesystem and continue
512094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._cleanup_temp()
513094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.temp_dir = tempfile.mkdtemp()
514094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.gdb_inferior = os.path.join(self.temp_dir, 'app_process')
515094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.adb.pull("/system/bin/app_process", self.gdb_inferior)
516094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._gdbcmd('file ' + self.gdb_inferior)
517094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
518094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def start_jdb(self, port):
519094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Kill if running
520094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._cleanup_jdb()
521094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
522094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Start the java debugger
523094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    args = ["jdb", "-connect",
524094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      "com.sun.jdi.SocketAttach:hostname=localhost,port=" + str(port)]
525094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if self.verbose:
526094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self.jdb_handle = subprocess.Popen(args, \
527094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        stdin=subprocess.PIPE)
528094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    else:
529094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      # Unix-only bit here..
530094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self.jdb_handle = subprocess.Popen(args, \
531094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        stdin=subprocess.PIPE,
532094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        stderr=subprocess.STDOUT,
533094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        stdout=open('/dev/null', 'w'))
534094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
535094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def invoke (self, arg, from_tty):
536094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # TODO: self._check_app_is_installed()
537094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._check_app_is_loaded()
538094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
539094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.intent = currentAppInfo.get_intent()
540094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.process_name = currentAppInfo.get_name()
541094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.data_directory = currentAppInfo.get_data_directory()
542094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.gdbserver_path = currentAppInfo.get_gdbserver_path()
543094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
544094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._update_status()
545094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
546094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if self.gdbserver_running:
547094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self.adb.kill_by_name(self.gdbserver_path)
548094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      if self.verbose:
549094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        print "--==Killed gdbserver process (pid=" \
550094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea          + str(self.gdbserver_pid) + ")==--"
551094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self._update_status()
552094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
553094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if self.running:
554094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self.adb.kill_by_name(self.process_name)
555094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      if self.verbose:
556094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        print "--==Killed app process (pid=" + str(self.pid) + ")==--"
557094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self._update_status()
558094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
559094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.start_process()
560094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
561094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Start remote gdbserver
562094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.start_gdbserver()
563094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
564094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Attach the gdb
565094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.attach_gdb(from_tty)
566094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
567094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Load symbolic libraries
568094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._load_local_libs()
569094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
570094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Set the debug output directory (for JIT debugging)
571094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if enable_renderscript_dumps:
572094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self._gdbcmd('set gDebugDumpDirectory="' + self.data_directory + '"')
573094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
574094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Start app
575094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # unblock the gdb by connecting with jdb
576094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.start_jdb(self.jdwp_port)
577094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
578094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaclass RunAndroidApp(StartAndroidApp):
579094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  """Implements the run-android-app gdb command."""
580094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
581094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def __init__(self, adb, name="run-android-app", cat=gdb.COMMAND_RUNNING, verbose=False):
582094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    super (RunAndroidApp, self).__init__(adb, name, cat, verbose)
583094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
584094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def invoke(self, arg, from_tty):
585094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    StartAndroidApp.invoke(self, arg, from_tty)
586094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._gdbcmd("continue")
587094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
588094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaclass AttachAndroidApp(StartAndroidApp):
589094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  """Implements the attach-android-app gdb command."""
590094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
591094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def __init__(self, adb, name="attach-android-app", cat=gdb.COMMAND_RUNNING, verbose=False):
592094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    super (AttachAndroidApp, self).__init__(adb, name, cat, verbose)
593094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
594094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def invoke(self, arg, from_tty):
595094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # TODO: self._check_app_is_installed()
596094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._check_app_is_loaded()
597094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
598094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.intent = currentAppInfo.get_intent()
599094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.process_name = currentAppInfo.get_name()
600094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.data_directory = currentAppInfo.get_data_directory()
601094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.gdbserver_path = currentAppInfo.get_gdbserver_path()
602094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
603094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._update_status()
604094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
605094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if self.gdbserver_running:
606094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self.adb.kill_by_name(self.gdbserver_path)
607094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      if self.verbose:
608094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        print "--==Killed gdbserver process (pid=" \
609094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea          + str(self.gdbserver_pid) + ")==--"
610094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self._update_status()
611094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
612094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Start remote gdbserver
613094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.start_gdbserver()
614094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
615094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Attach the gdb
616094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.attach_gdb(from_tty)
617094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
618094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Load symbolic libraries
619094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self._load_local_libs()
620094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
621094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # Set the debug output directory (for JIT debugging)
622094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if enable_renderscript_dumps:
623094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      self._gdbcmd('set gDebugDumpDirectory="' + self.data_directory + '"')
624094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
625094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaclass LoadApp(AndroidStatus):
626094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  """ Implements the load-android-app gbd command.
627094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  """
628094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _awk_script_path(self, script_name):
629094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if os.path.exists(script_name):
630094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      return script_name
631094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
632094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    script_root = os.path.join(os.getenv('ANDROID_BUILD_TOP'), \
633094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      'ndk', 'build', 'awk')
634094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
635094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    path_in_root = os.path.join(script_root, script_name)
636094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if os.path.exists(path_in_root):
637094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      return path_in_root
638094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
639094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    raise gdb.GdbError("Unable to find awk script " \
640094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      +  str(script_name) + " in " + path_in_root)
641094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
642094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _awk(self, script, command):
643094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    args = ["awk", "-f", self._awk_script_path(script), str(command)]
644094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
645094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if self.verbose:
646094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      print "### awk command: " + str(args)
647094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
648094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    awk_output = ""
649094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    try:
650094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      awk_output = check_output(args)
651094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    except subprocess.CalledProcessError, e:
652094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      raise gdb.GdbError("### Error in subprocess awk " + str(args))
653094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    except:
654094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      print "### Random error calling awk " + str(args)
655094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
656094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    return awk_output.rstrip()
657094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
658094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def __init__(self, adb, name="load-android-app", cat=gdb.COMMAND_RUNNING, verbose=False):
659094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    super (LoadApp, self).__init__(adb, name, cat, verbose)
660094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.manifest_name = "AndroidManifest.xml"
661094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.verbose = verbose
662094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.adb = adb
663094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.temp_libdir = None
664094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
665094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _find_manifests(self, path):
666094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    manifests = []
667094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    for root, dirnames, filenames in os.walk(path):
668094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      for filename in fnmatch.filter(filenames, self.manifest_name):
669094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        manifests.append(os.path.join(root, filename))
670094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    return manifests
671094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
672094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _usage(self):
673094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    return "Usage: load-android-app [<path-to-AndroidManifest.xml>" \
674094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea            + " | <package-name> <intent-name>]"
675094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
676094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def invoke(self, arg, from_tty):
677094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
678094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    package_name = ''
679094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    launchable = ''
680094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    args = arg.strip('"').split()
681094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if len(args) == 2:
682094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      package_name = args[0]
683094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      launchable = args[1]
684094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    elif len(args) == 1:
685094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      if os.path.isfile(args[0]) and os.path.basename(args[0]) == self.manifest_name:
686094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        self.manifest_path = args[0]
687094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      elif os.path.isdir(args[0]):
688094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        manifests = self._find_manifests(args[0])
689094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        if len(manifests) == 0:
690094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea          raise gdb.GdbError(self.manifest_name + " not found in: " \
691094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea            + args[0] + "\n" + self._usage())
692094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        elif len(manifests) > 1:
693094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea          raise gdb.GdbError("Ambiguous argument! Found too many " \
694094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea            + self.manifest_name + " files found:\n" + "\n".join(manifests))
695094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        else:
696094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea          self.manifest_path = manifests[0]
697094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      else:
698094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        raise gdb.GdbError("Invalid path: " + args[0] + "\n" + self._usage())
699094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
700094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      package_name = self._awk("extract-package-name.awk",
701094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        self.manifest_path)
702094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      launchable = self._awk("extract-launchable.awk",
703094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        self.manifest_path)
704094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    else:
705094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      raise gdb.GdbError(self._usage())
706094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
707094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
708094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    data_directory = self.adb._shell("run-as", package_name,
709094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      "/system/bin/sh", "-c", "pwd").rstrip()
710094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
711094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if not data_directory \
712094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      or len(data_directory) == 0 \
713094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      or not self.adb.exists(data_directory):
714094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      data_directory = os.path.join('/data', 'data', package_name)
715094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      print "Warning: unable to read data directory for package " \
716094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        + package_name + ". Meh, defaulting to " + data_directory
717094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
718094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    currentAppInfo.set_info(package_name, launchable, data_directory)
719094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
720094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if self.verbose:
721094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      print "--==Android App Loaded==--"
722094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      print "\tname=" + currentAppInfo.get_name()
723094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      print "\tintent=" + currentAppInfo.get_intent()
724094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
725094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    # TODO: Check status of app on device
726094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
727094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaclass SetAndroidDevice (gdb.Command):
728094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def __init__(self, adb, name="set-android-device", cat=gdb.COMMAND_RUNNING, verbose=False):
729094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    super (SetAndroidDevice, self).__init__(name, cat)
730094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.verbose = verbose
731094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    self.adb = adb
732094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
733094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def _usage(self):
734094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    return "Usage: set-android-device <serial>"
735094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
736094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  def invoke(self, arg, from_tty):
737094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if not arg or len(arg) == 0:
738094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      raise gdb.GdbError(self._usage)
739094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
740094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    serial = str(arg)
741094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    devices = adb.devices()
742094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    if serial in devices:
743094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      adb.set_current_device(serial)
744094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea    else:
745094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea      raise gdb.GdbError("Invalid serial. Serial numbers of connected " \
746094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea        + "device(s): \n" + "\n".join(devices))
747094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
748094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# Global initialization
749094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleadef initOnce(adb):
750094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  # Try to speed up startup by skipping most android shared objects
751094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  gdb.execute("set auto-solib-add 0", False);
752094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
753094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  # Set shared object search path
754094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea  gdb.execute("set solib-search-path " + local_symbols_library_directory, False)
755094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
756094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# Global instance of the object containing the info for current app
757094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel MaleacurrentAppInfo = DebugAppInfo ()
758094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
759094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# Global instance of ADB helper
760094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaadb = ADB(verbose=be_verbose)
761094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
762094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# Perform global initialization
763094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel MaleainitOnce(adb)
764094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
765094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea# Command registration
766094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel MaleaStartAndroidApp (adb, "start-android-app", gdb.COMMAND_RUNNING, be_verbose)
767094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel MaleaRunAndroidApp (adb, "run-android-app", gdb.COMMAND_RUNNING, be_verbose)
768094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel MaleaAndroidStatus (adb, "android-status", gdb.COMMAND_OBSCURE, be_verbose)
769094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel MaleaLoadApp (adb, "load-android-app", gdb.COMMAND_RUNNING, be_verbose)
770094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel MaleaSetAndroidDevice (adb, "set-android-device", gdb.COMMAND_RUNNING, be_verbose)
771094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel MaleaAttachAndroidApp (adb, "attach-android-app", gdb.COMMAND_RUNNING, be_verbose)
772