1# Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. 2# 3# Use of this source code is governed by a BSD-style license 4# that can be found in the LICENSE file in the root of the source 5# tree. An additional intellectual property rights grant can be found 6# in the file PATENTS. All contributing project authors may 7# be found in the AUTHORS file in the root of the source tree. 8 9''' A bunch of helper functions for querying gdb.''' 10 11import logging 12import os 13import re 14import tempfile 15 16GDB_LINE_RE = re.compile(r'Line ([0-9]*) of "([^"]*)".*') 17 18def _GdbOutputToFileLine(output_line): 19 ''' Parse the gdb output line, return a pair (file, line num) ''' 20 match = GDB_LINE_RE.match(output_line) 21 if match: 22 return match.groups()[1], match.groups()[0] 23 else: 24 return None 25 26def ResolveAddressesWithinABinary(binary_name, load_address, address_list): 27 ''' For each address, return a pair (file, line num) ''' 28 commands = tempfile.NamedTemporaryFile() 29 commands.write('add-symbol-file "%s" %s\n' % (binary_name, load_address)) 30 for addr in address_list: 31 commands.write('info line *%s\n' % addr) 32 commands.write('quit\n') 33 commands.flush() 34 gdb_commandline = 'gdb -batch -x %s 2>/dev/null' % commands.name 35 gdb_pipe = os.popen(gdb_commandline) 36 result = gdb_pipe.readlines() 37 38 address_count = 0 39 ret = {} 40 for line in result: 41 if line.startswith('Line'): 42 ret[address_list[address_count]] = _GdbOutputToFileLine(line) 43 address_count += 1 44 if line.startswith('No line'): 45 ret[address_list[address_count]] = (None, None) 46 address_count += 1 47 gdb_pipe.close() 48 commands.close() 49 return ret 50 51class AddressTable(object): 52 ''' Object to do batched line number lookup. ''' 53 def __init__(self): 54 self._load_addresses = {} 55 self._binaries = {} 56 self._all_resolved = False 57 58 def AddBinaryAt(self, binary, load_address): 59 ''' Register a new shared library or executable. ''' 60 self._load_addresses[binary] = load_address 61 62 def Add(self, binary, address): 63 ''' Register a lookup request. ''' 64 if binary == '': 65 logging.warn('adding address %s in empty binary?' % address) 66 if binary in self._binaries: 67 self._binaries[binary].append(address) 68 else: 69 self._binaries[binary] = [address] 70 self._all_resolved = False 71 72 def ResolveAll(self): 73 ''' Carry out all lookup requests. ''' 74 self._translation = {} 75 for binary in self._binaries.keys(): 76 if binary != '' and binary in self._load_addresses: 77 load_address = self._load_addresses[binary] 78 addr = ResolveAddressesWithinABinary( 79 binary, load_address, self._binaries[binary]) 80 self._translation[binary] = addr 81 self._all_resolved = True 82 83 def GetFileLine(self, binary, addr): 84 ''' Get the (filename, linenum) result of a previously-registered lookup 85 request. 86 ''' 87 if self._all_resolved: 88 if binary in self._translation: 89 if addr in self._translation[binary]: 90 return self._translation[binary][addr] 91 return (None, None) 92