Commit | Line | Data |
---|---|---|
be36f02d SA |
1 | #! /usr/bin/env python |
2 | ||
88b9d363 | 3 | # Copyright (C) 2011-2022 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 | ||
13123da8 | 42 | __author__ = "saugustine@google.com (Sterling Augustine)" |
be36f02d SA |
43 | |
44 | import os | |
45 | import subprocess | |
46 | import sys | |
47 | ||
48 | OBJCOPY = None | |
49 | READELF = None | |
50 | GDB = None | |
51 | ||
13123da8 | 52 | |
be36f02d | 53 | def get_pub_info(filename, readelf_option): |
13123da8 SM |
54 | """Parse and return all the pubnames or pubtypes produced by readelf with the |
55 | given option. | |
56 | """ | |
57 | readelf = subprocess.Popen( | |
58 | [READELF, "--debug-dump=" + readelf_option, filename], stdout=subprocess.PIPE | |
59 | ) | |
60 | pubnames = [] | |
61 | ||
62 | in_list = False | |
63 | for line in readelf.stdout: | |
64 | fields = line.split(None, 1) | |
65 | if len(fields) == 2 and fields[0] == "Offset" and fields[1].strip() == "Name": | |
66 | in_list = True | |
67 | # Either a blank-line or a new Length field terminates the current section. | |
68 | elif len(fields) == 0 or fields[0] == "Length:": | |
69 | in_list = False | |
70 | elif in_list: | |
71 | pubnames.append(fields[1].strip()) | |
72 | ||
73 | readelf.wait() | |
74 | return pubnames | |
be36f02d SA |
75 | |
76 | ||
77 | def get_gdb_index(filename): | |
13123da8 SM |
78 | """Use readelf to dump the gdb index and collect the types and names""" |
79 | readelf = subprocess.Popen( | |
80 | [READELF, "--debug-dump=gdb_index", filename], stdout=subprocess.PIPE | |
81 | ) | |
82 | index_symbols = [] | |
83 | symbol_table_started = False | |
84 | for line in readelf.stdout: | |
85 | if line == "Symbol table:\n": | |
86 | symbol_table_started = True | |
87 | elif symbol_table_started: | |
88 | # Readelf prints gdb-index lines formatted like so: | |
89 | # [ 4] two::c2<double>::c2: 0 | |
90 | # So take the string between the first close bracket and the last colon. | |
91 | index_symbols.append(line[line.find("]") + 2 : line.rfind(":")]) | |
92 | ||
93 | readelf.wait() | |
94 | return index_symbols | |
be36f02d SA |
95 | |
96 | ||
97 | def CheckSets(list0, list1, name0, name1): | |
13123da8 SM |
98 | """Report any setwise differences between the two lists""" |
99 | ||
100 | if len(list0) == 0 or len(list1) == 0: | |
101 | return False | |
102 | ||
103 | difference0 = set(list0) - set(list1) | |
104 | if len(difference0) != 0: | |
105 | print "Elements in " + name0 + " but not " + name1 + ": (", | |
106 | print len(difference0), | |
107 | print ")" | |
108 | for element in difference0: | |
109 | print " " + element | |
110 | ||
111 | difference1 = set(list1) - set(list0) | |
112 | if len(difference1) != 0: | |
113 | print "Elements in " + name1 + " but not " + name0 + ": (", | |
114 | print len(difference1), | |
115 | print ")" | |
116 | for element in difference1: | |
117 | print " " + element | |
118 | ||
119 | if len(difference0) != 0 or len(difference1) != 0: | |
120 | return True | |
121 | ||
122 | print name0 + " and " + name1 + " are identical." | |
be36f02d SA |
123 | return False |
124 | ||
be36f02d SA |
125 | |
126 | def find_executables(): | |
13123da8 SM |
127 | """Find the copies of readelf, objcopy and gdb to use.""" |
128 | # Executable finding logic follows cc-with-index.sh | |
129 | global READELF | |
130 | READELF = os.getenv("READELF") | |
131 | if READELF is None: | |
132 | READELF = "readelf" | |
133 | global OBJCOPY | |
134 | OBJCOPY = os.getenv("OBJCOPY") | |
135 | if OBJCOPY is None: | |
136 | OBJCOPY = "objcopy" | |
137 | ||
138 | global GDB | |
139 | GDB = os.getenv("GDB") | |
140 | if GDB is None: | |
141 | if 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 | elif os.path.isfile("../../gdb") and os.access("../../gdb", os.X_OK): | |
146 | GDB = "../../gdb" | |
147 | else: | |
148 | # Punt and use the gdb in the path. | |
149 | GDB = "gdb" | |
be36f02d SA |
150 | |
151 | ||
152 | def main(argv): | |
13123da8 SM |
153 | """The main subprogram.""" |
154 | if len(argv) != 2: | |
155 | print "Usage: test_pubnames_and_indexes.py <filename>" | |
156 | sys.exit(2) | |
157 | ||
158 | find_executables() | |
159 | ||
160 | # Get the index produced by Gold--It should have been built into the binary. | |
161 | gold_index = get_gdb_index(argv[1]) | |
162 | ||
163 | # Collect the pubnames and types list | |
164 | pubs_list = get_pub_info(argv[1], "pubnames") | |
165 | pubs_list = pubs_list + get_pub_info(argv[1], "pubtypes") | |
166 | ||
167 | # Generate a .gdb_index with gdb | |
168 | gdb_index_file = argv[1] + ".gdb-generated-index" | |
169 | subprocess.check_call( | |
170 | [OBJCOPY, "--remove-section", ".gdb_index", argv[1], gdb_index_file] | |
171 | ) | |
172 | subprocess.check_call( | |
173 | [ | |
174 | GDB, | |
175 | "-batch", | |
176 | "-nx", | |
177 | gdb_index_file, | |
178 | "-ex", | |
179 | "save gdb-index " + os.path.dirname(argv[1]), | |
180 | "-ex", | |
181 | "quit", | |
182 | ] | |
183 | ) | |
184 | subprocess.check_call( | |
185 | [ | |
186 | OBJCOPY, | |
187 | "--add-section", | |
188 | ".gdb_index=" + gdb_index_file + ".gdb-index", | |
189 | gdb_index_file, | |
190 | ] | |
191 | ) | |
192 | gdb_index = get_gdb_index(gdb_index_file) | |
193 | os.remove(gdb_index_file) | |
194 | os.remove(gdb_index_file + ".gdb-index") | |
195 | ||
196 | failed = False | |
197 | gdb_index.sort() | |
198 | gold_index.sort() | |
199 | pubs_list.sort() | |
200 | ||
201 | # Find the differences between the various indices. | |
202 | if len(gold_index) == 0: | |
203 | print "Gold index is empty" | |
204 | failed |= True | |
205 | ||
206 | if len(gdb_index) == 0: | |
207 | print "Gdb index is empty" | |
208 | failed |= True | |
209 | ||
210 | if len(pubs_list) == 0: | |
211 | print "Pubs list is empty" | |
212 | failed |= True | |
213 | ||
214 | failed |= CheckSets(gdb_index, gold_index, "gdb index", "gold index") | |
215 | failed |= CheckSets(pubs_list, gold_index, "pubs list", "gold index") | |
216 | failed |= CheckSets(pubs_list, gdb_index, "pubs list", "gdb index") | |
217 | ||
218 | if failed: | |
219 | print "Test failed" | |
220 | sys.exit(1) | |
221 | ||
222 | ||
223 | if __name__ == "__main__": | |
224 | main(sys.argv) |