Commit | Line | Data |
---|---|---|
be36f02d SA |
1 | #! /usr/bin/env python |
2 | ||
61baf725 | 3 | # Copyright (C) 2011-2017 Free Software Foundation, Inc. |
be36f02d SA |
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 | ||
33 | Test that the gdb_index produced by gold is identical to the gdb_index | |
34 | produced by gdb itself. | |
35 | ||
36 | Further check that the pubnames and pubtypes produced by gcc are identical | |
37 | to those that gdb produces. | |
38 | ||
39 | Finally, check that all strings are canonicalized identically. | |
40 | """ | |
41 | ||
42 | __author__ = 'saugustine@google.com (Sterling Augustine)' | |
43 | ||
44 | import os | |
45 | import subprocess | |
46 | import sys | |
47 | ||
48 | OBJCOPY = None | |
49 | READELF = None | |
50 | GDB = None | |
51 | ||
52 | def 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 | ||
76 | def 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 | ||
95 | def 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 | ||
124 | def 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 | ||
150 | def 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 | ||
206 | if __name__ == '__main__': | |
207 | main(sys.argv) |