xref: /llvm-project/llvm/test/DebugInfo/Inputs/check-fake-use.py (revision 3d08ade7bd32f0296e0ca3a13640cc95fa89229a)
1#!/usr/bin/python3
2
3# Parsing dwarfdump's output to determine whether the location list for the
4# parameter "b" covers all of the function. The script searches for information
5# in the input file to determine the [prologue, epilogue) range for the
6# function, the location list range for "b", and checks that the latter covers
7# the entirety of the former.
8import re
9import sys
10
11DebugInfoPattern = r"\.debug_info contents:"
12DebugLinePattern = r"\.debug_line contents:"
13ProloguePattern = r"^\s*0x([0-9a-f]+)\s.+prologue_end"
14EpiloguePattern = r"^\s*0x([0-9a-f]+)\s.+epilogue_begin"
15FormalPattern = r"^0x[0-9a-f]+:\s+DW_TAG_formal_parameter"
16LocationPattern = r"DW_AT_location\s+\[DW_FORM_([a-z_]+)\](?:.*0x([a-f0-9]+))"
17DebugLocPattern = r'\[0x([a-f0-9]+),\s+0x([a-f0-9]+)\) ".text": (.+)$'
18
19SeenDebugInfo = False
20SeenDebugLine = False
21LocationRanges = None
22PrologueEnd = None
23EpilogueBegin = None
24
25# The dwarfdump output should contain the DW_AT_location for "b" first, then the
26# line table which should contain prologue_end and epilogue_begin entries.
27with open(sys.argv[1], "r") as dwarf_dump_file:
28    dwarf_iter = iter(dwarf_dump_file)
29    for line in dwarf_iter:
30        if not SeenDebugInfo and re.match(DebugInfoPattern, line):
31            SeenDebugInfo = True
32        if not SeenDebugLine and re.match(DebugLinePattern, line):
33            SeenDebugLine = True
34        # Get the range of DW_AT_location for "b".
35        if LocationRanges is None:
36            if match := re.match(FormalPattern, line):
37                # Go until we either find DW_AT_location or reach the end of this entry.
38                location_match = None
39                while location_match is None:
40                    if (line := next(dwarf_iter, "")) == "\n":
41                        raise RuntimeError(
42                            ".debug_info output is missing DW_AT_location for 'b'"
43                        )
44                    location_match = re.search(LocationPattern, line)
45                # Variable has whole-scope location, represented by an empty tuple.
46                if location_match.group(1) == "exprloc":
47                    LocationRanges = ()
48                    continue
49                if location_match.group(1) != "sec_offset":
50                    raise RuntimeError(
51                        f"Unhandled form for DW_AT_location: DW_FORM_{location_match.group(1)}"
52                    )
53                # Variable has location range list.
54                if (
55                    debug_loc_match := re.search(DebugLocPattern, next(dwarf_iter, ""))
56                ) is None:
57                    raise RuntimeError(f"Invalid location range list for 'b'")
58                LocationRanges = (
59                    int(debug_loc_match.group(1), 16),
60                    int(debug_loc_match.group(2), 16),
61                )
62                while (
63                    debug_loc_match := re.search(DebugLocPattern, next(dwarf_iter, ""))
64                ) is not None:
65                    match_loc_start = int(debug_loc_match.group(1), 16)
66                    match_loc_end = int(debug_loc_match.group(2), 16)
67                    match_expr = debug_loc_match.group(3)
68                    if match_loc_start != LocationRanges[1]:
69                        raise RuntimeError(
70                            f"Location list for 'b' is discontinuous from [0x{LocationRanges[1]:x}, 0x{match_loc_start:x})"
71                        )
72                    if "stack_value" in match_expr:
73                        raise RuntimeError(
74                            f"Location list for 'b' contains a stack_value expression: {match_expr}"
75                        )
76                    LocationRanges = (LocationRanges[0], match_loc_end)
77        # Get the prologue_end address.
78        elif PrologueEnd is None:
79            if match := re.match(ProloguePattern, line):
80                PrologueEnd = int(match.group(1), 16)
81        # Get the epilogue_begin address.
82        elif EpilogueBegin is None:
83            if match := re.match(EpiloguePattern, line):
84                EpilogueBegin = int(match.group(1), 16)
85                break
86
87if not SeenDebugInfo:
88    raise RuntimeError(".debug_info section not found.")
89if not SeenDebugLine:
90    raise RuntimeError(".debug_line section not found.")
91
92if LocationRanges is None:
93    raise RuntimeError(".debug_info output is missing parameter 'b'")
94if PrologueEnd is None:
95    raise RuntimeError(".debug_line output is missing prologue_end")
96if EpilogueBegin is None:
97    raise RuntimeError(".debug_line output is missing epilogue_begin")
98
99if len(LocationRanges) == 2 and (
100    LocationRanges[0] > PrologueEnd or LocationRanges[1] < EpilogueBegin
101):
102    raise RuntimeError(
103        f"""Location list for 'b' does not cover the whole function:")
104    Prologue to Epilogue = [0x{PrologueEnd:x}, 0x{EpilogueBegin:x})
105    Location range = [0x{LocationRanges[0]:x}, 0x{LocationRanges[1]:x})
106"""
107    )
108