1*da0073e9SAndroid Build Coastguard Workerimport csv 2*da0073e9SAndroid Build Coastguard Workerimport os 3*da0073e9SAndroid Build Coastguard Workerimport re 4*da0073e9SAndroid Build Coastguard Workerimport sys 5*da0073e9SAndroid Build Coastguard Worker 6*da0073e9SAndroid Build Coastguard Worker 7*da0073e9SAndroid Build Coastguard Worker# This script takes the logs produced by the benchmark scripts (e.g., 8*da0073e9SAndroid Build Coastguard Worker# torchbench.py) and parses it into a CSV file that summarizes what 9*da0073e9SAndroid Build Coastguard Worker# is failing and why. It is kept separate from the benchmark script 10*da0073e9SAndroid Build Coastguard Worker# emitting a more structured output as it is often more convenient 11*da0073e9SAndroid Build Coastguard Worker# to iterate quickly on log files offline instead of having to make 12*da0073e9SAndroid Build Coastguard Worker# a change to the benchmark script and then do a full sweep to see 13*da0073e9SAndroid Build Coastguard Worker# the updates. 14*da0073e9SAndroid Build Coastguard Worker# 15*da0073e9SAndroid Build Coastguard Worker# This script is not very well written, feel free to rewrite it as necessary 16*da0073e9SAndroid Build Coastguard Worker 17*da0073e9SAndroid Build Coastguard Workerassert len(sys.argv) == 2 18*da0073e9SAndroid Build Coastguard Worker 19*da0073e9SAndroid Build Coastguard Workerfull_log = open(sys.argv[1]).read() 20*da0073e9SAndroid Build Coastguard Worker 21*da0073e9SAndroid Build Coastguard Worker# If the log contains a gist URL, extract it so we can include it in the CSV 22*da0073e9SAndroid Build Coastguard Workergist_url = "" 23*da0073e9SAndroid Build Coastguard Workerm = re.search(r"https://gist.github.com/[a-f0-9]+", full_log) 24*da0073e9SAndroid Build Coastguard Workerif m is not None: 25*da0073e9SAndroid Build Coastguard Worker gist_url = m.group(0) 26*da0073e9SAndroid Build Coastguard Worker 27*da0073e9SAndroid Build Coastguard Worker# Split the log into an entry per benchmark 28*da0073e9SAndroid Build Coastguard Workerentries = re.split( 29*da0073e9SAndroid Build Coastguard Worker r"(?:cuda (?:train|eval) +([^ ]+)|WARNING:root:([^ ]+) failed to load)", full_log 30*da0073e9SAndroid Build Coastguard Worker)[1:] 31*da0073e9SAndroid Build Coastguard Worker# Entries schema example: 32*da0073e9SAndroid Build Coastguard Worker# `['hf_Bert', None, ' 33*da0073e9SAndroid Build Coastguard Worker# PASS\nTIMING: entire_frame_compile:1.80925 backend_compile:6e-05\nDynamo produced 1 graph(s) covering 367 ops\n']` 34*da0073e9SAndroid Build Coastguard Worker 35*da0073e9SAndroid Build Coastguard Worker 36*da0073e9SAndroid Build Coastguard Workerdef chunker(seq, size): 37*da0073e9SAndroid Build Coastguard Worker return (seq[pos : pos + size] for pos in range(0, len(seq), size)) 38*da0073e9SAndroid Build Coastguard Worker 39*da0073e9SAndroid Build Coastguard Worker 40*da0073e9SAndroid Build Coastguard Workerc = 0 41*da0073e9SAndroid Build Coastguard Workeri = 0 42*da0073e9SAndroid Build Coastguard Worker 43*da0073e9SAndroid Build Coastguard Workerout = csv.DictWriter( 44*da0073e9SAndroid Build Coastguard Worker sys.stdout, 45*da0073e9SAndroid Build Coastguard Worker [ 46*da0073e9SAndroid Build Coastguard Worker "bench", 47*da0073e9SAndroid Build Coastguard Worker "name", 48*da0073e9SAndroid Build Coastguard Worker "result", 49*da0073e9SAndroid Build Coastguard Worker "component", 50*da0073e9SAndroid Build Coastguard Worker "context", 51*da0073e9SAndroid Build Coastguard Worker "explain", 52*da0073e9SAndroid Build Coastguard Worker "frame_time", 53*da0073e9SAndroid Build Coastguard Worker "backend_time", 54*da0073e9SAndroid Build Coastguard Worker "graph_count", 55*da0073e9SAndroid Build Coastguard Worker "op_count", 56*da0073e9SAndroid Build Coastguard Worker "graph_breaks", 57*da0073e9SAndroid Build Coastguard Worker "unique_graph_breaks", 58*da0073e9SAndroid Build Coastguard Worker ], 59*da0073e9SAndroid Build Coastguard Worker dialect="excel", 60*da0073e9SAndroid Build Coastguard Worker) 61*da0073e9SAndroid Build Coastguard Workerout.writeheader() 62*da0073e9SAndroid Build Coastguard Workerout.writerow({"explain": gist_url}) 63*da0073e9SAndroid Build Coastguard Worker 64*da0073e9SAndroid Build Coastguard Worker 65*da0073e9SAndroid Build Coastguard Worker# Sometimes backtraces will be in third party code, which results 66*da0073e9SAndroid Build Coastguard Worker# in very long file names. Delete the absolute path in this case. 67*da0073e9SAndroid Build Coastguard Workerdef normalize_file(f): 68*da0073e9SAndroid Build Coastguard Worker if "site-packages/" in f: 69*da0073e9SAndroid Build Coastguard Worker return f.split("site-packages/", 2)[1] 70*da0073e9SAndroid Build Coastguard Worker else: 71*da0073e9SAndroid Build Coastguard Worker return os.path.relpath(f) 72*da0073e9SAndroid Build Coastguard Worker 73*da0073e9SAndroid Build Coastguard Worker 74*da0073e9SAndroid Build Coastguard Worker# Assume we run torchbench, huggingface, timm_models in that order 75*da0073e9SAndroid Build Coastguard Worker# (as output doesn't say which suite the benchmark is part of) 76*da0073e9SAndroid Build Coastguard Worker# TODO: make this more robust 77*da0073e9SAndroid Build Coastguard Worker 78*da0073e9SAndroid Build Coastguard Workerbench = "torchbench" 79*da0073e9SAndroid Build Coastguard Worker 80*da0073e9SAndroid Build Coastguard Worker# 3 = 1 + number of matches in the entries split regex 81*da0073e9SAndroid Build Coastguard Workerfor name, name2, log in chunker(entries, 3): 82*da0073e9SAndroid Build Coastguard Worker if name is None: 83*da0073e9SAndroid Build Coastguard Worker name = name2 84*da0073e9SAndroid Build Coastguard Worker if name.startswith("Albert"): 85*da0073e9SAndroid Build Coastguard Worker bench = "huggingface" 86*da0073e9SAndroid Build Coastguard Worker elif name.startswith("adv_inc"): 87*da0073e9SAndroid Build Coastguard Worker bench = "timm_models" 88*da0073e9SAndroid Build Coastguard Worker 89*da0073e9SAndroid Build Coastguard Worker # Payload that will go into the csv 90*da0073e9SAndroid Build Coastguard Worker r = "UNKNOWN" 91*da0073e9SAndroid Build Coastguard Worker explain = "" 92*da0073e9SAndroid Build Coastguard Worker component = "" 93*da0073e9SAndroid Build Coastguard Worker context = "" 94*da0073e9SAndroid Build Coastguard Worker 95*da0073e9SAndroid Build Coastguard Worker if "PASS" in log: 96*da0073e9SAndroid Build Coastguard Worker r = "PASS" 97*da0073e9SAndroid Build Coastguard Worker if "TIMEOUT" in log: 98*da0073e9SAndroid Build Coastguard Worker r = "FAIL TIMEOUT" 99*da0073e9SAndroid Build Coastguard Worker if "Accuracy failed" in log: 100*da0073e9SAndroid Build Coastguard Worker r = "FAIL ACCURACY" 101*da0073e9SAndroid Build Coastguard Worker 102*da0073e9SAndroid Build Coastguard Worker # Attempt to extract out useful information from the traceback 103*da0073e9SAndroid Build Coastguard Worker 104*da0073e9SAndroid Build Coastguard Worker log = log.split( 105*da0073e9SAndroid Build Coastguard Worker "The above exception was the direct cause of the following exception" 106*da0073e9SAndroid Build Coastguard Worker )[0] 107*da0073e9SAndroid Build Coastguard Worker split = log.split("Traceback (most recent call last)", maxsplit=1) 108*da0073e9SAndroid Build Coastguard Worker if len(split) == 2: 109*da0073e9SAndroid Build Coastguard Worker log = split[1] 110*da0073e9SAndroid Build Coastguard Worker log = log.split("Original traceback:")[0] 111*da0073e9SAndroid Build Coastguard Worker m = re.search( 112*da0073e9SAndroid Build Coastguard Worker r'File "([^"]+)", line ([0-9]+), in .+\n +(.+)\n([A-Za-z]+(?:Error|Exception|NotImplementedError): ?.*)', 113*da0073e9SAndroid Build Coastguard Worker log, 114*da0073e9SAndroid Build Coastguard Worker ) 115*da0073e9SAndroid Build Coastguard Worker 116*da0073e9SAndroid Build Coastguard Worker if m is not None: 117*da0073e9SAndroid Build Coastguard Worker r = "FAIL" 118*da0073e9SAndroid Build Coastguard Worker component = f"{normalize_file(m.group(1))}:{m.group(2)}" 119*da0073e9SAndroid Build Coastguard Worker context = m.group(3) 120*da0073e9SAndroid Build Coastguard Worker explain = f"{m.group(4)}" 121*da0073e9SAndroid Build Coastguard Worker else: 122*da0073e9SAndroid Build Coastguard Worker m = re.search( 123*da0073e9SAndroid Build Coastguard Worker r'File "([^"]+)", line ([0-9]+), in .+\n +(.+)\nAssertionError', log 124*da0073e9SAndroid Build Coastguard Worker ) 125*da0073e9SAndroid Build Coastguard Worker if m is not None: 126*da0073e9SAndroid Build Coastguard Worker r = "FAIL" 127*da0073e9SAndroid Build Coastguard Worker component = f"{normalize_file(m.group(1))}:{m.group(2)}" 128*da0073e9SAndroid Build Coastguard Worker context = m.group(3) 129*da0073e9SAndroid Build Coastguard Worker explain = "AssertionError" 130*da0073e9SAndroid Build Coastguard Worker 131*da0073e9SAndroid Build Coastguard Worker # Sometimes, the benchmark will say FAIL without any useful info 132*da0073e9SAndroid Build Coastguard Worker # See https://github.com/pytorch/torchdynamo/issues/1910 133*da0073e9SAndroid Build Coastguard Worker if "FAIL" in log: 134*da0073e9SAndroid Build Coastguard Worker r = "FAIL" 135*da0073e9SAndroid Build Coastguard Worker 136*da0073e9SAndroid Build Coastguard Worker if r == "UNKNOWN": 137*da0073e9SAndroid Build Coastguard Worker c += 1 138*da0073e9SAndroid Build Coastguard Worker 139*da0073e9SAndroid Build Coastguard Worker backend_time = None 140*da0073e9SAndroid Build Coastguard Worker frame_time = None 141*da0073e9SAndroid Build Coastguard Worker if "TIMING:" in log: 142*da0073e9SAndroid Build Coastguard Worker result = re.search("TIMING:(.*)\n", log).group(1) 143*da0073e9SAndroid Build Coastguard Worker split_str = result.split("backend_compile:") 144*da0073e9SAndroid Build Coastguard Worker if len(split_str) == 2: 145*da0073e9SAndroid Build Coastguard Worker backend_time = float(split_str[1]) 146*da0073e9SAndroid Build Coastguard Worker frame_time = float(split_str[0].split("entire_frame_compile:")[1]) 147*da0073e9SAndroid Build Coastguard Worker 148*da0073e9SAndroid Build Coastguard Worker if "STATS:" in log: 149*da0073e9SAndroid Build Coastguard Worker result = re.search("STATS:(.*)\n", log).group(1) 150*da0073e9SAndroid Build Coastguard Worker # call_* op count: 970 | FakeTensor.__torch_dispatch__:35285 | ProxyTorchDispatchMode.__torch_dispatch__:13339 151*da0073e9SAndroid Build Coastguard Worker split_all = result.split("|") 152*da0073e9SAndroid Build Coastguard Worker # TODO: rewrite this to work with arbitrarily many stats 153*da0073e9SAndroid Build Coastguard Worker 154*da0073e9SAndroid Build Coastguard Worker graph_count = None 155*da0073e9SAndroid Build Coastguard Worker op_count = None 156*da0073e9SAndroid Build Coastguard Worker graph_breaks = None 157*da0073e9SAndroid Build Coastguard Worker unique_graph_breaks = None 158*da0073e9SAndroid Build Coastguard Worker if m := re.search( 159*da0073e9SAndroid Build Coastguard Worker r"Dynamo produced (\d+) graphs covering (\d+) ops with (\d+) graph breaks \((\d+) unique\)", 160*da0073e9SAndroid Build Coastguard Worker log, 161*da0073e9SAndroid Build Coastguard Worker ): 162*da0073e9SAndroid Build Coastguard Worker graph_count = m.group(1) 163*da0073e9SAndroid Build Coastguard Worker op_count = m.group(2) 164*da0073e9SAndroid Build Coastguard Worker graph_breaks = m.group(3) 165*da0073e9SAndroid Build Coastguard Worker unique_graph_breaks = m.group(4) 166*da0073e9SAndroid Build Coastguard Worker 167*da0073e9SAndroid Build Coastguard Worker # If the context string is too long, don't put it in the CSV. 168*da0073e9SAndroid Build Coastguard Worker # This is a hack to try to make it more likely that Google Sheets will 169*da0073e9SAndroid Build Coastguard Worker # offer to split columns 170*da0073e9SAndroid Build Coastguard Worker if len(context) > 78: 171*da0073e9SAndroid Build Coastguard Worker context = "" 172*da0073e9SAndroid Build Coastguard Worker 173*da0073e9SAndroid Build Coastguard Worker # Temporary file names are meaningless, report it's generated code in this 174*da0073e9SAndroid Build Coastguard Worker # case 175*da0073e9SAndroid Build Coastguard Worker if "/tmp/" in component: 176*da0073e9SAndroid Build Coastguard Worker component = "generated code" 177*da0073e9SAndroid Build Coastguard Worker context = "" 178*da0073e9SAndroid Build Coastguard Worker 179*da0073e9SAndroid Build Coastguard Worker out.writerow( 180*da0073e9SAndroid Build Coastguard Worker { 181*da0073e9SAndroid Build Coastguard Worker "bench": bench, 182*da0073e9SAndroid Build Coastguard Worker "name": name, 183*da0073e9SAndroid Build Coastguard Worker "result": r, 184*da0073e9SAndroid Build Coastguard Worker "component": component, 185*da0073e9SAndroid Build Coastguard Worker "context": context, 186*da0073e9SAndroid Build Coastguard Worker "explain": explain, 187*da0073e9SAndroid Build Coastguard Worker "frame_time": frame_time, 188*da0073e9SAndroid Build Coastguard Worker "backend_time": backend_time, 189*da0073e9SAndroid Build Coastguard Worker "graph_count": graph_count, 190*da0073e9SAndroid Build Coastguard Worker "op_count": op_count, 191*da0073e9SAndroid Build Coastguard Worker "graph_breaks": graph_breaks, 192*da0073e9SAndroid Build Coastguard Worker "unique_graph_breaks": unique_graph_breaks, 193*da0073e9SAndroid Build Coastguard Worker } 194*da0073e9SAndroid Build Coastguard Worker ) 195*da0073e9SAndroid Build Coastguard Worker i += 1 196*da0073e9SAndroid Build Coastguard Worker 197*da0073e9SAndroid Build Coastguard Workerif c: 198*da0073e9SAndroid Build Coastguard Worker print(f"failed to classify {c} entries", file=sys.stderr) 199