Compare commits

...

8 commits

Author SHA1 Message Date
aCompetentBean 6a7e3f98f5 Merge pull request 'Quality of Life Improvements' (#7) from ayrton/qol into main
Reviewed-on: #7
2023-11-25 14:16:30 -07:00
ayrton 880c8e0713 Quality of Life Improvements
- Counter for generated tests
- Nicer messages when things go wrong

Took 51 minutes
2023-11-25 14:15:07 -07:00
ayrton 9f915bd80e Fixed conditionals rarely evaluating
Added the setting in the yaml for truthiness, and added a timeout error to the code generation.

Need to fix the generation error that prevents a large amount of code from being generated...

Took 1 hour 47 minutes
2023-11-25 11:40:46 -07:00
aCompetentBean 0d96551e82 Merge pull request 'Generation Error Mitigation' (#4) from ayrton/generation-errors into main
Reviewed-on: #4
2023-11-24 15:47:48 -07:00
Ayrton 1c91f6ac86 Generation errors fixed or gracefully handled 2023-11-24 15:45:38 -07:00
ayrton ddc3875f56 Tried to deal with strange printing and created a problem
Took 1 hour 25 minutes
2023-11-24 09:31:18 -07:00
ayrton 1aef59672f Added extra options to the YAML
Took 33 minutes
2023-11-24 08:06:22 -07:00
aCompetentBean 15449c8899 Merge pull request 'Refactored ASTGenerator' (#3) from ayrton/refactor into main
Reviewed-on: #3
2023-11-24 07:34:46 -07:00
17 changed files with 458 additions and 56 deletions

1
.gitignore vendored
View file

@ -1,4 +1,5 @@
fuzzer/ fuzzer/
exec/
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/

View file

@ -1,7 +1,9 @@
import string import string
import warnings
from english_words import get_english_words_set from english_words import get_english_words_set
from ast_generator.tiny_py_unparse import TinyPyUnparser
from ast_generator.utils import * from ast_generator.utils import *
from ast_generator.utils import filter_options, _choose_option from ast_generator.utils import filter_options, _choose_option
from constants import * from constants import *
@ -84,8 +86,8 @@ class AstGenerator:
def _init_names(self): def _init_names(self):
names = get_english_words_set(['web2'], alpha=True) names = get_english_words_set(['web2'], alpha=True)
possible_names = filter(lambda x: self.settings['properties']['id-length']['max'] <= len(x) <= possible_names = filter(lambda x: (self.settings['properties']['id-length']['max'] <= len(x) <=
self.settings['properties']['id-length']['max'] and not keyword.iskeyword(x), self.settings['properties']['id-length']['max']) and not keyword.iskeyword(x),
names) names)
var_name_list = list(possible_names) var_name_list = list(possible_names)
var_name_len = len(var_name_list) var_name_len = len(var_name_list)
@ -140,21 +142,23 @@ class AstGenerator:
self.current_ast_element.append(element) self.current_ast_element.append(element)
self.current_ast_element = element self.current_ast_element = element
if block_type in [GAZ_PROCEDURE_TAG, GAZ_FUNCTION_TAG]:
self.generate_statements()
else:
self.generate_statements(include='declaration')
self.generate_statements(exclude='declaration')
# Generate the loop condition increment if we are in a loop # Generate the loop condition increment if we are in a loop
if block_type == GAZ_LOOP_TAG: if block_type == GAZ_LOOP_TAG:
self.generate_loop_condition_check(loop_var) self.generate_loop_condition_check(loop_var)
self.generate_loop_condition_increment(loop_var) self.generate_loop_condition_increment(loop_var)
if block_type not in [GAZ_PROCEDURE_TAG, GAZ_FUNCTION_TAG]:
self.generate_statements()
else:
self.generate_statements(include='declaration')
self.generate_statements(exclude='declaration')
if return_stmt: if return_stmt:
self.generate_return(return_type=return_type, return_value=return_value) self.generate_return(return_type=return_type, return_value=return_value)
if self.settings['generation-options']['generate-dead-code']: if self.settings['generation-options']['generate-dead-code']:
self.generate_statements(exclude='declaration') self.generate_statements(exclude='declaration')
self.print_all_current_scope_vars()
self.pop_scope() self.pop_scope()
self.current_ast_element = parent self.current_ast_element = parent
@ -433,13 +437,66 @@ class AstGenerator:
self.make_scoped_element(GAZ_LOOP_TAG, []) self.make_scoped_element(GAZ_LOOP_TAG, [])
self.current_control_flow_nesting_depth += 1 self.current_control_flow_nesting_depth += 1
self.generate_expression(GAZ_BOOL_KEY) # the loop entry condition #TODO force true truthiness = self.get_truthiness()
while True:
self.generate_expression(GAZ_BOOL_KEY) # the loop entry condition #TODO force true, if false, generate a simple block and return, no need to nest code we won't use
iterator = self.current_ast_element.iter()
next(iterator)
conditional = next(iterator)
if not self.truth_check(conditional, truthiness):
self.current_ast_element.remove(conditional)
else:
break
self.generate_block(block_type=GAZ_LOOP_TAG, self.generate_block(block_type=GAZ_LOOP_TAG,
loop_var=init_var) # append a variable increment and prepend a break statement if var is > max loop iterations loop_var=init_var) # append a variable increment and prepend a break statement if var is > max loop iterations
self.current_control_flow_nesting_depth -= 1 self.current_control_flow_nesting_depth -= 1
self.exit_scoped_element(parent) self.exit_scoped_element(parent)
def get_truthiness(self):
return random.random() < self.settings['misc-weights']['truthiness']
def truth_check(self, element: ET.Element, truthiness: bool):
# evaluate the element
uncompiled = self.unparse_conditional_statement(element)
substr1 = uncompiled.find("**")
substr2 = uncompiled.find("**", substr1 + 1)
if -1 not in [substr1, substr2]:
return False
# print(uncompiled)
try:
res = eval(uncompiled)
except (OverflowError, ZeroDivisionError):
res = False
# check it against truthiness and return the comparison
return res == truthiness
def unparse_conditional_statement(self, element: ET.Element):
py_unparse = TinyPyUnparser(element, True)
unparsed = ""
if element.tag == GAZ_LIT_TAG:
py_unparse.unparse_literal(element)
unparsed = py_unparse.source
elif element.tag == GAZ_OPERATOR_TAG:
py_unparse.unparse_operator(element)
unparsed = py_unparse.source
elif element.tag == GAZ_VAR_TAG:
xml = self.current_scope.resolve(element.get("name"))
py_unparse.unparse_variable(xml)
unparsed = py_unparse.source
elif element.tag == GAZ_BRACKET_TAG:
py_unparse.unparse_brackets(element)
unparsed = py_unparse.source
else:
raise ValueError("Unknown element type for conditional argument: " + element.tag)
return unparsed
def generate_zero_declaration(self): def generate_zero_declaration(self):
""" """
@brief generate a declaration int a = 0 for some a @brief generate a declaration int a = 0 for some a
@ -642,7 +699,7 @@ class AstGenerator:
self.comp_op_numline, self.comp_op_numline,
self.comp_op_cutoffs, self.comp_op_cutoffs,
self.comp_op_options, self.comp_op_options,
comparison=True) comparison=True) #, evals=self.get_truth())
def push_scope(self, xml_element: ET.Element = None): def push_scope(self, xml_element: ET.Element = None):
scope = Scope(self.current_scope) scope = Scope(self.current_scope)
@ -898,3 +955,25 @@ class AstGenerator:
self.generate_binary(op, random.choice([GAZ_INT_KEY, GAZ_FLOAT_KEY])) self.generate_binary(op, random.choice([GAZ_INT_KEY, GAZ_FLOAT_KEY]))
else: else:
self.generate_binary(op, random.choice(expr_type)) self.generate_binary(op, random.choice(expr_type))
def print_all_current_scope_vars(self):
for key, value in self.current_scope.symbols.items():
if isinstance(value, Variable):
self.print_variable(value)
def print_variable(self, var: Variable):
"""
@brief generate an outstream for a variable
@param var: the variable to print
@return:
"""
parent = self.current_ast_element
args = [
("type", GAZ_OUT_STREAM),
]
self.make_element(GAZ_STREAM_TAG, args)
self.current_ast_element.append(var.xml)
self.current_ast_element = parent

View file

@ -9,7 +9,7 @@ generation-options:
max-globals: 5 # maximum number of global variables max-globals: 5 # maximum number of global variables
properties: properties:
max-range-length: 5 # maximum length of ranges, vectors and tuples, (AxA matrices can exist) max-range-length: 5 # maximum length of ranges, vectors and tuples, (AxA matrices can exist)
use-english-words: True # use english words instead of random names (this may limit the maximum number of names) use-english-words: True # use english words instead of random names (if we run out, we switch to random)
id-length: # length of identifiers id-length: # length of identifiers
min: 1 min: 1
max: 5 max: 5
@ -68,12 +68,12 @@ statement-weights: # set to 0 for any statements y
in-stream: 5 in-stream: 5
type-weights: type-weights:
value-types: atomic-types:
integer: 50 int: 50 # TODO change these to the gaz types
real: 50 float: 50
boolean: 50 bool: 50
character: 50 char: 50
void: 10 void: 0 # TODO add support for void
composite-types: composite-types:
vector: 20 vector: 20
tuple: 5 tuple: 5
@ -90,6 +90,7 @@ misc-weights:
type-qualifier-weights: type-qualifier-weights:
const: 10 const: 10
var: 60 var: 60
truthiness: 0.9 # Probability of conditionals being true
block-termination-probability: 0.2 # probability for a block to terminate block-termination-probability: 0.2 # probability for a block to terminate

View file

@ -245,12 +245,13 @@ class TestGeneration(unittest.TestCase):
self.assertFalse(self.is_no_op(operator)) self.assertFalse(self.is_no_op(operator))
def test_create_global(self): def test_create_global(self):
self.ast_gen.ast = ET.Element("block") element = ET.Element("block")
self.ast_gen.ast = element
self.ast_gen.current_ast_element = self.ast_gen.ast self.ast_gen.current_ast_element = self.ast_gen.ast
self.ast_gen.generate_main() self.ast_gen.generate_main()
global_block = self.ast_gen.current_ast_element global_block = element
global_scope = self.ast_gen.current_scope global_scope = self.ast_gen.current_scope.get_top_scope()
self.assertIsNotNone(self.ast_gen.ast.find("procedure")) self.assertIsNotNone(self.ast_gen.ast.find("procedure"))
self.ast_gen.current_ast_element = self.ast_gen.ast.find("procedure") self.ast_gen.current_ast_element = self.ast_gen.ast.find("procedure")
@ -309,6 +310,79 @@ class TestGeneration(unittest.TestCase):
return res return res
def test_print_all_current_scope_vars(self):
element = ET.Element("block")
self.ast_gen.ast = element
self.ast_gen.current_ast_element = element
self.ast_gen.current_scope = Scope(None)
self.ast_gen.generate_declaration(mut='var')
self.ast_gen.generate_declaration(mut='var')
self.ast_gen.generate_declaration(mut='var')
self.ast_gen.generate_declaration(mut='var')
self.ast_gen.print_all_current_scope_vars()
# print(ET.tostring(self.ast_gen.ast))
streams = self.ast_gen.ast.findall("stream")
self.assertEqual(4, len(streams))
def test_conditional_test_1(self):
element = ET.fromstring('<literal type="bool" value="False"/>')
self.assertIsNotNone(element)
res = self.ast_gen.truth_check(element, True)
self.assertFalse(res)
def test_conditional_test_2(self):
element = ET.fromstring('<literal type="bool" value="True"/>')
self.assertIsNotNone(element)
res = self.ast_gen.truth_check(element, True)
self.assertTrue(res)
def test_conditional_test_3(self):
with open("xml/conditional.xml", 'r') as f:
element = ET.fromstring(f.read())
self.assertIsNotNone(element)
res = self.ast_gen.truth_check(element, True)
self.assertTrue(res)
def test_conditional_test_4(self):
with open("xml/conditional_2.xml", 'r') as f:
element = ET.fromstring(f.read())
self.assertIsNotNone(element)
res = self.ast_gen.truth_check(element, True)
self.assertFalse(res)
def test_conditional_test_5(self):
with open("xml/conditional_3.xml", 'r') as f:
element = ET.fromstring(f.read())
self.assertIsNotNone(element)
res = self.ast_gen.truth_check(element, True)
self.assertTrue(res)
# def test_conditional_test_variable(self):
# element = ET.fromstring('<variable name="harold" type="int" mut="var"/>')
#
# var = Variable("harold", "int", "var")
# var.decl_xml = ET.fromstring('<literal type="bool" value="True"/>')
# self.ast_gen.current_scope.append("harold", element)
# self.assertIsNotNone(element)
#
# res = self.ast_gen.truth_check(element, True)
# self.assertTrue(res)
def write_ast(self): def write_ast(self):
dom = xml.dom.minidom.parseString(ET.tostring(self.ast_gen.ast).decode('utf-8')) dom = xml.dom.minidom.parseString(ET.tostring(self.ast_gen.ast).decode('utf-8'))
pretty: str = dom.toprettyxml() pretty: str = dom.toprettyxml()

View file

@ -0,0 +1,8 @@
<operator op="==" type="int">
<lhs>
<literal mut="var" type="int" value="42"/>
</lhs>
<rhs>
<literal type="int" value="42" />
</rhs>
</operator>

View file

@ -0,0 +1,8 @@
<operator op="!=" type="int">
<lhs>
<literal mut="var" type="int" value="42"/>
</lhs>
<rhs>
<literal type="int" value="42" />
</rhs>
</operator>

View file

@ -0,0 +1,15 @@
<operator op="!=" type="int">
<lhs>
<literal mut="var" type="int" value="False"/>
</lhs>
<rhs>
<operator op=">" type="int">
<lhs>
<literal mut="var" type="int" value="43"/>
</lhs>
<rhs>
<literal type="int" value="42" />
</rhs>
</operator>
</rhs>
</operator>

View file

@ -0,0 +1,101 @@
from ast_parser.general_unparser import GeneralUnparser
import warnings
from xml.etree import ElementTree as ET
from ast_parser.gaz_unparser import GazUnparser
from ast_parser.general_unparser import GeneralUnparser
from constants import *
def to_python_type(ty):
if ty == GAZ_INT_KEY:
return "int"
elif ty == GAZ_BOOL_KEY:
return "bool"
elif ty == GAZ_FLOAT_KEY:
return "float"
elif ty == GAZ_CHAR_KEY:
return "str"
elif ty == GAZ_STRING_KEY:
return "str"
else:
raise Exception("Unknown type: " + ty)
def to_python_op(param, ty):
if param == "negation" or param == "subtraction":
return "-"
elif param == "addition" or param == "noop":
return "+"
elif param == "multiplication":
return "*"
elif param == "division" and ty != GAZ_INT_KEY:
return "/"
elif param == "division" and ty == GAZ_INT_KEY:
return "//"
elif param == "modulo":
return "%"
elif param == "power":
return "**"
elif param == "equality":
return "=="
elif param == "inequality":
return "!="
elif param == "less-than":
return "<"
elif param == "less-than-or-equal":
return "<="
elif param == "greater-than":
return ">"
elif param == "greater-than-or-equal":
return ">="
elif param == "xor":
return "!="
else:
warnings.warn("Warning, unknown operator: " + param)
return param
class TinyPyUnparser(GeneralUnparser):
def __init__(self, ast: ET.Element, debug=False):
super().__init__(ast, debug,
endline='\n',
outstream_begin_delimiter="gprint(",
outstream_end_delimiter=", end='')",
function_return_type_indicator_predicate="->",
loop_start_delimiter="while ",
loop_end_delimiter=":",
conditional_case_delimiter="elif ",
conditional_start_delimiter="if ",
conditional_else_delimiter="else:",
conditional_end_delimiter=":",
block_start_delimiter="",
block_end_delimiter="", # TODO can this contain the pass?
strip_conditionals=True)
self.source = ''
def format_variable(self, mut, ty, name, declaration: bool = False):
if declaration:
return "{}: {}".format(name, ty)
else:
return "{}".format(name)
def translate_value(self, val):
return str(val)
def translate_op(self, param, ty=None):
return to_python_op(param, ty)
def translate_type(self, ty):
return to_python_type(ty)
def unparse_block(self, node):
raise TypeError("Cannot unparse blocks for this type of evaluation")
def setup(self):
pass

View file

@ -5,12 +5,13 @@ from constants import GAZ_VAR_TAG, GAZ_ARG_TAG
class Variable: class Variable:
def __init__(self, name: str, type: str, qualifier: str, value: any = None): def __init__(self, name: str, type: str, qualifier: str, value: any = None): # decl_xml: ET.Element = None,
self.name = name self.name = name
self.type = type self.type = type
self.value = value self.value = value
self.qualifier = qualifier self.qualifier = qualifier
self.xml = self._build_xml() self.xml = self._build_xml()
# self.decl_xml = decl_xml
def _build_xml(self): def _build_xml(self):
args = [ args = [

View file

@ -98,3 +98,6 @@ class GazUnparser(GeneralUnparser):
def format_single_arg(self, ty, name): def format_single_arg(self, ty, name):
return "{} {}".format(ty, name) return "{} {}".format(ty, name)
def setup(self):
pass

View file

@ -69,6 +69,9 @@ class GeneralUnparser:
:return: a string of valid gazprea code :return: a string of valid gazprea code
""" """
self.source = "" self.source = ""
self.setup()
for node in self.xml: for node in self.xml:
self.unparse_node(node) self.unparse_node(node)
@ -324,6 +327,9 @@ class GeneralUnparser:
def format_single_arg(self, param, param1): def format_single_arg(self, param, param1):
raise NotImplementedError raise NotImplementedError
def setup(self):
raise NotImplementedError
def pretty_xml(self): def pretty_xml(self):
dom = xml.dom.minidom.parseString(ET.tostring(self.xml).decode('utf-8')) dom = xml.dom.minidom.parseString(ET.tostring(self.xml).decode('utf-8'))
pretty: str = dom.toprettyxml() pretty: str = dom.toprettyxml()

View file

@ -59,7 +59,7 @@ class PythonUnparser(GeneralUnparser):
def __init__(self, ast: ET.Element, debug=False): def __init__(self, ast: ET.Element, debug=False):
super().__init__(ast, debug, super().__init__(ast, debug,
endline='\n', endline='\n',
outstream_begin_delimiter="print(", outstream_begin_delimiter="gprint(",
outstream_end_delimiter=", end='')", outstream_end_delimiter=", end='')",
function_return_type_indicator_predicate="->", function_return_type_indicator_predicate="->",
loop_start_delimiter="while ", loop_start_delimiter="while ",
@ -113,3 +113,13 @@ class PythonUnparser(GeneralUnparser):
def unparse(self): def unparse(self):
super().unparse() super().unparse()
self.source += "\nif __name__ == '__main__':\n main()" self.source += "\nif __name__ == '__main__':\n main()"
def setup(self):
self.source += ("def gprint(expr, end=''):\n"
" if type(expr) is bool:\n"
" if expr:\n"
" print('T', end=end)\n"
" else:\n"
" print('F', end=end)\n"
" else:\n"
" print(expr, end=end)\n\n")

View file

@ -4,7 +4,7 @@ generation-options:
max-nesting-depth: 5 # maximum nesting depth for statements max-nesting-depth: 5 # maximum nesting depth for statements
max-conditionals-loops: 5 # maximum number of loops/conditionals per routine max-conditionals-loops: 5 # maximum number of loops/conditionals per routine
max-number-of-routines: 5 # maximum number of routines (main will always be generated) max-number-of-routines: 5 # maximum number of routines (main will always be generated)
generate-dead-code: True # generate dead code generate-dead-code: False # generate dead code
max-loop-iterations: 100 # maximum number of iterations in a loop max-loop-iterations: 100 # maximum number of iterations in a loop
max-globals: 5 # maximum number of global variables max-globals: 5 # maximum number of global variables
properties: properties:
@ -90,6 +90,7 @@ misc-weights:
type-qualifier-weights: type-qualifier-weights:
const: 10 const: 10
var: 60 var: 60
truthiness: 0.99 # Probability of conditionals being true
block-termination-probability: 0.2 # probability for a block to terminate block-termination-probability: 0.2 # probability for a block to terminate

View file

@ -61,3 +61,11 @@ class NoneTagException(Exception):
super().__init__(message) super().__init__(message)
self.xml = xml self.xml = xml
class GazTypeError(Exception):
def __init__(self, message, op, ty1, ty2):
super().__init__(message)
self.op = op
self.ty1 = ty1
self.ty2 = ty2

View file

@ -28,9 +28,13 @@ class GazpreaFuzzer:
self.ground_truth = None self.ground_truth = None
self.out = None self.out = None
try:
os.makedirs('debug/ast')
except FileExistsError:
pass
def fuzz(self): def fuzz(self):
self.generator.generate_ast() self.generator.generate_ast()
self.write_ast() # FIXME sometimes this is none
self.gaz_source_gen = GazUnparser(self.generator.ast, True) self.gaz_source_gen = GazUnparser(self.generator.ast, True)
try: try:
@ -48,8 +52,10 @@ class GazpreaFuzzer:
# input = "if __name__ == '__main__':\n while True:\n pass\n" # debug # input = "if __name__ == '__main__':\n while True:\n pass\n" # debug
with redirect_stdout(io.StringIO()) as buf: with redirect_stdout(io.StringIO()) as buf:
pass
# exec(str(self.ground_truth)) # exec(str(self.ground_truth))
exec(self.ground_truth, globals(), locals()) # FIXME the exec doesn't actually execute for some reason... # exec(self.ground_truth, globals(), locals()) # FIXME the exec doesn't actually execute for some reason...
self.write_ast() # FIXME sometimes this is none
self.out = buf.getvalue() self.out = buf.getvalue()

View file

@ -14,6 +14,18 @@ import xml.etree.ElementTree as ET
from constants import NoneTagException from constants import NoneTagException
import signal as sig
def gprint(expr, end=''):
if type(expr) is bool:
if expr:
print('T', end=end)
else:
print('F', end=end)
else:
print(expr, end=end)
class Fuzzer(): class Fuzzer():
def __init__(self, config: str, batch: int, seed: str, file_name: str = "fuzz"): def __init__(self, config: str, batch: int, seed: str, file_name: str = "fuzz"):
@ -27,16 +39,35 @@ class Fuzzer():
self.fuzzer = fz.GazpreaFuzzer(config) self.fuzzer = fz.GazpreaFuzzer(config)
def fuzz(self): def fuzz(self):
os.system("rm -rf fuzzer") sig.signal(sig.SIGALRM, handler)
os.mkdir("fuzzer") sig.alarm(30)
os.mkdir("fuzzer/input")
os.mkdir("fuzzer/debug") base_dir = os.getcwd()
os.mkdir("fuzzer/debug/ast_err") fuzzer_root = base_dir + '/fuzzer'
os.mkdir("fuzzer/instream")
os.mkdir("fuzzer/outputs") fuzzer_input = fuzzer_root + '/input/fuzzer'
os.mkdir("fuzzer/ground_truth") fuzzer_debug = fuzzer_root + '/debug'
os.system("cp tester_config.json fuzzer/tester_config.json") fuzzer_asterr = fuzzer_debug + '/ast_err'
for i in range(self.batch): fuzzer_instream = fuzzer_root + '/instream'
fuzzer_outputs = fuzzer_root + '/outputs/fuzzer'
fuzzer_ground_truth = fuzzer_root + '/ground_truth'
os.system(f"rm -rf {fuzzer_root}")
os.makedirs(fuzzer_root)
os.makedirs(fuzzer_input)
os.makedirs(fuzzer_debug)
os.makedirs(fuzzer_asterr)
os.makedirs(fuzzer_instream)
os.makedirs(fuzzer_outputs)
os.makedirs(fuzzer_ground_truth)
try:
os.system(f"cp tester_config.json {fuzzer_root}/tester_config.json")
except Exception as e:
pass
i = 0
min_i = 0
while i < self.batch:
try: try:
self.fuzzer.fuzz() self.fuzzer.fuzz()
except NoneTagException as n: except NoneTagException as n:
@ -44,41 +75,92 @@ class Fuzzer():
warnings.warn("None Tag Exception encountered, writing stack trace and xml to debug/ast/{}.xml\n" warnings.warn("None Tag Exception encountered, writing stack trace and xml to debug/ast/{}.xml\n"
"Look for a top-level <argument> tag or send it to Ayrton and I'll see what I can see" "Look for a top-level <argument> tag or send it to Ayrton and I'll see what I can see"
"".format(r)) "".format(r))
with open("fuzzer/debug/ast_err/{}.xml".format(r), 'w') as f: with open(f"{fuzzer_asterr}/{r}.xml", 'w') as f:
f.write(xml.dom.minidom.parseString(ET.tostring(self.fuzzer.ast).decode('utf-8')).toprettyxml()) try:
f.write(xml.dom.minidom.parseString(ET.tostring(self.fuzzer.ast).decode('utf-8')).toprettyxml())
except AttributeError:
print("Failed to write to the file", file=sys.stderr)
if i - 1 >= min_i:
i -= 1
else:
i = min_i
continue continue
dom = xml.dom.minidom.parseString(ET.tostring(self.fuzzer.ast).decode('utf-8')) dom = xml.dom.minidom.parseString(ET.tostring(self.fuzzer.ast).decode('utf-8'))
pretty: str = dom.toprettyxml() pretty: str = dom.toprettyxml()
with open("fuzzer/ground_truth/{}_{}.py".format(self.file_name, i), 'w') as f: with open("{}/{}_{}.py".format(fuzzer_ground_truth, self.file_name, i), 'w') as f:
f.write(self.fuzzer.ground_truth) f.write(self.fuzzer.ground_truth)
with open("fuzzer/ground_truth/{}_{}.py".format(self.file_name, i), 'r') as f: with open("{}/{}_{}.py".format(fuzzer_ground_truth, self.file_name, i), 'r') as f:
with open("fuzzer/outputs/{}_{}.out".format(self.file_name, i), 'w') as y: with open("{}/{}_{}.out".format(fuzzer_outputs, self.file_name, i), 'w') as y:
with redirect_stdout(y): # Workaround for fuzzer.py:49 with redirect_stdout(y): # Workaround for fuzzer.py:49
try: try:
exec(f.read(), globals(), locals()) read = f.read()
except (OverflowError, ZeroDivisionError, ValueError): exec(read, globals())
os.system("rm -f fuzzer/ground_truth/{}_{}.py".format(self.file_name, i)) except (OverflowError, ZeroDivisionError, ValueError, TypeError, SyntaxError):
os.system("rm -f {}/{}_{}.py".format(fuzzer_ground_truth, self.file_name, i))
os.system("rm -f {}/{}_{}.out".format(fuzzer_outputs, self.file_name, i))
warnings.warn("Runtime error encountered, retrying")
if i - 1 >= min_i:
i -= 1
else:
i = min_i
continue continue
except KeyboardInterrupt: except KeyboardInterrupt:
r = random.randint(0, 1000000) r = random.randint(0, 1000000)
warnings.warn("Execution halted, result written to debug/ast/{}.xml\n" warnings.warn("Execution halted, result written to debug/ast/{}.xml\n"
"".format(r)) "".format(r))
with open("fuzzer/debug/ast_err/{}.xml".format(r), 'w') as f: with open("{}/{}.xml".format(fuzzer_asterr, r), 'w') as f:
f.write(xml.dom.minidom.parseString(ET.tostring(self.fuzzer.ast).decode('utf-8')).toprettyxml()) f.write(xml.dom.minidom.parseString(
ET.tostring(self.fuzzer.ast).decode('utf-8')).toprettyxml())
sys.exit(1) sys.exit(1)
with open("fuzzer/input/{}_{}.in".format(self.file_name, i), 'w') as f: except TimeoutError:
r = random.randint(0, 1000000)
warnings.warn("Execution timed out, result written to debug/ast/{}.xml\n"
"".format(r))
with open("{}/{}.xml".format(fuzzer_asterr, r), 'w') as f:
f.write(xml.dom.minidom.parseString(
ET.tostring(self.fuzzer.ast).decode('utf-8')).toprettyxml())
if i - 1 >= min_i:
i -= 1
else:
i = min_i
continue
with open("{}/{}_{}.out".format(fuzzer_outputs, self.file_name, i), 'r') as y:
out = y.read()
if out == "":
print("Empty output, skipping", file=sys.stderr)
os.system("rm -f {}/{}_{}.py".format(fuzzer_ground_truth, self.file_name, i))
os.system("rm -f {}/{}_{}.out".format(fuzzer_outputs, self.file_name, i))
if i - 1 >= min_i:
i -= 1
else:
i = min_i
continue
with open("{}/{}_{}.in".format(fuzzer_input, self.file_name, i), 'w') as f:
f.write(self.fuzzer.source) f.write(self.fuzzer.source)
with open("fuzzer/debug/{}_{}.xml".format(self.file_name, i), 'w') as f: with open("{}/{}_{}.xml".format(fuzzer_debug, self.file_name, i), 'w') as f:
f.write(pretty) f.write(pretty)
# y.write(self.fuzzer.out) # y.write(self.fuzzer.out)
# with open("fuzzer/instream/{}.in".format(i), 'w') as f: # with open("fuzzer/instream/{}.in".format(i), 'w') as f:
# f.write(self.fuzzer.source) # f.write(self.fuzzer.source)
# with open("fuzzer/outputs/{}.out".format(i), 'w') as f: # with open("fuzzer/outputs/{}.out".format(i), 'w') as f:
# f.write(self.fuzzer.out) # f.write(self.fuzzer.out)
print("test {}/{} generated".format(i, self.batch))
i += 1
min_i = i
def handler(signum, frame):
print("Execution Timeout")
raise TimeoutError
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='Procedurally generate a test case for Gazprea' description='Procedurally generate a test case for Gazprea'
) )
@ -94,5 +176,3 @@ if __name__ == '__main__':
args = parser.parse_args() args = parser.parse_args()
fuzzer = Fuzzer(config=args.config_file, batch=args.batch_size, seed=args.seed) fuzzer = Fuzzer(config=args.config_file, batch=args.batch_size, seed=args.seed)
fuzzer.fuzz() fuzzer.fuzz()

View file

@ -1,12 +1,12 @@
{ {
"inDir": "<inDir>", "inDir": "input",
"outDir": "<outDir>", "outDir": "outputs",
"inStrDir": "<inStrDir>", "inStrDir": "instream",
"testedExecutablePaths": { "testedExecutablePaths": {
"<team id>": "<path_to_gazc_exe>" "fuzzer": "../exec/gazc"
}, },
"runtimes": { "runtimes": {
"<team id>": "<path_to_libgazrt.so>" "fuzzer": "../exec/libgazrt.so"
}, },
"toolchains": { "toolchains": {
"gazprea": [ "gazprea": [