def test_basic_no_prune(self): inp1 = MockConnectToControllers(fingerprint="a") inp2 = MockInputEvent(fingerprint="b") int1 = MockInternalEvent(fingerprint="c") inp3 = MockInputEvent(fingerprint="d") events = [inp1, inp2, int1, inp3] def fake_find_internal_events(replay_dag, inject_input, wait_time): if inject_input == inp1: return [] elif inject_input == inp2: return [int1] elif inject_input == inp3: return [] else: raise AssertionError("Unexpected event sequence queried: %s" % replay_dag.events) # first, prefix peeker self.prefix_peeker.find_internal_events = fake_find_internal_events new_dag = self.prefix_peeker.peek(EventDag(events)) self.assertEquals(events, new_dag.events) # next, snapshot peeker # Hack alert! throw away first two args def snapshotter_fake_find_internal_events(s, c, dag_interval, inject_input, wait_time): return (fake_find_internal_events(dag_interval, inject_input, wait_time), self.mock_snapshotter) self.snapshot_peeker.find_internal_events = snapshotter_fake_find_internal_events new_dag = self.snapshot_peeker.peek(EventDag(events)) self.assertEquals(events, new_dag.events)
def test_basic_noop(self): """ test_basic_noop: running on a dag with no internal events returns the same dag """ events = [MockConnectToControllers(fingerprint=("class", 0))] + [ MockInputEvent(fingerprint=("class", f)) for f in range(1, 7) ] new_dag = self.prefix_peeker.peek(EventDag(events)) self.assertEquals(events, new_dag.events) new_dag = self.snapshot_peeker.peek(EventDag(events)) self.assertEquals(events, new_dag.events)
def __init__(self, simulation_cfg, superlog_path_or_dag, invariant_check=InvariantChecker.check_correspondence, transform_dag=None, end_wait_seconds=0.5, mcs_trace_path=None, extra_log=None, dump_runtime_stats=True, **kwargs): super(MCSFinder, self).__init__(simulation_cfg) self.sync_callback = None self._log = logging.getLogger("mcs_finder") if type(superlog_path_or_dag) == str: superlog_path = superlog_path_or_dag # The dag is codefied as a list, where each element has # a list of its dependents self.dag = EventDag(superlog_parser.parse_path(superlog_path)) else: self.dag = superlog_path_or_dag self.invariant_check = invariant_check self.transform_dag = transform_dag self.mcs_trace_path = mcs_trace_path self._extra_log = extra_log self._runtime_stats = None self.kwargs = kwargs self.end_wait_seconds = end_wait_seconds if dump_runtime_stats: self._runtime_stats = {}
def test_all(self): trace = [MockInputEvent(fingerprint=("class", f)) for f in range(1, 7)] dag = EventDag(trace) mcs = trace mcs_finder = MockMCSFinder(dag, mcs) result = mcs_finder.simulate() self.assertEqual(mcs, result)
def _track_new_internal_events(self, simulation, replayer): ''' Pre: simulation must have been run through a replay''' # We always check against internal events that were buffered at the end of # the original run (don't want to overcount) prev_buffered_receives = [] try: path = self.superlog_path + ".unacked" if not os.path.exists(path): log.warn("unacked internal events file from original run does not exist") return prev_buffered_receives = set([ e.pending_receive for e in [ f for f in EventDag(log_parser.parse_path(path)).events if type(f) == ControlMessageReceive ] ]) except ValueError as e: log.warn("unacked internal events is corrupt? %r" % e) return buffered_message_receipts = [] for p in simulation.openflow_buffer.pending_receives: if p not in prev_buffered_receives: buffered_message_receipts.append(repr(p)) else: prev_buffered_receives.remove(p) self._runtime_stats.record_buffered_message_receipts(buffered_message_receipts) new_internal_events = replayer.unexpected_state_changes + replayer.passed_unexpected_messages self._runtime_stats.record_new_internal_events(new_internal_events) self._runtime_stats.record_early_internal_events(replayer.early_state_changes) self._runtime_stats.record_timed_out_events(replayer.event_scheduler_stats.get_timeouts_dict()) self._runtime_stats.record_matched_events(replayer.event_scheduler_stats.get_matches_dict())
def __init__(self, simulation_cfg, superlog_path_or_dag, mock_controllers=True, input_logger=None, show_flow_tables_each_step=True): # TODO(cs): allow user to specify a round number where they want to stop, # otherwise play forward events without asking for interactive ack. Interactive.__init__(self, simulation_cfg, input_logger=input_logger) if mock_controllers is False: raise NotImplementedError("Live controllers not yet supported") self.mock_controllers = mock_controllers if type(superlog_path_or_dag) == str: superlog_path = superlog_path_or_dag # The dag is codefied as a list, where each element has # a list of its dependents. self.event_list = EventDag( log_parser.parse_path(superlog_path)).events else: self.event_list = superlog_path_or_dag.events # TODO(cs): support compute_interpolated_time whenever we support # non-mocked controllers. if self.mock_controllers: self.event_list = [ e for e in self.event_list if type(e) in InteractiveReplayer.supported_input_events or type(e) in InteractiveReplayer.supported_internal_events ] self.show_flow_tables_each_step = show_flow_tables_each_step
def __init__(self, simulation_cfg, superlog_path_or_dag, create_event_scheduler=None, **kwargs): ControlFlow.__init__(self, simulation_cfg) self.sync_callback = ReplaySyncCallback(self.get_interpolated_time) if type(superlog_path_or_dag) == str: superlog_path = superlog_path_or_dag # The dag is codefied as a list, where each element has # a list of its dependents self.dag = EventDag(superlog_parser.parse_path(superlog_path)) else: self.dag = superlog_path_or_dag # compute interpolate to time to be just before first event self.compute_interpolated_time(self.dag.events[0]) if create_event_scheduler: self.create_event_scheduler = create_event_scheduler else: self.create_event_scheduler = \ lambda simulation: EventScheduler(simulation, **{ k: v for k,v in kwargs.items() if k in EventScheduler.kwargs })
def test_basic_noop(self): """ test_basic_noop: running on a dag with no input events returns the same dag """ events = [ MockInputEvent(fingerprint=("class", f)) for f in range(1, 7) ] new_dag = self.peeker.peek(EventDag(events)) self.assertEquals(events, new_dag.events)
def _ignore_interposition(self): ''' Configure all interposition points to immediately pass through all internal events (possibly useful for replays affected by non-determinism) ''' filtered_events = [ e for e in self.dag.events if type(e) not in all_internal_events ] self.dag = EventDag(filtered_events) self.default_dp_permit = True self.dp_checker = AlwaysAllowDataplane(self.dag)
def all(self, mcs_finder_type): trace = [MockInputEvent(fingerprint=("class", f)) for f in range(1, 7)] trace.append(InvariantViolation(["violation"], persistent=True)) dag = EventDag(trace) mcs = trace[0:6] mcs_finder = mcs_finder_type(dag, mcs) try: os.makedirs(mcs_results_path) mcs_finder.init_results(mcs_results_path) mcs_finder.simulate() finally: shutil.rmtree(mcs_results_path) self.assertEqual(mcs, mcs_finder.dag.input_events)
def setUp(self): self.input_trace = [ MockInputEvent(fingerprint=("class", f)) for f in range(1, 7) ] self.dag = EventDag(self.input_trace) self.prefix_peeker = PrefixPeeker(None) IPAddressSpace._claimed_addresses.clear() ControllerConfig._controller_labels.clear() controller_cfg = ControllerConfig(start_cmd="sleep") simulation_cfg = SimulationConfig(controller_configs=[controller_cfg]) self.snapshot_peeker = SnapshotPeeker(simulation_cfg, default_dp_permit=True) self.snapshot_peeker.setup_simulation = lambda: (None, None) # N.B. this assumes that no internal events occur before the first input # event. self.snapshot_peeker.snapshot_and_play_forward = lambda *args: ([], None) self.snapshot_peeker.replay_interval = lambda *args: [] self.mock_snapshotter = MockSnapshotter()
def test_basic_prune(self): inp1 = MockInputEvent(fingerprint="a") inp2 = MockInputEvent(fingerprint="b") int1 = MockInternalEvent(fingerprint="c") inp3 = MockInputEvent(fingerprint="d") int2 = MockInternalEvent(fingerprint="e") all_events = [inp1, inp2, int1, inp3, int2] sub_events = [inp2, int1, inp3, int2] def fake_find_internal_events(replay_dag, wait_time): if replay_dag.events == [inp2]: return [] elif replay_dag.events == [inp2, inp3]: return [int2] else: raise AssertionError("Unexpected event sequence queried: %s" % replay_dag.events) self.peeker.find_internal_events = fake_find_internal_events new_dag = self.peeker.peek(EventDag(sub_events)) self.assertEquals([inp2, inp3, int2], new_dag.events)
def __init__(self, simulation_cfg, superlog_path_or_dag, ignore_trailing_flow_mod_deletes=True): # TODO(cs): allow the user to specify a stop point in the event dag, in # case the point they are interested in occurs before the end of the trace. self.simulation_cfg = simulation_cfg self.sync_callback = ReplaySyncCallback() if type(superlog_path_or_dag) == str: superlog_path = superlog_path_or_dag # The dag is codefied as a list, where each element has # a list of its dependents. self.event_list = EventDag( log_parser.parse_path(superlog_path)).events else: self.event_list = superlog_path_or_dag.events self.event_list = [ e for e in self.event_list if type(e) == ControlMessageReceive and type(e.get_packet()) != ofp_packet_out ] self.ignore_trailing_flow_mod_deletes = ignore_trailing_flow_mod_deletes
def __init__(self, simulation_cfg, superlog_path_or_dag, create_event_scheduler=None, print_buffers=True, wait_on_deterministic_values=False, default_dp_permit=False, fail_to_interactive=False, fail_to_interactive_on_persistent_violations=False, end_in_interactive=False, input_logger=None, allow_unexpected_messages=False, expected_message_round_window=3, pass_through_whitelisted_messages=False, delay_flow_mods=False, invariant_check_name="", bug_signature="", end_wait_seconds=0.5, transform_dag=None, pass_through_sends=False, fail_fast=False, check_interval=5, **kwargs): ''' - If invariant_check_name is not None, check it at the end for the execution - If bug_signature is not None, check whether this particular signature appears in the output of the invariant check at the end of the execution ''' ControlFlow.__init__(self, simulation_cfg) # Label uniquely identifying this replay, set in init_results() self.replay_id = "N/A" self.logical_time = 0 if wait_on_deterministic_values: self.sync_callback = ReplaySyncCallback() else: self.sync_callback = ReplaySyncCallback(self.get_interpolated_time) if type(superlog_path_or_dag) == str: superlog_path = superlog_path_or_dag # The dag is codefied as a list, where each element has # a list of its dependents self.dag = EventDag(log_parser.parse_path(superlog_path)) else: self.dag = superlog_path_or_dag if len(self.dag.events) == 0: raise ValueError("No events to replay!") self.default_dp_permit = default_dp_permit self.dp_checker = self._setup_dp_checker(default_dp_permit) if self.default_dp_permit: # Set DataplanePermit and DataplaneDrop to passive if permit is set # to default # TODO(cs): rather than setting these to passive (which still causes them to # be scheduled as regular events) should these just be removed from the # event dag altogether? for event in [e for e in self.dag.events if type(e) in dp_events]: event.passive = default_dp_permit self.print_buffers_flag = print_buffers self.fail_fast = fail_fast self.check_interval = check_interval # compute interpolate to time to be just before first event self.compute_interpolated_time(self.dag.events[0]) # String repesentations of unexpected state changes we've passed through, for # statistics purposes. self.unexpected_state_changes = [] self.early_state_changes = [] self.event_scheduler_stats = None self.end_in_interactive = end_in_interactive self.fail_to_interactive = fail_to_interactive self.fail_to_interactive_on_persistent_violations =\ fail_to_interactive_on_persistent_violations self._input_logger = input_logger self.allow_unexpected_messages = allow_unexpected_messages self.expected_message_round_window = expected_message_round_window self.pass_through_whitelisted_messages = pass_through_whitelisted_messages self.pass_through_sends = pass_through_sends # How many logical rounds to peek ahead when deciding if a message is # expected or not. # String repesentations of unexpected messages we've passed through, for # statistics purposes. self.passed_unexpected_messages = [] self.delay_flow_mods = delay_flow_mods self.end_wait_seconds = end_wait_seconds self.transform_dag = transform_dag self.bug_signature = bug_signature self.invariant_check_name = invariant_check_name self.invariant_check = None if self.invariant_check_name: if self.invariant_check_name not in name_to_invariant_check: raise ValueError( '''Unknown invariant check %s.\n''' '''Invariant check name must be defined in config.invariant_checks''', self.invariant_check_name) self.invariant_check = name_to_invariant_check[ self.invariant_check_name] if self.pass_through_whitelisted_messages: for event in self.dag.events: if hasattr(event, "ignore_whitelisted_packets"): event.ignore_whitelisted_packets = True if self.simulation_cfg.ignore_interposition: self._ignore_interposition() if create_event_scheduler: self.create_event_scheduler = create_event_scheduler else: if self.default_dp_permit: # Tell EventScheduler to use call into us whenever it wants to sleep or # select, so that we can keep forwarding dataplane packets. kwargs[ 'sleep_continuation'] = self._sleep_with_dataplane_passthrough kwargs[ 'select_continuation'] = self._select_with_dataplane_passthrough self.create_event_scheduler = \ lambda simulation: EventScheduler(simulation, **{ k: v for k,v in kwargs.items() if k in EventScheduler.kwargs }) unknown_kwargs = [ k for k in kwargs.keys() if k not in EventScheduler.kwargs ] if unknown_kwargs != []: raise ValueError("Unknown kwargs %s" % str(unknown_kwargs))
def setUp(self): self.input_trace = [ MockInputEvent(fingerprint=("class", f)) for f in range(1, 7) ] self.dag = EventDag(self.input_trace) self.peeker = Peeker(None)
def peek(self, dag): ''' If dag.events == [], returns immediately. If dag.events != [], assumes that isinstance(dag.events[0], ConnectToControllers) ''' if dag.input_events == []: return dag # post: len(dag.input_events) > 0 unsupported_types = [ProcessFlowMod, DataplaneDrop] if find(lambda e: type(e) in unsupported_types, dag.events) is not None: raise ValueError('''Delayed flow_mods not yet supported. Please ''' '''implement the TODO near the sleep() call in play_forward()''') if not isinstance(dag.events[0], ConnectToControllers): raise ValueError("First event must be ConnectToControllers") simulation = None try: # Inferred events includes input events and internal events inferred_events = [] (simulation, controller) = self.setup_simulation() # dag.input_events returns only prunable input events. We also need # include ConnectToControllers, which is a non-prunable input event. assert(dag.input_events[0] != dag.events[0]) snapshot_inputs = [dag.events[0]] + dag.input_events # The input event from the previous iteration, followed by the internal # events that were inferred from the previous peek(). # Since we assume that the first event is always ConnectToControllers, # there are never internal events (nor input events) preceding the # initial ConnectToControllers. # TODO(cs): might not want to contrain the caller's dag in this way. events_inferred_last_iteration = [] for inject_input_idx in xrange(0, len(snapshot_inputs)): inject_input = get_inject_input(inject_input_idx, snapshot_inputs) following_input = get_following_input(inject_input_idx, snapshot_inputs) expected_internal_events = \ get_expected_internal_events(inject_input, following_input, dag.events) log.debug("peek()'ing after input %d (%s)" % (inject_input_idx, str(inject_input))) inferred_events += events_inferred_last_iteration # We replay events_inferred_last_iteration (internal events preceding # inject_input), as well as a NOPInput with the same timestamp as inject_input # to ensure that the timing is the same before peek()ing. fencepost = NOPInput(time=inject_input.time, round=inject_input.round) dag_interval = EventDag(events_inferred_last_iteration + [fencepost]) if expected_internal_events == []: # Optimization: if no internal events occured between this input and the # next, no need to peek(), just bring the simulation's state forward up to # inject_input log.debug("Optimization: no expected internal events") Peeker.ambiguous_counts[0.0] += 1 self.replay_interval(simulation, dag_interval, 0) events_inferred_last_iteration = [inject_input] continue wait_time_seconds = self.get_wait_time_seconds(inject_input, following_input) (found_events, snapshotter) = self.find_internal_events(simulation, controller, dag_interval, inject_input, wait_time_seconds) events_inferred_last_iteration = [inject_input] events_inferred_last_iteration += match_and_filter(found_events, expected_internal_events) snapshotter.snapshot_proceed() # Don't forget the final iteration's output inferred_events += events_inferred_last_iteration finally: if simulation is not None: simulation.clean_up() return EventDag(inferred_events)
def peek(self, dag): ''' Infer which internal events are/aren't going to occur (for the entire sequence of input events in dag)''' # TODO(cs): optimization: write the prefix trie to a file, in case we want to run # FindMCS again? if len(dag.input_events) == 0: # Postcondition: input_events[-1] is not None # and self._events_list[-1] is not None return dag # dag.input_events returns only prunable input events. We also need # include ConnectToControllers, which is a non-prunable input event. if not isinstance(dag.events[0], ConnectToControllers): raise ValueError("First event must be ConnectToControllers") assert(dag.input_events[0] != dag.events[0]) input_events = [dag.events[0]] + dag.input_events # Initilize current_input_prefix to the longest_match prefix we've # inferred previously (or [] if this is an entirely new prefix) current_input_prefix = list(self._prefix_trie\ .longest_prefix(input_events, default=[])) log.debug("Current input prefix: %s" % str(current_input_prefix)) # The value is both internal events and input events (values of the trie) # leading up, but not including the next input following the tail of the # prefix. # Note that we assume that there are no internal events before the first # input event (i.e. we assume quiescence) inferred_events = list(self._prefix_trie\ .longest_prefix_value(input_events, default=[])) log.debug("Current inferred_events: %s" % str(inferred_events)) inject_input_idx = len(current_input_prefix) # While we still have inputs to inject while inject_input_idx < len(input_events): # The input we're about to inject inject_input = input_events[inject_input_idx] following_input = get_following_input(inject_input_idx, dag.input_events) # The input following the one we're going to inject log.debug("peek()'ing after input %d" % (inject_input_idx)) expected_internal_events = \ get_expected_internal_events(inject_input, following_input, dag.events) # Optimization: if no internal events occured between this input and the # next, no need to peek() if expected_internal_events == []: log.debug("Optimization: no expected internal events") newly_inferred_events = [] Peeker.ambiguous_counts[0.0] += 1 else: wait_time_seconds = self.get_wait_time_seconds(inject_input, following_input) # We inject a NOPInput with the same timestamp as inject_input # to ensure that the timing is the same before peek()ing, then replay # inject_input in play_forward() fencepost = NOPInput(time=inject_input.time, round=inject_input.round) replay_dag = EventDag(inferred_events + [ fencepost ]) found_events = self.find_internal_events(replay_dag, inject_input, wait_time_seconds) newly_inferred_events = match_and_filter(found_events, expected_internal_events) (current_input_prefix, inferred_events) = self._update_trie(current_input_prefix, inject_input, inferred_events, newly_inferred_events) inject_input_idx += 1 return EventDag(inferred_events)
def peek(self, dag): ''' Infer which internal events are/aren't going to occur, ''' # TODO(cs): optimization: write the prefix trie to a file, in case we want to run # FindMCS again? input_events = dag.input_events if len(input_events) == 0: # Postcondition: input_events[-1] is not None # and self._events_list[-1] is not None return dag # Initilize current_input_prefix to the longest_match prefix we've # inferred previously (or [] if this is an entirely new prefix) current_input_prefix = list(self._prefix_trie\ .longest_prefix(input_events, default=[])) log.debug("Current input prefix: %s" % str(current_input_prefix)) # The value is both internal events and input events (values of the trie) # leading up, but not including the next input following the tail of the # prefix. # Note that we assume that there are no internal events before the first # input event (i.e. we assume quiescence) inferred_events = list(self._prefix_trie\ .longest_prefix_value(input_events, default=[])) log.debug("Current inferred_events: %s" % str(inferred_events)) inject_input_idx = len(current_input_prefix) # While we still have inputs to inject while inject_input_idx < len(input_events): # The input we're about to inject inject_input = input_events[inject_input_idx] if inject_input_idx < len(input_events) - 1: # there is a following input_event following_input = input_events[inject_input_idx + 1] else: following_input = None # The input following the one we're going to inject log.debug("peek()'ing after input %d" % (inject_input_idx)) expected_internal_events = \ get_expected_internal_events(inject_input, following_input, dag.events) # Optimization: if no internal events occured between this input and the # next, no need to peek() if expected_internal_events == []: log.debug("Optimization: no expected internal events") newly_inferred_events = [] else: wait_time_seconds = self.get_wait_time_seconds(inject_input, following_input) replay_dag = EventDag(inferred_events + [ inject_input ]) found_events = self.find_internal_events(replay_dag, wait_time_seconds) newly_inferred_events = self.match_and_filter(found_events, expected_internal_events) (current_input_prefix, inferred_events) = self._update_trie(current_input_prefix, inject_input, inferred_events, newly_inferred_events) inject_input_idx += 1 return EventDag(inferred_events)
def parse_event_trace(trace_path): with open(trace_path) as input_file: return EventDag(parse(input_file))
def __init__(self, simulation_cfg, superlog_path_or_dag, invariant_check_name="", bug_signature="", transform_dag=None, mcs_trace_path=None, extra_log=None, runtime_stats_path=None, max_replays_per_subsequence=1, optimized_filtering=False, forker=LocalForker(), replay_final_trace=True, strict_assertion_checking=False, no_violation_verification_runs=None, **kwargs): ''' Note that you may pass in any keyword argument for Replayer to MCSFinder, except 'bug_signature' and 'invariant_check_name' ''' super(MCSFinder, self).__init__(simulation_cfg) # number of subsequences delta debugging has examined so far, for # distingushing runtime stats from different intermediate runs. self.subsequence_id = 0 self.mcs_log_tracker = None self.replay_log_tracker = None self.mcs_trace_path = mcs_trace_path self.sync_callback = None self._log = logging.getLogger("mcs_finder") if invariant_check_name == "": raise ValueError("Must specify invariant check") if invariant_check_name not in name_to_invariant_check: raise ValueError('''Unknown invariant check %s.\n''' '''Invariant check name must be defined in config.invariant_checks''', invariant_check_name) self.invariant_check_name = invariant_check_name self.invariant_check = name_to_invariant_check[invariant_check_name] if type(superlog_path_or_dag) == str: self.superlog_path = superlog_path_or_dag # The dag is codefied as a list, where each element has # a list of its dependents self.dag = EventDag(log_parser.parse_path(self.superlog_path)) else: self.dag = superlog_path_or_dag if self.simulation_cfg.ignore_interposition: filtered_events = [e for e in self.dag.events if type(e) not in all_internal_events] self.dag = EventDag(filtered_events) last_invariant_violation = self.dag.get_last_invariant_violation() if last_invariant_violation is None: raise ValueError("No invariant violation found in dag...") violations = last_invariant_violation.violations self.bug_signature = bug_signature if len(violations) > 1: while self.bug_signature == "": msg.interactive("\n------------------------------------------\n") msg.interactive("Multiple violations detected! Choose one for MCS Finding:") for i, violation in enumerate(violations): msg.interactive(" [%d] %s" % (i+1, violation)) violation_index = msg.raw_input("> ") if re.match("^\d+$", violation_index) is None or\ int(violation_index) < 1 or\ int(violation_index) > len(violations): msg.fail("Must provide an integer between 1 and %d!" % len(violations)) continue self.bug_signature = violations[int(violation_index)-1] if self.bug_signature == "": self.bug_signature = violations[0] msg.success("\nBug signature to match is %s. Proceeding with MCS finding!\n" % self.bug_signature) self.transform_dag = transform_dag # A second log with just our MCS progress log messages self._extra_log = extra_log self.kwargs = kwargs unknown_kwargs = [ k for k in kwargs.keys() if k not in Replayer.kwargs ] if unknown_kwargs != []: raise ValueError("Unknown kwargs %s" % str(unknown_kwargs)) if no_violation_verification_runs is not None: raise ValueError('''no_violation_verification_runs parameter is deprecated. ''' '''Use max_replays_per_subsequence.''') self.max_replays_per_subsequence = max_replays_per_subsequence self._runtime_stats = RuntimeStats(self.subsequence_id, runtime_stats_path=runtime_stats_path) # Whether to try alternate trace splitting techiques besides splitting by time. self.optimized_filtering = optimized_filtering self.forker = forker self.replay_final_trace = replay_final_trace self.strict_assertion_checking = strict_assertion_checking
def __init__(self, simulation_cfg, superlog_path_or_dag, invariant_check_name=None, transform_dag=None, end_wait_seconds=0.5, mcs_trace_path=None, extra_log=None, runtime_stats_path=None, wait_on_deterministic_values=False, no_violation_verification_runs=1, optimized_filtering=False, forker=LocalForker(), replay_final_trace=True, strict_assertion_checking=False, delay_flow_mods=False, **kwargs): super(MCSFinder, self).__init__(simulation_cfg) # number of subsequences delta debugging has examined so far, for # distingushing runtime stats from different intermediate runs. self.subsequence_id = 0 self.mcs_log_tracker = None self.replay_log_tracker = None self.mcs_trace_path = mcs_trace_path self.sync_callback = None self._log = logging.getLogger("mcs_finder") if invariant_check_name is None: raise ValueError("Must specify invariant check") if invariant_check_name not in name_to_invariant_check: raise ValueError( '''Unknown invariant check %s.\n''' '''Invariant check name must be defined in config.invariant_checks''', invariant_check_name) self.invariant_check = name_to_invariant_check[invariant_check_name] if type(superlog_path_or_dag) == str: self.superlog_path = superlog_path_or_dag # The dag is codefied as a list, where each element has # a list of its dependents self.dag = EventDag(log_parser.parse_path(self.superlog_path)) else: self.dag = superlog_path_or_dag last_invariant_violation = self.dag.get_last_invariant_violation() if last_invariant_violation is None: raise ValueError("No invariant violation found in dag...") violations = last_invariant_violation.violations if len(violations) > 1: self.bug_signature = None while self.bug_signature is None: msg.interactive( "\n------------------------------------------\n") msg.interactive( "Multiple violations detected! Choose one for MCS Finding:" ) for i, violation in enumerate(violations): msg.interactive(" [%d] %s" % (i + 1, violation)) violation_index = msg.raw_input("> ") if re.match("^\d+$", violation_index) is None or\ int(violation_index) < 1 or\ int(violation_index) > len(violations): msg.fail("Must provide an integer between 1 and %d!" % len(violations)) continue self.bug_signature = violations[int(violation_index) - 1] else: self.bug_signature = violations[0] msg.success( "\nBug signature to match is %s. Proceeding with MCS finding!\n" % self.bug_signature) self.transform_dag = transform_dag # A second log with just our MCS progress log messages self._extra_log = extra_log self.kwargs = kwargs self.end_wait_seconds = end_wait_seconds self.wait_on_deterministic_values = wait_on_deterministic_values # `no' means "number" self.no_violation_verification_runs = no_violation_verification_runs self._runtime_stats = RuntimeStats( self.subsequence_id, runtime_stats_path=runtime_stats_path) # Whether to try alternate trace splitting techiques besides splitting by time. self.optimized_filtering = optimized_filtering self.forker = forker self.replay_final_trace = replay_final_trace self.strict_assertion_checking = strict_assertion_checking self.delay_flow_mods = delay_flow_mods
def __init__(self, simulation_cfg, superlog_path_or_dag, create_event_scheduler=None, print_buffers=True, wait_on_deterministic_values=False, default_dp_permit=False, fail_to_interactive=False, fail_to_interactive_on_persistent_violations=False, end_in_interactive=False, input_logger=None, allow_unexpected_messages=False, pass_through_whitelisted_messages=True, delay_flow_mods=False, **kwargs): ControlFlow.__init__(self, simulation_cfg) if wait_on_deterministic_values: self.sync_callback = ReplaySyncCallback() else: self.sync_callback = ReplaySyncCallback(self.get_interpolated_time) if type(superlog_path_or_dag) == str: superlog_path = superlog_path_or_dag # The dag is codefied as a list, where each element has # a list of its dependents self.dag = EventDag(log_parser.parse_path(superlog_path)) else: self.dag = superlog_path_or_dag if len(self.dag.events) == 0: raise ValueError("No events to replay!") self.default_dp_permit = default_dp_permit # Set DataplanePermit and DataplaneDrop to passive if permit is set # to default for event in [ e for e in self.dag.events if type(e) in dp_events ]: event.passive = default_dp_permit self.dp_checker = None if default_dp_permit: self.dp_checker = DataplaneChecker(self.dag) self.print_buffers_flag = print_buffers # compute interpolate to time to be just before first event self.compute_interpolated_time(self.dag.events[0]) # String repesentations of unexpected state changes we've passed through, for # statistics purposes. self.unexpected_state_changes = [] self.early_state_changes = [] self.event_scheduler_stats = None self.end_in_interactive = end_in_interactive self.fail_to_interactive = fail_to_interactive self.fail_to_interactive_on_persistent_violations =\ fail_to_interactive_on_persistent_violations self._input_logger = input_logger self.allow_unexpected_messages = allow_unexpected_messages self.pass_through_whitelisted_messages = pass_through_whitelisted_messages # How many logical rounds to peek ahead when deciding if a message is # expected or not. self.expected_message_round_window = 3 # String repesentations of unexpected messages we've passed through, for # statistics purposes. self.passed_unexpected_messages = [] self.delay_flow_mods = delay_flow_mods if self.pass_through_whitelisted_messages: for event in self.dag.events: if hasattr(event, "ignore_whitelisted_packets"): event.ignore_whitelisted_packets = True if create_event_scheduler: self.create_event_scheduler = create_event_scheduler else: self.create_event_scheduler = \ lambda simulation: EventScheduler(simulation, **{ k: v for k,v in kwargs.items() if k in EventScheduler.kwargs })