def __init__(self, working_dir, src, buggy, oracle, tests, golden, asserts, lines, build, configure, config): self.working_dir = working_dir self.config = config self.repair_test_suite = tests[:] self.validation_test_suite = tests[:] extracted = join(working_dir, 'extracted') os.mkdir(extracted) angelic_forest_file = join(working_dir, 'last-angelic-forest.json') tester = Tester(config, oracle, abspath(working_dir)) self.run_test = tester self.get_suspicious_groups = Localizer(config, lines) self.reduce = Reducer(config) if self.config['use_semfix_syn']: self.synthesize_fix = Semfix_Synthesizer(working_dir, config, extracted, angelic_forest_file) self.infer_spec = Semfix_Inferrer(working_dir, config, tester) else: self.synthesize_fix = Synthesizer(config, extracted, angelic_forest_file) self.infer_spec = Inferrer(config, tester, Load(working_dir)) self.instrument_for_localization = RepairableTransformer(config) self.instrument_for_inference = SuspiciousTransformer(config, extracted) self.apply_patch = FixInjector(config) validation_dir = join(working_dir, "validation") shutil.copytree(src, validation_dir, symlinks=True) self.validation_src = Validation(config, validation_dir, buggy, build, configure) self.validation_src.configure() compilation_db = self.validation_src.export_compilation_db() self.validation_src.import_compilation_db(compilation_db) self.validation_src.initialize() frontend_dir = join(working_dir, "frontend") shutil.copytree(src, frontend_dir, symlinks=True) self.frontend_src = Frontend(config, frontend_dir, buggy, build, configure) self.frontend_src.import_compilation_db(compilation_db) self.frontend_src.initialize() backend_dir = join(working_dir, "backend") shutil.copytree(src, backend_dir, symlinks=True) self.backend_src = Backend(config, backend_dir, buggy, build, configure) self.backend_src.import_compilation_db(compilation_db) self.backend_src.initialize() if golden is not None: golden_dir = join(working_dir, "golden") shutil.copytree(golden, golden_dir, symlinks=True) self.golden_src = Frontend(config, golden_dir, buggy, build, configure) self.golden_src.import_compilation_db(compilation_db) self.golden_src.initialize() else: self.golden_src = None self.dump = Dump(working_dir, asserts) self.trace = Trace(working_dir)
class Angelix: def __init__(self, working_dir, src, buggy, oracle, tests, golden, asserts, lines, build, configure, config): self.working_dir = working_dir self.config = config self.repair_test_suite = tests[:] self.validation_test_suite = tests[:] extracted = join(working_dir, 'extracted') os.mkdir(extracted) angelic_forest_file = join(working_dir, 'last-angelic-forest.json') tester = Tester(config, oracle, abspath(working_dir)) self.run_test = tester self.get_suspicious_groups = Localizer(config, lines) self.reduce = Reducer(config) if self.config['use_semfix_syn']: self.synthesize_fix = Semfix_Synthesizer(working_dir, config, extracted, angelic_forest_file) self.infer_spec = Semfix_Inferrer(working_dir, config, tester) else: self.synthesize_fix = Synthesizer(config, extracted, angelic_forest_file) self.infer_spec = Inferrer(config, tester, Load(working_dir)) self.instrument_for_localization = RepairableTransformer(config) self.instrument_for_inference = SuspiciousTransformer( config, extracted) self.apply_patch = FixInjector(config) # check build only options if self.config['build_validation_only']: validation_dir = join(working_dir, "validation") shutil.copytree(src, validation_dir, symlinks=True) self.validation_src = Validation(config, validation_dir, buggy, build, configure) self.validation_src.configure() compilation_db = self.validation_src.export_compilation_db() self.validation_src.import_compilation_db(compilation_db) sys.exit() if self.config['build_golden_only']: golden_dir = join(working_dir, "golden") shutil.copytree(golden, golden_dir, symlinks=True) self.golden_src = Frontend(config, golden_dir, buggy, build, configure) self.golden_src.configure() self.golden_src.build() sys.exit() if self.config['build_backend_only']: backend_dir = join(working_dir, "backend") shutil.copytree(src, backend_dir, symlinks=True) self.backend_src = Backend(config, backend_dir, buggy, build, configure) self.backend_src.configure() self.backend_src.build() sys.exit() validation_dir = join(working_dir, "validation") shutil.copytree(src, validation_dir, symlinks=True) self.validation_src = Validation(config, validation_dir, buggy, build, configure) self.validation_src.configure() compilation_db = self.validation_src.export_compilation_db() self.validation_src.import_compilation_db(compilation_db) frontend_dir = join(working_dir, "frontend") shutil.copytree(src, frontend_dir, symlinks=True) self.frontend_src = Frontend(config, frontend_dir, buggy, build, configure) self.frontend_src.import_compilation_db(compilation_db) backend_dir = join(working_dir, "backend") shutil.copytree(src, backend_dir, symlinks=True) self.backend_src = Backend(config, backend_dir, buggy, build, configure) self.backend_src.import_compilation_db(compilation_db) if golden is not None: golden_dir = join(working_dir, "golden") shutil.copytree(golden, golden_dir, symlinks=True) self.golden_src = Frontend(config, golden_dir, buggy, build, configure) self.golden_src.import_compilation_db(compilation_db) else: self.golden_src = None self.dump = Dump(working_dir, asserts) self.trace = Trace(working_dir) def evaluate(self, src): testing_start_time = time.time() positive = [] negative = [] for test in self.validation_test_suite: if self.run_test(src, test): positive.append(test) else: negative.append(test) # make sure if failing tests really fail if self.config['redundant_test']: negative_copy = negative[:] for test in negative_copy: if self.run_test(src, test): negative.remove(test) positive.append(test) testing_end_time = time.time() testing_elapsed = testing_end_time - testing_start_time statistics.data['time']['testing'] += testing_elapsed statistics.save() return positive, negative def generate_patch(self): positive, negative = self.evaluate(self.validation_src) self.frontend_src.configure() if config['build_before_instr']: self.frontend_src.build() self.instrument_for_localization(self.frontend_src) self.frontend_src.build() testing_start_time = time.time() if len(positive) > 0: logger.info('running positive tests for debugging') for test in positive: self.trace += test if test not in self.dump: self.dump += test _, instrumented = self.run_test(self.frontend_src, test, dump=self.dump[test], trace=self.trace[test], check_instrumented=True) if not instrumented: self.repair_test_suite.remove(test) else: _, instrumented = self.run_test(self.frontend_src, test, trace=self.trace[test], check_instrumented=True) if not instrumented: self.repair_test_suite.remove(test) golden_is_built = False excluded = [] if len(negative) > 0: logger.info('running negative tests for debugging') for test in negative: self.trace += test _, instrumented = self.run_test(self.frontend_src, test, trace=self.trace[test], check_instrumented=True) if not instrumented: self.repair_test_suite.remove(test) if test not in self.dump: if self.golden_src is None: logger.error( "golden version or assert file needed for test {}". format(test)) return None if not golden_is_built: self.golden_src.configure() self.golden_src.build() golden_is_built = True self.dump += test result = self.run_test(self.golden_src, test, dump=self.dump[test]) if not result: excluded.append(test) for test in excluded: if not self.config['mute_test_message']: logger.warning( 'excluding test {} because it fails in golden version'. format(test)) negative.remove(test) if test in self.repair_test_suite: self.repair_test_suite.remove(test) self.validation_test_suite.remove(test) testing_end_time = time.time() testing_elapsed = testing_end_time - testing_start_time statistics.data['time']['testing'] += testing_elapsed statistics.save() logger.info("repair test suite: {}".format(self.repair_test_suite)) logger.info("validation test suite: {}".format( self.validation_test_suite)) positive_traces = [(test, self.trace.parse(test)) for test in positive] negative_traces = [(test, self.trace.parse(test)) for test in negative] suspicious = self.get_suspicious_groups(self.validation_test_suite, positive_traces, negative_traces) if self.config['localize_only']: for idx, (group, score) in enumerate(suspicious): logger.info('group {}: {} ({})'.format(idx + 1, group, score)) exit(0) if len(suspicious) == 0: logger.warning('no suspicious expressions localized') repaired = len(negative) == 0 while not repaired and len(suspicious) > 0: if self.config['use_semfix_syn']: # prepare a clean directory shutil.rmtree(join(self.working_dir, 'semfix-syn-input'), ignore_errors='true') expressions = suspicious.pop(0) logger.info( 'considering suspicious expressions {}'.format(expressions)) current_repair_suite = self.reduce(self.repair_test_suite, positive_traces, negative_traces, expressions) self.backend_src.restore_buggy() self.backend_src.configure() if config['build_before_instr']: self.backend_src.build() self.instrument_for_inference(self.backend_src, expressions) self.backend_src.build() angelic_forest = dict() inference_failed = False for test in current_repair_suite: try: angelic_forest[test] = self.infer_spec( self.backend_src, test, self.dump[test], self.frontend_src) if len(angelic_forest[test]) == 0: if test in positive: logger.warning( 'angelic forest for positive test {} not found' .format(test)) current_repair_suite.remove(test) del angelic_forest[test] continue inference_failed = True break except InferenceError: logger.warning('inference failed (error was raised)') inference_failed = True break except NoSmtError: if test in positive: current_repair_suite.remove(test) continue inference_failed = True break if inference_failed: continue initial_fix = self.synthesize_fix(angelic_forest) if initial_fix is None: logger.info('cannot synthesize fix') continue logger.info('candidate fix synthesized') self.validation_src.restore_buggy() try: self.apply_patch(self.validation_src, initial_fix) except TransformationError: logger.info('cannot apply fix') continue self.validation_src.build() pos, neg = self.evaluate(self.validation_src) if not set(neg).isdisjoint(set(current_repair_suite)): not_repaired = list(set(current_repair_suite) & set(neg)) logger.warning( "generated invalid fix (tests {} not repaired)".format( not_repaired)) continue repaired = len(neg) == 0 neg = list(set(neg) & set(self.repair_test_suite)) current_positive, current_negative = pos, neg if len(current_negative) == 0 and not repaired: logger.warning("cannot repair using instrumented tests") continue negative_idx = 0 while not repaired: counterexample = current_negative[negative_idx] logger.info('counterexample test is {}'.format(counterexample)) current_repair_suite.append(counterexample) try: angelic_forest[counterexample] = self.infer_spec( self.backend_src, counterexample, self.dump[counterexample], self.frontend_src) except NoSmtError: logger.warning( "no smt file for test {}".format(counterexample)) negative_idx = negative_idx + 1 if len(current_negative) - negative_idx > 0: continue break if len(angelic_forest[counterexample]) == 0: break fix = self.synthesize_fix(angelic_forest) if fix is None: logger.info('cannot refine fix') break logger.info('refined fix is synthesized') self.validation_src.restore_buggy() self.apply_patch(self.validation_src, fix) self.validation_src.build() pos, neg = self.evaluate(self.validation_src) repaired = len(neg) == 0 neg = list(set(neg) & set(self.repair_test_suite)) current_positive, current_negative = pos, neg if not set(current_negative).isdisjoint( set(current_repair_suite)): not_repaired = list( set(current_repair_suite) & set(current_negative)) logger.warning( "generated invalid fix (tests {} not repaired)".format( not_repaired)) break negative_idx = 0 if not repaired: return None else: return self.validation_src.diff_buggy() def dump_outputs(self): self.frontend_src.configure() if config['build_before_instr']: self.frontend_src.build() self.instrument_for_localization(self.frontend_src) self.frontend_src.build() logger.info('running tests for dumping') for test in self.validation_test_suite: self.dump += test result = self.run_test(self.frontend_src, test, dump=self.dump[test]) if result: logger.info('test passed') else: logger.info('test failed') return self.dump.export() def synthesize_from(self, af_file): with open(af_file) as file: data = json.load(file) repair_suite = data.keys() expressions = set() for _, paths in data.items(): for path in paths: for value in path: expr = tuple(map(int, value['expression'].split('-'))) expressions.add(expr) # we need this to extract buggy expressions: self.backend_src.restore_buggy() self.backend_src.configure() if config['build_before_instr']: self.backend_src.build() self.instrument_for_inference(self.backend_src, list(expressions)) fix = self.synthesize_fix(af_file) if fix is None: logger.info('cannot synthesize fix') return None logger.info('fix is synthesized') self.validation_src.restore_buggy() self.apply_patch(self.validation_src, fix) self.validation_src.build() positive, negative = self.evaluate(self.validation_src) if not set(negative).isdisjoint(set(repair_suite)): not_repaired = list(set(repair_suite) & set(negative)) logger.warning( "generated invalid fix (tests {} not repaired)".format( not_repaired)) return None if len(negative) > 0: logger.info("tests {} fail".format(negative)) return None else: return self.validation_src.diff_buggy()
class Angelix: def __init__(self, working_dir, src, buggy, oracle, tests, golden, asserts, lines, build, configure, config): self.working_dir = working_dir self.config = config self.test_suite = tests extracted = join(working_dir, 'extracted') os.mkdir(extracted) angelic_forest_file = join(working_dir, 'last-angelic-forest.json') tester = Tester(config, oracle, abspath(working_dir)) self.run_test = tester self.get_suspicious_groups = Localizer(config, lines) self.reduce = Reducer(config) if self.config['use_semfix_syn']: self.synthesize_fix = Semfix_Synthesizer(working_dir, config, extracted, angelic_forest_file) self.infer_spec = Semfix_Inferrer(working_dir, config, tester) else: self.synthesize_fix = Synthesizer(working_dir, config, extracted, angelic_forest_file) self.infer_spec = Inferrer(config, tester) self.instrument_for_localization = RepairableTransformer(config) self.instrument_for_inference = SuspiciousTransformer(config, extracted) self.apply_patch = FixInjector(config) if not self.config['synthesis_only']: validation_dir = join(working_dir, "validation") shutil.copytree(src, validation_dir, symlinks=True) self.validation_src = Validation(config, validation_dir, buggy, build, configure) self.validation_src.configure() compilation_db = self.validation_src.export_compilation_db() self.validation_src.import_compilation_db(compilation_db) frontend_dir = join(working_dir, "frontend") shutil.copytree(src, frontend_dir, symlinks=True) self.frontend_src = Frontend(config, frontend_dir, buggy, build, configure) self.frontend_src.import_compilation_db(compilation_db) backend_dir = join(working_dir, "backend") shutil.copytree(src, backend_dir, symlinks=True) self.backend_src = Backend(config, backend_dir, buggy, build, configure) self.backend_src.import_compilation_db(compilation_db) if golden is not None: golden_dir = join(working_dir, "golden") shutil.copytree(golden, golden_dir, symlinks=True) self.golden_src = Frontend(config, golden_dir, buggy, build, configure) self.golden_src.import_compilation_db(compilation_db) else: self.golden_src = None self.dump = Dump(working_dir, asserts) self.trace = Trace(working_dir) def evaluate(self, src): positive = [] negative = [] for test in self.test_suite: if self.run_test(src, test): positive.append(test) else: negative.append(test) return positive, negative def generate_patch(self): positive, negative = self.evaluate(self.validation_src) self.frontend_src.configure() self.instrument_for_localization(self.frontend_src) self.frontend_src.build() if len(positive) > 0: logger.info('running positive tests for debugging') for test in positive: self.trace += test if test not in self.dump: self.dump += test self.run_test(self.frontend_src, test, dump=self.dump[test], trace=self.trace[test]) else: self.run_test(self.frontend_src, test, trace=self.trace[test]) golden_is_built = False excluded = [] if len(negative) > 0: logger.info('running negative tests for debugging') for test in negative: self.trace += test self.run_test(self.frontend_src, test, trace=self.trace[test]) if test not in self.dump: if self.golden_src is None: logger.error("golden version or assert file needed for test {}".format(test)) return None if not golden_is_built: self.golden_src.configure() self.golden_src.build() golden_is_built = True self.dump += test result = self.run_test(self.golden_src, test, dump=self.dump[test]) if not result: excluded.append(test) for test in excluded: logger.warning('excluding test {} because it fails in golden version'.format(test)) negative.remove(test) self.test_suite.remove(test) positive_traces = [(test, self.trace.parse(test)) for test in positive] negative_traces = [(test, self.trace.parse(test)) for test in negative] suspicious = self.get_suspicious_groups(positive_traces, negative_traces) if len(suspicious) == 0: logger.warning('no suspicious expressions localized') while len(negative) > 0 and len(suspicious) > 0: if self.config['use_semfix_syn']: # prepare a clean directory shutil.rmtree(join(self.working_dir, 'semfix-syn-input'), ignore_errors='true') expressions = suspicious.pop(0) logger.info('considering suspicious expressions {}'.format(expressions)) repair_suite = self.reduce(positive_traces, negative_traces, expressions) self.backend_src.restore_buggy() self.backend_src.configure() self.instrument_for_inference(self.backend_src, expressions) self.backend_src.build() angelic_forest = dict() inference_failed = False for test in repair_suite: try: angelic_forest[test] = self.infer_spec(self.backend_src, test, self.dump[test]) if len(angelic_forest[test]) == 0: inference_failed = True break except InferenceError: inference_failed = True break if inference_failed: continue initial_fix = self.synthesize_fix(angelic_forest) if initial_fix is None: logger.info('cannot synthesize fix') continue logger.info('candidate fix synthesized') self.validation_src.restore_buggy() self.apply_patch(self.validation_src, initial_fix) self.validation_src.build() pos, neg = self.evaluate(self.validation_src) if not set(neg).isdisjoint(set(repair_suite)): not_repaired = list(set(repair_suite) & set(neg)) logger.warning("generated invalid fix (tests {} not repaired)".format(not_repaired)) continue positive, negative = pos, neg while len(negative) > 0: counterexample = negative[0] logger.info('counterexample test is {}'.format(counterexample)) repair_suite.append(counterexample) angelic_forest[counterexample] = self.infer_spec(self.backend_src, counterexample, self.dump[counterexample]) if len(angelic_forest[counterexample]) == 0: break fix = self.synthesize_fix(angelic_forest) if fix is None: logger.info('cannot refine fix') break logger.info('refined fix is synthesized') self.validation_src.restore_buggy() self.apply_patch(self.validation_src, fix) self.validation_src.build() pos, neg = self.evaluate(self.validation_src) if not set(neg).isdisjoint(set(repair_suite)): not_repaired = list(set(repair_suite) & set(neg)) logger.warning("generated invalid fix (tests {} not repaired)".format(not_repaired)) break positive, negative = pos, neg if len(negative) > 0: logger.warning("tests {} not repaired".format(negative)) return None else: return self.validation_src.diff_buggy() def dump_outputs(self): self.frontend_src.configure() self.instrument_for_localization(self.frontend_src) self.frontend_src.build() logger.info('running tests for dumping') for test in self.test_suite: self.dump += test result = self.run_test(self.frontend_src, test, dump=self.dump[test]) if result: logger.info('test passed') else: logger.info('test failed') return self.dump.export() def synthesize_from(self, af_file): with open(af_file) as file: data = json.load(file) repair_suite = data.keys() expressions = set() for _, paths in data.items(): for path in paths: for value in path: expr = tuple(map(int, value['expression'].split('-'))) expressions.add(expr) if not config['binfix']: # we need this to extract buggy expressions: self.backend_src.restore_buggy() self.backend_src.configure() self.instrument_for_inference(self.backend_src, list(expressions)) fix = self.synthesize_fix(af_file) if fix is None: logger.info('cannot synthesize fix') return None logger.info('fix is synthesized') if config['synthesis_only']: return fix else: self.validation_src.restore_buggy() self.apply_patch(self.validation_src, fix) self.validation_src.build() positive, negative = self.evaluate(self.validation_src) if not set(negative).isdisjoint(set(repair_suite)): not_repaired = list(set(repair_suite) & set(negative)) logger.warning("generated invalid fix (tests {} not repaired)".format(not_repaired)) return None if len(negative) > 0: logger.info("tests {} fail".format(negative)) return None else: return self.validation_src.diff_buggy()
class Angelix: def __init__(self, working_dir, src, buggy, oracle, tests, golden, asserts, lines, build, configure, config): self.working_dir = working_dir self.config = config self.repair_test_suite = tests[:] self.validation_test_suite = tests[:] extracted = join(working_dir, 'extracted') os.mkdir(extracted) angelic_forest_file = join(working_dir, 'last-angelic-forest.json') tester = Tester(config, oracle, abspath(working_dir)) self.run_test = tester self.get_suspicious_groups = Localizer(config, lines) self.reduce = Reducer(config) if self.config['use_semfix_syn']: self.synthesize_fix = Semfix_Synthesizer(working_dir, config, extracted, angelic_forest_file) self.infer_spec = Semfix_Inferrer(working_dir, config, tester) else: self.synthesize_fix = Synthesizer(config, extracted, angelic_forest_file) self.infer_spec = Inferrer(config, tester, Load(working_dir)) self.instrument_for_localization = RepairableTransformer(config) self.instrument_for_inference = SuspiciousTransformer(config, extracted) self.apply_patch = FixInjector(config) validation_dir = join(working_dir, "validation") shutil.copytree(src, validation_dir, symlinks=True) self.validation_src = Validation(config, validation_dir, buggy, build, configure) self.validation_src.configure() compilation_db = self.validation_src.export_compilation_db() self.validation_src.import_compilation_db(compilation_db) self.validation_src.initialize() frontend_dir = join(working_dir, "frontend") shutil.copytree(src, frontend_dir, symlinks=True) self.frontend_src = Frontend(config, frontend_dir, buggy, build, configure) self.frontend_src.import_compilation_db(compilation_db) self.frontend_src.initialize() backend_dir = join(working_dir, "backend") shutil.copytree(src, backend_dir, symlinks=True) self.backend_src = Backend(config, backend_dir, buggy, build, configure) self.backend_src.import_compilation_db(compilation_db) self.backend_src.initialize() if golden is not None: golden_dir = join(working_dir, "golden") shutil.copytree(golden, golden_dir, symlinks=True) self.golden_src = Frontend(config, golden_dir, buggy, build, configure) self.golden_src.import_compilation_db(compilation_db) self.golden_src.initialize() else: self.golden_src = None self.dump = Dump(working_dir, asserts) self.trace = Trace(working_dir) def evaluate(self, src): testing_start_time = time.time() positive = [] negative = [] for test in self.validation_test_suite: if self.run_test(src, test): positive.append(test) else: negative.append(test) # make sure if failing tests really fail if self.config['redundant_test']: negative_copy = negative[:] for test in negative_copy: if self.run_test(src, test): negative.remove(test) positive.append(test) testing_end_time = time.time() testing_elapsed = testing_end_time - testing_start_time statistics.data['time']['testing'] += testing_elapsed statistics.save() return positive, negative def generate_patch(self): positive, negative = self.evaluate(self.validation_src) self.frontend_src.configure() if config['build_before_instr']: self.frontend_src.build() self.instrument_for_localization(self.frontend_src) self.frontend_src.build() testing_start_time = time.time() if len(positive) > 0: logger.info('running positive tests for debugging') for test in positive: self.trace += test if test not in self.dump: self.dump += test _, instrumented = self.run_test(self.frontend_src, test, dump=self.dump[test], trace=self.trace[test], check_instrumented=True) if not instrumented: self.repair_test_suite.remove(test) else: _, instrumented = self.run_test(self.frontend_src, test, trace=self.trace[test], check_instrumented=True) if not instrumented: self.repair_test_suite.remove(test) golden_is_built = False excluded = [] if len(negative) > 0: logger.info('running negative tests for debugging') for test in negative: self.trace += test _, instrumented = self.run_test(self.frontend_src, test, trace=self.trace[test], check_instrumented=True) if not instrumented: self.repair_test_suite.remove(test) if test not in self.dump: if self.golden_src is None: logger.error("golden version or assert file needed for test {}".format(test)) return [] if not golden_is_built: self.golden_src.configure() self.golden_src.build() golden_is_built = True self.dump += test result = self.run_test(self.golden_src, test, dump=self.dump[test]) if not result: excluded.append(test) for test in excluded: if not self.config['mute_test_message']: logger.warning('excluding test {} because it fails in golden version'.format(test)) negative.remove(test) if test in self.repair_test_suite: self.repair_test_suite.remove(test) self.validation_test_suite.remove(test) testing_end_time = time.time() testing_elapsed = testing_end_time - testing_start_time statistics.data['time']['testing'] += testing_elapsed statistics.save() logger.info("repair test suite: {}".format(self.repair_test_suite)) logger.info("validation test suite: {}".format(self.validation_test_suite)) positive_traces = [(test, self.trace.parse(test)) for test in positive] negative_traces = [(test, self.trace.parse(test)) for test in negative] suspicious = self.get_suspicious_groups(self.validation_test_suite, positive_traces, negative_traces) if self.config['localize_only']: for idx, (group, score) in enumerate(suspicious): logger.info('group {}: {} ({})'.format(idx+1, group, score)) exit(0) if len(suspicious) == 0: logger.warning('no suspicious expressions localized') repaired = len(negative) == 0 patches = [] while (config['generate_all'] or not repaired) and len(suspicious) > 0: if self.config['use_semfix_syn']: # prepare a clean directory shutil.rmtree(join(self.working_dir, 'semfix-syn-input'), ignore_errors='true') expressions = suspicious.pop(0) logger.info('considering suspicious expressions {}'.format(expressions)) current_repair_suite = self.reduce(self.repair_test_suite, positive_traces, negative_traces, expressions) self.backend_src.restore_buggy() self.backend_src.configure() if config['build_before_instr']: self.backend_src.build() self.instrument_for_inference(self.backend_src, expressions) self.backend_src.build() angelic_forest = dict() inference_failed = False for test in current_repair_suite: try: angelic_forest[test] = self.infer_spec(self.backend_src, test, self.dump[test], self.frontend_src) if len(angelic_forest[test]) == 0: if test in positive: logger.warning('angelic forest for positive test {} not found'.format(test)) current_repair_suite.remove(test) del angelic_forest[test] continue inference_failed = True break except InferenceError: logger.warning('inference failed (error was raised)') inference_failed = True break except NoSmtError: if test in positive: current_repair_suite.remove(test) continue inference_failed = True break if inference_failed: continue initial_fix = self.synthesize_fix(angelic_forest) if initial_fix is None: logger.info('cannot synthesize fix') continue logger.info('candidate fix synthesized') self.validation_src.restore_buggy() try: self.apply_patch(self.validation_src, initial_fix) except TransformationError: logger.info('cannot apply fix') continue self.validation_src.build() pos, neg = self.evaluate(self.validation_src) if not set(neg).isdisjoint(set(current_repair_suite)): not_repaired = list(set(current_repair_suite) & set(neg)) logger.warning("generated invalid fix (tests {} not repaired)".format(not_repaired)) continue repaired = len(neg) == 0 if repaired: patches.append(self.validation_src.diff_buggy()) neg = list(set(neg) & set(self.repair_test_suite)) current_positive, current_negative = pos, neg if len(current_negative) == 0 and not repaired: logger.warning("cannot repair using instrumented tests") continue negative_idx = 0 while not repaired: counterexample = current_negative[negative_idx] logger.info('counterexample test is {}'.format(counterexample)) current_repair_suite.append(counterexample) try: angelic_forest[counterexample] = self.infer_spec(self.backend_src, counterexample, self.dump[counterexample], self.frontend_src) except NoSmtError: logger.warning("no smt file for test {}".format(counterexample)) negative_idx = negative_idx + 1 if len(current_negative) - negative_idx > 0: continue break if len(angelic_forest[counterexample]) == 0: break fix = self.synthesize_fix(angelic_forest) if fix is None: logger.info('cannot refine fix') break logger.info('refined fix is synthesized') self.validation_src.restore_buggy() self.apply_patch(self.validation_src, fix) self.validation_src.build() pos, neg = self.evaluate(self.validation_src) repaired = len(neg) == 0 if repaired: patches.append(self.validation_src.diff_buggy()) neg = list(set(neg) & set(self.repair_test_suite)) current_positive, current_negative = pos, neg if not set(current_negative).isdisjoint(set(current_repair_suite)): not_repaired = list(set(current_repair_suite) & set(current_negative)) logger.warning("generated invalid fix (tests {} not repaired)".format(not_repaired)) break negative_idx = 0 return patches def dump_outputs(self): self.frontend_src.configure() if config['build_before_instr']: self.frontend_src.build() self.instrument_for_localization(self.frontend_src) self.frontend_src.build() logger.info('running tests for dumping') for test in self.validation_test_suite: self.dump += test result = self.run_test(self.frontend_src, test, dump=self.dump[test]) if result: logger.info('test passed') else: logger.info('test failed') return self.dump.export() def synthesize_from(self, af_file): with open(af_file) as file: data = json.load(file) repair_suite = data.keys() expressions = set() for _, paths in data.items(): for path in paths: for value in path: expr = tuple(map(int, value['expression'].split('-'))) expressions.add(expr) # we need this to extract buggy expressions: self.backend_src.restore_buggy() self.backend_src.configure() if config['build_before_instr']: self.backend_src.build() self.instrument_for_inference(self.backend_src, list(expressions)) fix = self.synthesize_fix(af_file) if fix is None: logger.info('cannot synthesize fix') return [] logger.info('fix is synthesized') self.validation_src.restore_buggy() self.apply_patch(self.validation_src, fix) self.validation_src.build() positive, negative = self.evaluate(self.validation_src) if not set(negative).isdisjoint(set(repair_suite)): not_repaired = list(set(repair_suite) & set(negative)) logger.warning("generated invalid fix (tests {} not repaired)".format(not_repaired)) return [] if len(negative) > 0: logger.info("tests {} fail".format(negative)) return [] else: return [self.validation_src.diff_buggy()]