yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

CopilotAdd utility to trace creation of problematic IRInsts to assist LLM in debugging (#7820)368ddbb7b

master
4.8 KiB115 linesraw
1# InstTrace Debugging Utility
2#
3# This script is used to trace the callstack at the creation of a specific IR instruction in the Slang compiler.
4# This is useful for debugging purposes, especially if you encounter a compiler bug related to an instruction
5# that appears to be incorrect, and you want to find out where it was created in the codebase.
6#
7# Usage: python3 ./extras/insttrace.py <inst UID> <commandline_to_slangc_or_slang-test>
8#
9# The script will print the callstack at the point where the specified instruction was created.
10# <inst UID> is the unique identifier of the instruction you want to trace, which can be found by inspecting
11# the _debugUID field of an IRInst object in the Slang compiler source code.
12# If the instruction is a clone of another instruction, it will also trace the creation of the original instruction
13# recursively.
14
15import sys
16import subprocess
17import re
18import os
19
20def traceInst(inst_uid, command):
21    # Run the command with the provided arguments
22    # Set the environment variable SLANG_IR_ALLOC_BREAK to the instruction UID
23    env = dict(os.environ, SLANG_DEBUG_IR_BREAK=inst_uid)
24    process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
25    stdout, stderr = process.communicate()
26
27    # Parse the output to find the string between "BEGIN IR Trace" and "END IR Trace"
28    ir_trace = re.search(r"BEGIN IR Trace(.*?)END IR Trace", stdout.decode(encoding='utf-8', errors='ignore'), re.DOTALL)
29    if not ir_trace:
30        print("No IR Trace found in the output.")
31        return
32
33    traceOutput = ir_trace.group(1)
34    regex = r"(\S+)\(\+(0x[0-9a-f]+)\) \[0x[0-9a-f]+\]"
35
36    lines = traceOutput.splitlines()
37    
38    # First, collect all addresses grouped by library file for batching
39    lib_addresses = {}  # libFile -> list of addresses
40    line_info = []  # (line, libFile, address) for each line
41    
42    for line in lines:
43        match = re.search(regex, line)
44        if match:
45            libFile = match.group(1)
46            address = match.group(2)
47            line_info.append((line, libFile, address))
48            
49            if libFile not in lib_addresses:
50                lib_addresses[libFile] = []
51            lib_addresses[libFile].append(address)
52        else:
53            line_info.append((line, None, None))
54    
55    # Batch call addr2line for each library file
56    address_to_symbol = {}  # (libFile, address) -> symbol info
57    
58    for libFile, addresses in lib_addresses.items():
59        if not addresses:
60            continue
61            
62        # Call addr2line once with all addresses for this library
63        addr2line_command = ["addr2line", "-e", libFile, "-f", "-C"] + addresses
64        try:
65            addr2line_process = subprocess.Popen(addr2line_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
66            stdout, stderr = addr2line_process.communicate()
67            
68            # Parse the output - addr2line returns function name and location for each address
69            output_lines = stdout.decode(encoding='utf-8', errors='ignore').strip().split('\n')
70            
71            # Each address produces 2 lines: function name, then file:line
72            for i, address in enumerate(addresses):
73                if i * 2 + 1 < len(output_lines):
74                    function_name = output_lines[i * 2].strip()
75                    location = output_lines[i * 2 + 1].strip()
76                    symbol_info = f"{function_name} {location}"
77                    address_to_symbol[(libFile, address)] = symbol_info
78                else:
79                    address_to_symbol[(libFile, address)] = f"<unknown> {address}"
80        except Exception as e:
81            # Fallback if addr2line fails
82            for address in addresses:
83                address_to_symbol[(libFile, address)] = f"<addr2line failed> {address}"
84    
85    # Now print the results using the cached symbol information
86    for line, libFile, address in line_info:
87        if libFile and address:
88            symbol_info = address_to_symbol.get((libFile, address), f"<not found> {address}")
89            print(symbol_info)
90        else:
91            # print the line as is if it doesn't match the address format
92            print(line)
93
94    print("(end of stacktrace)")
95
96    # Find "Inst #%u is a clone of Inst #%u" in the trace output, and trace the original instruction
97    # if it exists.
98    clone_match = re.search(r"Inst #(\d+) is a clone of Inst #(\d+)", traceOutput)
99    if clone_match:
100        clone_inst_uid = clone_match.group(1)
101        original_inst_uid = clone_match.group(2)
102        traceInst(original_inst_uid, command)
103
104def main():
105    if len(sys.argv) < 3:
106        print("InstTrace Debugging Utility")
107        print("Usage: python insttrace.py <inst UID> <commandline_to_slangc_or_slang-test>")
108        sys.exit(1)
109
110    inst_uid = sys.argv[1]
111    command = sys.argv[2:]
112    traceInst(inst_uid, command)
113
114if __name__ == "__main__":
115    main()