xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/contrib/test_pubnames_and_indexes.py (revision 3117ece4fc4a4ca4489ba793710b60b0d26bab6c)
1#! /usr/bin/env python3
2
3# Copyright (C) 2011-2023 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
52
53def get_pub_info(filename, readelf_option):
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
75
76
77def get_gdb_index(filename):
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
95
96
97def CheckSets(list0, list1, name0, name1):
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 + ": (", end=" ")
106        print(len(difference0), end=" ")
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 + ": (", end=" ")
114        print(len(difference1), end=" ")
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.")
123    return False
124
125
126def find_executables():
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"
150
151
152def main(argv):
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
223if __name__ == "__main__":
224    main(sys.argv)
225