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..c4d97d40 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 @@ -337,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: @@ -389,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): @@ -460,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): @@ -898,6 +910,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..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) @@ -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 @@ -542,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(): @@ -559,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 7b3e7d5e..561c102b 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 @@ -692,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.", \ @@ -953,7 +956,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/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 debf2d05..ea0e9e31 100755 --- a/emme.py +++ b/emme.py @@ -15,17 +15,20 @@ 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 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" @@ -95,6 +98,7 @@ class Config(object): runs = None nexecs = None time = None + fuzzing = None def __init__(self): PrintersFactory.init_printers() @@ -123,6 +127,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 +389,76 @@ 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(False) if x.is_write_or_modify()] + + beprinter = BePrinter() + + evdic = set([]) + + cycle = 0 + while (cycle < cycles): + cycle += 1 + + for ev in events: + 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 + 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 +522,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 +549,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 +649,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 +742,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