From 622722a104bf5619438faaf57bcfa0bd9361872a Mon Sep 17 00:00:00 2001 From: Cristian Mattarei Date: Thu, 26 Oct 2017 11:30:13 -0700 Subject: [PATCH 1/2] Added support for programs fuzzing --- configs/printers.ini | 12 +++---- ecmasab/execution.py | 13 +++++++ ecmasab/parsing.py | 5 +-- ecmasab/printers.py | 8 +++-- emme.py | 81 ++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 105 insertions(+), 14 deletions(-) diff --git a/configs/printers.ini b/configs/printers.ini index aa95f9af..ef8e56b3 100644 --- a/configs/printers.ini +++ b/configs/printers.ini @@ -1,5 +1,5 @@ [JS-TEST262-JSC] -type: JST262_Printer +type: JST262Printer name: JS-TEST262-JSC description: TEST262 format (Accepted by JSC) add_wait: True @@ -12,7 +12,7 @@ string_report: True waiting_limit: 10000 [JS-TEST262-SM] -type: JST262_Printer +type: JST262Printer name: JS-TEST262-SM description: TEST262 format (Accepted by SM) add_wait: True @@ -24,19 +24,19 @@ string_report: True waiting_limit: 10000 [JS-TEST262-V8] -type: JST262_Printer +type: JST262Printer name: JS-TEST262-V8 description: TEST262 format (Accepted by V8) add_wait: True expected_outputs: True only_reads_reports: True or_zero: False -output_assertion: False +output_assertion: True string_report: True waiting_limit: 10000 [JS-TEST262-W-V8] -type: JST262_Printer +type: JST262Printer name: JS-TEST262-W-V8 description: TEST262 format with WASM (Accepted by V8) add_wait: True @@ -49,7 +49,7 @@ waiting_limit: 10000 use_wasm: True [JS-TEST262-W-JSC] -type: JST262_Printer +type: JST262Printer name: JS-TEST262-W-JSC description: TEST262 format with WASM (Accepted by JSC) add_wait: True diff --git a/ecmasab/execution.py b/ecmasab/execution.py index 35c5e3d3..4cdb4c8a 100644 --- a/ecmasab/execution.py +++ b/ecmasab/execution.py @@ -81,6 +81,10 @@ def get_coherent_executions(self): def get_size(self): return len(self.executions) + + def invalidate_executions(self): + for exe in self.executions: + exe.reads_values = [] class Execution(object): agent_order = None @@ -898,6 +902,15 @@ def has_info(self, key): if not self.info: return False return key in self.info + + def set_values_from_num(self, value): + if self.is_wtear(): + value = float(value) + self.set_values_from_float(value, self.address[0], self.address[-1]) + else: + value = int(value) + self.set_values_from_int(value, self.address[0], self.address[-1]) + def set_values_from_int(self, int_value, begin, end): self.offset = begin diff --git a/ecmasab/parsing.py b/ecmasab/parsing.py index 922a33ef..4c0a2ba7 100644 --- a/ecmasab/parsing.py +++ b/ecmasab/parsing.py @@ -243,7 +243,7 @@ def executions_from_string(self, strinput, program=None): if program: program.expand_events() - self.__compute_reads_values(executions) + self.compute_reads_values(executions) return executions @@ -311,7 +311,7 @@ def __list_from_relation(self, relation): events = [x[1] for x in events] return events - def __compute_reads_values(self, executions): + def compute_reads_values(self, executions): for exe in executions.executions: events = exe.get_events() ev_map = dict((x.name, x) for x in events) @@ -354,6 +354,7 @@ def __compute_reads_values(self, executions): new_read_event = copy.deepcopy(read_event) new_read_event.set_values(values) exe.add_read_values(new_read_event) + return executions def __op_values(self, is_float, ev1, ev2, fun): val2 = ev2.values diff --git a/ecmasab/printers.py b/ecmasab/printers.py index 7b3e7d5e..e75e07d7 100644 --- a/ecmasab/printers.py +++ b/ecmasab/printers.py @@ -75,6 +75,8 @@ ASMACCESS += "})`;\n" def float_approx(value, approx=FLOAT_APPROX): + if value != value: + return value val = value*(10**approx) if (value*(10**approx)) % 1 >= float(0.5): val += 1 @@ -108,7 +110,7 @@ class PrintersFactory(object): # Additional printers should be registered here # @staticmethod def init_printers(): - PrintersFactory.register_printer(JST262_Printer(), True) + PrintersFactory.register_printer(JST262Printer(), True) PrintersFactory.register_printer(DotPrinter()) PrintersFactory.register_printer(BePrinter()) @@ -419,7 +421,7 @@ def print_event(self, event, postfix=None): def get_extension(self): return self.EXT -class JST262_Printer(EPrinter): +class JST262Printer(EPrinter): name = "JS-TEST262" description = "TEST262 format (Standard)" string_report = True @@ -953,7 +955,7 @@ class JSONPrinter(EPrinter): name = "JSON" description = "\tJSON format" TYPE = PrinterType.JSON - float_pri_js = "%.2f" + float_pri_js = "%."+str(FLOAT_APPROX)+"f" EXT = ".json" def __init__(self): diff --git a/emme.py b/emme.py index debf2d05..5c61e677 100755 --- a/emme.py +++ b/emme.py @@ -15,11 +15,13 @@ import sys from six.moves import range import subprocess +import random +import copy from argparse import RawTextHelpFormatter from ecmasab.parsing import BeParser -from ecmasab.printers import DotPrinter, PrintersFactory, PrinterType, BePrinter +from ecmasab.printers import DotPrinter, PrintersFactory, PrinterType, BePrinter, JSONPrinter from ecmasab.encoders import CVC4Encoder, AlloyEncoder from ecmasab.execution import RBF, HB, SW from ecmasab.exceptions import UnreachableCodeException @@ -95,6 +97,7 @@ class Config(object): runs = None nexecs = None time = None + fuzzing = None def __init__(self): PrintersFactory.init_printers() @@ -123,6 +126,7 @@ def __init__(self): self.use_alloy = False self.hybrid = False self.time = False + self.fuzzing = False def generate_filenames(self): if self.prefix: @@ -384,6 +388,55 @@ def synth_program(config): return 0 + +def fuzz_program(program, executions, pprinter, cycles): + + orig_program = copy.deepcopy(program) + orig_executions = copy.deepcopy(executions) + + def get_wvalue(size): + if size == 1: + length = 2 + if size == 2: + length = 4 + if size >= 4: + length = 9 + + retval = float("%s.%s"%(random.randint(0,(10**length)-1), random.randint(0,1000))) + return retval + + p_execs_n = len(pprinter.compute_possible_executions(program, executions)) + + if len(executions.executions) == p_execs_n: + return (orig_program, orig_executions) + + Logger.log("Starting: %s "%p_execs_n, 1) + beparser = BeParser() + events = [x for x in program.get_events() if x.is_write_or_modify()] + + beprinter = BePrinter() + + cycle = 0 + while (cycle < cycles): + cycle += 1 + + for ev in events: + ev.set_values_from_num(get_wvalue(ev.get_size())) + + executions.invalidate_executions() + executions = beparser.compute_reads_values(executions) + execs_n = len(pprinter.compute_possible_executions(program, executions)) + diff = execs_n - p_execs_n + Logger.msg("%s(%s) "%(execs_n, "+" if diff > 0 else "-" if diff < 0 else "="), 1) + + if diff > 0: + Logger.msg("<-- BEST ", 1) + p_execs_n = execs_n + Logger.msg(".", 0) + (orig_program, orig_executions) = (copy.deepcopy(program), copy.deepcopy(executions)) + + return (orig_program, orig_executions) + def analyze_program(config): config.generate_filenames() @@ -447,7 +500,24 @@ def analyze_program(config): executions = parser.executions_from_string(modelfile.read(), program) Logger.log("DONE", 0) - + + + if config.fuzzing: + Logger.msg("Fuzzing... ", 0) + + len_execs = len(pprinter.compute_possible_executions(program, executions)) + (program, executions) = fuzz_program(program, executions, pprinter, 100) + nlen_execs = len(pprinter.compute_possible_executions(program, executions)) + + if len_execs != nlen_execs: + with open(config.inputfile, "w") as newfile: + beprinter = BePrinter() + newfile.write(beprinter.print_program(program, executions)) + + Logger.log(" DONE\n -> Increased possible outputs from %s to %s"%(len_execs, nlen_execs), 0) + else: + Logger.log("DONE", 0) + Logger.msg("Generating program... ", 0) outfiles = [config.outprogram] @@ -457,7 +527,7 @@ def analyze_program(config): outfiles = [outprogram] extension = pprinter.get_extension() - + for outfile in outfiles: with open(outfile+extension, "w") as f: f.write(pprinter.print_program(program, executions)) @@ -557,6 +627,10 @@ def main(args): parser.add_argument('-v', dest='verbosity', metavar="verbosity", type=int, help="verbosity level. (Default is \"%s\")"%1) + parser.set_defaults(fuzzing=False) + parser.add_argument('-z', '--fuzzing', dest='fuzzing', action='store_true', + help="fuzz the input program in order to achieve better outputs coverage. (Default is \"%s\")"%False) + parser.set_defaults(nexecs=-1) parser.add_argument('-e', '--max-executions', dest='nexecs', metavar='nexecs', type=int, help='maximum number of executions. (Default is \"unlimited\")') @@ -646,6 +720,7 @@ def main(args): config.use_alloy = not args.use_cvc4 config.unmatched = args.unmatched config.time = args.time + config.fuzzing = args.fuzzing if args.jsprinter in [str(x.get_name()) for x in PrintersFactory.get_printers_by_type(PrinterType.PROGRAMS)]: config.jsprinter = args.jsprinter From cd7940b38fedba0af5c3691e1031af3ed2736c46 Mon Sep 17 00:00:00 2001 From: Cristian Mattarei Date: Thu, 26 Oct 2017 14:52:11 -0700 Subject: [PATCH 2/2] Fixed support of for loops --- ecmasab/execution.py | 18 +++++++++++++----- ecmasab/parsing.py | 13 +++++++++++-- ecmasab/printers.py | 3 ++- ecmasab/utils.py | 7 +++++++ emme.py | 26 ++++++++++++++++++++++++-- 5 files changed, 57 insertions(+), 10 deletions(-) diff --git a/ecmasab/execution.py b/ecmasab/execution.py index 4cdb4c8a..c4d97d40 100644 --- a/ecmasab/execution.py +++ b/ecmasab/execution.py @@ -341,9 +341,9 @@ def apply_param(self, pardic): for thread in self.threads: thread.apply_param(pardic) - def expand_events(self): + def expand_events(self, force=False): for thread in self.threads: - thread.expand_events() + thread.expand_events(force) def get_params(self): if not self.params: @@ -393,10 +393,14 @@ def has_conditions(self): self.get_conditions() return len(self.conditions) - def get_events(self): + def get_events(self, expand_loops=True): events = [] for thread in self.threads: - events += thread.get_events(True) + events += thread.get_events(expand_loops) + if not expand_loops: + for i in range(len(events)): + if isinstance(events[i], For_Loop): + events = events[:i] + events[i].events + events[i+1:] return events def sort_threads(self): @@ -464,8 +468,12 @@ def apply_param(self, pardic): for event in self.events: event.apply_param(pardic) - def expand_events(self): + def expand_events(self, force=False): self.uevents = None + if force: + for el in self.events: + if isinstance(el, For_Loop): + el.uevents = None self.get_events(True) def get_events(self, expand_loops, conditions=None): diff --git a/ecmasab/parsing.py b/ecmasab/parsing.py index 4c0a2ba7..69960ec0 100644 --- a/ecmasab/parsing.py +++ b/ecmasab/parsing.py @@ -155,7 +155,7 @@ def __init_execution_parser(self): def __init_program_parser(self): ivalue = Word(nums) fvalue = Combine(ivalue + Optional(Literal(T_DOT) + Optional(ivalue))) - nvalue = fvalue | ivalue + nvalue = Optional(T_MIN) + (fvalue | ivalue) strname = Word(alphas+nums+T_US) parval = (Combine(Literal(T_LT) + Literal(T_VAL) + Word(alphas+nums+T_US) + Literal(T_GT)))(P_PARAM) @@ -543,7 +543,7 @@ def __gen_memory_event(self, command, ctype, parametric, thread, blocks): if parametric: me.size = opsize - me.value = list(value) + me.value = self.__flat_expr(value) me.offset = offset me.tear = WTEAR if self.__var_type_is_float(command.typeop) else NTEAR elif me.is_write_or_modify(): @@ -560,6 +560,15 @@ def __gen_memory_event(self, command, ctype, parametric, thread, blocks): return me + def __flat_expr(self, expr): + expr = list(expr) + for i in range(len(expr)): + if type(expr[i]) != str: + expr[i] = list(expr[i]) + expr = expr[:i] + self.__flat_expr(expr[i]) + expr[i+1:] + + return expr + def __populate_program(self, commands): program = Program() thread = Thread(MAIN) diff --git a/ecmasab/printers.py b/ecmasab/printers.py index e75e07d7..561c102b 100644 --- a/ecmasab/printers.py +++ b/ecmasab/printers.py @@ -694,7 +694,8 @@ def print_event(self, event, postfix=None): if event.operation == WRITE: if is_float and event.address: - event_values = self.print_float(event_values) + if not event.is_parametric(): + event_values = self.print_float(event_values) if self.use_wasm and not is_float: mop = ("(${asm_memacc}(this, {}, %s%s_sab)).store%s(%s, %s)")%("" if self.string_report else "data.", \ diff --git a/ecmasab/utils.py b/ecmasab/utils.py index 0f084b6d..b4d0d34d 100644 --- a/ecmasab/utils.py +++ b/ecmasab/utils.py @@ -82,3 +82,10 @@ def auto_convert(strval): return float(strval) except Exception: return strval + +def is_number(value): + value = auto_convert(str(value)) + return (type(value) == int) or (type(value) == float) + +def is_operator(value): + return str(value) in ["+","-","*","/"] diff --git a/emme.py b/emme.py index 5c61e677..ea0e9e31 100755 --- a/emme.py +++ b/emme.py @@ -28,6 +28,7 @@ from ecmasab.analyzers import ValidExecutionAnalyzer, EquivalentExecutionSynthetizer, ConstraintsAnalyzer from ecmasab.preprocess import ExtPreprocessor, QuantPreprocessor, CPP from ecmasab.logger import Logger +from ecmasab.utils import is_number, is_operator CVC_FORMAL_MODEL = "./model/memory_model.cvc" @@ -412,18 +413,39 @@ def get_wvalue(size): Logger.log("Starting: %s "%p_execs_n, 1) beparser = BeParser() - events = [x for x in program.get_events() if x.is_write_or_modify()] + events = [x for x in program.get_events(False) if x.is_write_or_modify()] beprinter = BePrinter() + + evdic = set([]) cycle = 0 while (cycle < cycles): cycle += 1 for ev in events: - ev.set_values_from_num(get_wvalue(ev.get_size())) + if ev.is_init(): continue + if ev.is_parametric(): + newoff = (float(random.randint(1,40))/10.0)-(2.0) + newoff = int(newoff) if not ev.is_wtear() else newoff + + for i in range(len(ev.value)): + newval = get_wvalue(ev.get_size()) + newval = int(newval) if not ev.is_wtear() else newval + + if is_number(ev.value[i]): + ev.value[i] = str(newval) + + if ev not in evdic: + evdic.add(ev) + ev.value.append("*"+str(newoff)) + else: + ev.value[-1] = "*"+str(newoff) + else: + ev.set_values_from_num(get_wvalue(ev.get_size())) executions.invalidate_executions() + program.expand_events(True) executions = beparser.compute_reads_values(executions) execs_n = len(pprinter.compute_possible_executions(program, executions)) diff = execs_n - p_execs_n