(WIP) python scripts to generate csmith benchs
This is not working yet, and might well be an abandonned approach
This commit is contained in:
parent
6bdeda910f
commit
fd8478552a
4 changed files with 235 additions and 0 deletions
0
__init__.py
Normal file
0
__init__.py
Normal file
0
benching/__init__.py
Normal file
0
benching/__init__.py
Normal file
0
benching/csmith/__init__.py
Normal file
0
benching/csmith/__init__.py
Normal file
235
benching/csmith/csmith_compile.py
Executable file
235
benching/csmith/csmith_compile.py
Executable file
|
@ -0,0 +1,235 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
""" Helper file to generate Csmith test cases for benchmarking purposes """
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import copy
|
||||||
|
|
||||||
|
sys.path.append('../..')
|
||||||
|
|
||||||
|
from shared_python import do_remote
|
||||||
|
from generate_eh_elf import gen_eh_elfs
|
||||||
|
|
||||||
|
|
||||||
|
def get_base_id(out_dir, base_name):
|
||||||
|
''' Find the smallest xx such that
|
||||||
|
∀y >= x, `[out_dir]/[base_name]0*[yy].*` does not exist '''
|
||||||
|
|
||||||
|
direntry_id_re = re.compile(r'0*(\d+)\..*')
|
||||||
|
|
||||||
|
out = -1
|
||||||
|
|
||||||
|
for entry in os.scandir(out_dir):
|
||||||
|
fname = entry.name
|
||||||
|
if not fname.startswith(base_name):
|
||||||
|
continue
|
||||||
|
fname = fname[len(base_name):] # Truncate base_name from fname
|
||||||
|
match = direntry_id_re.match(fname)
|
||||||
|
if match is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
out = max(out, int(match.group(1)))
|
||||||
|
|
||||||
|
return out + 1
|
||||||
|
|
||||||
|
|
||||||
|
def csmith_generate(name, args):
|
||||||
|
''' Generate a C file using Csmith (possibly on --remote) '''
|
||||||
|
|
||||||
|
command = [
|
||||||
|
args.csmith_binary,
|
||||||
|
]
|
||||||
|
|
||||||
|
if args.fast_csmith:
|
||||||
|
command += ['--max-funcs', '15']
|
||||||
|
else:
|
||||||
|
command += ['--max-funcs', '100']
|
||||||
|
|
||||||
|
def write_file(content):
|
||||||
|
with open(name, 'w') as handle:
|
||||||
|
handle.write(content)
|
||||||
|
|
||||||
|
def postprocess(content):
|
||||||
|
csmith_h_re = re.compile(r'#include "csmith.h"')
|
||||||
|
return_re = re.compile(r'\breturn\b')
|
||||||
|
content = csmith_h_re.sub(
|
||||||
|
'#include <bench.h>\n#include <csmith.h>',
|
||||||
|
return_re.sub(
|
||||||
|
'bench_unwinding(); return',
|
||||||
|
content))
|
||||||
|
write_file(content)
|
||||||
|
|
||||||
|
if args.remote:
|
||||||
|
out = do_remote(args.remote, command)
|
||||||
|
else:
|
||||||
|
out = subprocess.check_output(command).decode('utf-8')
|
||||||
|
|
||||||
|
if out is None:
|
||||||
|
print("Generating {} failed".format(name), file=sys.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
postprocess(out)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def csmith_compile_libunwind(name, src, args):
|
||||||
|
''' Compile a Csmith-generated file, using libunwind as an unwinding
|
||||||
|
mechanism for benchmarking '''
|
||||||
|
|
||||||
|
base_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
benchlib_path = os.path.normpath(os.path.join(base_dir, '../benchlib/'))
|
||||||
|
env = copy.copy(os.environ)
|
||||||
|
env.update({
|
||||||
|
'LD_RUN_PATH': benchlib_path,
|
||||||
|
})
|
||||||
|
command = [
|
||||||
|
'gcc', '-O3',
|
||||||
|
'-L' + benchlib_path, '-lbench.unwind',
|
||||||
|
'-I' + benchlib_path,
|
||||||
|
'-I' + '/usr/include/csmith-2.3.0',
|
||||||
|
'-o', name,
|
||||||
|
src,
|
||||||
|
]
|
||||||
|
print('LD_RUN_PATH={} {}'.format(env['LD_RUN_PATH'], ' '.join(command)))
|
||||||
|
|
||||||
|
try:
|
||||||
|
subprocess.run(' '.join(command), env=env, check=True, shell=True)
|
||||||
|
except subprocess.CalledProcessError as exn:
|
||||||
|
print("Compiling {}: failed with return code {}".format(
|
||||||
|
name, exn.returncode), file=sys.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def csmith_compile_eh_elf(name, src, args):
|
||||||
|
''' Compile a Csmith-generated file, using eh_elf and stack_walker as a
|
||||||
|
mechanism for benchmarking '''
|
||||||
|
|
||||||
|
base_dir = os.path.realpath(__file__)
|
||||||
|
env = {
|
||||||
|
'LD_RUN_PATH': ':'.join(
|
||||||
|
[
|
||||||
|
os.path.join(base_dir, '../benchlib'),
|
||||||
|
os.path.join(base_dir, '../../stack_walker'),
|
||||||
|
'eh_elfs'
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
command = [
|
||||||
|
'gcc', '-O3',
|
||||||
|
src,
|
||||||
|
'-L', os.path.join(base_dir, '../benchlib/'), '-lbench.eh_elf',
|
||||||
|
'-L', os.path.join(base_dir, '../../stack_walker'),
|
||||||
|
'-lstack_walker.global',
|
||||||
|
'-o', name,
|
||||||
|
]
|
||||||
|
|
||||||
|
try:
|
||||||
|
subprocess.run(command, env=env, check=True)
|
||||||
|
except subprocess.CalledProcessError as exn:
|
||||||
|
print("Compiling {}: failed with return code {}".format(
|
||||||
|
name, exn.returncode), file=sys.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
eh_elf_path = os.path.join(args.output, 'eh_elfs')
|
||||||
|
os.makedirs(eh_elf_path, exist_ok=True)
|
||||||
|
gen_eh_elfs(name,
|
||||||
|
eh_elf_path,
|
||||||
|
global_switch=True,
|
||||||
|
deps=True,
|
||||||
|
remote=args.remote)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def generate_and_compile(name_base, args):
|
||||||
|
c_name = name_base + '.c'
|
||||||
|
unwind_bin_name = name_base + '.unwind.bin'
|
||||||
|
eh_elf_bin_name = name_base + '.eh_elf.bin'
|
||||||
|
|
||||||
|
print('\tGenerating C file…')
|
||||||
|
if not csmith_generate(c_name, args):
|
||||||
|
return False
|
||||||
|
print('\tCompiling with libunwind…')
|
||||||
|
if not csmith_compile_libunwind(unwind_bin_name, c_name, args):
|
||||||
|
return False
|
||||||
|
print('\tCompiling with eh_elf…')
|
||||||
|
if not csmith_compile_eh_elf(eh_elf_bin_name, c_name, args):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def process_args():
|
||||||
|
''' Process `sys.argv` arguments '''
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(prog="csmith_compile")
|
||||||
|
|
||||||
|
parser.add_argument('--remote',
|
||||||
|
help=("Execute the heavyweight steps of the "
|
||||||
|
"computation on the remote machine "
|
||||||
|
"indicated, formatted for ssh"))
|
||||||
|
subparsers = parser.add_subparsers()
|
||||||
|
|
||||||
|
gc_parser = subparsers.add_parser(
|
||||||
|
'gen-test',
|
||||||
|
help=("Generate one or more Csmith test cases for benchmarking, and "
|
||||||
|
"compile them into the corresponding binaries linked against "
|
||||||
|
"libunwind and eh_elfs")
|
||||||
|
)
|
||||||
|
|
||||||
|
gc_parser.add_argument('--csmith-binary', default='csmith',
|
||||||
|
help=("Use a different csmith binary path"))
|
||||||
|
gc_parser.add_argument('--output', '-o', required=True,
|
||||||
|
help=("Store the produced tests in this directory"))
|
||||||
|
gc_parser.add_argument('--name', '-t', default='',
|
||||||
|
help=("Define the naming scheme for the output "
|
||||||
|
"files. The files will be named "
|
||||||
|
"[name]xx.ext, where xx is the id of the "
|
||||||
|
"generated test. By default, [name] is empty,"
|
||||||
|
" and xx is initialized as the smallest id"
|
||||||
|
"such that no [name]yy.ext exists, with "
|
||||||
|
"yy >= xx."))
|
||||||
|
gc_parser.add_argument('--fast-csmith', action='store_true',
|
||||||
|
help=("Quickly generate a small (and probably "
|
||||||
|
"uninterresting) Csmith source file. Useful "
|
||||||
|
"for fast setup testing."))
|
||||||
|
gc_parser.add_argument('--number', '-n', default='1',
|
||||||
|
help=("The number of test cases to generate. "
|
||||||
|
"Defaults to 1."))
|
||||||
|
gc_parser.set_defaults(main_func=main_gen_compile)
|
||||||
|
|
||||||
|
parsed = parser.parse_args()
|
||||||
|
if 'main_func' not in parsed: # No subcommand
|
||||||
|
parser.print_help()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
|
||||||
|
def main_gen_compile(args):
|
||||||
|
""" Main function handling gen-test """
|
||||||
|
|
||||||
|
base_id = get_base_id(args.output, args.name)
|
||||||
|
|
||||||
|
def naming_scheme(test_id):
|
||||||
|
return os.path.join(
|
||||||
|
args.output,
|
||||||
|
"{}{:03}".format(args.name, test_id))
|
||||||
|
|
||||||
|
for zeroed_test_id in range(int(args.number)):
|
||||||
|
test_id = base_id + zeroed_test_id
|
||||||
|
name_base = naming_scheme(test_id)
|
||||||
|
print('> {}…'.format(name_base))
|
||||||
|
generate_and_compile(name_base, args=args)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = process_args()
|
||||||
|
args.main_func(args)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
Loading…
Reference in a new issue