[llvm] r264482 - Put my abtest scripts into the util directory
Quentin Colombet via llvm-commits
llvm-commits at lists.llvm.org
Fri Mar 25 17:36:14 PDT 2016
Hi Matthias,
Thanks for sharing those, now, you are on the spot for improvement requests :).
> On Mar 25, 2016, at 5:24 PM, Matthias Braun via llvm-commits <llvm-commits at lists.llvm.org> wrote:
>
> Author: matze
> Date: Fri Mar 25 19:23:59 2016
> New Revision: 264482
>
> URL: http://llvm.org/viewvc/llvm-project?rev=264482&view=rev
> Log:
> Put my abtest scripts into the util directory
>
> See http://lists.llvm.org/pipermail/llvm-dev/2016-March/097640.html
> There is also a description/tutorial in the comments of the abtest.py
> file.
>
> Added:
> llvm/trunk/utils/abtest/
> llvm/trunk/utils/abtest/abtest.py (with props)
> llvm/trunk/utils/abtest/mark_aarch64fns.py (with props)
> llvm/trunk/utils/abtest/mark_armfns.py (with props)
>
> Added: llvm/trunk/utils/abtest/abtest.py
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/utils/abtest/abtest.py?rev=264482&view=auto
> ==============================================================================
> --- llvm/trunk/utils/abtest/abtest.py (added)
> +++ llvm/trunk/utils/abtest/abtest.py Fri Mar 25 19:23:59 2016
Could you have the script also accept .o inputs (not the bisecting function part of course)?
> @@ -0,0 +1,223 @@
> +#!/usr/bin/env python
> +#
> +# Given a previous good compile narrow down miscompiles.
> +# Expectes two directories named "before" and "after" each containing a set of
> +# assembly files where the "after" version is assumed to be broken.
> +# Also assumes the presence of a executable or script "link_test" which when
> +# called with a set of assembly files will link them together and test if the
> +# resulting executable is "good".
> +#
> +# Example usage:
> +# 1. Create a link_test script, make it executable. Simple Example:
> +# clang "$@" -o /tmp/test && /tmp/test || echo "PROBLEM"
> +# 2. Run the script to figure out which files are miscompiled:
> +# > ./abtest.py
> +# somefile.s: ok
> +# someotherfile.s: skipped: same content
> +# anotherfile.s: failed: './link_test' exitcode != 0
> +# ...
> +# 3. If you want to replace+test with the functions inside a single file
> +# you first have to mark function begins and ends in the .s files
> +# this directory comes with some: mark_XXX.py example scripts,
> +# unfortunately you usually have to adapt them to each environment.
> +# > for i in before/*.s after/*.s; do mark_xxx.py $i; done
> +# 4. Run the tests on a single file
> +# > ./abtest.py
> +# funcname1 [0/XX]: ok
> +# funcname2 [1/XX]: ok
> +# funcname3 [2/XX]: skipped: same content
> +# funcname4 [3/XX]: failed: './link_test' exitcode != 0
> +# ...
> +import sys
> +import os
> +from os import system, mkdir, walk, makedirs, errno, getenv
> +from os.path import dirname, isdir
> +from shutil import rmtree, copyfile
> +from fnmatch import filter
> +from itertools import chain
> +from subprocess import call
> +from sys import stderr
> +import argparse
> +import filecmp
> +
> +LINKTEST="./link_test"
> +ESCAPE="\033[%sm"
> +BOLD=ESCAPE % "1"
> +RED=ESCAPE % "31"
> +NORMAL=ESCAPE % "0"
> +FAILED=RED+"failed"+NORMAL
> +
> +def mkdirtree(path):
> + try:
> + makedirs(path)
> + except OSError as exc:
> + if exc.errno == errno.EEXIST and isdir(path):
> + pass
> + else:
> + raise
> +
> +def find(dir, file_filter=None):
> + files = [walkdir[0]+"/"+file for walkdir in walk(dir) for file in walkdir[2]]
> + if file_filter != None:
> + files = filter(files, file_filter)
> + return files
> +
> +def error(message):
> + stderr.write("Error: %s\n" % (message,))
> +
> +def warn(message):
> + stderr.write("Warning: %s\n" % (message,))
> +
> +def notice(message):
> + stderr.write("%s\n" % message)
> +
> +def extract_functions(file):
> + functions = []
> + in_function = None
> + for line in open(file):
> + if line.startswith("# -- Begin "):
> + if in_function != None:
> + warn("Missing end of function %s" % (in_function,))
> + funcname = line[12:-1]
> + in_function = funcname
> + text = line
> + elif line.startswith("# -- End "):
> + function_name = line[10:-1]
> + if in_function != function_name:
> + warn("End %s does not match begin %s" % (function_name, in_function))
> + else:
> + text += line
> + functions.append( (in_function, text) )
> + in_function = None
> + elif in_function != None:
> + text += line
> + return functions
> +
> +def replace_function(file, function, replacement, dest):
> + out = open(dest, "w")
> + skip = False
> + found = False
> + in_function = None
> + for line in open(file):
> + if line.startswith("# -- Begin "):
> + if in_function != None:
> + warn("Missing end of function %s" % (in_function,))
> + funcname = line[12:-1]
> + in_function = funcname
> + if in_function == function:
> + out.write(replacement)
> + skip = True
> + elif line.startswith("# -- End "):
> + function_name = line[10:-1]
> + if in_function != function_name:
> + warn("End %s does not match begin %s" % (function_name, in_function))
> + in_function = None
> + if skip:
> + skip = False
> + continue
> + if not skip:
> + out.write(line)
> +
> +def announce_test(name):
> + stderr.write("%s%s%s: " % (BOLD, name, NORMAL))
> + stderr.flush()
> +
> +def announce_result(result, info):
> + stderr.write(result)
> + if info != "":
> + stderr.write(": %s" % info)
> + stderr.write("\n")
> + stderr.flush()
> +
> +def testrun(files):
> + linkline="%s %s" % (LINKTEST, " ".join(files),)
> + res = call(linkline, shell=True)
> + if res != 0:
> + announce_result(FAILED, "'%s' exitcode != 0" % LINKTEST)
> + else:
> + announce_result("ok", "")
> +
> +def check_files():
> + """Check files mode"""
> + for f in NO_PREFIX:
> + b=baddir+"/"+f
> + if b not in BAD_FILES:
> + warn("There is no corresponding file to '%s' in %s" \
> + % (gooddir+"/"+f, baddir))
> + continue
> +
> + announce_test(f)
> +
> + # combine files (everything from good except f)
> + testfiles=[]
> + skip=False
> + for c in NO_PREFIX:
> + badfile = baddir+"/"+c
> + goodfile = gooddir+"/"+c
> + if c == f:
> + testfiles.append(badfile)
> + if filecmp.cmp(goodfile, badfile):
> + announce_result("skipped", "same content")
> + skip = True
> + break
> + else:
> + testfiles.append(goodfile)
> + if skip:
> + continue
> + testrun(testfiles)
> +
> +def check_functions_in_file(base, goodfile, badfile):
> + functions = extract_functions(goodfile)
> + if len(functions) == 0:
> + warn("Couldn't find any function in %s, missing annotations?" % (goodfile,))
> + return
> + badfunctions = dict(extract_functions(badfile))
> + if len(functions) == 0:
> + warn("Couldn't find any function in %s, missing annotations?" % (badfile,))
> + return
> +
> + COMBINED="/tmp/combined.s"
> + i = 0
> + for (func,func_text) in functions:
> + announce_test(func + " [%s/%s]" % (i, len(functions)))
> + i+=1
> + if func not in badfunctions:
> + warn("Function '%s' missing from bad file" % func)
> + continue
> + if badfunctions[func] == func_text:
> + announce_result("skipped", "same content")
> + continue
> + replace_function(goodfile, func, badfunctions[func], COMBINED)
> + testfiles=[]
> + for c in NO_PREFIX:
> + if c == base:
> + testfiles.append(COMBINED)
> + continue
> + testfiles.append(gooddir + "/" + c)
> +
> + testrun(testfiles)
> +
> +parser = argparse.ArgumentParser()
> +parser.add_argument('--a', dest='dir_a', default='before')
> +parser.add_argument('--b', dest='dir_b', default='after')
> +parser.add_argument('file', metavar='file', nargs='?')
> +config = parser.parse_args()
> +
> +# Check if environment is sane
> +if not os.access(LINKTEST, os.X_OK):
> + error("Expect '%s' to be present and executable" % (LINKTEST,))
> + exit(1)
> +
> +gooddir=config.dir_a
> +baddir=config.dir_b
> +
> +BAD_FILES=find(baddir, "*.s")
> +GOOD_FILES=find(gooddir, "*.s")
> +NO_PREFIX=sorted([x[len(gooddir)+1:] for x in GOOD_FILES])
> +
> +if config.file is not None:
> + goodfile = gooddir+"/"+config.file
> + badfile = baddir+"/"+config.file
> + check_functions_in_file(config.file, goodfile, badfile)
> +else:
> + check_files()
>
> Propchange: llvm/trunk/utils/abtest/abtest.py
> ------------------------------------------------------------------------------
> svn:executable = *
>
> Added: llvm/trunk/utils/abtest/mark_aarch64fns.py
This script may be useful for hand written assembly (though the compiler is not involved for those), but in general, I believe we could teach MC to emit those.
Comment are cheap in assembly files :).
Cheers,
-Quentin
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/utils/abtest/mark_aarch64fns.py?rev=264482&view=auto
> ==============================================================================
> --- llvm/trunk/utils/abtest/mark_aarch64fns.py (added)
> +++ llvm/trunk/utils/abtest/mark_aarch64fns.py Fri Mar 25 19:23:59 2016
> @@ -0,0 +1,65 @@
> +#!/usr/bin/env python
> +#
> +# Mark functions in an arm assembly file. This is done by surrounding the
> +# function with "# -- Begin Name" and "# -- End Name"
> +# (This script is designed for aarch64 ios assembly syntax)
> +import sys
> +import re
> +
> +inp = open(sys.argv[1], "r").readlines()
> +
> +# First pass
> +linenum = 0
> +INVALID=-100
> +last_align = INVALID
> +last_code = INVALID
> +last_globl = INVALID
> +last_globl_name = None
> +begin = INVALID
> +in_text_section = False
> +begins = dict()
> +for line in inp:
> + linenum += 1
> + if re.search(r'.section\s+__TEXT,__text,regular,pure_instructions', line):
> + in_text_section = True
> + continue
> + elif ".section" in line:
> + in_text_section = False
> + continue
> +
> + if not in_text_section:
> + continue
> +
> + if ".align" in line:
> + last_align = linenum
> + gl = re.search(r'.globl\s+(\w+)', line)
> + if gl:
> + last_globl_name = gl.group(1)
> + last_globl = linenum
> + m = re.search(r'^(\w+):', line)
> + if m and begin == INVALID:
> + labelname = m.group(1)
> + if last_globl+2 == linenum and last_globl_name == labelname:
> + begin = last_globl
> + funcname = labelname
> + if line == "\n" and begin != INVALID:
> + end = linenum
> + triple = (funcname, begin, end)
> + begins[begin] = triple
> + begin = INVALID
> +
> +# Second pass: Mark
> +out = open(sys.argv[1], "w")
> +in_func = None
> +linenum = 0
> +for line in inp:
> + linenum += 1
> + if in_func is not None and linenum == end:
> + out.write("# -- End %s\n" % in_func)
> + in_func = None
> +
> + triple = begins.get(linenum)
> + if triple is not None:
> + in_func, begin, end = triple
> + out.write("# -- Begin %s\n" % in_func)
> + out.write(line)
>
> Propchange: llvm/trunk/utils/abtest/mark_aarch64fns.py
> ------------------------------------------------------------------------------
> svn:executable = *
>
> Added: llvm/trunk/utils/abtest/mark_armfns.py
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/utils/abtest/mark_armfns.py?rev=264482&view=auto
> ==============================================================================
> --- llvm/trunk/utils/abtest/mark_armfns.py (added)
> +++ llvm/trunk/utils/abtest/mark_armfns.py Fri Mar 25 19:23:59 2016
> @@ -0,0 +1,54 @@
> +#!/usr/bin/env python
> +#
> +# Mark functions in an arm assembly file. This is done by surrounding the
> +# function with "# -- Begin Name" and "# -- End Name"
> +# (This script is designed for arm ios assembly syntax)
> +import sys
> +import re
> +
> +inp = open(sys.argv[1], "r").readlines()
> +
> +# First pass
> +linenum = 0
> +INVALID=-100
> +last_align = INVALID
> +last_code = INVALID
> +last_globl = INVALID
> +begin = INVALID
> +begins = dict()
> +for line in inp:
> + linenum += 1
> + if ".align" in line:
> + last_align = linenum
> + if ".code" in line:
> + last_code = linenum
> + if ".globl" in line:
> + last_globl = linenum
> + m = re.search(r'.thumb_func\s+(\w+)', line)
> + if m:
> + funcname = m.group(1)
> + if last_code == last_align+1 and (linenum - last_code) < 4:
> + begin = last_align
> + if last_globl+1 == last_align:
> + begin = last_globl
> + if line == "\n" and begin != INVALID:
> + end = linenum
> + triple = (funcname, begin, end)
> + begins[begin] = triple
> + begin = INVALID
> +
> +# Second pass: Mark
> +out = open(sys.argv[1], "w")
> +in_func = None
> +linenum = 0
> +for line in inp:
> + linenum += 1
> + if in_func is not None and linenum == end:
> + out.write("# -- End %s\n" % in_func)
> + in_func = None
> +
> + triple = begins.get(linenum)
> + if triple is not None:
> + in_func, begin, end = triple
> + out.write("# -- Begin %s\n" % in_func)
> + out.write(line)
>
> Propchange: llvm/trunk/utils/abtest/mark_armfns.py
> ------------------------------------------------------------------------------
> svn:executable = *
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
More information about the llvm-commits
mailing list