1*9880d681SAndroid Build Coastguard Worker#!/usr/bin/env python 2*9880d681SAndroid Build Coastguard Worker# 3*9880d681SAndroid Build Coastguard Worker# Given a previous good compile narrow down miscompiles. 4*9880d681SAndroid Build Coastguard Worker# Expects two directories named "before" and "after" each containing a set of 5*9880d681SAndroid Build Coastguard Worker# assembly or object files where the "after" version is assumed to be broken. 6*9880d681SAndroid Build Coastguard Worker# You also have to provide a script called "link_test". It is called with a list 7*9880d681SAndroid Build Coastguard Worker# of files which should be linked together and result tested. "link_test" should 8*9880d681SAndroid Build Coastguard Worker# returns with exitcode 0 if the linking and testing succeeded. 9*9880d681SAndroid Build Coastguard Worker# 10*9880d681SAndroid Build Coastguard Worker# abtest.py operates by taking all files from the "before" directory and 11*9880d681SAndroid Build Coastguard Worker# in each step replacing one of them with a file from the "bad" directory. 12*9880d681SAndroid Build Coastguard Worker# 13*9880d681SAndroid Build Coastguard Worker# Additionally you can perform the same steps with a single .s file. In this 14*9880d681SAndroid Build Coastguard Worker# mode functions are identified by "# -- Begin FunctionName" and 15*9880d681SAndroid Build Coastguard Worker# "# -- End FunctionName" markers. The abtest.py then takes all functions from 16*9880d681SAndroid Build Coastguard Worker# the file in the "before" directory and replaces one function with the 17*9880d681SAndroid Build Coastguard Worker# corresponding function from the "bad" file in each step. 18*9880d681SAndroid Build Coastguard Worker# 19*9880d681SAndroid Build Coastguard Worker# Example usage to identify miscompiled files: 20*9880d681SAndroid Build Coastguard Worker# 1. Create a link_test script, make it executable. Simple Example: 21*9880d681SAndroid Build Coastguard Worker# clang "$@" -o /tmp/test && /tmp/test || echo "PROBLEM" 22*9880d681SAndroid Build Coastguard Worker# 2. Run the script to figure out which files are miscompiled: 23*9880d681SAndroid Build Coastguard Worker# > ./abtest.py 24*9880d681SAndroid Build Coastguard Worker# somefile.s: ok 25*9880d681SAndroid Build Coastguard Worker# someotherfile.s: skipped: same content 26*9880d681SAndroid Build Coastguard Worker# anotherfile.s: failed: './link_test' exitcode != 0 27*9880d681SAndroid Build Coastguard Worker# ... 28*9880d681SAndroid Build Coastguard Worker# Example usage to identify miscompiled functions inside a file: 29*9880d681SAndroid Build Coastguard Worker# 3. First you have to mark begin and end of the functions. 30*9880d681SAndroid Build Coastguard Worker# The script comes with some examples called mark_xxx.py. 31*9880d681SAndroid Build Coastguard Worker# Unfortunately this is very specific to your environment and it is likely 32*9880d681SAndroid Build Coastguard Worker# that you have to write a custom version for your environment. 33*9880d681SAndroid Build Coastguard Worker# > for i in before/*.s after/*.s; do mark_xxx.py $i; done 34*9880d681SAndroid Build Coastguard Worker# 4. Run the tests on a single file (assuming before/file.s and 35*9880d681SAndroid Build Coastguard Worker# after/file.s exist) 36*9880d681SAndroid Build Coastguard Worker# > ./abtest.py file.s 37*9880d681SAndroid Build Coastguard Worker# funcname1 [0/XX]: ok 38*9880d681SAndroid Build Coastguard Worker# funcname2 [1/XX]: ok 39*9880d681SAndroid Build Coastguard Worker# funcname3 [2/XX]: skipped: same content 40*9880d681SAndroid Build Coastguard Worker# funcname4 [3/XX]: failed: './link_test' exitcode != 0 41*9880d681SAndroid Build Coastguard Worker# ... 42*9880d681SAndroid Build Coastguard Workerfrom fnmatch import filter 43*9880d681SAndroid Build Coastguard Workerfrom sys import stderr 44*9880d681SAndroid Build Coastguard Workerimport argparse 45*9880d681SAndroid Build Coastguard Workerimport filecmp 46*9880d681SAndroid Build Coastguard Workerimport os 47*9880d681SAndroid Build Coastguard Workerimport subprocess 48*9880d681SAndroid Build Coastguard Workerimport sys 49*9880d681SAndroid Build Coastguard Worker 50*9880d681SAndroid Build Coastguard WorkerLINKTEST="./link_test" 51*9880d681SAndroid Build Coastguard WorkerESCAPE="\033[%sm" 52*9880d681SAndroid Build Coastguard WorkerBOLD=ESCAPE % "1" 53*9880d681SAndroid Build Coastguard WorkerRED=ESCAPE % "31" 54*9880d681SAndroid Build Coastguard WorkerNORMAL=ESCAPE % "0" 55*9880d681SAndroid Build Coastguard WorkerFAILED=RED+"failed"+NORMAL 56*9880d681SAndroid Build Coastguard Worker 57*9880d681SAndroid Build Coastguard Workerdef find(dir, file_filter=None): 58*9880d681SAndroid Build Coastguard Worker files = [walkdir[0]+"/"+file for walkdir in os.walk(dir) for file in walkdir[2]] 59*9880d681SAndroid Build Coastguard Worker if file_filter != None: 60*9880d681SAndroid Build Coastguard Worker files = filter(files, file_filter) 61*9880d681SAndroid Build Coastguard Worker return files 62*9880d681SAndroid Build Coastguard Worker 63*9880d681SAndroid Build Coastguard Workerdef error(message): 64*9880d681SAndroid Build Coastguard Worker stderr.write("Error: %s\n" % (message,)) 65*9880d681SAndroid Build Coastguard Worker 66*9880d681SAndroid Build Coastguard Workerdef warn(message): 67*9880d681SAndroid Build Coastguard Worker stderr.write("Warning: %s\n" % (message,)) 68*9880d681SAndroid Build Coastguard Worker 69*9880d681SAndroid Build Coastguard Workerdef extract_functions(file): 70*9880d681SAndroid Build Coastguard Worker functions = [] 71*9880d681SAndroid Build Coastguard Worker in_function = None 72*9880d681SAndroid Build Coastguard Worker for line in open(file): 73*9880d681SAndroid Build Coastguard Worker if line.startswith("# -- Begin "): 74*9880d681SAndroid Build Coastguard Worker if in_function != None: 75*9880d681SAndroid Build Coastguard Worker warn("Missing end of function %s" % (in_function,)) 76*9880d681SAndroid Build Coastguard Worker funcname = line[12:-1] 77*9880d681SAndroid Build Coastguard Worker in_function = funcname 78*9880d681SAndroid Build Coastguard Worker text = line 79*9880d681SAndroid Build Coastguard Worker elif line.startswith("# -- End "): 80*9880d681SAndroid Build Coastguard Worker function_name = line[10:-1] 81*9880d681SAndroid Build Coastguard Worker if in_function != function_name: 82*9880d681SAndroid Build Coastguard Worker warn("End %s does not match begin %s" % (function_name, in_function)) 83*9880d681SAndroid Build Coastguard Worker else: 84*9880d681SAndroid Build Coastguard Worker text += line 85*9880d681SAndroid Build Coastguard Worker functions.append( (in_function, text) ) 86*9880d681SAndroid Build Coastguard Worker in_function = None 87*9880d681SAndroid Build Coastguard Worker elif in_function != None: 88*9880d681SAndroid Build Coastguard Worker text += line 89*9880d681SAndroid Build Coastguard Worker return functions 90*9880d681SAndroid Build Coastguard Worker 91*9880d681SAndroid Build Coastguard Workerdef replace_function(file, function, replacement, dest): 92*9880d681SAndroid Build Coastguard Worker out = open(dest, "w") 93*9880d681SAndroid Build Coastguard Worker skip = False 94*9880d681SAndroid Build Coastguard Worker found = False 95*9880d681SAndroid Build Coastguard Worker in_function = None 96*9880d681SAndroid Build Coastguard Worker for line in open(file): 97*9880d681SAndroid Build Coastguard Worker if line.startswith("# -- Begin "): 98*9880d681SAndroid Build Coastguard Worker if in_function != None: 99*9880d681SAndroid Build Coastguard Worker warn("Missing end of function %s" % (in_function,)) 100*9880d681SAndroid Build Coastguard Worker funcname = line[12:-1] 101*9880d681SAndroid Build Coastguard Worker in_function = funcname 102*9880d681SAndroid Build Coastguard Worker if in_function == function: 103*9880d681SAndroid Build Coastguard Worker out.write(replacement) 104*9880d681SAndroid Build Coastguard Worker skip = True 105*9880d681SAndroid Build Coastguard Worker elif line.startswith("# -- End "): 106*9880d681SAndroid Build Coastguard Worker function_name = line[10:-1] 107*9880d681SAndroid Build Coastguard Worker if in_function != function_name: 108*9880d681SAndroid Build Coastguard Worker warn("End %s does not match begin %s" % (function_name, in_function)) 109*9880d681SAndroid Build Coastguard Worker in_function = None 110*9880d681SAndroid Build Coastguard Worker if skip: 111*9880d681SAndroid Build Coastguard Worker skip = False 112*9880d681SAndroid Build Coastguard Worker continue 113*9880d681SAndroid Build Coastguard Worker if not skip: 114*9880d681SAndroid Build Coastguard Worker out.write(line) 115*9880d681SAndroid Build Coastguard Worker 116*9880d681SAndroid Build Coastguard Workerdef announce_test(name): 117*9880d681SAndroid Build Coastguard Worker stderr.write("%s%s%s: " % (BOLD, name, NORMAL)) 118*9880d681SAndroid Build Coastguard Worker stderr.flush() 119*9880d681SAndroid Build Coastguard Worker 120*9880d681SAndroid Build Coastguard Workerdef announce_result(result, info): 121*9880d681SAndroid Build Coastguard Worker stderr.write(result) 122*9880d681SAndroid Build Coastguard Worker if info != "": 123*9880d681SAndroid Build Coastguard Worker stderr.write(": %s" % info) 124*9880d681SAndroid Build Coastguard Worker stderr.write("\n") 125*9880d681SAndroid Build Coastguard Worker stderr.flush() 126*9880d681SAndroid Build Coastguard Worker 127*9880d681SAndroid Build Coastguard Workerdef testrun(files): 128*9880d681SAndroid Build Coastguard Worker linkline="%s %s" % (LINKTEST, " ".join(files),) 129*9880d681SAndroid Build Coastguard Worker res = subprocess.call(linkline, shell=True) 130*9880d681SAndroid Build Coastguard Worker if res != 0: 131*9880d681SAndroid Build Coastguard Worker announce_result(FAILED, "'%s' exitcode != 0" % LINKTEST) 132*9880d681SAndroid Build Coastguard Worker return False 133*9880d681SAndroid Build Coastguard Worker else: 134*9880d681SAndroid Build Coastguard Worker announce_result("ok", "") 135*9880d681SAndroid Build Coastguard Worker return True 136*9880d681SAndroid Build Coastguard Worker 137*9880d681SAndroid Build Coastguard Workerdef check_files(): 138*9880d681SAndroid Build Coastguard Worker """Check files mode""" 139*9880d681SAndroid Build Coastguard Worker for i in range(0, len(NO_PREFIX)): 140*9880d681SAndroid Build Coastguard Worker f = NO_PREFIX[i] 141*9880d681SAndroid Build Coastguard Worker b=baddir+"/"+f 142*9880d681SAndroid Build Coastguard Worker if b not in BAD_FILES: 143*9880d681SAndroid Build Coastguard Worker warn("There is no corresponding file to '%s' in %s" \ 144*9880d681SAndroid Build Coastguard Worker % (gooddir+"/"+f, baddir)) 145*9880d681SAndroid Build Coastguard Worker continue 146*9880d681SAndroid Build Coastguard Worker 147*9880d681SAndroid Build Coastguard Worker announce_test(f + " [%s/%s]" % (i+1, len(NO_PREFIX))) 148*9880d681SAndroid Build Coastguard Worker 149*9880d681SAndroid Build Coastguard Worker # combine files (everything from good except f) 150*9880d681SAndroid Build Coastguard Worker testfiles=[] 151*9880d681SAndroid Build Coastguard Worker skip=False 152*9880d681SAndroid Build Coastguard Worker for c in NO_PREFIX: 153*9880d681SAndroid Build Coastguard Worker badfile = baddir+"/"+c 154*9880d681SAndroid Build Coastguard Worker goodfile = gooddir+"/"+c 155*9880d681SAndroid Build Coastguard Worker if c == f: 156*9880d681SAndroid Build Coastguard Worker testfiles.append(badfile) 157*9880d681SAndroid Build Coastguard Worker if filecmp.cmp(goodfile, badfile): 158*9880d681SAndroid Build Coastguard Worker announce_result("skipped", "same content") 159*9880d681SAndroid Build Coastguard Worker skip = True 160*9880d681SAndroid Build Coastguard Worker break 161*9880d681SAndroid Build Coastguard Worker else: 162*9880d681SAndroid Build Coastguard Worker testfiles.append(goodfile) 163*9880d681SAndroid Build Coastguard Worker if skip: 164*9880d681SAndroid Build Coastguard Worker continue 165*9880d681SAndroid Build Coastguard Worker testrun(testfiles) 166*9880d681SAndroid Build Coastguard Worker 167*9880d681SAndroid Build Coastguard Workerdef check_functions_in_file(base, goodfile, badfile): 168*9880d681SAndroid Build Coastguard Worker functions = extract_functions(goodfile) 169*9880d681SAndroid Build Coastguard Worker if len(functions) == 0: 170*9880d681SAndroid Build Coastguard Worker warn("Couldn't find any function in %s, missing annotations?" % (goodfile,)) 171*9880d681SAndroid Build Coastguard Worker return 172*9880d681SAndroid Build Coastguard Worker badfunctions = dict(extract_functions(badfile)) 173*9880d681SAndroid Build Coastguard Worker if len(functions) == 0: 174*9880d681SAndroid Build Coastguard Worker warn("Couldn't find any function in %s, missing annotations?" % (badfile,)) 175*9880d681SAndroid Build Coastguard Worker return 176*9880d681SAndroid Build Coastguard Worker 177*9880d681SAndroid Build Coastguard Worker COMBINED="/tmp/combined.s" 178*9880d681SAndroid Build Coastguard Worker i = 0 179*9880d681SAndroid Build Coastguard Worker for (func,func_text) in functions: 180*9880d681SAndroid Build Coastguard Worker announce_test(func + " [%s/%s]" % (i+1, len(functions))) 181*9880d681SAndroid Build Coastguard Worker i+=1 182*9880d681SAndroid Build Coastguard Worker if func not in badfunctions: 183*9880d681SAndroid Build Coastguard Worker warn("Function '%s' missing from bad file" % func) 184*9880d681SAndroid Build Coastguard Worker continue 185*9880d681SAndroid Build Coastguard Worker if badfunctions[func] == func_text: 186*9880d681SAndroid Build Coastguard Worker announce_result("skipped", "same content") 187*9880d681SAndroid Build Coastguard Worker continue 188*9880d681SAndroid Build Coastguard Worker replace_function(goodfile, func, badfunctions[func], COMBINED) 189*9880d681SAndroid Build Coastguard Worker testfiles=[] 190*9880d681SAndroid Build Coastguard Worker for c in NO_PREFIX: 191*9880d681SAndroid Build Coastguard Worker if c == base: 192*9880d681SAndroid Build Coastguard Worker testfiles.append(COMBINED) 193*9880d681SAndroid Build Coastguard Worker continue 194*9880d681SAndroid Build Coastguard Worker testfiles.append(gooddir + "/" + c) 195*9880d681SAndroid Build Coastguard Worker 196*9880d681SAndroid Build Coastguard Worker testrun(testfiles) 197*9880d681SAndroid Build Coastguard Worker 198*9880d681SAndroid Build Coastguard Workerparser = argparse.ArgumentParser() 199*9880d681SAndroid Build Coastguard Workerparser.add_argument('--a', dest='dir_a', default='before') 200*9880d681SAndroid Build Coastguard Workerparser.add_argument('--b', dest='dir_b', default='after') 201*9880d681SAndroid Build Coastguard Workerparser.add_argument('--insane', help='Skip sanity check', action='store_true') 202*9880d681SAndroid Build Coastguard Workerparser.add_argument('file', metavar='file', nargs='?') 203*9880d681SAndroid Build Coastguard Workerconfig = parser.parse_args() 204*9880d681SAndroid Build Coastguard Worker 205*9880d681SAndroid Build Coastguard Workergooddir=config.dir_a 206*9880d681SAndroid Build Coastguard Workerbaddir=config.dir_b 207*9880d681SAndroid Build Coastguard Worker 208*9880d681SAndroid Build Coastguard WorkerBAD_FILES=find(baddir, "*") 209*9880d681SAndroid Build Coastguard WorkerGOOD_FILES=find(gooddir, "*") 210*9880d681SAndroid Build Coastguard WorkerNO_PREFIX=sorted([x[len(gooddir)+1:] for x in GOOD_FILES]) 211*9880d681SAndroid Build Coastguard Worker 212*9880d681SAndroid Build Coastguard Worker# "Checking whether build environment is sane ..." 213*9880d681SAndroid Build Coastguard Workerif not config.insane: 214*9880d681SAndroid Build Coastguard Worker announce_test("sanity check") 215*9880d681SAndroid Build Coastguard Worker if not os.access(LINKTEST, os.X_OK): 216*9880d681SAndroid Build Coastguard Worker error("Expect '%s' to be present and executable" % (LINKTEST,)) 217*9880d681SAndroid Build Coastguard Worker exit(1) 218*9880d681SAndroid Build Coastguard Worker 219*9880d681SAndroid Build Coastguard Worker res = testrun(GOOD_FILES) 220*9880d681SAndroid Build Coastguard Worker if not res: 221*9880d681SAndroid Build Coastguard Worker # "build environment is grinning and holding a spatula. Guess not." 222*9880d681SAndroid Build Coastguard Worker linkline="%s %s" % (LINKTEST, " ".join(GOOD_FILES),) 223*9880d681SAndroid Build Coastguard Worker stderr.write("\n%s\n\n" % linkline) 224*9880d681SAndroid Build Coastguard Worker stderr.write("Returned with exitcode != 0\n") 225*9880d681SAndroid Build Coastguard Worker sys.exit(1) 226*9880d681SAndroid Build Coastguard Worker 227*9880d681SAndroid Build Coastguard Workerif config.file is not None: 228*9880d681SAndroid Build Coastguard Worker # File exchange mode 229*9880d681SAndroid Build Coastguard Worker goodfile = gooddir+"/"+config.file 230*9880d681SAndroid Build Coastguard Worker badfile = baddir+"/"+config.file 231*9880d681SAndroid Build Coastguard Worker check_functions_in_file(config.file, goodfile, badfile) 232*9880d681SAndroid Build Coastguard Workerelse: 233*9880d681SAndroid Build Coastguard Worker # Function exchange mode 234*9880d681SAndroid Build Coastguard Worker check_files() 235