/// <summary> /// Checks whether the <paramref name="formula" /> holds in all states of the <paramref name="model" />. /// </summary> /// <param name="createModel">The creator for the model that should be checked.</param> /// <param name="checkArgument">The argument passed to LtsMin that indicates which kind of check to perform.</param> private AnalysisResult <SafetySharpRuntimeModel> Check(CoupledExecutableModelCreator <SafetySharpRuntimeModel> createModel, string checkArgument) { try { using (var modelFile = new TemporaryFile("ssharp")) { File.WriteAllBytes(modelFile.FilePath, RuntimeModelSerializer.Save((ModelBase)createModel.SourceModel, createModel.StateFormulasToCheckInBaseModel)); try { CreateProcess(modelFile.FilePath, checkArgument); Run(); } catch (Win32Exception e) { throw new InvalidOperationException( "Failed to start LTSMin. Ensure that pins2lts-seq.exe can be found by either copying it next " + "to the executing assembly or by adding it to the system path. The required cygwin dependencies " + $"must also be available. The original error message was: {e.Message}", e); } var success = InterpretExitCode(_ltsMin.ExitCode); return(new AnalysisResult <SafetySharpRuntimeModel> { FormulaHolds = success }); } } finally { _ltsMin = null; } }
/// <summary> /// Creates a <see cref="RuntimeModel" /> instance from the model and the <paramref name="formulas" />. /// </summary> /// <param name="formulas">The formulas the model should be able to check.</param> internal RuntimeModel ToRuntimeModel(params Formula[] formulas) { Requires.NotNull(formulas, nameof(formulas)); var serializer = new RuntimeModelSerializer(); serializer.Serialize(this, formulas); return(serializer.Load()); }
/// <summary> /// Initizializes the model that should be analyzed. /// </summary> /// <param name="configuration">The configuration that should be used for the analyses.</param> /// <param name="hazard">The hazard that should be analyzed.</param> protected override void InitializeModel(AnalysisConfiguration configuration, Formula hazard) { var serializer = new RuntimeModelSerializer(); serializer.Serialize(Model, !hazard); Func<AnalysisModel> createModel = () => new ActivationMinimalExecutedModel(CreateModel(hazard), configuration.SuccessorCapacity); _invariantChecker = new InvariantChecker(createModel, OnOutputWritten, configuration, formulaIndex: 0); }
/// <summary> /// Checks whether the <paramref name="invariant" /> holds in all states of the <paramref name="model" />. /// </summary> /// <param name="model">The model that should be checked.</param> /// <param name="invariant">The invariant that should be checked.</param> public AnalysisResult CheckInvariant(ModelBase model, Formula invariant) { Requires.NotNull(model, nameof(model)); Requires.NotNull(invariant, nameof(invariant)); var serializer = new RuntimeModelSerializer(); serializer.Serialize(model, invariant); return(CheckInvariant(serializer.Load)); }
/// <summary> /// Gets a function that initializes the runtime model. /// </summary> private Func<RuntimeModel> CreateModel(Formula hazard) { var serializer = new RuntimeModelSerializer(); serializer.Serialize(Model, !hazard); return () => { var serializedData = serializer.LoadSerializedData(); return new RuntimeModel(serializedData, _stateHeaderBytes); }; }
/// <summary> /// Creates a <see cref="RuntimeModel" /> instance. /// </summary> private static Func <RuntimeModel> CreateRuntimeModel(RuntimeModelSerializer serializer, Fault[] faultTemplates) { return(() => { var serializedData = serializer.LoadSerializedData(); var faults = serializedData.ObjectTable.OfType <Fault>().Where(f => f.Identifier >= 0).OrderBy(f => f.Identifier).ToArray(); Requires.That(faults.Length == faultTemplates.Length, "Unexpected fault count."); for (var i = 0; i < faults.Length; ++i) { Requires.That(faults[i].Identifier == faultTemplates[i].Identifier, "Fault mismatch."); faults[i].Activation = faultTemplates[i].Activation; } return new RuntimeModel(serializedData); }); }
protected void GenerateStateSpace(params IComponent[] components) { var serializer = new RuntimeModelSerializer(); serializer.Serialize(TestModel.InitializeModel(components), new StateFormula(() => true)); var configuration = AnalysisConfiguration.Default; configuration.StateCapacity = 10000; configuration.StackCapacity = 10000; configuration.CpuCount = 1; var checker = new InvariantChecker(serializer.Load, s => Output.Log("{0}", s), configuration); _result = checker.Check(); CounterExample.ShouldBe(null); Output.Log($"States: {_result.StateCount}"); Output.Log($"Actual Transitions: {_result.TransitionCount}"); Output.Log($"Computed Transitions: {_result.ComputedTransitionCount}"); }
public static CoupledExecutableModelCreator <SafetySharpRuntimeModel> CreateExecutedModelCreator(ModelBase model, params Formula[] formulasToCheckInBaseModel) { Requires.NotNull(model, nameof(model)); Requires.NotNull(formulasToCheckInBaseModel, nameof(formulasToCheckInBaseModel)); Func <int, SafetySharpRuntimeModel> creatorFunc; // serializer.Serialize has potentially a side effect: Model binding. The model gets bound when it isn't // bound already. The lock ensures that this binding is only made by one thread because model.EnsureIsBound // is not reentrant. lock (model) { var serializer = new RuntimeModelSerializer(); model.EnsureIsBound(); // Bind the model explicitly. Otherwise serialize.Serializer makes it implicitly. serializer.Serialize(model, formulasToCheckInBaseModel); creatorFunc = serializer.Load; } var faults = model.Faults; return(new CoupledExecutableModelCreator <SafetySharpRuntimeModel>(creatorFunc, model, formulasToCheckInBaseModel, faults)); }
/// <summary> /// Computes the minimal critical sets for the <paramref name="hazard" />. /// </summary> /// <param name="model">The model the safety analysis should be conducted for.</param> /// <param name="hazard">The hazard the minimal critical sets should be computed for.</param> /// <param name="maxCardinality"> /// The maximum cardinality of the fault sets that should be checked. By default, all minimal /// critical fault sets are determined. /// </param> public Result ComputeMinimalCriticalSets(ModelBase model, Formula hazard, int maxCardinality = Int32.MaxValue) { Requires.NotNull(model, nameof(model)); Requires.NotNull(hazard, nameof(hazard)); _modelChecker.Configuration = Configuration; ConsoleHelpers.WriteLine("Running Deductive Cause Consequence Analysis."); var stopwatch = new Stopwatch(); stopwatch.Start(); var allFaults = model.Faults; FaultSet.CheckFaultCount(allFaults.Length); var forcedFaults = allFaults.Where(fault => fault.Activation == Activation.Forced).ToArray(); var suppressedFaults = allFaults.Where(fault => fault.Activation == Activation.Suppressed).ToArray(); var nondeterministicFaults = allFaults.Where(fault => fault.Activation == Activation.Nondeterministic).ToArray(); var suppressedSet = new FaultSet(suppressedFaults); var forcedSet = new FaultSet(forcedFaults); var isComplete = true; var safeSets = new HashSet <FaultSet>(); var criticalSets = new HashSet <FaultSet>(); var checkedSets = new HashSet <FaultSet>(); var counterExamples = new Dictionary <FaultSet, CounterExample>(); var exceptions = new Dictionary <FaultSet, Exception>(); // Store the serialized model to improve performance var serializer = new RuntimeModelSerializer(); serializer.Serialize(model, !hazard); // We check fault sets by increasing cardinality; this is, we check the empty set first, then // all singleton sets, then all sets with two elements, etc. We don't check sets that we // know are going to be critical sets due to monotonicity for (var cardinality = 0; cardinality <= allFaults.Length; ++cardinality) { // Generate the sets for the current level that we'll have to check var sets = GeneratePowerSetLevel(safeSets, criticalSets, cardinality, allFaults); // Clear the safe sets, we don't need the previous level to generate the next one safeSets.Clear(); // If there are no sets to check, we're done; this happens when there are so many critical sets // that this level does not contain any set that is not a super set of any of those critical sets if (sets.Count == 0) { break; } // Remove all sets that conflict with the forced or suppressed faults; these sets are considered to be safe. // If no sets remain, skip to the next level sets = RemoveInvalidSets(sets, suppressedSet, forcedSet, safeSets); if (sets.Count == 0) { continue; } // Abort if we've exceeded the maximum fault set cardinality; doing the check here allows us // to report the analysis as complete if the maximum cardinality is never reached if (cardinality > maxCardinality) { isComplete = false; break; } if (cardinality == 0) { ConsoleHelpers.WriteLine("Checking the empty fault set..."); } else { ConsoleHelpers.WriteLine($"Checking {sets.Count} sets of cardinality {cardinality}..."); } // We have to check each set; if one of them is a critical set, it has no effect on the other // sets we have to check foreach (var set in sets) { // Enable or disable the faults that the set represents set.SetActivation(nondeterministicFaults); // If there was a counter example, the set is a critical set try { var result = _modelChecker.CheckInvariant(CreateRuntimeModel(serializer, allFaults)); if (!result.FormulaHolds) { ConsoleHelpers.WriteLine($" critical: {{ {set.ToString(allFaults)} }}", ConsoleColor.DarkRed); criticalSets.Add(set); } else { safeSets.Add(set); } checkedSets.Add(set); if (result.CounterExample != null) { counterExamples.Add(set, result.CounterExample); } } catch (AnalysisException e) { ConsoleHelpers.WriteLine($" critical: {{ {set.ToString(allFaults)} }} [exception thrown]", ConsoleColor.DarkRed); checkedSets.Add(set); criticalSets.Add(set); exceptions.Add(set, e.InnerException); if (e.CounterExample != null) { counterExamples.Add(set, e.CounterExample); } } } } // Reset the nondeterministic faults so as to not influence subsequent analyses foreach (var fault in nondeterministicFaults) { fault.Activation = Activation.Nondeterministic; } return(new Result( model, isComplete, criticalSets, checkedSets, allFaults, suppressedFaults, forcedFaults, counterExamples, exceptions, stopwatch.Elapsed)); }
/// <summary> /// Creates a <see cref="RuntimeModel" /> instance from the <paramref name="model" /> and the <paramref name="formulas" />. /// </summary> /// <param name="model">The model the runtime model should be created for.</param> /// <param name="formulas">The formulas the model should be able to check.</param> internal static RuntimeModel Create(ModelBase model, params Formula[] formulas) { Requires.NotNull(formulas, nameof(formulas)); var serializer = new RuntimeModelSerializer(); serializer.Serialize(model, formulas); return serializer.Load(); }
/// <summary> /// Generates a <see cref="StateGraph" /> for the <paramref name="model" />. /// </summary> /// <param name="model">The model the state graph should be generated for.</param> /// <param name="stateFormulas">The state formulas that should be evaluated during state graph generation.</param> internal StateGraph GenerateStateGraph(ModelBase model, params Formula[] stateFormulas) { Requires.NotNull(model, nameof(model)); Requires.NotNull(stateFormulas, nameof(stateFormulas)); var serializer = new RuntimeModelSerializer(); serializer.Serialize(model, stateFormulas); return GenerateStateGraph((Func<RuntimeModel>)serializer.Load, stateFormulas); }
/// <summary> /// Checks whether the <paramref name="invariant" /> holds in all states of the <paramref name="model" />. /// </summary> /// <param name="model">The model that should be checked.</param> /// <param name="invariant">The invariant that should be checked.</param> public AnalysisResult CheckInvariant(ModelBase model, Formula invariant) { Requires.NotNull(model, nameof(model)); Requires.NotNull(invariant, nameof(invariant)); var serializer = new RuntimeModelSerializer(); serializer.Serialize(model, invariant); return CheckInvariant((Func<RuntimeModel>)serializer.Load, formulaIndex: 0); }
/// <summary> /// Loads a counter example from the <paramref name="file" />. /// </summary> /// <param name="file">The path to the file the counter example should be loaded from.</param> public static CounterExample Load(string file) { Requires.NotNullOrWhitespace(file, nameof(file)); using (var reader = new BinaryReader(File.OpenRead(file), Encoding.UTF8)) { if (reader.ReadInt32() != FileHeader) { throw new InvalidOperationException("The file does not contain a counter example that is compatible with this version of S#."); } var endsWithException = reader.ReadBoolean(); var serializedRuntimeModel = reader.ReadBytes(reader.ReadInt32()); var modelData = RuntimeModelSerializer.LoadSerializedData(serializedRuntimeModel); foreach (var fault in modelData.ObjectTable.OfType <Fault>()) { fault.Activation = (Activation)reader.ReadInt32(); } var runtimeModel = new RuntimeModel(modelData); var metadataStream = new MemoryStream(reader.ReadBytes(reader.ReadInt32())); var formatter = new BinaryFormatter(); var slotMetadata = new StateVectorLayout((StateSlotMetadata[])formatter.Deserialize(metadataStream)); var modelMetadata = runtimeModel.StateVectorLayout; var counterExample = new byte[reader.ReadInt32()][]; var slotCount = reader.ReadInt32(); if (slotCount != runtimeModel.StateVectorSize) { throw new InvalidOperationException( $"State slot count mismatch; the instantiated model requires {runtimeModel.StateVectorSize} state slots, " + $"whereas the counter example uses {slotCount} state slots."); } if (slotMetadata.SlotCount != modelMetadata.SlotCount) { throw new InvalidOperationException( $"State slot metadata count mismatch; the instantiated model has {modelMetadata.SlotCount} state slot metadata entries, " + $"whereas the counter example has {slotMetadata.SlotCount} state slot entries."); } for (var i = 0; i < slotMetadata.SlotCount; ++i) { if (modelMetadata[i] != slotMetadata[i]) { throw new StateVectorMismatchException(slotMetadata, modelMetadata); } } for (var i = 0; i < counterExample.Length; ++i) { counterExample[i] = new byte[runtimeModel.StateVectorSize]; for (var j = 0; j < runtimeModel.StateVectorSize; ++j) { counterExample[i][j] = reader.ReadByte(); } } var replayInfo = new int[reader.ReadInt32()][]; for (var i = 0; i < replayInfo.Length; ++i) { replayInfo[i] = new int[reader.ReadInt32()]; for (var j = 0; j < replayInfo[i].Length; ++j) { replayInfo[i][j] = reader.ReadInt32(); } } return(new CounterExample(runtimeModel, counterExample, replayInfo, endsWithException)); } }