1#! /usr/bin/env python
2
3# Copyright (C) 2011-2013 Free Software Foundation, Inc.
4#
5# This file is part of GDB.
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 3 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20# This program requires readelf, gdb and objcopy.  The default values are gdb
21# from the build tree and objcopy and readelf from $PATH.  They may be
22# overridden by setting environment variables GDB, READELF and OBJCOPY
23# respectively.  We assume the current directory is either $obj/gdb or
24# $obj/gdb/testsuite.
25#
26# Example usage:
27#
28# bash$ cd $objdir/gdb/testsuite
29# bash$ python test_pubnames_and_indexes.py <binary_name>
30
31"""test_pubnames_and_indexes.py
32
33Test that the gdb_index produced by gold is identical to the gdb_index
34produced by gdb itself.
35
36Further check that the pubnames and pubtypes produced by gcc are identical
37to those that gdb produces.
38
39Finally, check that all strings are canonicalized identically.
40"""
41
42__author__ = 'saugustine@google.com (Sterling Augustine)'
43
44import os
45import subprocess
46import sys
47
48OBJCOPY = None
49READELF = None
50GDB = None
51
52def get_pub_info(filename, readelf_option):
53  """Parse and return all the pubnames or pubtypes produced by readelf with the
54  given option.
55  """
56  readelf = subprocess.Popen([READELF, '--debug-dump=' + readelf_option,
57                                       filename], stdout=subprocess.PIPE)
58  pubnames = []
59
60  in_list = False;
61  for line in readelf.stdout:
62    fields = line.split(None, 1)
63    if (len(fields) == 2 and fields[0] == 'Offset'
64        and fields[1].strip() == 'Name'):
65      in_list = True
66    # Either a blank-line or a new Length field terminates the current section.
67    elif (len(fields) == 0 or fields[0] == 'Length:'):
68      in_list = False;
69    elif (in_list):
70      pubnames.append(fields[1].strip())
71
72  readelf.wait()
73  return pubnames
74
75
76def get_gdb_index(filename):
77  """Use readelf to dump the gdb index and collect the types and names"""
78  readelf = subprocess.Popen([READELF, '--debug-dump=gdb_index',
79                              filename], stdout=subprocess.PIPE)
80  index_symbols = []
81  symbol_table_started = False
82  for line in readelf.stdout:
83    if (line == 'Symbol table:\n'):
84      symbol_table_started = True;
85    elif (symbol_table_started):
86      # Readelf prints gdb-index lines formatted like so:
87      # [  4] two::c2<double>::c2: 0
88      # So take the string between the first close bracket and the last colon.
89      index_symbols.append(line[line.find(']') + 2: line.rfind(':')])
90
91  readelf.wait()
92  return index_symbols
93
94
95def CheckSets(list0, list1, name0, name1):
96  """Report any setwise differences between the two lists"""
97
98  if len(list0) == 0 or len(list1) == 0:
99    return False
100
101  difference0 = set(list0) - set(list1)
102  if len(difference0) != 0:
103    print "Elements in " + name0 + " but not " + name1 + ": (",
104    print len(difference0),
105    print ")"
106    for element in difference0:
107      print "  " + element
108
109  difference1 = set(list1) - set(list0)
110  if len(difference1) != 0:
111    print "Elements in " + name1 + " but not " + name0 + ": (",
112    print len(difference1),
113    print ")"
114    for element in difference1:
115      print "  " + element
116
117  if (len(difference0) != 0 or len(difference1) != 0):
118    return True
119
120  print name0 + " and " + name1 + " are identical."
121  return False
122
123
124def find_executables():
125  """Find the copies of readelf, objcopy and gdb to use."""
126  # Executable finding logic follows cc-with-index.sh
127  global READELF
128  READELF = os.getenv('READELF')
129  if READELF is None:
130    READELF = 'readelf'
131  global OBJCOPY
132  OBJCOPY = os.getenv('OBJCOPY')
133  if OBJCOPY is None:
134    OBJCOPY = 'objcopy'
135
136  global GDB
137  GDB = os.getenv('GDB')
138  if (GDB is None):
139    if os.path.isfile('./gdb') and os.access('./gdb', os.X_OK):
140      GDB = './gdb'
141    elif os.path.isfile('../gdb') and os.access('../gdb', os.X_OK):
142      GDB = '../gdb'
143    elif os.path.isfile('../../gdb') and os.access('../../gdb', os.X_OK):
144      GDB = '../../gdb'
145    else:
146      # Punt and use the gdb in the path.
147      GDB = 'gdb'
148
149
150def main(argv):
151  """The main subprogram."""
152  if len(argv) != 2:
153    print "Usage: test_pubnames_and_indexes.py <filename>"
154    sys.exit(2)
155
156  find_executables();
157
158  # Get the index produced by Gold--It should have been built into the binary.
159  gold_index = get_gdb_index(argv[1])
160
161  # Collect the pubnames and types list
162  pubs_list = get_pub_info(argv[1], "pubnames")
163  pubs_list = pubs_list + get_pub_info(argv[1], "pubtypes")
164
165  # Generate a .gdb_index with gdb
166  gdb_index_file = argv[1] + '.gdb-generated-index'
167  subprocess.check_call([OBJCOPY, '--remove-section', '.gdb_index',
168                         argv[1], gdb_index_file])
169  subprocess.check_call([GDB, '-batch', '-nx', gdb_index_file,
170                         '-ex', 'save gdb-index ' + os.path.dirname(argv[1]),
171                         '-ex', 'quit'])
172  subprocess.check_call([OBJCOPY, '--add-section',
173                         '.gdb_index=' + gdb_index_file + '.gdb-index',
174                         gdb_index_file])
175  gdb_index = get_gdb_index(gdb_index_file)
176  os.remove(gdb_index_file)
177  os.remove(gdb_index_file + '.gdb-index')
178
179  failed = False
180  gdb_index.sort()
181  gold_index.sort()
182  pubs_list.sort()
183
184  # Find the differences between the various indices.
185  if len(gold_index) == 0:
186    print "Gold index is empty"
187    failed |= True
188
189  if len(gdb_index) == 0:
190    print "Gdb index is empty"
191    failed |= True
192
193  if len(pubs_list) == 0:
194    print "Pubs list is empty"
195    failed |= True
196
197  failed |= CheckSets(gdb_index, gold_index, "gdb index", "gold index")
198  failed |= CheckSets(pubs_list, gold_index, "pubs list", "gold index")
199  failed |= CheckSets(pubs_list, gdb_index, "pubs list", "gdb index")
200
201  if failed:
202    print "Test failed"
203    sys.exit(1)
204
205
206if __name__ == '__main__':
207  main(sys.argv)
208