| OLD | NEW |
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """ | 5 """ |
| 6 Manages a debugging session with GDB. | 6 Manages a debugging session with GDB. |
| 7 | 7 |
| 8 This module is meant to be imported from inside GDB. Once loaded, the | 8 This module is meant to be imported from inside GDB. Once loaded, the |
| 9 |DebugSession| attaches GDB to a running Mojo Shell process on an Android | 9 |DebugSession| attaches GDB to a running Mojo Shell process on an Android |
| 10 device using a remote gdbserver. | 10 device using a remote gdbserver. |
| (...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 183 # This library file is not known locally. Download it from the device or | 183 # This library file is not known locally. Download it from the device or |
| 184 # the cloud and put it in cache so, if it got symbols, we can see them. | 184 # the cloud and put it in cache so, if it got symbols, we can see them. |
| 185 local_file = os.path.join(self._remote_file_cache, signature) | 185 local_file = os.path.join(self._remote_file_cache, signature) |
| 186 if not os.path.exists(local_file): | 186 if not os.path.exists(local_file): |
| 187 tmp_output = self._download_file(signature, remote_file) | 187 tmp_output = self._download_file(signature, remote_file) |
| 188 shutil.move(tmp_output, local_file) | 188 shutil.move(tmp_output, local_file) |
| 189 self._associate_symbols(mapping, local_file) | 189 self._associate_symbols(mapping, local_file) |
| 190 return True | 190 return True |
| 191 return False | 191 return False |
| 192 | 192 |
| 193 def _update_symbols(self): | 193 def _find_mappings_current_thread(self, mapped_files): |
| 194 frame = gdb.newest_frame() |
| 195 while frame and frame.is_valid(): |
| 196 if frame.name() is None: |
| 197 m = self._find_mapping_for_address(mapped_files, frame.pc()) |
| 198 if m is not None and self._try_to_map(m): |
| 199 # Force gdb to recompute its frames. |
| 200 _gdb_execute("info threads") |
| 201 frame = gdb.newest_frame() |
| 202 assert frame.is_valid() |
| 203 if (frame.older() is not None and |
| 204 frame.older().is_valid() and |
| 205 frame.older().pc() != frame.pc()): |
| 206 frame = frame.older() |
| 207 else: |
| 208 frame = None |
| 209 |
| 210 def update_symbols(self, current_thread_only): |
| 194 """Updates the mapping between symbols as seen from GDB and local library | 211 """Updates the mapping between symbols as seen from GDB and local library |
| 195 files.""" | 212 files. |
| 213 |
| 214 If current_thread_only is True, only update symbols for the current thread. |
| 215 """ |
| 196 logging.info("Updating symbols") | 216 logging.info("Updating symbols") |
| 197 mapped_files = _get_mapped_files() | 217 mapped_files = _get_mapped_files() |
| 198 _gdb_execute("info threads") | |
| 199 nb_threads = len(_gdb_execute("info threads").split("\n")) - 2 | |
| 200 # Map all symbols from native libraries packages with the APK. | 218 # Map all symbols from native libraries packages with the APK. |
| 201 for file_mappings in mapped_files: | 219 for file_mappings in mapped_files: |
| 202 filename = file_mappings[0].filename | 220 filename = file_mappings[0].filename |
| 203 if ((filename.startswith('/data/data/') or | 221 if ((filename.startswith('/data/data/') or |
| 204 filename.startswith('/data/app')) and | 222 filename.startswith('/data/app')) and |
| 205 not filename.endswith('.apk') and | 223 not filename.endswith('.apk') and |
| 206 not filename.endswith('.dex')): | 224 not filename.endswith('.dex')): |
| 207 logging.info('Pre-mapping: %s' % file_mappings[0].filename) | 225 logging.info('Pre-mapping: %s' % file_mappings[0].filename) |
| 208 self._try_to_map(file_mappings) | 226 self._try_to_map(file_mappings) |
| 209 for i in xrange(nb_threads): | 227 |
| 210 try: | 228 if current_thread_only: |
| 211 _gdb_execute("thread %d" % (i + 1)) | 229 self._find_mappings_current_thread(mapped_files) |
| 212 frame = gdb.newest_frame() | 230 else: |
| 213 while frame and frame.is_valid(): | 231 logging.info('Updating all threads\' symbols') |
| 214 if frame.name() is None: | 232 current_thread = gdb.selected_thread() |
| 215 m = self._find_mapping_for_address(mapped_files, frame.pc()) | 233 nb_threads = len(_gdb_execute("info threads").split("\n")) - 2 |
| 216 if m is not None and self._try_to_map(m): | 234 for i in xrange(nb_threads): |
| 217 # Force gdb to recompute its frames. | 235 try: |
| 218 _gdb_execute("info threads") | 236 _gdb_execute("thread %d" % (i + 1)) |
| 219 frame = gdb.newest_frame() | 237 self._find_mappings_current_thread(mapped_files) |
| 220 assert frame.is_valid() | 238 except gdb.error: |
| 221 if (frame.older() is not None and | 239 traceback.print_exc() |
| 222 frame.older().is_valid() and | 240 current_thread.switch() |
| 223 frame.older().pc() != frame.pc()): | |
| 224 frame = frame.older() | |
| 225 else: | |
| 226 frame = None | |
| 227 except gdb.error: | |
| 228 traceback.print_exc() | |
| 229 | 241 |
| 230 def _get_device_application_pid(self, application): | 242 def _get_device_application_pid(self, application): |
| 231 """Gets the PID of an application running on a device.""" | 243 """Gets the PID of an application running on a device.""" |
| 232 output = subprocess.check_output([self._adb, 'shell', 'ps']) | 244 output = subprocess.check_output([self._adb, 'shell', 'ps']) |
| 233 for line in output.split('\n'): | 245 for line in output.split('\n'): |
| 234 elements = line.split() | 246 elements = line.split() |
| 235 if len(elements) > 0 and elements[-1] == application: | 247 if len(elements) > 0 and elements[-1] == application: |
| 236 return elements[1] | 248 return elements[1] |
| 237 return None | 249 return None |
| 238 | 250 |
| (...skipping 20 matching lines...) Expand all Loading... |
| 259 remote_file_reader_pid]) | 271 remote_file_reader_pid]) |
| 260 self._remote_file_reader_process = subprocess.Popen( | 272 self._remote_file_reader_process = subprocess.Popen( |
| 261 [self._adb, 'shell', config.REMOTE_FILE_READER_DEVICE_PATH], | 273 [self._adb, 'shell', config.REMOTE_FILE_READER_DEVICE_PATH], |
| 262 stdout=subprocess.PIPE, preexec_fn = os.setpgrp) | 274 stdout=subprocess.PIPE, preexec_fn = os.setpgrp) |
| 263 port = int(self._remote_file_reader_process.stdout.readline()) | 275 port = int(self._remote_file_reader_process.stdout.readline()) |
| 264 subprocess.check_call([self._adb, 'forward', 'tcp:10000', 'tcp:%d' % port]) | 276 subprocess.check_call([self._adb, 'forward', 'tcp:10000', 'tcp:%d' % port]) |
| 265 self._rfc.connect() | 277 self._rfc.connect() |
| 266 | 278 |
| 267 _gdb_execute('target remote localhost:9999') | 279 _gdb_execute('target remote localhost:9999') |
| 268 | 280 |
| 269 self._update_symbols() | 281 self.update_symbols(False) |
| 270 def on_stop(_): | 282 def on_stop(_): |
| 271 self._update_symbols() | 283 self.update_symbols(True) |
| 272 gdb.events.stop.connect(on_stop) | 284 gdb.events.stop.connect(on_stop) |
| 273 gdb.events.exited.connect(self.stop) | 285 gdb.events.exited.connect(self.stop) |
| 286 |
| 287 # Register the update-symbols command. |
| 288 UpdateSymbols(self) |
| 289 |
| 290 class UpdateSymbols(gdb.Command): |
| 291 """Command to update symbols loaded into GDB. |
| 292 |
| 293 GDB usage: update-symbols [all|current] |
| 294 """ |
| 295 _UPDATE_COMMAND = "update-symbols" |
| 296 |
| 297 def __init__(self, session): |
| 298 super(UpdateSymbols, self).__init__(self._UPDATE_COMMAND, gdb.COMMAND_STACK) |
| 299 self._session = session |
| 300 |
| 301 def invoke(self, arg, _unused_from_tty): |
| 302 if arg == 'current': |
| 303 self._session.update_symbols(True) |
| 304 else: |
| 305 self._session.update_symbols(False) |
| 306 |
| 307 def complete(self, text, _unused_word): |
| 308 if text == self._UPDATE_COMMAND: |
| 309 return ('all', 'current') |
| 310 elif text in self._UPDATE_COMMAND + ' all': |
| 311 return ['all'] |
| 312 elif text in self._UPDATE_COMMAND + ' current': |
| 313 return ['current'] |
| 314 else: |
| 315 return [] |
| OLD | NEW |