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